Pool Process Flows

Automating Tried And Tested Procedures

Pool unlocking/initialization

Overview

No action (minting / burning / swaps) can be performed prior to pool initialization.

In addition to setting the initial sqrt price, a small amount of token0 and token1 is required to be seeded for the initialization of reinvestL to the value of MIN_LIQUIDITY. This is required to prevent division by zero in the calcRMintQty() function when swaps are performed. MIN_LIQUIDITY was chosen to be reasonably small enough to avoid calculation inaccuracies for swaps, and from taking unreasonably large capital amounts from the caller.

Minting and burning (add/remove liquidity)

Overview

Adding and removing liquidity have very similar flows. One of the main differences is that mint() is possibly a permissioned function, but burn() is not. More information relating to the requirement for this can be found in this section on whitelisting position managers.

Implementation details

  • A simple check is performed to ensure that the requested liquidity amount to mint / burn is non-zero

  • _tweakPosition() is called, which does the following:

    • Load the pool state into memory poolData (current price, tick and liquidity values)

    • Call _syncFeeGrowth() to update fee growth data. Mints reinvestment tokens if necessary

    • Call _syncSecondsPerLiquidity() to update seconds per liquidity data

    • The updated global values and poolData is passed into _updatePosition()

      • Updates (initializes) the lower and upper position ticks. Will insert or remove the tick from the linked list whenever necessary

      • Calculates feeGrowthInside and returns the amount of reinvestment tokens claimable by the position

    • Transfers the claimable reinvestment tokens to the position owner, if any

    • Calculates the token0 and token1 quantity required to be collected from (add liquidity) or sent to (remove liquidity) msg.sender. Will apply liquidity changes to pool liquidity if the specified position is active

  • In the case of adding liquidity, a callback is made to collect the tokens

  • Emit event

Swap

Overview

Like KyberSwap Classic, there are 4 different types of swaps available that a user can specify.

  1. Swap from a specified amount of token 0 (exactInput0)

  2. Swap from a specified amount of token 1 (exactInput1)

  3. Swap to a specified amount of token 0 (exactOutput0)

  4. Swap to a specified amount of token 1 (exactOutput1)

Swapping token 0 for token 1 (cases 1 and 4) cause the pool price and tick to move downwards, while swapping token 1 for token 0 (cases 2 and 3) cause the pool price and tick to move upwards.

In addition, the user can specify a price limit that the swap can reach. The minimum and maximum price limits a user can specify is MIN_SQRT_RATIO + 1 and MAX_SQRT_RATIO - 1.

The algorithm exits when either the specified amount has been fully used, or if the price limit has been reached.

Implementation details

The swap amount is a int256 to implicitly suggest whether it is exact input (> 0) or exact output (< 0).

  1. Fetch the initial pool state

    • Call SwapMath.computeSwapStep() to calculate the actual swap input and output amounts to be used, swap fee amount and next pool price

      • Subtract amount to be used (usedAmount) to swapData.specifiedAmount

      • Add amount to be sent to user (returnedAmount) to swapData.returnedAmount

    • Check if swap will reach next tick

      • If true, set swapData.currentTick = willUpTick ? tempNextTick : tempNextTick - 1 and continue

      • If false, recalculate the current tick based on current price and break the loop

      • Load variables (if not loaded already) that are initialized when crossing ticks

      • Calculate amount of reinvestment tokens to be minted for fees to be sent to government and to for LP contributions, and update feeGrowthGlobal

  2. Perform actual minting of reinvestment tokens if necessary

  3. Update pool state (price, ticks, liquidity, feeGrowth, reinvestment variables)

  4. Send token to caller, execute swap callback to collect token

    • Negative quantity = transfer to caller

    • Positive quantity = collect from caller

computeSwapStep() Flow

Inputs

Outputs

  1. Calculate the amount required to reach targetSqrtP from currentSqrtP by calling calcReachAmount().

  2. If amount required exceeds specifiedAmount, then the targetPrice will not be reached, and we expect the resulting price nextSqrtP to not exceed targetSqrtP.

    • usedAmount := specifiedAmount

    • Calculate the final price nextSqrtP by calling calcFinalPrice()

  3. Otherwise, the temporary next tick will be crossed.

    • usedAmount will be the amount calculated in step 1

    • set the resulting price nextSqrtP = targetSqrtP

  4. Finally, calculate returnedAmount by calling calcReturnedAmount().

Swapping formula

Assume that:

  • x1, x2: the amount of token0 before/after swap

  • y1, y2: the amount of token1 before/after swap

  • L1, L2: the liquidity before/after swap

  • p1, p2: the price before/after swap

Swap exact input from token0 -> token1

Swap exact input from token1 -> token0

Swap exact output from token0 -> token1 (isExactInput = false, isToken0 = false)

This can be transformed into

Swap exact output token1 -> token0 (isExactInput = false, isToken0 = true)

Last updated