SIP-272: Atomic Swaps - Dynamic Fees

Author
StatusDraft
TypeGovernance
NetworkEthereum
ImplementorTBD
ReleaseTBD
Created2022-08-17

Simple Summary

This SIP proposes to incorporate dynamic fees into the atomic swap methodology.

Abstract

The dynamic fee model can 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. The calibrated dynamic model takes the following form:

$$ G(x,y) = 2 \frac {u_0 (|x|-|y|) + \frac{2}{3} u_1 (|x| ^ {3/2} - |y|^ {3/2} ) + \frac{1}{2} u_2 (|x|^2 - |y|^2) + \frac{1}{3} u_3 (|x|^3-|y|^3)} {|x|-|y|} $$

  • cumulative trade volume in USD is updated at each interaction, where trades into synth increase cumulativeVolume and out of synth lowers the amount
  • $x$ is the post-trade and $y$ the pre-trade cumulative volume in USD
  • In case cumulative volume flips signs, due to a significant trade in the opposing direction, then $G(x,0)$ is used to compute slippage
  • All trades will have to route through sUSD
  • The frequency by which cumulativeVolume resets is configurable via SCCP, denoted atomicKBlocks
  • $u_{0}$, $u_{1}$, $u_{2}$ and $u_{3}$ are calibrated off-chain for each synth, reference to repo

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. Hence, this sips aims at addressing this issue while giving the protocol ability to fine tune slippage based on the volatility of the synth.

Function Derivation

The function that would be used to fit an order book curve takes the following form:

$$ f(x) = \beta_0 + \beta_1 \sqrt x + \beta_2 x + \beta_3 x^2 $$

However, given the necessity to ensure that trade chunks are agnostic to volume flow (i.e. whether to use small trade transactions or a one-lump sum transaction), then the integral, $F(x)$ is used. $F(x)$ represents the area under the slippage curve and is not affected by the size of an individual trade. In order to compute the weighted average fee, $2\frac{F(x)}{x}$ is used which gives back the weighted average slippage from executing a trade with a volume of $x$ . Hence, deriving the dynamic fee incurred from executing a trade that pushes cumulative volume from $y$ to $x$ is obtained by the function $G(x,y)$ shown under the Abstract section.

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, calculated by computing the delta between best prevailing spot price and the average price expected from executing an order of a certain magnitude (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 modeled curves, fitted based on the uni-5bp and cex order books data.

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 a no slippage policy can be still configured into a specific synth, simply by setting the functional parameters (u0, u1, u2, u3) to zero.

The table below displays the slippage incurred when trading on the uniswap-5bp pool and on the binance ETH/USDT pool and the dynamic fees executed with the calibrated models that replicate those order books:

Trade cex cex_model uni uni_model
$25,000 0.00 3.82 0.00 0.26
$525,000 4.39 5.08 6.72 6.39
$1,025,000 8.14 8.05 13.44 13.14
$1,525,000 12.77 12.43 20.21 19.90
$2,025,000 17.63 16.92 27.03 26.69
$2,525,000 17.63 21.23 33.90 33.51
$3,025,000 24.68 25.21 40.81 40.36
$3,525,000 26.94 28.75 47.79 47.25
$4,025,000 26.94 31.82 54.85 54.19
$4,525,000 26.94 34.36 61.99 61.15
$5,000,000 26.94 36.26 68.83 67.81

Specification

The specification includes the following fundamental sections:

  • Cumulative Volume Structure
  • Functional Parameter Structure
  • Pricing Methodology
  • Dynamic Fee Bounds

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, with the price obtained using the directionality of the source currencyKey being sUSD and destination currencyKey the synth being traded into. 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 the cumulativeVolume is first reset to zero for that synth, before being updated with the latest volume being traded.
  • otherwise, the cumulativeVolume is updated cumulatively for the synth 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 same atomicKBlocks blocks in different direction cancel out the the dynamic fee applied.

Functional Parameters

functionalParameters is a nested structure that takes the following form:

{currencyKey: 
  {'u0': u0,
   'u1': u1,
   'u2': u2,
   'u3': u3}}

The parameters are saved in a structure and configurable via SCCP per currencyKey

Pricing Methodology

The exchange would take place with the following computation, assuming a cumulativeVolume y, having the same sign as x and a price P of the synth in USD terms:

  • if the sourceCurrencyKey is sUSD:

$$
SrcAmount * \frac{1}{P} * (1- baseFee) * [1-G(x,y)] => DestAmount $$

  • Otherwise:

$$ SrcAmount * P * (1- baseFee) * [1-G(x,y)] => DestAmount $$

Dynamic Fee Bounds

Dynamic fee are bounded between 0 and a variable specified via SCCP, denoted maxAtomicDynamicFee

Technical Specification

Pending SC engineers input.

Test Cases

System Configurations:

  • kBlocks: 2
  • Current cumulativeVolume structure:
{'sETH':
        {'blockNumber': 10,
        'cumulativeVolume':0}}
  • functionalParameters structure:
{currencyKey: 
  {'u0': u0,
   'u1': u1,
   'u2': u2,
   'u3': u3}}

$$ 2 \frac{ F(x)} {x} = -8.506 * 10^{-1} +4.883 * 10^{-4} \sqrt{x} +1.308 * 10^{-5}x +8.642 * 10^{-14}x^2 $$

  • atomic baseFee set to 0

Trade Examples Single Large Trade:

  • At blockNumber 10, Swap 1m sUSD to sETH with atomic price at 1,600$ per ETH and x=0

    1. blockNumber in cumulativeVolume structure is not updated
    2. dynamic fee is calculated by computing |2 * [F(1m)-F(0)] / (1m$ - 0)|
    • $$ 2 * F(1m) = -8.506 * 10^{-1} +4.883 * 10^{-4} \sqrt{1m} +1.308 * 10^{-5} * 1m +8.642 * 10^{-14} * 1m^2 = 12.804 bp$$
    • $$ 2 * F(0) = -8.506 * 10^{-1} +4.883 * 10^{-4} \sqrt{0} +1.308 * 10^{-5} * 0 +8.642 * 10^{-14} * 0^2 = -0.8506 bp$$
    • |12.804 bp - -0.85 bp| = 13.65 bp
    1. User receives 1m$ / 1600$ * (1-13.65bp) = 624.15 sETH
    2. cumulativeVolume in structure is updated to 1m sUSD
  • At blockNumber 11, Swap 300 sETH to sUSD with atomic price at 1,600$ per ETH

    1. volumeInUSD = 624.15 * 1600 = 480k$
    2. blockNumber in cumulativeVolume structure doesn't need updating
    3. dynamic fee is calculated by computing |2 * [F(480k)-F(1m)] / (480k - 1m)|
    • $$ 2 * F(480k) = 5.79 bp
    • $$ 2 * F(0) = -8.506 * 10^{-1} +4.883 * 10^{-4} \sqrt{0} +1.308 * 10^{-5} * 0 +8.642 * 10^{-14} * 0^2 = -0.8506 bp$$
    • |12.804 bp - -0.85 bp| = 13.65 bp
    1. User receives 624.18 eth * 1600$ = 998,685 sUSD
    2. cumulativeVolume updated to 1m$ - 998,685$ = +1,315$
  • At blockNumber 12, Swap 625 sETH to sUSD with atomic price at 1,600$ per ETH

    1. volumeInUSD = 625 * 1600 = 1m$
    2. blockNumber in cumulativeVolume is updated to 12 from 10 (since 12-10 = 2)
    3. cumulativeVolume is reset to zero, then it's updated to -1m$
    4. f(|-1m$|) = 13.15 bp
    5. 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

    1. volumeInUSD = 998,685
    2. blockNumber in cumulativeVolume does not need an update
    3. cumulativeVolume is updated to 998,685$ - 1m$ = -1,315$
    4. f(|-1,315$|) = 0
    5. User receives 998,685$ / 1600 = 624.18 sETH

Configurable Values (Via SCCP)

  • $u_{0}$ , $u_{1}$ ,$u_{2}$ and $u_{3}$, 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 resets
  • maxAtomicDynamicFee

Copyright and related rights waived via CC0.