owned this note changed 2 years ago
Linked with GitHub

Core Libraries Documentation

BaseSplitCodeFactory

Inherited by the Factory contract. Main purpose is to hold the Kyber Elastic v2 Pool creation code in a separate address, since its creation code is close to the bytecode size limit of 24kB.

Taken from Balancer Labs's solidity utils repo. The only modification made is the unchecked keyword for sol 0.8 compatibility.

getCreationCodeContracts()

Returns the 2 addresses where the creation code of the contract created by this factory is stored.

getCreationCode()

Returns the creation code of the contract this factory creates.


CodeDeployer

Taken from Balancer Labs's solidity utils repo. Imported and used by the BaseSplitCodeFactory contract to handle deployment.


FullMath

Taken from https://xn2-umb.com/21/muldiv. Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision. Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits.

mulDivFloor()

Returns (a * b / denominator) rounded down.

Input Type Explanation
a uint256 multiplicand
b uint256 multiplier
denominator uint256 divisor

mulDivCeiling()

Similar to mulDivFloor, but rounded up.


LinkedList

A doubly linked list to be used for tick management.

Struct: Data

Field Type Explanation
previous int24 previous tick
next int24 next tick

init()

Initializes the LinkedList with the lowestValue and highestValue, where

  • lowestValue.previous = lowestValue
  • lowestValue.next = highestValue
  • highestValue.previous = lowestValue
  • highestValue.next = highestValue
Field Type Explanation
self mapping(int24 => Data) A mapping of int24 values to the Data struct
lowestValue int24 lowest value
highestValue int24 highest value

insert()

Inserts a new value into the LinkedList, given an existing lower value. The new value to be inserted should not be an existing value. Also, the condition lowerValue < newValue < lowerValue.next should be satisfied.

Field Type Explanation
self mapping(int24 => Data) A mapping of int24 values to the Data struct
newValue int24 value to be inserted
lowerValue int24 highest existing value in the linked list that is < newValue

remove()

Removes an existing value from the LinkedList. Returns the next lowest value (existingValue.previous).

Note that no removal is performed if removedValue happens to be the lowestValue or highestValue passed in init().

Field Type Explanation
self mapping(int24 => Data) A mapping of int24 values to the Data struct
removedValue int24 value to be removed

LiqDeltaMath

Contains a function to assist with the addition of signed liquidityDelta to unsigned liquidity.

applyLiquidityDelta()

Adds or remove uint128 liquidityDelta to uint128 liquidity

Field Type Explanation
liquidity uint128 Liquidity to be adjusted
liquidityDelta int128 quantity change to be applied
isAddLiquidity bool true = add liquidity, false = remove liquidity

MathConstants

Contains constants commonly used by multiple files.


QtyDeltaMath

Contains functions for calculating token0 and token1 quantites from differences in prices or from burning reinvestment tokens

getQtysForInitialLockup()

Calculate the token0 and token1 quantities needed for unlocking the pool given an initial price and liquidity.

Input Field Type Explanation
initialSqrtP uint160 initial sqrt price raised by 2**96
liquidity uint128 initial liquidity. should be MIN_LIQUIDITY = 100000
Return Field Type Explanation
qty0 uint256 token0 quantity required
qty1 uint256 token1 quantity required

calcRequiredQty0()

Calculates the token0 quantity between 2 sqrt prices for a given liquidity quantity.

Note that the function assumes that upperSqrtP > lowerSqrtP.

Input

Field Type Explanation
lowerSqrtP uint160 the lower sqrt price
upperSqrtP uint128 the upper sqrt price
liquidity int128 liquidity quantity
isAddLiquidity bool true = add liquidity, false = remove liquidity

Output

Type Explanation
int256 token0 qty required for position with liquidity between the 2 sqrt prices

Generally, if the return value > 0, it will be transferred into the pool. Conversely, if the return value < 0, it will be transferred out of the pool.

calcRequiredQty1()

Calculates the token1 quantity between 2 sqrt prices for a given liquidity quantity.

Note that the function assumes that upperSqrtP > lowerSqrtP.

Input

Field Type Explanation
lowerSqrtP uint160 the lower sqrt price
upperSqrtP uint128 the upper sqrt price
liquidity int128 liquidity quantity
isAddLiquidity bool true = add liquidity, false = remove liquidity

Output

Type Explanation
int256 token0 qty required for position with liquidity between the 2 sqrt prices

Generally, if the return value > 0, it will be transferred into the pool. Conversely, if the return value < 0, it will be transferred out of the pool.

getQty0FromBurnRTokens()

Calculates the token0 quantity to be sent to the user for a given amount of reinvestment tokens to be burnt.

Input Field Type Explanation
sqrtP uint160 the current sqrt price
liquidity uint128 expected change in reinvestment liquidity due to the burning of reinvestment tokens

getQty1FromBurnRTokens()

Calculates the token1 quantity to be sent to the user for a given amount of reinvestment tokens to be burnt.

Input Field Type Explanation
sqrtP uint160 the current sqrt price
liquidity uint128 expected change in reinvestment liquidity due to the burning of reinvestment tokens

divCeiling()

Returns ceil(x / y). y should not be zero.


QuadMath

getSmallerRootOfQuadEqn()

Given a variant of the quadratic equation \(ax^2 - 2bx + c - 0\) where \(a\), \(b\) and \(c > 0\), calculate the smaller root via the quadratic formula.

Returns \(\frac{b - \sqrt{b^2 - ac}}{a}\)

Input Field Type
a uint256
b uint256
c uint256

sqrt()

Unchanged from Elastic v1. Calculates the square root of a value using the Babylonian method.


ReinvestmentMath

Contains a helper function to calculate reinvestment tokens to be minted given an increase in reinvestment liquidity.

calcRMintQty()

Given the difference between reinvestL and reinvestLLast, calculate how many reinvestment tokens are to be minted.
\(rMintQty = rTotalSupply * \frac{reinvestL - reinvestL_{Last}}{reinvestL_{Last}} * \frac{baseL}{baseL + reinvestL}\)

Input Field Type Explanation
reinvestL uint256 latest reinvestment liquidity value. Should be >= reinvestLLast
reinvestLLast uint256 reinvestmentLiquidityLast value
baseL uint256 active base liquidity
rTotalSupply uint256 total supply of reinvestment token

SafeCast

Contains methods for safely casting between different types.

toUint32()

Casts a uint256 to a uint32. Reverts on overflow.

toInt128()

Casts a uint128 to a int128. Reverts on overflow.

toUint128()

Casts a uint256 to a uint128. Reverts on overflow.

revToUint128()

Given int128 y, returns uint128 z = -y.

toUint160()

Casts a uint256 to a uint160. Reverts on overflow.

toInt256()

Casts a uint256 to a int256. Reverts on overflow.

revToInt256()

Cast a uint256 to a int256 and reverses the sign. Reverts on overflow.

revToUint256()

Given int256 y, returns uint256 z = -y.


SwapMath

Contains the logic needed for computing swap input / output amounts and fees. The primary function to look at is computeSwapStep, as it is where the bulk of the swap flow logic is in, and where calls to the other functions in the library are made.

computeSwapStep()

Computes the actual swap input / output amounts to be deducted or added, the swap fee to be collected and the resulting price.

Inputs

Field Type Explanation
liquidity uint256 active base liquidity + reinvestment liquidity
currentSqrtP uint160 current sqrt price
targetSqrtP uint160 sqrt price limit nextSqrtP can take
feeInBps uint256 swap fee in basis points
specifiedAmount int256 amount remaining to be used for the swap
isExactInput bool true if specifiedAmount refers to input amount, false if specifiedAmount refers to output amount
isToken0 bool true if specifiedAmount is in token0, false if specifiedAmount is in token1

Outputs

Field Type Explanation
usedAmount int256 actual amount to be used for the swap. >= 0 if isExactInput = true, <= 0 if isExactInput = false
returnedAmount int256 output qty (<= 0) to be accumulated if isExactInput = true, input qty (>= 0) if isExactInput = false
deltaL uint256 collected swap fee, to be incremented to reinvest liquidity
nextSqrtP uint160 new sqrt price after the computed swap step

Note

nextSqrtP should not exceed targetSqrtP.

calcReachAmount()

Calculates the amount needed to reach targetSqrtP from currentSqrtP. Note that currentSqrtP and targetSqrtP are casted from uint160 to uint256 as they are multiplied by TWO_BPS (20_000) or feeInBps.

The mathematical formulas are provided below for reference.

isExactInput isToken0 Formula
true true \(\frac{2*BPS*L(\sqrt{p_c} - \sqrt{p_n})}{\sqrt{p_c}(2*BPS*\sqrt{p_n} - fee\sqrt{p_c})}\) (>0)
true false \(\frac{2*BPS*\sqrt{p_c}*L(\sqrt{p_n} - \sqrt{p_c})}{(2*BPS*\sqrt{p_c} - fee\sqrt{p_n})}\) (>0)
false true \(-\frac{2*BPS*L(\sqrt{p_n} - \sqrt{p_c})}{\sqrt{p_c}(2*BPS*\sqrt{p_n} - fee\sqrt{p_c})}\) (<0)
false false \(-\frac{2*BPS*\sqrt{p_c}*L(\sqrt{p_c} - \sqrt{p_n})}{(2*BPS*\sqrt{p_c} - fee\sqrt{p_n})}\) (<0)

Note that while cases 1 and 3 and cases 2 and 4 are mathematically equivalent, the implementation differs by performing a double negation for the exact output cases. It takes the difference of \(\sqrt{p_n}\) and \(\sqrt{p_c}\) in the numerator (>0), then performing a second negation.

Input Field Type Formula Variable Explanation
liquidity uint256 \(L\) active base liquidity + reinvestment liquidity
currentSqrtP uint160 \(\sqrt{p_c}\) current sqrt price
targetSqrtP uint160 \(\sqrt{p_n}\) sqrt price limit nextSqrtP can take
feeInBps uint256 \(fee\) swap fee in basis points
isExactInput bool N.A. true / false if specified swap amount refers to input / output amount respectively
isToken0 bool N.A. true / false if specified swap amount is in token0 / token1 respectively

estimateIncrementalLiquidity()

Estimates deltaL, the swap fee to be collected based on amountSpecified. This is called only for the final swap step, where the next (temporary) tick will not be crossed.

In the case where exact input is specified, the formula is rather straightforward.

isToken0 Formula
true \(\frac{delta*fee*\sqrt{p_c}}{2*BPS}\)
false \(\frac{delta*fee}{2*BPS*\sqrt{p_c}}\)

In the case where exact ouput is specified, a quadratic equation has to be solved. The desired result is the smaller root of the quadratic equation.

isToken0 Formula
true \(fee*(\Delta{L})^2-2[(BPS-fee)*L-BPS*delta*\sqrt{p_c}]\Delta{L}+fee*L*delta*\sqrt{p_c}=0\)
false \(fee*(\Delta{L})^2-2[(BPS-fee)*L-\frac{BPS*delta}{\sqrt{p_c}}]\Delta{L}+\frac{fee*L*delta}{\sqrt{p_c}}=0\)
Input Field Type Formula Variable Explanation
absDelta uint256 \(delta\) \({|}\)usedAmount\({|}\), absolute value of usedAmount (actual amount used for swap)
liquidity uint256 \(L\) active base liquidity + reinvestment liquidity
currentSqrtP uint160 \(\sqrt{p_c}\) current sqrt price
feeInBps uint256 \(fee\) swap fee in basis points
isExactInput bool N.A. true / false if specified swap amount refers to input / output amount respectively
isToken0 bool N.A. true / false if specified swap amount is in token0 / token1 respectively

calcIncrementalLiquidity()

Calculates deltaL, the swap fee to be collected based on amountSpecified. This is called for an intermediate swap step, where the next (temporary) tick will be crossed.

The mathematical formulas are provided below for reference.

isExactInput isToken0 Formula
true true \(\sqrt{p_n}*(\frac{L}{\sqrt{p_c}}+|delta|)-L\)
true false \(\frac{(L*\sqrt{p_c})+|delta|}{\sqrt{p_n}}-L\)
false true \(\sqrt{p_n}*(\frac{L}{\sqrt{p_c}}-|delta|)-L\)
false false \(\frac{(L*\sqrt{p_c})-|delta|}{\sqrt{p_n}}-L\)
Input Field Type Formula Variable Explanation
absDelta uint256 |\(delta\)| \({|}\)usedAmount\({|}\), absolute value of usedAmount (actual amount used for swap)
liquidity uint256 \(L\) active base liquidity + reinvestment liquidity
currentSqrtP uint160 \(\sqrt{p_c}\) current sqrt price
nextSqrtP uint160 \(\sqrt{p_n}\) next sqrt price
isExactInput bool N.A. true / false if specified swap amount refers to input / output amount respectively
isToken0 bool N.A. true / false if specified swap amount is in token0 / token1 respectively

calcFinalPrice()

Calculates the sqrt price of the final swap step where the next (temporary) tick will not be crossed.

The mathematical formulas are provided below for reference.

isExactInput isToken0 Formula
true true \(\frac{(L+\Delta{L})\sqrt{p_c}}{L+|delta|\sqrt{p_c}}\)
true false \(\frac{L\sqrt{p_c}+|delta|}{L+\Delta{L}}\)
false true \(\frac{(L+\Delta{L})\sqrt{p_c}}{L-|delta|\sqrt{p_c}}\)
false false \(\frac{L\sqrt{p_c}-|delta|}{L+\Delta{L}}\)
Input Field Type Formula Variable Explanation
absDelta uint256 |\(delta\)| \({|}\)usedAmount\({|}\), absolute value of usedAmount (actual amount used for swap)
liquidity uint256 \(L\) active base liquidity + reinvestment liquidity
deltaL uint256 \(\Delta{L}\) collected swap fee
currentSqrtP uint160 \(\sqrt{p_c}\) current sqrt price
isExactInput bool N.A. true / false if specified swap amount refers to input / output amount respectively
isToken0 bool N.A. true / false if specified swap amount is in token0 / token1 respectively

calcReturnedAmount()

Calculates returnedAmount for the computeSwapStep function. Rounds down when isExactInput = true (calculating output < 0) so that we avoid sending too much. Conversely, rounds up when isExactInput = false to ensure sufficient input > 0 will be received.

The mathematical formulas are provided below for reference.

isToken0 = true

The formula is actually the same, with the difference being made to the operands to ensure the price difference is non-negative.

isExactInput Formula
true \(\Delta{L}\sqrt{p_n}-L(\sqrt{p_c}-\sqrt{p_n})\)
false \(\Delta{L}\sqrt{p_n}+L(\sqrt{p_n}-\sqrt{p_c})\)

isToken0 = false

\(\frac{L+\Delta{L}}{\sqrt{p_n}} - \frac{L}{\sqrt{p_c}}\)

Input Field Type Formula Variable Explanation
liquidity uint256 \(L\) active base liquidity + reinvestment liquidity
currentSqrtP uint160 \(\sqrt{p_c}\) current sqrt price
nextSqrtP uint160 \(\sqrt{p_n}\) next sqrt price
deltaL uint256 \(\Delta{L}\) collected swap fee
isExactInput bool N.A. true / false if specified swap amount refers to input / output amount respectively
isToken0 bool N.A. true / false if specified swap amount is in token0 / token1 respectively

TickMath

Contains functions for computing square root prices from ticks and vice versa. Adapted from Uniswap V3's TickMath library.

Constants

Field Type Value Explanation
MIN_TICK int24 -887272 Minimum possible tick = \({log_{1.0001}2^{-128}}\)
MAX_TICK int24 887272 Minimum possible tick = \({log_{1.0001}2^{128}}\)
MIN_SQRT_RATIO uint160 4295128739 getSqrtRatioAtTick(MIN_TICK)
MAX_SQRT_RATIO uint160 1461446703485210103287273052203988822378723970342 getSqrtRatioAtTick(MAX_TICK)

getSqrtRatioAtTick()

Given a int24 tick, calculates \({\sqrt{1.0001^{tick}} * 2^{96}}\).

getTickAtSqrtRatio()

Given a square root price ratio uint160 sqrtP, calculates the greatest tick such that getSqrtRatioAtTick(tick) <= sqrtP.

Note that MIN_SQRT_RATIO <= sqrtP <= MAX_SQRT_RATIO, otherwise the function will revert.

getMaxNumberTicks()

Used to calculate the maximum liquidity allowable per tick. This function calculates the maximum number of ticks that can be inserted into the LinkedList, given a tickDistance.

Field Type Explanation
_tickDistance int24 Ticks can only be initialized at multiples of this value.

Core Contracts Documentation

Factory

Handles deployment of Kyber Elastic v2 pools and where administrative configurations are held, such as the whitelisting of NFT position managers, and government fee settings.

parameters

Used by the Pool's constructor to fetch the parameters. The reason why they are not passed directly is for the automatic contract verification on Etherscan.

Field Type Explanation
factory address this contract's address
poolOracle address the pool oracle contract's address to store all observations
token0 address first pool token by address sort order
token1 address second pool token by address sort order
swapFeeBps uint16 fee to be collected upon every swap in the pool, in basis points
_tickDistance int24 Minimum number of ticks between initialized ticks

poolInitHash

Type Explanation
bytes32 keccak256 hash of the pool's creation code. This is accessed by periphery contracts for the deterministic computation of a pool's address

configMaster

Type Explanation
address The address that is able to modify different settings and parameters

whitelistDisabled

Type Explanation
bool If true, anyone can directly mint liquidity from the pool. Otherwise, only whitelisted addresses are able to do so

vestingPeriod

Type Explanation
uint32 The maximum time duration for which LP fees are proportionally burnt upon LP removals

feeAmountTickDistance()

Returns the tick distance for a specified fee. Once added, cannot be updated or removed.

Field Type Explanation
Input, swapFeeBps uint16 swap fee, in basis points
Output int24 configured tick distance

feeConfiguration()

Field Type Explanation
_feeTo address recipient of government fees
_governmentFeeBps uint16 current government fee charged in basis points. Taken out of swap fee

getPool()

Returns the pool address for a given pair of tokens and a swap fee. Note that the token order does not matter.

Input

Field Type Explanation
tokenA address contract address of either token0 or token1
tokenB address contract address of the other token
swapFeeBps uint16 swap fee, in basis points

Output

Field Type Explanation
pool address The pool address. Returns null address if it does not exist

createPool()

Creates a pool for the given two tokens and fee. The token order does not matter.

The call reverts under the following conditions:

  1. Pool already exists
  2. Invalid swap fee (tickDistance is zero)
  3. Invalid token arguments

Input

Field Type Explanation
tokenA address contract address of either token0 or token1
tokenB address contract address of the other token
swapFeeBps uint16 swap fee, in basis points

Output

Field Type Explanation
pool address created pool address

isWhitelistedNFTManager()

Checks if an address is a whitelisted NFT manager. Returns true if it is, false if it is not.

Field Type Explanation
sender address address to be checked

getWhitelistedNFTManagers()

Returns an array of addresses of all whitelisted NFT managers.

updateConfigMaster()

To update the address of configMaster. Can only be changed by configMaster.

Field Type Explanation
_configMaster address new config master address

enableWhitelist()

Sets whitelistDisabled to false, meaning, only whitelisted addresses are able to call the Pool's mint method. Can only be called by configMaster.

disableWhitelist()

Sets whitelistDisabled to true. Will allow anyone to call the Pool's mint method. Can only be called by configMaster.

addNFTManager()

Whitelists an NFT manager. Returns true if addition was successful, that is if it was not already present. Can only be called by configMaster.

Field Type Explanation
_nftManager address address to be whitelisted

removeNFTManager()

Removes a whitelisted NFT manager. Returns true if removal was successful, that is if it was not already present. Can only be called by configMaster.

Field Type Explanation
_nftManager address whitelisted address to be removed

updateVestingPeriod()

Updates the value of vestingPeriod. Can only be called by configMaster.

enableSwapFee()

Enables a fee amount with the given tickDistance. The value cannot be updated or removed after setting. Can only be called by configMaster.

Field Type Explanation
swapFeeBps uint16 swap fee, in basis points
tickDistance int24 desired tick distance

updateFeeConfiguration()

Updates the address receiving government fees and government fee to be charged (taken out of swap fee).

Field Type Explanation
feeTo address recipient of government fees
governmentFeeBps uint16 government fee charged in basis points

Pool

Primarily contains the implementation of actionable items by users, such as adding or removing liquidity, executing swaps or flash loans, and burning reinvestment tokens in exchange for fees collected.

unlockPool()

Provides initial liquidity and sets initial price for the pool. All other actions cannot be performed prior to the execution of this function.

Note that this function should also be called at most once.

Input

Field Type Explanation
initialSqrtP uint160 Initial sqrt price, multiplied by \(2^{96}\)
data bytes Data, if any, to be passed into the callback function

Output

Field Type Explanation
qty0 uint256 token0 quantity permanently locked in the pool
qty1 uint256 token1 quantity permanently locked in the pool

mint()

Adds liquidity to a specified recipient/tickLower/tickUpper position. Any token0 or token1 owed for the liquidity provision have to be paid for in the callback function.

Also sends reinvestment tokens (fees) to the recipient for any fees collected by the position. Reinvestment tokens have to be burnt via burnRTokens in exchange for token0 and token1.

Input

Field Type Explanation
recipient address address for which the added liquidity is credited to. Equivalent to position owner
tickLower int24 position's lower tick
tickUpper int24 position's upper tick
ticksPrevious int24[2] an array containing 2 values tickLowerPrevious and tickUpperPrevious which are expected to be the nearest initialized tick <= tickLower and tickUpper respectively
qty uint128 Liquidity quantity to mint
data bytes Data, if any, to be passed into the callback function

Output

Field Type Explanation
qty0 uint256 token0 quantity sent to the pool in exchange for specified liquidity quantity
qty1 uint256 token1 quantity sent to the pool in exchange for specified liquidity quantity
feeGrowthInside uint256 position's updated feeGrowthInside value

burn()

Removes liquidity from the caller. In other words, the caller is treated as the position owner.

Also sends reinvestment tokens (fees) to the caller for any fees collected by the position. Reinvestment tokens have to be burnt via burnRTokens in exchange for token0 and token1.

Input

Field Type Explanation
tickLower int24 position's lower tick
tickUpper int24 position's upper tick
qty uint128 Liquidity quantity to burn

Output

Field Type Explanation
qty0 uint256 token0 quantity sent to the caller
qty1 uint256 token1 quantity sent to the caller
feeGrowthInside uint256 position's updated feeGrowthInside value

burnRTokens()

Burns reinvestment tokens in exchange to receive the fees collected in token0 and token1.

Input

Field Type Explanation
qty uint256 Reinvestment token quantity to burn
bool isLogicalBurn false = burning tokens without receiving any fees in exchange. true = fees should be calculated and sent

The use-case for burning tokens whilst leaving the collected fees in the pool is the anti-snipping attack mechanism.

Output

Field Type Explanation
qty0 uint256 token0 quantity sent to the caller
qty1 uint256 token1 quantity sent to the caller

tweakPosZeroLiq()

Update the position data to sync the latest fee growth to Position Manager in order to update the latest data for reinvestment tokens of a position.

Input

Field Type Explanation
tickLower int24 The position's lower tick
tickUpper int24 The position's upper tick

Output

Field Type Explanation
feeGrowthInsideLast uint256 The position's updated feeGrowthInside value

swap()

Swap token0 -> token1, or vice versa. Note that swaps will either fully use up the specified swap quantity, or swap up to the specified price limit, depending on whichever condition is satisfied first.

Input

Field Type Explanation
recipient address address to receive the swap output
swapQty int256 swap quantity, which implicitly configures the swap as exact input (>0), or exact output (<0)
isToken0 bool whether the swapQty is specified in token0 (true) or token1 (false)
limitSqrtP uint160 sqrt price limit to reach
data bytes Data, if any, to be passed into the callback function
Note

To specify an unlimited price limit for a swap, use the following values.

isToken0 swapQty Value
true > 0 MIN_SQRT_RATIO + 1
true < 0 MAX_SQRT_RATIO - 1
false > 0 MAX_SQRT_RATIO - 1
false < 0 MIN_SQRT_RATIO + 1

Output

Field Type Explanation
qty0 int256 exact token0 qty sent to recipient if < 0. Minimally received quantity if > 0
qty1 int256 exact token1 qty sent to recipient if < 0. Minimally received quantity if > 0

flash()

Request for token0 and/or token1 and pay it back in the same transaction, plus a fee, in the callback. Fees collected are distributed to all rToken holders since no rTokens are minted from it.

Input

Field Type Explanation
recipient address address which will receive the token0 and token1 quantities
qty0 uint256 token0 quantity to be loaned to the recipient
qty1 uint256 token1 quantity to be loaned to the recipient
data bytes Data, if any, to be passed into the callback function

PoolStorage

Contains all variables and getter methods to be used by the Pool contract. Inherited by PoolTickState.

factory

Type Explanation
IFactory Canonical factory address fetched upon pool creation

poolOracle

Type Explanation
IPoolOracle Canonical pool oracle address fetched upon pool creation

token0

Type Explanation
IERC20 First of the two tokens of the pool, sorted by address

token1

Type Explanation
IERC20 Second of the two tokens of the pool, sorted by address

maxTickLiquidity

Type Explanation
uint128 Maximum gross liquidity that an initialized tick can have. This is to prevent overflow the pool's active base liquidity (uint128)

swapFeeBps

Type Explanation
uint16 fee to be charged for a swap in basis points

tickDistance

Type Explanation
int24 Ticks can only be initialized and used at multiples of this value

For instance, a tickDistance of 5 means ticks can be initialized every 5th tick, i.e., , -10, -5, 0, 5, 10,

ticks()

Retrieve information about a specified int24 tick.

Output Field Type Explanation
liquidityGross uint128 total liquidity amount from positions that uses this tick as a lower or upper tick
liquidityNet int128 how much liquidity changes when the pool tick crosses up this tick
feeGrowthOutside uint256 fee growth on the other side of the tick relative to the current tick.
Appendix-A
secondsPerLiquidityOutside uint128 seconds spent on the other side of the tick relative to the current tick.
Appendix-A

initializedTicks()

Returns the previous and next initialized ticks from the specified tick.

Note

If the specified tick is uninitialized, the returned values are zero.

Output Field Type Explanation
previous int24 Next initalized tick below the specified tick
next int24 Next initalized tick above the specified tick

getPositions()

Returns the information about a position.

Inputs

Field Type Explanation
owner address position owner
tickLower int24 position's lower tick
tickUpper int24 position's upper tick

Outputs

Field Type Explanation
liquidity uint128 position's liquidity amount
feeGrowthInsideLast uint256 last cached fee growth inside the position as of the last action performed

getPoolState()

Primarily returns the pool's current price and ticks, and whether it is locked.

Field Type Explanation
sqrtP uint160 current sqrt price, multiplied by \(2^{96}\)
currentTick int24 current tick that closely reflects sqrtP
nearestCurrentTick int24 nearest initialized tick that is <= currentTick
locked bool true if pool is locked, false otherwise

getLiquidityState()

Fetches the pool's liquidity values.

Field Type Explanation
baseL uint128 total liquidity provided by active positions
reinvestL uint128 liquidity is reinvested into the pool
reinvestLLast uint128 last cached value of reinvestL, used for calculating reinvestment token qty

getFeeGrowthGlobal()

Returns the all-time fee growth per unit of liquidity of the pool. More information can be found here.

getSecondsPerLiquidityData()

Field Type Explanation
secondsPerLiquidityGlobal uint128 all-time seconds per unit of liquidity of the pool. More information can be found here
lastUpdateTime uint32 timestamp of last performed updated to secondsPerLiquidityGlobal

getSecondsPerLiquidityInside()

Calculates and returns the active time per unit of liquidity until up to the current timestamp.

Input

Field Type Explanation
tickLower int24 a lower tick
tickUpper int24 an upper tick

Output

Field Type Explanation
secondsPerLiquidityInside uint128 active time between tickLower and tickUpper. Note that its value is multiplied by \(2^{96}\). More information can be found here

_initPoolStorage()

Used by the pool to initialize relevant pool variables.

_positionKey()

Calculates the key to the positions mapping. It is simply the keccak256 hash of a specified address and lower and upper ticks.


PoolTicksState

Inherits PoolStorage. Contains functions for updating a tick's, position's or pool's state.

_updatePosition()

Calls _updateTick on a position's lower and upper tick, calculates the fee growth inside between the 2 ticks and updates the position by calling _updatePositionData().

Returns the calculated reinvestment tokens to be minted for the position's accumulated fees.

_updateLiquidityAndCrossTick()

Called by the pool when crossing a tick. Updates the tick's outside values (read more here), and applies any change in liquidity to the pool's base liquidity.

Returns the new base liquidity and next tick values.

_updatePoolData()

Called after a pool swap's calculations are completed, and new values are to be assigned to the pool's price, liquidity and tick values.

_getInitialSwapData()

Returns the stored pool's price, liquidity and tick values before swap calculations are to be performed.

_updatePositionData()

Updates a position's liquidity and feeGrowthInside value. Also calculates the reinvestment tokens to be minted for the position's accumulated fees.

_updateTick()

Initializes / updates / deletes a tick's state (liquidity, outside values) and if required, inserts and removes the tick from the linked list by calling _updateTickList().

_updateTickList()

Add / remove a specified tick to / from the linked list. No changes are applied if the specified tick is either the MIN_TICK or MAX_TICK.


Oracle Contracts

Pool Oracle

rescueFund()

Rescue fund from contract if someone transfer into pool.

Input Field Type Explanation
token address token address
amount uint256 amount of token

initializeOracle()

Initialize oracle pool at time.

Input Field Type Explanation
time uint32 timestamp

write()

Write a new observation to pool. Also update index and cardinality with observation index and observation cardinality.

Input Field Type Explanation
timestamp uint32 token address
tick int24 tick number
liquidity uint128

writeNewEntry()

Write a new observation to pool. Also update index and cardinality.

Input Field Type Explanation
timestamp uint32 token address
tick int24 tick number
liquidity uint128

increaseObservationCardinalityNext()

Anyone can pay storage gas to increase the observation size of their desired Elastic pool.

Input Field Type Explanation
pool address pool address
observationCardinalityNext uint16 next cardinality number

observeFromPool()

Read observation from a pool with the latest state of the pool.

Input Field Type Explanation
pool address pool address
secondsAgos uint32[] list second input ago

observeSingleFromPool()

Read a single observation from a pool with the latest state of the pool.

Input Field Type Explanation
pool address pool address
secondsAgos uint32 second input ago

getPoolObservation()

Get current state of pool observation.

Input Field Type Explanation
pool address pool address

getObservationAt()

Get current state of pool observation at an index.

Input Field Type Explanation
pool address pool address
index uint256 index data

Periphery Base Contracts

DeadlineValidation

Validate if the block timestamp has not reached the deadline yet, use for transactions with a deadline.

Modifier: onlyNotExpired()

Reverts if the current block's timestamp is greater than the specified deadline.

Field Type Explanation
deadline uint256 Timestamp to check against current block's timestamp

_blockTimestamp()

Returns the current block timestamp. Used for overriding by mock contracts for tests.


ERC721Permit

Nonfungible tokens that support an approve via signature, i.e. permit for ERC721.


ImmutableRouterStorage

Immutable variables that are used by Periphery contracts.

Immutables

Field Type Explanation
factory address Factory contract address
WETH address Canonical WETH address
poolInitHash bytes32 keccak256 hash of the pool's creation code. Used to compute the pool address without reading storage from the Factory

RouterTokenHelper

A helper contract to handle transfers, wrapping and unwrapping of tokens.

unwrapWeth()

Unwraps the contract's entire WETH balance to ETH, then transfers them to the recipient.

Input Field Type Explanation
minAmount uint256 Contract's WETH balance should not be lower than this amount
recioient address Desired recipient of unwrapped ETH

transferAllTokens()

Transfers the contract's entire token balance to the recipient

Input Field Type Explanation
token address Token to transfer
minAmount uint256 Contract's token balance should not be lower than this amount
recipient address Desired recipient of the token

refundEth()

Transfers all ETH balance to the sender.

_transferTokens()

Internal function to help transfer tokens from the sender to the recipient. If token is WETH and the contract has enough ETH balance, then wrap and transfer WETH, otherwise use TokenHelper to handle transfers.

Input Field Type Explanation
token address Token to transfer
sender address Address to pull token funds from
recipient address Desired recipient of the token
amount uint256 Amount to be transferred

RouterTokenHelperWithFee

Inherits RouteTokenHelper. Contains additional functions to charge a fee for transfers as well.

unwrapWethWithFee()

Unwraps the contract's entire WETH balance to ETH, then transfers them to the recipient. Charges a fee which is transferred to feeRecipient

Input Field Type Explanation
minAmount uint256 Contract's WETH balance should not be lower than this amount
recipient address Desired recipient of unwrapped ETH
feeBps uint256 Fee to charge in basis points
feeRecipient address Address to receive the fee charged

transferAllTokensWithFee()

Transfers the contract's entire token balance to the recipient. Charges a fee which is transferred to feeRecipient.

Input Field Type Explanation
token address Token to transfer
minAmount uint256 Contract's token balance should not be lower than this amount
recipient address Desired recipient of the token
feeBps uint256 Fee to charge in basis points
feeRecipient address Address to receive the fee charged

Multicall

Enables calling multiple methods in a single call to the contract.

multicall()

Uses delegateCall to sequentially execute functions, then return the result of each execution.

Input

Field Type Explanation
data bytes[] encoded function data for each of the calls to make to this contract

Output

Field Type Explanation
results bytes[] results from each of the calls passed in

LiquidityHelper

A helper contract to handle liquidity related actions, including mint/add/remove liquidity.

Struct: AddLiquidityParams

Used when minting a new position or adding liquidity to an existing one.

Field Type Explanation
token0 address first token of the pool
token1 address second token of the pool
fee uint16 the pool's swap fee
tickLower int24 position's lower tick
tickUpper int24 position's upper tick
ticksPrevious int24[2] an array containing 2 values tickLowerPrevious and tickUpperPrevious which are expected to be the nearest initialized tick <= tickLower and tickUpper respectively
amount0Desired uint256 token0 amount user wants to add
amount1Desired uint256 token1 amount user wants to add
amount0Desired uint256 minimum token0 amount that should be used
amount1Desired uint256 minimum token1 amount that should be used

Struct: CallbackData

Data for callback from Pool contract.

Field Type Explanation
token0 address first token of the pool
token1 address second token of the pool
fee uint16 the pool's swap fee
source address address to transfer token0/token1 from

mintCallback()

Mint callback function implementation called by a Pool.

Input Field Type Explanation
deltaQty0 uint256 token0 amount requested by the pool
deltaQty1 uint256 token1 amount requested by the pool
data bytes Encoded CallbackData

_addLiquidity()

Add liquidity to a pool. token0 and token1 should be in the correct order, i.e token0 < token1.

Input

Type Explanation
AddLiquidityParams Parameters for addiing liquidity to a pool

Output

Field Type Explanation
liquidity uint128 amount of liquidity added
amount0 uint256 amount of token0 required
amount1 uint256 amount of token1 required
feeGrowthInsideLast uint256 latest feeGrowthInsideLast calculated by the pool
pool IPool pool address

_callbackData()

Function to encode input parameters to CallbackData

Input Field Type Explanation
token0 address first token of the pool
token1 address second token of the pool
fee uint16 the pool's swap fee

_getPool()

Function for computing the pool address with the given input parameters.

Input Field Type Explanation
token0 address first token of the pool
token1 address second token of the pool
fee uint16 the pool's swap fee

Periphery Libraries Contracts

AntiSnippingAttack

The motivation and explanation of the mechanism can be found here. The function containing the bulk of the logic is update().

Struct: Data

Field Type Formula Variable Explanation
lastActionTime uint32 \(t_{last}\) timestamp of last action performed
lockTime uint32 \(t_{lock}\) average start time of lock schedule
unlockTime uint32 \(t_{unlock}\) average unlock time of locked fees
feesLocked uint32 \(fee_{locked}\) locked rToken qty since last update

initialize()

Initializes Data values for a new position. The time variables are set to the current timestamp, while feesLocked is set to zero.

update()

Updates Data values for an existing position. Calculates the amount of claimable reinvestment tokens to be sent to the user and, in the case of liquidity removal, the amount of burnable reinvestment tokens as well.

Formula

\(claimBps_{new} = min(BPS, \frac{t_{now}-t_{lock}}{t_{target}})\)
\(claimBps_{current} = min(BPS, \frac{t_{now}-t_{last}}{t_{unlock} - t_{target}})\) if \(t_{unlock} > t_{target}\), \(BPS\) otherwise
\(fee_{harvest}\) and \(fee_{lock}\) updated through calcFeeProportions()
\(t_{unlock} = \frac{(t_{lock} + t_{target}) * (BPS - claimBps_{new}) * fee_{collect} + t_{unlock} * (BPS - claimBps_{current}) * fee_{locked}}{fee_{lock} * BPS}\)

  • If adding liquidity, update \(t_{lock} = ceil(\frac{max(t_{lock}, t_{now} - t_{target})*L + t_{now} * \Delta{L}}{L + \Delta{L}})\)
  • If removing liquidity,
    • \(fee_{burn} = fee_{harvest} * \frac{\Delta{L}}{L}\)
    • \(fee_{harvest}\) -= \(fee_{burn}\)

Input

Field Type Formula Variable Explanation
self Data N.A. stored data values for an existing position
currentLiquidity uint128 \(L\) current position liquidity
liquidityDelta uint128 \(\Delta{L}\) quantity change to be applied
currentTime uint32 \(t_{now}\) current block timestamp
isAddLiquidity bool N.A. true = add liquidity, false = remove liquidity
feesSinceLastAction uint256 \(fee_{collect}\) fees accrued since last action
vestingPeriod uint256 \(t_{target}\) maximum time duration for which LP fees are proportionally burnt upon LP removals

Output

Field Type Formula Variable Explanation
feesClaimable uint256 \(fee_{harvest}\) claimable reinvestment token amount
feesBurnable uint256 \(fee_{burn}\) reinvestment token amount to burn

calcFeeProportions()

Calculates the proportion of locked fees and claimable fees given the fee amounts and claimable fee basis points.

Formula

\(fee_{harvest} = \frac{claimBps_{current}}{BPS} * fee_{locked} + \frac{claimBps_{new}}{BPS} * fee_{collect}\)
\(fee_{lock} = fee_{locked} + fee_{collect} - fee_{harvest}\)

Input

Field Type Formula Variable Explanation
currentFees uint256 \(fee_{locked}\) currently locked fees
nextFees uint256 \(fee_{collect}\) fees since last action
currentClaimableBps uint256 \(claimBps_{current}\) proportion of claimable / unlocked currentFees in basis points
nextClaimableBps uint256 \(claimBps_{new}\) proportion of claimable nextFees in basis points

Output

Field Type Formula Variable Explanation
feesLockedNew uint256 \(fee_{lock}\) new fee amount to be locked
feesClaimable uint256 \(fee_{harvest}\) claimable fees to be sent to user

BytesLib

Solidity Bytes Arrays Utils @author Gonçalo Sá goncalo.sa@consensys.net
Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. The library lets you slice and type cast bytes arrays both in memory and storage.


LiquidityMath

Contract to calculate the expected amount of liquidity given the amounts of tokens.

getLiquidityFromQty0()

​​​​Params:
​​​​    uint160 lowerSqrtP     // a lower sqrt price of the position
​​​​    uint160 upperSqrtP     // a upper sqrt price of the position
​​​​    uint256 qty0           // the amount of token0 to add
​​​​Returns:
​​​​    uint128 liquidity       // amount of liquidity to receive

Get liquidity from qty0 of the first token given the price range.

getLiquidityFromQty1()

​​​​Params:
​​​​    uint160 lowerSqrtP     // a lower sqrt price of the position
​​​​    uint160 upperSqrtP     // a upper sqrt price of the position
​​​​    uint256 qty1           // the amount of token1 to add
​​​​Returns:
​​​​    uint128 liquidity       // amount of liquidity to receive

Get liquidity from qty1 of the second token given the price range.

getLiquidityFromQties()

​​​​Params:
​​​​    uint160 currentSqrtP    // the current price, e.g the pool's current price
​​​​    uint160 lowerSqrtP      // a lower sqrt price of the position
​​​​    uint160 upperSqrtP      // a upper sqrt price of the position
​​​​    uint256 qty0            // the amount of token0 to add
​​​​    uint256 qty1            // the amount of token1 to add
​​​​Returns:
​​​​    uint128 liquidity       // amount of liquidity to receive

Get liquidity given the price range and amounts of 2 tokens


PathHelper

Functions for manipulating path data for multihop swaps

Variables

​​​​ADDR_SIZE = 20 // length of the address, i.e 20 bytes
​​​​FEE_SIZE = 2 // length of the fee, i.e 2 bytes
​​​​TOKEN_AND_POOL_OFFSET = ADDR_SIZE + FEE_SIZE// the offset of a single token address + pool fee
​​​​POOL_DATA_OFFSET = TOKEN_AND_POOL_OFFSET + ADDR_SIZE // the offset of 2 token addresses + pool fee
​​​​MULTIPLE_POOLS_MIN_LENGTH = POOL_DATA_OFFSET + TOKEN_AND_POOL_OFFSET // min length that contains at least 2 pools

hasMultiplePools()

​​​​Params:
​​​​    bytes path
​​​​Returns:
​​​​    bool

Return true if the path contains 2 or more pools, false otherwise

numPools()

​​​​Params:
​​​​    bytes path
​​​​Returns:
​​​​    uint256

Returns the number of pools in the path.

decodeFirstPool()

​​​​Params:
​​​​    bytes path
​​​​Returns:
​​​​    address tokenA
​​​​    address tokenB
​​​​    uint16 fee

Return the first pool's data from the path, including tokenA, tokenB and fee.

getFirstPool()

​​​​Params:
​​​​    bytes path
​​​​Returns:
​​​​    bytes data

Return the segment corresponding to the first pool in the path.

skipToken()

​​​​Params:
​​​​    bytes path
​​​​Returns:
​​​​    bytes newPath

Skip a token + fee from the buffer and returns the remainder.


PoolAddress

Provides a function for deriving a pool address from the factory, tokens, and swap fee
ProAMM

computeAddress()

​​​​Params:
​​​​    address factory   // Elastic v2 factory contract
​​​​    address token0    // the first token of the pool
​​​​    address token1    // the second token of the pool
​​​​    uint16 swapFee    // the fee of the pool
​​​​    bytes32 poolInitHash    // the keccake256 hash of the Pool creation code
​​​​Returns:
​​​​    address pool    // the pool address

Deterministically computes the pool address from the given data.


PoolTicksCounter

countInitializedTicksCrossed()

​​​​Params:
​​​​    IPool
​​​​    int24 nearestCurrentTickBefore
​​​​    int24 nearestCurrentTickAfter
​​​​Returns:
​​​​    uint32 initializedTicksCrossed

Count the number of initialized ticks have been crossed given the previous/current nearest initialized ticks to the current tick.


TokenHelper

A helper contract to transfer token/ETH.

transferToken()

​​​​Params:
​​​​    IERC20 token
​​​​    uint256 amount
​​​​    address sender
​​​​    address receiver

Transfer an amount of ERC20 token from the sender to the receiver.

transferEth()

​​​​Params:
​​​​    uint256 amount
​​​​    address receiver

Transfer an amount of ETH to the receiver.


Periphery Core Contracts

Router

The Router contract to handle swapping.

Struct: ExactInputSingleParams

Contains data for swap exact input with a single pool.

​​​​ExactInputSingleParams
​​​​    address tokenIn;     // source token to swap
​​​​    address tokenOut;    // dest token to receive
​​​​    uint16 fee;          // fee of the pool to swap
​​​​    address recipient;   // the recipient of tokenOut
​​​​    uint256 deadline;    // deadline for the transaction
​​​​    uint256 amountIn;    // the amount of tokenIn to swap
​​​​    uint256 minAmountOut;// min acceptable amount of tokenOut
​​​​    uint160 limitSqrtP;  // the limit of sqrt price, partial swap if price reaches the limitation

Struct: ExactInputParams

Contains data for swap exact input with one or multiple pools.

​​​​ExactInputParams
​​​​    bytes path;           // contains data to identify list of pools to use for the swap
​​​​    address recipient;    // the recipient of token out
​​​​    uint256 deadline;     // deadline for the transaction
​​​​    uint256 amountIn;     // the amount of token in to swap
​​​​    uint256 minAmountOut; // the min acceptable amount of token out

Struct: ExactOutputSingleParams

Contains data for swap to an exact amount out of token out, using only one pool.

​​​​ExactOutputSingleParams:
​​​​    address tokenIn;    // source token to swap
​​​​    address tokenOut;   // dest token to receive
​​​​    uint16 fee;         // fee of the pool
​​​​    address recipient;  // the recipient of tokenOut
​​​​    uint256 deadline;   // deadline for the transaction
​​​​    uint256 amountOut;  // the amount of tokenOut to expect
​​​​    uint256 maxAmountIn;// the max amount of tokenIn that is allowed to use
​​​​    uint160 limitSqrtP; // the limit of sqrt price, partial swap if price reaches the limitation

Struct: ExactOutputParams

Contains data for swap to an exact amount out of token out using one or multiple pools.

​​​​ExactOutputParams
​​​​    bytes path;
​​​​    address recipient;
​​​​    uint256 deadline;
​​​​    uint256 amountOut;
​​​​    uint256 maxAmountIn;

swapCallback()

​​​​Params:
​​​​    int256 deltaQty0
​​​​    int256 deltaQty1
​​​​    bytes data

Callback function that is be triggerred by the pool when swapping tokens.
Either deltaQty0 or deltaQty1 must be positive. Positive value means the pool (also the caller) is expecting to receive that amount of token.

swapExactInputSingle()

​​​​Params:
​​​​    ExactInputSingleParams params
​​​​Returns:
​​​​    uint256 amountOut

Given the params of ExactInputSingleParams, it uses only one pool to swap exact an amountIn of tokenIn to tokenOut and return the amount of tokenOut from the swap.
It calls the swap function in the pool contract, the pool will transfer the tokenOut to the Router, and expect to receive the corresponding tokenIn in the swap callback function.

swapExactInput()

​​​​Params:
​​​​    ExactInputParams params
​​​​Returns:
​​​​    uint256 amountOut

Given the params of ExactInputParams, it uses only one or multiple pools that can be decoded from the path.
It calls the swap function in each pool contract that is decoded from the path param, and uses callback to handle transferring tokens.

Flow:

  • Swap path: tokenIn -> token0 -> token1 -> -> tokenOut.
  • Call the first pool to borrow token0 to the Router, then transfer tokenIn from user's wallet to the first pool.
  • Call the second pool to borrow token1 to the Router, then transfer token0 from the Router to the second pool.
  • Repeat the action until the last pool, and transfer tokenOut directly to the recipient.

swapExactOutputSingle()

​​​​Params:
​​​​    ExactOutputSingleParams params
​​​​Returns:
​​​​    uint256 amountIn

Given the params of ExactOutputSingleParams, it uses only one pool to swap to get exact amountOut of tokenOut (or stop if price limit reaches).

  • Call the swap function in the pool contract.
  • The pool transfers the tokenOut to the recipient, and make the swap callback.
  • The Router transfer the corresponding tokenIn from user's wallet to the pool.
    The required amount of tokenIn should not be greater than maxAmountIn.

swapExactOutput()

​​​​Params:
​​​​    ExactOutputParams params
​​​​Returns:
​​​​    uint256 amountIn

Given the params of ExactOutputParams, it uses one or multiple pools that can be decoded from the path.
It calls the swap function in each pool contract that is decoded from the path param, and uses callback to handle transferring tokens.
The list of pools in the path params should be in the reverse order, i.e the last pool first since we don't know the exact amount of token in to be used.

Flow:

  • Origial swap path: tokenIn -> token0 -> token1 -> -> tokenN -> tokenOut.
  • Reverse swap path: tokenOut -> tokenN -> -> token1 -> token0 -> tokenIn.
  • Consider as we are swapping from amountOut of tokenOut to tokenIn.

BasePositionManager

Inherit from an ERC721 contract, it stores all positions of all liquidity providers and mint corresponding NFT.

Users can use the PositionManager to create & unlock pool, mint new position, add/remove liquidity to/from an existing position and burn their reinvestment tokens to receive back the LP fees.

It also inherits from Multicall contract, thus, users can make multiple function/method calls to the PositionManager in a single transaction.

All reinvestment tokens will be held in the contract, thus, it blocks anyone from transferring reinvestment tokens out. As a result, the contract can not support pools with any reinvestment tokens.

Struct: MintParams

Params for the first time adding liquidity, mint new nft to sender

​​​​MintParams
​​​​    address token0; // the token0 of the pool
​​​​    address token1; // token1 of the pool, must make sure that token0 < token1
​​​​    uint16 fee; // the pool's fee in bps
​​​​    int24 tickLower; // the position's lower tick
​​​​    int24 tickUpper; // the position's upper tick
​​​​    int24[2] ticksPrevious; // the nearest tick that has been initialized and lower than or equal to the tickLower and tickUpper, use to help insert the tickLower and tickUpper if haven't initialized
​​​​    uint256 amount0Desired; // the desired amount for token0
​​​​    uint256 amount1Desired; // the desired amount for token1
​​​​    uint256 amount0Min; // min amount of token 0 to add
​​​​    uint256 amount1Min; // min amount of token 1 to add
​​​​    address recipient; // the owner of the position
​​​​    uint256 deadline; // time that the transaction will be expired

Struct: IncreaseLiquidityParams

Params for adding liquidity to the existing position

​​​​IncreaseLiquidityParams
​​​​    uint256 tokenId // id of the position to increase its liquidity
​​​​    int24[2] ticksPrevious // the nearest tick that has been initialized and lower than or equal to the tickLower and tickUpper, use to help insert the tickLower and tickUpper if haven't initialized. Only needed if the position has been closed and the owner wants to add more liquidity
​​​​    uint256 amount0Desired // the desired amount for token0
​​​​    uint256 amount1Desired // the desired amount for token1
​​​​    uint256 amount0Min // min amount of token 0 to add
​​​​    uint256 amount1Min // min amount of token 1 to add
​​​​    uint256 deadline // time that the transaction will be expired

Struct: RemoveLiquidityParams

Params for remove liquidity from the existing position

​​​​RemoveLiquidityParams
​​​​    uint256 tokenId // id of the position to remove its liquidity
​​​​    uint256 amount0Min // min amount of token 0 to receive
​​​​    uint256 amount1Min // min amount of token 1 to receive
​​​​    uint256 deadline // time that the transaction will be expired

Struct: BurnRTokenParams

Burn the rTokens to get back token0 + token1 as fees

​​​​BurnRTokenParams
​​​​    uint256 tokenId // id of the position to burn r token
​​​​    uint256 amount0Min // min amount of token 0 to receive
​​​​    uint256 amount1Min // min amount of token 1 to receive
​​​​    uint256 deadline // time that the transaction will be expired

createAndUnlockPoolIfNecessary()

​​​​Params:
​​​​    address token0;
​​​​    address token1;
​​​​    uint16 fee;
​​​​    uint160 currentSqrtP
​​​​Returns:
​​​​    address pool // deployed pool given the set of params

Use this function to create & unlock a pool if needed given (token0, token1, fee) params and the initial sqrt price.
Required: token0 < token1

mint()

​​​​Params:
​​​​    MintParams params
​​​​Returns:
​​​​    uint256 tokenId;
​​​​    uint128 liquidity,
​​​​    uint256 amount0,
​​​​    uint256 amount1

Call the _addLiquidity function in the LiquidityHelper contract with the data from *params.
It mints a new NFT token represents the position to the recipient, stores the position data and pool information.

addLiquidity()

​​​​Params:
​​​​    IncreaseLiquidityParams params
​​​​Returns:
​​​​    uint128 liquidity,
​​​​    uint256 amount0,
​​​​    uint256 amount1,
​​​​    uint256 additionalRTokenOwed

Call the _addLiquidity function in the LiquidityHelper contract with the data from params to add more liquidity to a given position (i.e tokenId). Calculate and update the additional reinvestment tokens that the position should have earned.

removeLiquidity()

​​​​Params:
​​​​    RemoveLiquidityParams params
​​​​Returns:
​​​​    uint256 amount0,
​​​​    uint256 amount1,
​​​​    uint256 additionalRTokenOwed

Call the burn function in the pool contract with the data from params to remove liquidity and get back 2 tokens. Calculate and update the additional reinvestment tokens that the position should have earned.

burnRTokens()

​​​​Params:
​​​​    BurnRTokenParams params
​​​​Returns:
​​​​    uint256 rTokenQty,
​​​​    uint256 amount0,
​​​​    uint256 amount1

Call the burnRTokens function in the pool contract to burn all reinvestment tokens from the position and get back 2 tokens. Return the amount of reinvestment tokens to burn and expected amounts of 2 tokens to receive

syncFeeGrowth()

​​​​Params:
​​​​    uint256 tokenId
​​​​Returns:
​​​​    uint256 additionalRTokenOwed

Call the tweakPosZeroLiq function in the pool contract to sync fee growth in order to have the latest data for the reinvestment tokens, it should be called before calling the burnRTokens() function.

Functions: tokenURI(), getApproved(), _approve(), _getAndIncrementNonce()

Overriding functions for the inherited ERC721 and permit contracts.


AntiSnipAttackPositionManager

Inherits BasePositionManager and adds the anti-snipping attack feature into addLiquidity, removeLiquidity and syncFeeGrowth functions.

Reinvestment tokens of a position will be locked and vested to prevent LPs from adding then removing liquidity within a small period of time.

The motivation, mechanism and formula for Anti-Snipping Attack can be found here. The AntiSnippingAttack library will be of interest as well.


Appendix

Appendix A: Range Mechanism

Objective

The goal is to accurately account a position's accured fees accured and duration for which it is active. A position is defined to be active if the current pool tick lies within the lower and upper ticks of the position.

Global value

The feeGrowthGlobal and secondsPerLiquidityGlobal variables represent the total amount for 1 unit of unbounded liquidity.

Outside value

The outside value (feeGrowthOutside and secondsPerLiquidityOutside) for a tick represents the accumulated value relative to the currentTick. By definition, we can visually represent it to be as such:

  • tick <= currentTick
  • tick > currentTick

Initialization

When a tick is initialized, all growth is assumed to happen below it. Hence, the outside value is initialized to the following values under these cases:

  • tick <= currentTick: outside value := global value
  • tick > currentTick: outside value := 0

Crossing ticks

Due to the definition of the outside value, a tick's outside value is reversed whenever the pool tick crosses it. Specifically, outside value := global value - outside value.

Note

When swapping downtick, the current tick is further decremented by 1. This is to ensure a tick's outside value is interpreted correctly.
swapData.currentTick = willUpTick ? tempNextTick : tempNextTick - 1;

Calculating value inside ticks

The current tick can be

  1. below a position (currentTick < lowerTick)

    The value inside can therefore be calculated as tickLower's outside value - tickUpper's outside value
  2. within a position (lowerTick <= currentTick < upperTick)

    The value inside can therefore be calculated as global value - (lower + upper tick's outside values)
  3. above a position (upperTick <= currentTick)

    The value inside can therefore be calculated as tickUpper's outside value - tickLower's outside value

Appendix B: Tick-range Mechanism

  • To represent liquidity mapping, Kyber Elastic uses linked list, the linked list always starts by MIN_TICK and ends by MAX_TICK.

  • The pool also need to track to the highest initialized tick which is lower than or equal to currentTick (aka nearestCurrentTick).

For example:

  • the pool is initalized at tick 5, the linked list will be [MIN_TICK, MAX_TICK] andnearestCurrentTick will be MIN_TICK.
  • A adds liquidity at tick [-5, 10], the linked list will be [MIN_TICK, -5, 10, MAX_TICK] and nearestCurrentTick will be -5.
  • C adds liquidity at tick [0, 100], the linked list will be [MIN_TICK, -5, 0, 10, 100, MAX_TICK] and nearestCurrentTick will be 0.
  • B swaps and currentTick = 15, then nearestCurrentTick will be 10
  • A remove all liquidity at tick [-5, 10], the linked list will be [MIN_TICK, 0, 100, MAX_TICK] andnearestCurrentTick will be 0.

Appendix C: Reinvest Mechanism

Kyber Elastic does not take fee in forms of token0 and token1, the fee will be both tokens and reinvested into tick range from [0, infinity]

  • baseL is the liquidity from lp provider and reinvestL is the liquidity from the fee
  • User can still earn fee from reinvestL, so we create reinvestment token to represent the proportion of user in reinvestL.
  • Each time, users add/remove liquidity or cross a tick, we have to mint the reinvestment token equavalent to the increment of reinvestL by baseL. The formula to calculate mintQty is at ReinvestmentMath.calcrMintQty
  • reinvestment token: after London hardfork, it is costly to warm up another contract. Therfore, we decided to merge the reinvestment token and pool contract and the pool contract will be inherited from ERC20.
Select a repo