Skip to main content

QuadraticSwapFees

Git Source

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:

  1. 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 approximately F.
  1. 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 price\sqrt{price} at which we switch from quadratic fee to a more linear fee.

MAX_QUADRATIC_FEE_PERCENT+2NN\frac{MAX\_QUADRATIC\_FEE\_PERCENT + 2\cdot N}{N}
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

N100Q64N \cdot 100 \cdot Q64, 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, MAX_QUADRATIC_FEE_PERCENTQ64MAX\_QUADRATIC\_FEE\_PERCENT \cdot 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 on pastBy:
  • 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.
fϕ(Xin)={n2(X0XR)+XinXRif X0XR & Xin+2X0XR(MQ+ 2nn)MQ(2XRMQn(Xin+2(X0XR)))if X0XR & Xin+2X0XR(MQ+ 2nn)n(pastBy2XR)Xin if X0+Xin>XR & Xin+2X0XR(MQ+ 2nn)MQ(2XRMQnpastBy)pastByXin if X0+Xin>XR & Xin+2X0XR(MQ+ 2nn)MinBips otherwise X0+XinXR\begin{equation*} f_\phi(X_{in}) = \begin{cases} n \cdot \frac{2(X_{0}-X_{R})+X_{in}}{X_{R}} &\text{if } X_0 \ge X_R \text{ \& } X_{in} + 2 X_{0} \le X_{R}\left(\frac{M_Q +\ 2n}{n}\right) \\ M_Q \left( 2 - X_R \frac{M_Q}{n\left( X_{in} + 2 (X_0 - X_R) \right)} \right) &\text {if } X_0 \ge X_R \text{ \& } X_{in} + 2X_{0} \ge X_{R}\left(\frac{M_Q+\ 2n}{n}\right) \\ \frac{n \cdot \left( \frac{pastBy^2}{X_R} \right)}{X_{in}} &\text{ if } X_0 + X_{in} \gt X_R \text{ \& } X_{in} + 2 X_{0} \le X_{R}\left(\frac{M_Q +\ 2n}{n}\right) \\ \frac{M_Q \left( 2 - X_R \frac{M_Q}{n \cdot pastBy} \right) \cdot pastBy}{X_{in}} &\text{ if } X_0 + X_{in} \gt X_R \text{ \& } X_{in} + 2 X_{0} \ge X_{R}\left(\frac{M_Q +\ 2n}{n}\right) \\ MinBips &\text{ otherwise } X_0 + X_{in} \le X_R \\ \end{cases} \end{equation*}

where:

  • X_in is the input amount
  • X_0 is the currentReserve
  • X_R is the referenceReserve
  • M_Q is MAX_QUADRATIC_FEE_PERCENT
  • n is N
  • pastBy is the amount by which the price has moved past the referenceReserve
  • MinBips is MIN_FEE_Q64
function calculateSwapFeeBipsQ64(
uint256 input,
uint256 currentReserve,
uint256 referenceReserve
) internal pure returns (uint256 fee);

Parameters

NameTypeDescription
inputuint256The input amount of the asset (units of the asset).
currentReserveuint256The current reserve of the input asset in the pool.
referenceReserveuint256The start-of-block reference reserve for the input asset.

Returns

NameTypeDescription
feeuint256The swap fee in Q64 bips.

calculateQuadraticFeeBipsQ64

Calculates the quadratic fee in Q64 bips.

The quadratic fee model charges fees proportional to the square of price movement. Refer to calculateSwapFeeBipsQ64 NatSpec for the quadratic fee formula.

function calculateQuadraticFeeBipsQ64(
uint256 input,
uint256 currentReserve,
uint256 referenceReserve
) private pure returns (uint256 fee);

Parameters

NameTypeDescription
inputuint256The input amount of the asset (units of the asset).
currentReserveuint256The current reserve of the input asset in the pool.
referenceReserveuint256The start-of-block reference reserve for the input asset.

Returns

NameTypeDescription
feeuint256The quadratic fee in Q64 bips.

calculateLinearFeeBipsQ64

Calculates the linear fee in Q64 bips.

The linear fee model charges fees that grow linearly (not quadratically) for extreme price deviations. Refer to calculateSwapFeeBipsQ64 NatSpec for the linear fee formula.

function calculateLinearFeeBipsQ64(
uint256 input,
uint256 currentReserve,
uint256 referenceReserve
) private pure returns (uint256 fee);

Parameters

NameTypeDescription
inputuint256The input amount of the asset (units of the asset).
currentReserveuint256The current reserve of the input asset in the pool.
referenceReserveuint256The start-of-block reference reserve for the input asset.

Returns

NameTypeDescription
feeuint256The linear fee in Q64 bips.