github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/spec/03_state_transitions.md (about) 1 <!-- order: 3 --> 2 3 # State Transitions 4 5 These messages (Msg) in the liquidity module trigger state transitions. 6 7 ## Coin Escrow for Liquidity Module Messages 8 9 Transaction confirmation causes state transition on the [Bank](https://docs.cosmos.network/master/modules/bank/) module. Some messages on the liquidity module require coin escrow before confirmation. 10 11 The coin escrow processes for each message type are: 12 13 ### MsgDepositWithinBatch 14 15 To deposit coins into an existing `Pool`, the depositor must escrow `DepositCoins` into `LiquidityModuleEscrowAccount`. 16 17 ### MsgWithdrawWithinBatch 18 19 To withdraw coins from a `Pool`, the withdrawer must escrow `PoolCoin` into `LiquidityModuleEscrowAccount`. 20 21 ### MsgSwapWithinBatch 22 23 To request a coin swap, the swap requestor must escrow `OfferCoin` into `LiquidityModuleEscrowAccount`. 24 25 ## LiquidityPoolBatch Execution 26 27 Batch execution causes state transitions on the `Bank` module. The following categories describe state transition executed by each process in the `PoolBatch` execution. 28 29 ### Coin Swap 30 31 After a successful coin swap, coins accumulated in `LiquidityModuleEscrowAccount` for coin swaps are sent to other swap requestors(self-swap) or to the `Pool`(pool-swap). Fees are also sent to the liquidity `Pool`. 32 33 ### LiquidityPool Deposit 34 35 After a successful deposit transaction, escrowed coins are sent to the `ReserveAccount` of the targeted `Pool` and new pool coins are minted and sent to the depositor. 36 37 ### LiquidityPool Withdrawal 38 39 After a successful withdraw transaction, escrowed pool coins are burned and a corresponding amount of reserve coins are sent to the withdrawer from the liquidity `Pool`. 40 41 ## Pseudo Algorithm for LiquidityPoolBatch Execution 42 43 If you are curious, you can see a Python simulation script on the B-Harvest [GitHub repo](https://github.com/b-harvest/Liquidity-Module-For-the-Hub/blob/master/pseudo-batch-execution-logic/batch.py). 44 45 ## Swap Price Calculation 46 47 Swap execution applies a universal swap ratio for all swap requests. 48 49 Swap price calculations are used for these cases. 50 51 **Find price direction** 52 53 Variables: 54 55 - `X`: Reserve of X coin 56 - `Y`: Reserve of Y coin before this batch execution 57 - `PoolPrice` = `X`/`Y` 58 - `XOverLastPrice`: amount of orders that swap X for Y with order price higher than the last `PoolPrice` 59 - `XAtLastPrice`: amount of orders that swap X for Y with order price equal to the last `PoolPrice` 60 - `YUnderLastPrice`: amount of orders that swap Y for X with order price lower than last `PoolPrice` 61 - `YAtLastPrice`: amount of orders that swap Y for X with order price equal to the last `PoolPrice` 62 63 - **Increase**: swap price is increased from the last `PoolPrice` 64 65 - `XOverLastPrice` > (`YUnderLastPrice`+`YAtLastPrice`)*`PoolPrice` 66 67 - **Decrease**: swap price is decreased from the last `PoolPrice` 68 69 - `YUnderLastPrice` > (`XOverLastPrice`+`XAtLastPrice`)/`PoolPrice` 70 71 - **Stay**: swap price is not changed from the last `PoolPrice` when the increase and decrease inequalities do not hold 72 73 ### Stay case 74 75 Variables: 76 77 - `swapPrice` = last `PoolPrice` 78 - `EX`: All executable orders that swap X for Y with order price equal to or greater than last `PoolPrice` 79 - `EY`: All executable orders that swap Y for X with order price equal or lower than last `PoolPrice` 80 81 - **ExactMatch**: If `EX` == `EY`*`swapPrice` 82 83 - Amount of X coins matched from swap orders = `EX` 84 - Amount of Y coins matched from swap orders = `EY` 85 86 - **FractionalMatch** 87 88 - If `EX` > `EY`*`swapPrice`: Residual X order amount remains 89 90 - Amount of X coins matched from swap orders = `EY`*`swapPrice` 91 - Amount of Y coins matched from swap orders = `EY` 92 93 - If `EY` > `EX`/`swapPrice`: Residual Y order amount remains 94 95 - Amount of X coins matched from swap orders = `EX` 96 - Amount of Y coins matched from swap orders = `EX`/`swapPrice` 97 98 ### Increase case 99 100 Iteration: iterate `orderPrice(i)` of all swap orders from low to high. 101 102 Variables: 103 104 - `EX(i)`: Sum of all order amount of swap orders that swap X for Y with order price equal or higher than this `orderPrice(i)` 105 - `EY(i)`: Sum of all order amounts of swap orders that swap Y for X with order price equal or lower than this `orderPrice(i)` 106 107 - ExactMatch: SwapPrice is found between two orderPrices 108 109 - `swapPrice(i)` = (`X` + 2_`EX(i)`)/(`Y` + 2_`EY(i-1)`) 110 111 - condition1) `orderPrice(i-1)` < `swapPrice(i)` < `orderPrice(i)` 112 113 - `PoolY(i)` = (`swapPrice(i)`_`Y` - `X`) / (2_`swapPrice(i)`) 114 115 - condition2) `PoolY(i)` >= 0 116 117 - If both above conditions are met, `swapPrice` is the swap price for this iteration 118 119 - Amount of X coins matched = `EX(i)` 120 121 - If one of these conditions doesn't hold, go to FractionalMatch 122 123 - FractionalMatch: SwapPrice is found at an orderPrice 124 125 - `swapPrice(i)` = `orderPrice(i)` 126 - `PoolY(i)` = (`swapPrice(i)`_`Y` - `X`) / (2_`swapPrice(i)`) 127 - Amount of X coins matched: 128 129 - `EX(i)` ← min[ `EX(i)`, (`EY(i)`+`PoolY(i)`)*`swapPrice(i)` ] 130 131 - Find optimized swapPrice: 132 133 - Find `swapPrice(k)` that has the largest amount of X coins matched 134 135 - this is our optimized swap price 136 - corresponding swap result variables 137 138 - `swapPrice(k)`, `EX(k)`, `EY(k)`, `PoolY(k)` 139 140 ### Decrease case 141 142 Iteration: iterate `orderPrice(i)` of all swap orders from high to low. 143 144 Variables: 145 146 - `EX(i)`: Sum of all order amount of swap orders that swap X for Y with order price equal or higher than this `orderPrice(i)` 147 - `EY(i)`: Sum of all order amount of swap orders that swap Y for X with order price equal or lower than this `orderPrice(i)` 148 149 - ExactMatch: SwapPrice is found between two orderPrices 150 151 - `swapPrice(i)` = (`X` + 2_`EX(i)`)/(`Y` + 2_`EY(i-1)`) 152 153 - condition1) `orderPrice(i)` < `swapPrice(i)` < `orderPrice(i-1)` 154 155 - `PoolX(i)` = (`X` - `swapPrice(i)`*`Y`)/2 156 157 - condition2) `PoolX(i)` >= 0 158 159 - If both above conditions are met, `swapPrice` is the swap price for this iteration 160 161 - Amount of Y coins matched = `EY(i)` 162 163 - If one of these conditions doesn't hold, go to FractionalMatch 164 165 - FractionalMatch: SwapPrice is found at an orderPrice 166 167 - `swapPrice(i)` = `orderPrice(i)` 168 169 - `PoolX(i)` = (`X` - `swapPrice(i)`*`Y`)/2 170 171 - Amount of Y coins matched: 172 173 - `EY(i)` ← min[ `EY(i)`, (`EX(i)`+`PoolX(i)`)/`swapPrice(i)` ] 174 175 - Find optimized swapPrice 176 177 - Find `swapPrice(k)` that has the largest amount of Y coins matched 178 179 - this is our optimized swap price 180 - corresponding swap result variables 181 182 - `swapPrice(k)`, `EX(k)`, `EY(k)`, `PoolX(k)` 183 184 ### Calculate matching result 185 186 - for swap orders from X to Y 187 188 - Iteration: iterate `orderPrice(i)` of swap orders from X to Y (high to low) 189 190 - sort by order price (high to low), sum all order amount with each `orderPrice(i)` 191 - if `EX(i)` ≤ `EX(k)` 192 193 - `fractionalRatio` = 1 194 195 - if `EX(i)` > `EX(k)` 196 197 - `fractionalRatio(i)` = (`EX(k)` - `EX(i-1)`) / (`EX(i)` - `EX(i-1)`) 198 - break the iteration 199 200 - matching amount for swap orders with this `orderPrice(i)`: 201 202 - `matchingAmt` = `offerAmt` * `fractionalRatio(i)` 203 204 - for swap orders from Y to X 205 206 - Iteration: iterate `orderPrice(i)` of swap orders from Y to X (low to high) 207 208 - sort by order price (low to high), sum all order amount with each `orderPrice(i)` 209 - if `EY(i)` ≤ `EY(k)` 210 211 - `fractionalRatio` = 1 212 213 - if `EY(i)` > `EY(k)` 214 215 - `fractionalRatio(i)` = (`EY(k)` - `EY(i-1)`) / (`EY(i)` - `EY(i-1)`) 216 - break the iteration 217 218 - matching amount for swap orders with this `orderPrice(i)`: 219 220 - `matchingAmt` = `offerAmt` * `fractionalRatio(i)` 221 222 ### Swap Fee Payment 223 224 Rather than taking fee solely from `OfferCoin`, liquidity module is designed to take fees half from `OfferCoin`, and the other half from `ExchangedCoin`. This smooths out an impact of the fee payment process. 225 - **OfferCoin Fee Reservation ( fee before batch process, in OfferCoin )** 226 - when user orders 100 Xcoin, the swap message demands 227 - `OfferCoin`(in Xcoin) : 100 228 - `ReservedOfferCoinFeeAmount`(in Xcoin) = `OfferCoin`*(`SwapFeeRate`/2) 229 - user needs to have at least 100+100*(`SwapFeeRate`/2) amount of Xcoin to successfully commit this swap message 230 - the message fails when user's balance is below this amount 231 - **Actual Fee Payment** 232 - if 10 Xcoin is executed 233 - **OfferCoin Fee Payment from Reserved OfferCoin Fee** 234 - `OfferCoinFeeAmount`(in Xcoin) = (10/100)*`ReservedOfferCoinFeeAmount` 235 - `ReservedOfferCoinFeeAmount` is reduced from this fee payment 236 - **ExchangedCoin Fee Payment ( fee after batch process, in ExchangedCoin )** 237 - `ExchangedCoinFeeAmount`(in Ycoin) = `OfferCoinFeeAmount` / `SwapPrice` 238 - this is exactly equal value compared to advance fee payment assuming the current SwapPrice, to minimize the pool price impact from fee payment process 239 240 - Swap fees are proportional to the coins received from matched swap orders. 241 - Swap fees are sent to the liquidity pool. 242 - The decimal points of the swap fees are rounded up. 243 244 ## Cancel unexecuted swap orders with expired CancelHeight 245 246 After execution of `PoolBatch`, all remaining swap orders with `CancelHeight` equal to or higher than current height are cancelled. 247 248 ## Refund escrowed coins 249 250 Refunds are issued for escrowed coins for cancelled swap order and failed create pool, deposit, and withdraw messages.