LiquidityManager (LiquidityManager.sol)

This is a decentralised Uniswap v3 position manager; for liquidity of the Mementos token.

The key feature of this contract is:

  • Automated protocol managed token liquidity
flowchart TD
    T["fas:fa-coins Mementos (MOTE)"<br>ERC-20 Tokens<br>0% Transfer Fees 0,0]
    MP(* Mining Pool<br>61.8034% MOTE Supply)
    LP(* Liquidity Pools)
    LP2(* Liquidity v2 Pool<br>LP Tokens Burned)
    LP3Weth(* Liquidity Pool<br>Locked in Protocol)
    LP3Reth(* Liquidity Pool<br>Locked in Protocol)
    T --> |61.8034% MOTE Supply| MP
    T --> |38.1966% MOTE Supply| LP
    LP --> |Only at init<br>0.5 ETH| LP2
    LP --> LP3Weth
    LP --> LP3Reth
    MP -->|post-mine<br>unmined MOTE supply| B
    LP2 --> |LP Tokens| B
    LP3Weth --> |post mine<br>+3 day| F
    LP3Reth --> |post mine<br>+3 day| F
    F --> |Collected ETH| LP
    F --> |Collected MOTE| B
    F{Collect Position Fees<br>or Adjust Floor Position}
    B[Burn -> 0xdEaD]

Non-standard EOA callable non-view functions on this contract

  • allocateToResilience() returns (bool)
    1. Checks has been 3 days since last call, or trade start
    2. Sets next call time to be 3 days from now
    3. Calls _injectLiquidity for MOTE/WETH pool
    4. Calls _injectLiquidity for MOTE/RETH pool
  • streamlineReserves() returns (bool)
    1. Checks has been 3 days since last call, or trade start
    2. Sets next call time to be 3 days from now
    3. Find if any position can be removed by calling prospectNadir()
    4. Calls _consolidateLiquidity for the selected pool
    5. If was the last position in the start set, also calls _removeBand on the floor position for that pool

Notable public view only functions on this contract

  • prospectNadir() returns (address)

    Finds the liquidity pool with the most distant lower liquidity band and sees if it can be removed while maintaining the protocol's liquidity coverage for 25% of the token supply.

    1. Gets the MOTE/RETH price reverting if beyond an acceptable distance of the TWAP
    2. Gets the MOTE/WETH price reverting if beyond an acceptable distance of the TWAP
    3. Check how many tokens the MOTE/WETH pool can absorb
    4. Check how many tokens the MOTE/RETH pool can absorb
    5. Simulate a sale of remaining tokens from the 25% of outstanding supply to v2 pool and get a price.
    6. If v2 after price is higher than MOTE/WETH pool's lowest band it can be removed
    7. If both prices are higher than MOTE/RETH pool's lowest band it can be removed instead

Ecosystem only callable methods on this contract

  • initializePools() payable onlyHBHContract

    Called by the HalfBakedHeroes function constructor(); initializes the 3 liquidity pools.

    1. Creates Uniswap v2 pair (MOTE/WETH)
    2. Creates Uniswap v3 pair (MOTE/WETH)
    3. Initializes v3 pair pool (MOTE/WETH)
    4. Gets current RETH/WETH price
    5. Creates Uniswap v3 pair (MOTE/RETH)
    6. Recalibrates WETH pool ticks for RETH price
    7. Initializes v3 pair pool (MOTE/RETH)
  • activateTradeWinds() payable onlyHBHContract

    This function enables trading on the Mementos token by adding all the initial liquidity to the pools.

    1. Converts the sent ETH to WETH
    2. Calls _setUpLiquidity to set up the liquidity positions
    3. Sets nextConsolidation and nextInjection to be 3 days in the future
    4. Renounces ownership of this contract
  • addToLiquidity(address) payable onlyHBHContract returns (uint256)

    1. Checks is coming from HBH Contract
    2. Calls _selectPool() to choose v2 or v3 pool, exiting if neither
    3. Use 20% of ETH to covert to RETH if more favorable MOTE pool
    4. Use the ETH or RETH to buy MOTE to replenish the rewards pool
    5. Convert any remaining ETH to WETH
  • replenishRewards(address) payable onlyHBHContract returns (uint256)

    1. Checks is coming from HBH Contract
    2. Calls _selectPool() to choose v2 or v3 pool, exiting if neither
    3. Works out if the V2 or V3 WETH/MOTE price will get more tokens
    4. Works out if the RETH pool will give more tokens than the selected WETH pool
      • If RETH, swaps ETH to RETH
    5. Calls Liquidity replenishRewards specifying whether to use WETH or RETH and to return the MOTE to the sender parameter returning the amount of Mementos
  • harvestAndRecycleFees() payable

    1. Checks is Mementos token calling from _afterTokenTransfer
    2. Iterates a single band collecting any liquidity fees if available; switching between each pool for each invocation
      • Any fees in RETH are kept in the LiquidityManager (e.g. no action)
      • Any fees in WETH are sent to the CertificateOfAuthenticity for distribution
      • Any fees in Mementos token are burnt

Notable private or internal functions on this contract

  • _selectPool()

    1. Works out if the V2 or V3 WETH/MOTE price will get more tokens
    2. Returns the price and version of pool
  • _setUpLiquidity(uint256) private

    1. Adds liquidity to the Uniswap v2 position (0.5 WETH); burning the LP tokens.
    2. Calls _setUpUniswapV3(uint256) passing the count of remaining WETH
  • _setUpUniswapV3(uint256) private

    1. Splits the WETH by Golden ratio (1.618) with larger fraction (61.8%) being allocated to RETH pool.
    2. Converts the initial WETH for RETH into RETH
    3. Sets up the Uniswap v3 position bands by calling _setUpPositions for each v3 pool and stores the returned floor position
    4. Burns any unallocated Memento tokens
  • _setUpPositions

    Called twice. Once for MOTE/WETH and once for MOTE/RETH pool; minting

    1. Queries v3 pool for the initial price
    2. Mints 32 Uniswap v3 liquidity positions (64 including both pools)
      • at precalculated ticks _wethTicks or _rethTicks, approximately Fibonacci tick positions (1.618), with 0.382 retracement widths
      • linear distribution of tokens per band, except in the initial 8 where it is reduced so someone cannot scoop up a large amount of supply with a very small buy
      • inserts them in the position array backwards; in order of high price to low price; so when removing positions (starting at lows) the length of the array can be reduced by one rather than shuffling up.
    3. Add a full range (start price to infinity) band at index 0 so it isn't considered for removal and there is liquidity between the main 32 positions.
    4. Add a support floor band below price that just contains the WETH and no tokens; for mined token liquidity.

    32 (regular) + 1 (full range) + 1 (floor) = 34 liquidity positions are minted per invocation. This makes 68 uniswap positions across the two pools.

  • _consolidateLiquidity(uint256[POSITIONS],uint256) private

    1. get the liquidity amount in the position
    2. call _removeBand(uint256,uint128) with the liquidity to remove and Uniswap's positionId
    3. burn any Memento's token received in liquidity fees
  • _removeBand(uint256,uint128) private

    1. Decrease the liquidity in the position to 0
    2. Collect any fees or tokens from the position
    3. Burn the Uniswap v3 position
  • _getIndexForPrice(uint160,int24[TICKS]) private view returns (uint256)

    • Given the current price, searches the tick array to find the nearest position index to price (either price in, or next above).
  • _injectLiquidity(IUniswapV3Pool,address,int24[TICKS],uint256[POSITIONS],uint256) private returns (bool)

    1. If price is far away from TWAP, early exit
    2. Find the liquidity position index below price
    3. If not enough pair token to inject early exit
    4. Find how much pair liquidity in position band above
    5. Cap the amount of liquidity injected at 1% of balance or the pair liquidity in higher band, whichever is smaller
    6. Split the liquidity into 3 bands below price: Near, Far, Support
      • 20% goes to the Support band
      • If bearish price action (price below TWAP); 60% to Far band, 20% to Near band
      • If bullish price action (price above TWAP); 40% to Far band, 40% to Near band
    7. Call _increaseLiquidity on each of the positions with the designated amount
    8. Emits DeployingMoreCapitalSteadyLads(address,uint256,uint256,uint256)