SIP-272: Atomic Swaps - Dynamic Fees
Author | |
---|---|
Status | Draft |
Type | Governance |
Network | Ethereum |
Implementor | TBD |
Release | TBD |
Created | 2022-08-17 |
Simple Summary
This SIP proposes to incorporate dynamic fees into the atomic swap methodology.
Abstract
The dynamic fee model would be calibrated to fit the slippage function of any designated order book, hence higher fees would be levied as per the directional flow of the trade, resulting in more compensation to snx stakers when they take on larger positions:
$$ f(x) = u_0 + u_1 \sqrt{x} + u_2 x + u_3 x^2 $$
x
is the cumulative volume since the last trade reset blocku0
,u1
,u2
andu3
are calibrated off-chain for each synth
Important considerations:
- All trades will have to route through
sUSD
under this function - The frequency by which
cumulativeVolume
resets is configurable via SCCP, denotedatomicKBlocks
Motivation
The Synthetix protocol spot trading product is an attempt at being a market maker that offers trading through the use of an oracle reading. This is different from the approach offered by most other AMMs, which in contrast use the composition of liquidity pools in order to gauge a price and subsequently incorporate slippage into the trade in order to compensate LPs.
The usage of oracle sources to execute a fill results in the Synthetix protocol being a constant target of latency arbitrage aimed at picking off advantageous pricing. To compensate, fees have been increased to a point at which the product itself failing at its mission of offering a venue for cheap spot trading. Though latency is as much of a problem for amm's as it is for Synthetix, the difference lies in AMM fees from small random trades which more make up for the capital losses incurred by price adjustments; as well as the slippage incorporated ensuring these lp price adjustments are efficient.
From Synthetix's perspective, attempts at this tackling this problem have sought to maintain a no slippage policy at the protocol level. The Fee reclamation mechanism tackles the latency issue but breaks composability and is unattractive due to the extreme uncertainty on price execution that it causes. The second iteration was the original atomic mechanism, which uses uni-v3 as an additional lively oracle source. Although uniswap is a very effective low-latency oracle source, it still suffers from the impact of a 1 block delay. This opens up a small gap for oracle front-running.
Proof Of Concept
In order to showcase the model's ability to replicate order books of any form this figure is used, where we show the slippage incurred when executing orders of different sizes in different markets:
- The x axis represents the market order amount in dollar millions
- The y axis is the slippage in bp for different order book and models of order books. The delta is calculated by computing the delta between the best prevailing spot price at a given moment and the price that would be expected after executing a order of a certain size (shown in the x-axis).
- The
uni 5bp
line is the slippage incurred when trading on the Uniswap 5 bp ETH/USDC pool - The
cex
is the slippage incurred from trading on the binance ETH/USDT orderbook. - The dashed lines, represent the curve from the modeled that were fitted based on the
uni-5bp
andcex
order books
The proposed model can replicate any order book to a certain degree of precision with the help of the least squares optimization algorithm, an implementation demonstrating this capability is available in this repository.
It is important to mention that no slippage can be still configured into the trade, simply by setting the functional parameters (u0
, u1
, u2
, u3
) to zero, in appropriate situations (such as when dealing with stables).
The table below was generated by fitting the proposed model on the ETH/USDT binance order book and the uniswap ETH/USDC 5 bp pool:
Trade | cex_slippage | cex_model_slippage | uni_slippage | uni_model_slippage |
---|---|---|---|---|
$25,000 | 0.00 | 2.79 | 0.00 | -0.04 |
$525,000 | 4.39 | 3.59 | 6.72 | 6.74 |
$1,025,000 | 8.14 | 8.28 | 13.44 | 13.49 |
$1,525,000 | 12.77 | 13.08 | 20.21 | 20.28 |
$2,025,000 | 17.63 | 17.43 | 27.03 | 27.12 |
$2,525,000 | 17.63 | 21.11 | 33.90 | 34.02 |
$3,025,000 | 24.68 | 24.01 | 40.81 | 40.97 |
$3,525,000 | 26.94 | 26.06 | 47.79 | 47.99 |
$4,025,000 | 26.94 | 27.22 | 54.85 | 55.07 |
$4,525,000 | 26.94 | 27.45 | 61.99 | 62.21 |
$5,000,000 | 26.94 | 26.79 | 68.83 | 69.05 |
It shows how slippage can be replicated with simple equations and tweaked in order to provide better execution by diluting the respective coefficients of the equation.
- cex_model_slippage:
f(x) = 5 - 0.0178x^0.5 + 2.296e-5x -2.125e-12x^2
- uni_model_slippage:
f(x) = -0.42 +0.00036x^0.5 + 1.3e-5x + 1.295e-13x^2
Specification
The specification includes three fundamental variable structures:
- Cumulative Volume Structure
- Best Execution Functional Parameters
- Pricing Methodology
Cumulative Volume Structure
cumulativeVolume
is a nested structure that takes the following form:
{currencyKey:
{'blockNumber': blockNumber,
'cumulativeVolume':cumulativeVolume}}
Each time someone trades a synth, the first thing done is that the volume traded in sUSD
is computed using the price obtained with the atomicPrice
methodology denoted in SIP-158.
The following logic is then applied on updating the structure:
- In situations in which more than
atomicKBlocks
blocks have passed since the last time the structure is updated, then thecumulativeVolume
is first reset to zero for that synth, before being updated with the latest volume being traded. - otherwise, the
cumulativeVolume
is updated cumulatively for thesynth
being traded into or out from. In case the direction of the trade is into a synth from sUSD the number incorporated into structure is a positive number, otherwise it's a negative number. Hence trades within the sameatomicKBlocks
blocks in different direction cancel out the the slippage applied.
A through example of this process is provided under the Test Cases.
Best Execution Functional Parameters
functionalParameters
is a nested structure that takes the following form:
{currencyKey:
{'a': A,
'b': B,
'c': C,
'd': D}}
The parameters of the best execution function are saved in a structure and configurable via SCCP.
Pricing Methodology
In order for fees to be quasi-agnostic to the distribution of trade flow (i.e. batched trades or single trades), the integral of f(x), F(x) would be used, given by the following expression:
$$ F(x) = u_0 x + \frac{2}{3} u_1 x ^ {3/2} + \frac{1}{2} u_2 x^2 + \frac{1}{3} u_3 x^3 $$
The dynamic fee function, that is imposed for a trade that pushes cumulativeVolume
from x
to y
, is denoted by the function G(x,y)
below:
$$ G(x,y) = |2 \frac{ F(y) - F(x)} {y - x}| $$
Few important considerations with G(x,y) model:
- if a trade flows, denoted
z
, results in cumulative volume changing signs from a pre-trade cumulative volume ofx
, then dynamic fees are calculate withG(|z-x|,0)
- dynamic fees are constrained between 0 and
maxAtomicDynamicFee
, configurable via SCCP
Hence the exchange would take place with the following computation, assuming that cumulativeVolume
starts at zero:
- if the sourceCurrencyKey is
sUSD
:
SrcAmount * 1/P * (1- baseFee) * [1-G(x,0)] => DestAmount
- Otherwise:
SrcAmount * P * (1- baseFee) * [1-G(x,0)] => DestAmount
Technical Specification
Pending SC engineers input.
Test Cases
System Configurations:
kBlocks
: 2- Current cumulative volume:
{'sETH':
{'blockNumber': 10,
'cumulativeVolume':0}}
- Current
f(x)
parameters as configured here for mimicking uniswap-v3 - atomic fees set to 0
Trade Examples:
-
At blockNumber 10, Swap 1m$ sUSD to sETH with atomic price at 1,600$ per ETH
- f(|1m$|) = -4.25e-1 + 3.66-4 * sqrt(1m$) + 1.31e-5 * (1m$) + 1.3e-13 * (1m$^2) = 13.15 bp
- blockNumber in
cumulativeVolume
structure doesn't need updating (since currentBlockNumber less blockNumber in structure is thankBlocks
) - cumulativeVolume updated to 1m$
- User receives 1m$ / 1600$ * (1-13.5bp) = 624.18 sETH
-
At blockNumber 11, Swap 624.18 sETH to sUSD with atomic price at 1,600$ per ETH
- volumeInUSD = 624.18 * 1600 = 998,685$
- blockNumber in
cumulativeVolume
structure doesn't need updating - cumulativeVolume updated to 1m$ - 998,685$ = +1,315$
- f(|+1,315$|) = 0
- User receives 624.18 eth * 1600$ = 998,685 sUSD
-
At blockNumber 12, Swap 625 sETH to sUSD with atomic price at 1,600$ per ETH
- volumeInUSD = 625 * 1600 = 1m$
- blockNumber in
cumulativeVolume
is updated to 12 from 10 (since 12-10 = 2) cumulativeVolume
is reset to zero, then it's updated to -1m$- f(|-1m$|) = 13.15 bp
- User receives 625 * 1600 * (1-13.5bp) = 998,685 sUSD
-
At blockNumber 13, Swap 998,685$ sUSD back to sETH with atomic price at 1,600$ per ETH
- volumeInUSD = 998,685
- blockNumber in
cumulativeVolume
does not need an update cumulativeVolume
is updated to 998,685$ - 1m$ = -1,315$- f(|-1,315$|) = 0
- User receives 998,685$ / 1600 = 624.18 sETH
Configurable Values (Via SCCP)
a
,b
,c
andd
, can be configurable per synth, allowing the Spartan Council to fine-tune slippage based on the asset in mind.atomicKBlocks
would also be configurable per synth, specifying the number of block intervals at which slippage resetsmaxAtomicDynamicFee
Copyright
Copyright and related rights waived via CC0.