Desmond Ho
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee
  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 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](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/solidity-utils/contracts/helpers/BaseSplitCodeFactory.sol). 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](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/solidity-utils/contracts/helpers/CodeDeployer.sol). Imported and used by the [`BaseSplitCodeFactory`](#BaseSplitCodeFactory) contract to handle deployment. --- ## FullMath Taken from [https://xn--2-umb.com/21/muldiv](https://xn--2-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`](#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-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-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()`](#init). | Field | Type | Explanation | | -------- | -------- | -------- | | `self` | `mapping(int24 => Data)` | A mapping of `int24` values to the [`Data`](#Struct-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](https://github.com/dynamic-amm/smart-contracts/blob/master/contracts/libraries/MathExt.sol#L36-L48). 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`](#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`](#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](https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol). ### 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`](#configMaster). Can only be changed by `configMaster`. | Field | Type | Explanation | | ---- | ----------- | ------ | | `_configMaster` | `address` | new config master address | ### `enableWhitelist()` Sets [`whitelistDisabled`](#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`](#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`](#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](#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](#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](#PoolTicksState). ### `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. <br/> [Appendix-A](#Appendix-A-Range-Mechanism) | | `secondsPerLiquidityOutside` | `uint128` | seconds spent on the other side of the tick relative to the current tick. <br/> [Appendix-A](#Appendix-A-Range-Mechanism) | ### `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](##Global-value). ### `getSecondsPerLiquidityData()` | Field | Type | Explanation | | ---- | ----------- | ------ | | `secondsPerLiquidityGlobal` | `uint128` | all-time seconds per unit of liquidity of the pool. More information can be found [here](##Global-value) | | `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](#Calculating-value-inside-ticks) | ### `_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](#PoolStorage). Contains functions for updating a tick's, position's or pool's state. <!-- ### struct: UpdatePositionData | Field | Type | Explanation | | ---- | ----------- | ------ | | `owner` | `address` | position owner | | `tickLower` | `int24` | position's lower tick | | `tickUpper` | `int24` | position's upper tick | | `tickLowerPrevious` | `int24` | the expected nearest initialized tick that is <= `tickLower`| | `tickUpperPrevious` | `int24` | the expected nearest initialized tick that is <= `tickLower`| | `liquidityDelta` | `int128` | any change in liquidity | --> ### `_updatePosition()` Calls [`_updateTick`](#_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()`](#_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](#Crossing-ticks)), 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). ### `_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](#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](#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](#RouterTokenHelper). 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](#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`](#Struct-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](#Struct-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](https://docs.google.com/document/d/1F50RWQRRyaNxnW5RvKgw09fN2FofIVLVccijgcOt-Iw/edit#heading=h.trv8zmis8u4c). The function containing the bulk of the logic is [`update()`](#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](#Struct-Data1) values for a new position. The time variables are set to the current timestamp, while `feesLocked` is set to zero. ### `update()` Updates [Data](#Struct-Data1) 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()`](#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`](#Struct-Data1) | 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](https://docs.google.com/document/d/1F50RWQRRyaNxnW5RvKgw09fN2FofIVLVccijgcOt-Iw/edit#heading=h.trv8zmis8u4c). The [AntiSnippingAttack library](#AntiSnippingAttack) 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. ![](https://i.imgur.com/DoPB569.png) ### 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 ![](https://i.imgur.com/IW8y1Gd.png) - tick > currentTick ![](https://i.imgur.com/X0uXxvB.png) #### 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) ![](https://i.imgur.com/s3jWPgr.png) The value inside can therefore be calculated as `tickLower's outside value - tickUpper's outside value` 2. within a position (lowerTick <= currentTick < upperTick) ![](https://i.imgur.com/pO9HPYb.png) The value inside can therefore be calculated as `global value - (lower + upper tick's outside values)` 3. above a position (upperTick <= currentTick) ![](https://i.imgur.com/Yi8iwu0.png) 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]` and`nearestCurrentTick` 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]` and`nearestCurrentTick` 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](https://hackmd.io/@fvictorio/gas-costs-after-berlin) 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.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully