|
| 1 | +# Liquidity AMO —— rebalancing the liquidity and stable price altogether |
| 2 | + |
| 3 | +## Organization |
| 4 | +The AMO manages a significant portion of the USDC backing for the stablecoin (referred to as BOOST in this version). There are two functions: |
| 5 | +* solidlyv3liquidityamo.sol For SolidlyV3 Dexes (based on Uniswap v3 contracts). |
| 6 | +* solidlyv2liquidityamo.sol For SolidlyV2 Dexes (based on Uniswap v2 contracts). |
| 7 | + |
| 8 | +*Note 1:* These two functions have identical logic, they just interact with two different AMM contracts => similarity qualitatively over 90% |
| 9 | + |
| 10 | +*Note 2:* Price rebalancing is triggered by a bot but can also be activated by the community through the publicAMO.sol contracts. This rebalancing is designed to be beneficial for the protocol, with no possible risk to the stablecoin project from either community actions or flash loans. |
| 11 | + |
| 12 | + |
| 13 | +## Audit Scope |
| 14 | + |
| 15 | +* Both the v2 and v3 AMO logic. |
| 16 | +* The utils contract (which manages veNFT, our voting power in Dexes) on the other branch. |
| 17 | +* Interfaces: note that some interfaces are external and do not need to be audited: |
| 18 | + + v2: IGauge.sol, IPair.sol, ISolidityRouter.sol |
| 19 | + + v3: IsolidityV3Factory.sol, ISolidityv3Pool.sol |
| 20 | + |
| 21 | +*Note*: Each Solidly Dex has slightly different contract versions, meaning adaptations for each chain or Dex may be required. This could lead to later ad-hoc reviews by an auditor. |
| 22 | + |
| 23 | +## Running the project |
| 24 | +This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract. |
| 25 | + |
| 26 | +Try running some of the following tasks: |
| 27 | + |
| 28 | +```shell |
| 29 | +npx hardhat help |
| 30 | +npx hardhat test |
| 31 | +REPORT_GAS=true npx hardhat test |
| 32 | +npx hardhat node |
| 33 | +npx hardhat run scripts/deploy.ts |
| 34 | +``` |
| 35 | + |
| 36 | +# Description of contracts and components |
| 37 | + |
| 38 | +## I) Booststablecoin: |
| 39 | +The BoostStablecoin contract implements an ERC-20 token called "BOOST," which serves as the foundation of the BOOST stablecoin project. This token is upgradable and includes several key features for managing and securing its functionality: |
| 40 | + |
| 41 | +**Main Functions** |
| 42 | +* **Pause & Unpause**: These functions allow addresses with the PAUSER_ROLE to pause token transfers and UNPAUSER_ROLE to resume transfers. This can be useful in emergency scenarios. |
| 43 | +The pause function can be delegated to a security monitoring firms for automatic responses. |
| 44 | +* **Minting**: This function allows addresses with the MINTER_ROLE to mint new tokens (using the Minter.Sol contract) and send them to a specified address (to_). |
| 45 | +Token Transfer Guard: This ensures that token transfers are only allowed when the contract is not paused, adding an additional layer of security. |
| 46 | + |
| 47 | +## II) LiquidityAMO: |
| 48 | +The LiquidityAMO smart contract is designed for a dual purpose: |
| 49 | +* it maintains the BOOST peg to USD in Solidly pool. |
| 50 | +* It provides protocol-owned liquidity to the pools |
| 51 | + |
| 52 | +This joint operation involves |
| 53 | +* When BOOST is above par, minting BOOST tokens, selling them for USD, then farming the USDC with free-minted BOOST |
| 54 | +* When BOOST is below par, removing liquidity from the pool, buying back BOOST from the pool with the USD, then burning BOOST |
| 55 | + |
| 56 | +**Note on vocabulary:** |
| 57 | +* Free-minted BOOST is called protocol-owned BOOST in the Frax vocabulary; it has no backing and is created when the protocol receives USD — and burned when the USDC is redeemed. |
| 58 | +* USD is a generic name for a reference stable coin paired with BOOST in the AMO ( USDC and USDT are the first natural candidates ) |
| 59 | + |
| 60 | +Below are the key functions that define the core logic of the contract: |
| 61 | +**Main Functions:** |
| 62 | +### 1. Initialize |
| 63 | +Purpose: The initialize function sets up the Liquidity AMO contract, defining the addresses of BOOST, USD, Minter, Solidly Pool and Treasury. Typically called when the contract is first deployed, this replaces a constructor in upgradeable contracts. |
| 64 | +### 2. setVault Function |
| 65 | +This function sets or changes the treasury vault address. Only an account with the SETTER_ROLE can call it. |
| 66 | +### 3. setTickBounds |
| 67 | +Purpose: this function is only available in the main branch which relies on uniswap v3 tech. setTickBounds defines the price range (ticks) at which it provides liquidity in the BOOST-USD pool. The current tech, however, uses full-range liquidity. |
| 68 | + |
| 69 | +### 4. mintAndSellBoost |
| 70 | + Purpose: Mints a specified amount of BOOST and sells it for USD in the pool. |
| 71 | +Triggered: When the BOOST-USD price diverges from peg (e.g., BOOST is trading above $1), this function is triggered to mint additional BOOST and sell it for USD to bring the price back down to peg. |
| 72 | + |
| 73 | +**Parameters:** |
| 74 | +* boostAmount: The amount of BOOST tokens to be minted and sold |
| 75 | +* minUsdAmountOut: The minimum USD amount should be received following the swap |
| 76 | +* deadline: Timestamp representing the deadline for the operation to be executed |
| 77 | +* usdAmountOut: The USD amount that received from the swap |
| 78 | + |
| 79 | +**Return values:** |
| 80 | + usdAmountOut: The USD amount that received from the swap |
| 81 | +Logic and economic security: the function reverts if Boost is not sold above par, so this function can never induce a loss for the protocol. |
| 82 | + |
| 83 | +### 5. addLiquidity (solidly v3) and addLiquidityAndDeposit (solidly v2) |
| 84 | + |
| 85 | +**Purpose (brief):** These addLiquidity functions add protocol-owned liquidity to the BOOST-USD pool, with minor implementation changes between the Solidly v3 (which “mints” positions) and Solidly v2 (which adds liquidity and stakes it). |
| 86 | +It involves free-minting BOOST tokens, pairing it with USDC backing, and after approving both BOOST and USD tokens for transfer to the pool, ading them as liquidity. |
| 87 | + |
| 88 | +**Purpose (detailed)**: |
| 89 | +1. The function first calculates the amount of BOOST to be minted based on the usdAmount provided. The minted BOOST amount is determined by converting the USD amount to its BOOST equivalent and applying a margin for error with the boostMultiplier. |
| 90 | +2. The contract interacts with the boostMinter to mint the calculated amount of BOOST tokens. |
| 91 | +3. The contract approves the BOOST and USD tokens for transfer to the pool. |
| 92 | +4. The contract calculates the amount of liquidity that will be provided to the pool based on the USD amount. It also calculates the minimum amounts of BOOST and USD that can be used to add liquidity (from minBoostSpend and minUsdSpend). |
| 93 | +5. The function sorts the amounts of BOOST and USD to determine the actual amounts spent when adding liquidity. |
| 94 | +6. It then checks that the USD spent is within the valid range based on the validRangeWidth and BOOST spent. This ensures the liquidity added is balanced between BOOST and USD. |
| 95 | +7. If any BOOST tokens are left unused (i.e., not spent to provide liquidity), the contract burns them. |
| 96 | + |
| 97 | +**Triggered**: When the protocol needs to add liquidity to the BOOST-USD pool, usually right after price rebalancing (MintAndSell). |
| 98 | +**Example**: The protocol decides to add liquidity using 100,000 USD. It free-mints the corresponding amount of BOOST and adds both BOOST and USD as liquidity to the pool. |
| 99 | +**Parameters**: |
| 100 | +* usdAmount (uint256): The total amount of USD that will be added as liquidity to the pool. |
| 101 | +* minBoostSpend (uint256): The minimum required amount of BOOST tokens that must be spent in order to proceed with the liquidity addition. |
| 102 | +* minUsdSpend (uint256): The minimum required amount of USD that must be contributed to the pool for the liquidity addition to be valid. |
| 103 | +* deadline (uint256): Timestamp representing the deadline for the operation to be executed. If the deadline is exceeded, the transaction will revert. |
| 104 | + |
| 105 | +### 6. mintSellFarm |
| 106 | + Purpose (brief): The mintSellFarm essentially bundles the mintAndSell and the addLiquidity functions. |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +### 7.unfarmBuyBurn |
| 111 | + **Purpose (brief)**: The unfarmBuyBurn function is used to increase BOOST price back to peg and is symmetrical to the MintSellFarm function. |
| 112 | +First it removes protocol owned liquidity, swaps the USD for Boost, then burns the BOOST |
| 113 | + |
| 114 | +**Purpose (detailed):** |
| 115 | +* The function first checks the available liquidity using the position() function and ensures the requested liquidity removal does not exceed the protocol's allowed limits (liquidityAmountLimit). |
| 116 | +* The function removes liquidity using the appropriate underlying pool logic: it calls burnAndCollect() in solidly v3, and withdraw() and removeLiquidity() in Solidly v2. |
| 117 | +* The function reverts if BOOST amount withdrawn is lesser than USDC amount, as BOOST pool implied price would not be below peg. |
| 118 | +* Once liquidity is removed, the function swaps the USD tokens for BOOST tokens in the pool. The swap ensures that at least minBoostAmountOut BOOST is received. |
| 119 | +* The BOOST tokens received from removing liquidity and swapping USD are burned to reduce the circulating supply of BOOST. |
| 120 | + |
| 121 | +**Return values:** |
| 122 | +* boostRemoved (uint256): The amount of BOOST tokens removed from the liquidity pool. |
| 123 | +* usdRemoved (uint256): The amount of USD tokens removed from the liquidity pool. |
| 124 | +* boostAmountOut (uint256): The amount of BOOST tokens received from swapping USD tokens. |
| 125 | + |
| 126 | +**Parameters (for Solidly v3)**: |
| 127 | +* liquidity (uint256): The amount of liquidity tokens to be removed from the BOOST-USD pool. |
| 128 | +* minBoostRemove (uint256): The minimum amount of BOOST tokens that should be removed from the pool when liquidity is withdrawn. |
| 129 | +* minUsdRemove (uint256): The minimum amount of USD tokens that should be removed from the pool when liquidity is withdrawn. |
| 130 | +* minBoostAmountOut (uint256): The minimum amount of BOOST tokens to be received after swapping USD tokens for BOOST. |
| 131 | +* deadline (uint256): The deadline by which the transaction must be completed. If this deadline is exceeded, the transaction will fail. |
| 132 | + |
| 133 | +## III) PublicAMO: |
| 134 | + |
| 135 | +Purpose: |
| 136 | + |
| 137 | +1) The contract ensures decentralised security, |
| 138 | +It lets any participant (even though the contract allows for whitelisting) rebalance the BOOST price permissionless. |
| 139 | +It ensures that the health of the protocol does not depend on our team or any given (possibly centralised) infrastructure: it is permissionless. |
| 140 | + |
| 141 | +2) Mechanism: |
| 142 | +Technically, it triggers the AMO mechanisms where the amount to mint, sell and farm, or unfarm and buy back, is computed onchain —— rather than by an off-chain bot. |
| 143 | + |
| 144 | +3) A user-friendly interface: |
| 145 | +The PublicAMO contract provides a simpler interface for users or other contracts to interact with the underlying LiquidityAMO functionality, such as minting, selling, adding/removing liquidity, etc., without exposing all the internal difficulties of the LiquidityAMO contract. |
| 146 | + |
0 commit comments