beforeTick and afterTick) and applying a fee based on the magnitude. The resulting fee we apply is a mixture of the tick difference, recent swaps on the pool, and safeguards to prevent bot manipulation.
User Controlled Configuration Variables
The following configuration is used to control the range and sensitivity of the dynamic fees. The fee unit is in Uniswap BPS, where1 = 1/100th of a basis point. A fee of 1% is represented by 10_000, and a fee of 30% by 300_000.
Dynamic Fee Calculation
The dynamic fee is calculated by the following equation:volatilityAccumulator is a value that is updated on each swap and is calculated by the process described below.
Volatility Accumulator
The volatility accumulator is our way of measuring the amount of volatility on the pool. It is a value that is updated on each swap and is affected by recent swaps on the pool under different time frames. We use two lagging tick values, two time periods, and a decayed version of a previous volatility accumulator to calculate a swap’s volatility accumulator.
The equation for the volatility accumulator is:
referenceTick and prevVR are updated based on snapshots of pool activity captured by the lagging ticks and time periods.
The two lagging tick values are:
referenceTick: When thereferenceTickFilterPeriodhas passed or if aresetPeriodhas been triggered with a failing reset condition, this tick value is updated to thebeforeTickof a swap. This tick value is kept the same until hitting one of these update conditions.resetTick: This tick is updated to thebeforeTickof a swap when thereferenceTickis updated, and is additionally updated when theresetPeriodhas passed and a swap’sbeforeTickis at leastresetTickFilterticks away from theresetTick(a passing reset condition).
referenceTickFilterPeriod: If the previous swap was less thanreferenceTickFilterPeriodseconds away, we use the recordedreferenceTickfor the dynamic fee calculation instead of thebeforeTickof the current swap. This prevents bots from avoiding the dynamic fee by cutting their swap into smaller swaps.resetPeriod: If areferenceTickhas not been updated for overresetPeriodseconds, we want to ensure that there is true volatility happening on the pool. The goal is to prevent bots from keeping the pool’s fee high by sending in micro swaps to avoid updating thereferenceTick. If theresetPeriodhas passed and thebeforeTick’s difference from the recordedresetTickis smaller than theresetTickFilter(a failing reset condition), we update both thereferenceTickandresetTickto thebeforeTickof the current swap to clear out the stored volatility. If theresetPeriodhas passed and thebeforeTick’s difference from the recordedresetTickis larger than theresetTickFilter(a passing reset condition), we only update theresetTickto thebeforeTickof the current swap and keep thereferenceTickthe same.
prevVR value. The prevVR is used to smooth volatility over a period of time and is set to a value when the referenceTickFilterPeriod has passed but the resetPeriod has not. The value is set to the last swap’s volatility accumulator multiplied by the decayFilterBps value. The prevVR is set to zero when the resetPeriod passes with a failed condition or if a resetPeriod amount of time has passed without a swap. The same prevVR is used as long as the referenceTick has not been updated.
Note: there is a slight drift between the simulated afterTick and the resulting afterTick. This is because the applied fee for the simulated swap is an estimate either reusing the previous swap fee or just the prevVR under some reset conditions. We deemed that this drift is acceptable for our intended outcomes.