QuadraticSwapFees
Author: Will
A library to calculate fees that grow with respect to price deviation from a reference point. This library relies on a reference reserve from the start of the block to determine total same-block price movement.
Fee model overview
The fee structure uses two models depending on the magnitude of price deviation:
- Quadratic fee model for moderate price changes:
- Used when price deviation is relatively small.
- Fee grows proportionally to the square of the price movement.
- If one swap would pay fee
F, splitting it into two equal swaps pays two fees that sum to approximatelyF.
- Linear fee model for extreme price changes:
- Used when price deviation exceeds the quadratic threshold.
- Fee grows linearly beyond the threshold point, capping at
MAX_QUADRATIC_FEE_PERCENT. - This keeps very large swaps from reaching a non-monotonic fee curve.
Why two models?
- Quadratic fees alone can become so high that larger swaps eventually receive less output than smaller swaps, breaking the expected monotonic behavior.
- Linear fees alone do not provide enough protection against moderate price manipulation.
Price movement behavior
- If the price moves away from the reference and then back toward it, the fee is minimal until the price crosses the starting reference price again.
- Only the net price deviation from the start-of-block reference is charged.
State Variables
MIN_FEE_Q64
Minimum fee is one tenth of a basis point.
uint256 public constant MIN_FEE_Q64 = 0x1999999999999999;
BIPS_Q64
10000 bips per 100 percent in Q64.
uint256 public constant BIPS_Q64 = 0x27100000000000000000;
MAX_QUADRATIC_FEE_PERCENT
Max percent fee growing at a quadratic rate. After this the growths slows down.
uint256 internal constant MAX_QUADRATIC_FEE_PERCENT = 40;
N
A scaler that controls how fast the fee grows, at 20, 9x price change will be a 40% fee.
uint256 internal constant N = 20;
RESERVE_MULTIPLIER
A reserve multiplier used to determine the boundary at which we switch from quadratic to linear fee.
uint256 private constant RESERVE_MULTIPLIER = 2;
LINEAR_START_REFERENCE_SCALER
the at which we switch from quadratic fee to a more linear fee.
uint256 private constant LINEAR_START_REFERENCE_SCALER = 4;
MAX_QUADRATIC_FEE_PERCENT_BIPS
the fee at LINEAR_START_REFERENCE_SCALER in bips
uint256 private constant MAX_QUADRATIC_FEE_PERCENT_BIPS = 4000;
N_TIMES_BIPS_Q64_PER_PERCENT
, or N times bips in one percent in Q64.
uint256 private constant N_TIMES_BIPS_Q64_PER_PERCENT = 0x7d00000000000000000;
TWO_Q64
2 times Q64 or Q65
uint256 private constant TWO_Q64 = 0x20000000000000000;
MAX_QUADRATIC_FEE_Q64
MAX_QUADRATIC_FEE_PERCENT in Q64, .
uint256 private constant MAX_QUADRATIC_FEE_Q64 = 0x280000000000000000;
Functions
calculateSwapFeeBipsQ64
Computes the swap fee based on the input amount and the pool's
currentReserve and referenceReserve for the input asset.
Control flow by scenario:
currentReserve >= referenceReserve(price already at/away from reference):- If
input + RESERVE_MULTIPLIER * currentReserve < referenceReserve * LINEAR_START_REFERENCE_SCALER: Use the quadratic fee model (closer to reference, pre-threshold). - Otherwise: Use the linear fee model (farther from reference, post-threshold).
currentReserve < referenceReserve(price at/moving back toward the start-of-block reference):- If
input + currentReserve <= referenceReserve: Does not cross the reference. Only the global minimum fee will apply at the end. - If
input + currentReserve > referenceReserve: Crosses the reference. The portion beyond the reference (pastBy) is charged using either the quadratic or linear model depending onpastBy: pastBy > RESERVE_MULTIPLIER * referenceReserve→ use linear fee model- otherwise → use quadratic fee model
The resulting fee for the beyond-reference portion is then weighted by
pastBy / input.