github.com/gravity-devs/liquidity@v1.5.3/x/liquidity/keeper/msg_server_test.go (about) 1 package keeper_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 sdk "github.com/cosmos/cosmos-sdk/types" 8 "github.com/stretchr/testify/require" 9 10 "github.com/gravity-devs/liquidity/app" 11 "github.com/gravity-devs/liquidity/x/liquidity" 12 "github.com/gravity-devs/liquidity/x/liquidity/types" 13 ) 14 15 // Although written in msg_server_test.go, it is approached at the keeper level rather than at the msgServer level 16 // so is not included in the coverage. 17 18 func TestMsgCreatePool(t *testing.T) { 19 simapp, ctx := createTestInput() 20 simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams()) 21 params := simapp.LiquidityKeeper.GetParams(ctx) 22 23 poolTypeID := types.DefaultPoolTypeID 24 addrs := app.AddTestAddrs(simapp, ctx, 3, params.PoolCreationFee) 25 26 denomA := "uETH" 27 denomB := "uUSD" 28 denomA, denomB = types.AlphabeticalDenomPair(denomA, denomB) 29 30 deposit := sdk.NewCoins(sdk.NewCoin(denomA, sdk.NewInt(100*1000000)), sdk.NewCoin(denomB, sdk.NewInt(2000*1000000))) 31 app.SaveAccount(simapp, ctx, addrs[0], deposit) 32 33 depositA := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomA) 34 depositB := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomB) 35 depositBalance := sdk.NewCoins(depositA, depositB) 36 37 require.Equal(t, deposit, depositBalance) 38 39 msg := types.NewMsgCreatePool(addrs[0], poolTypeID, depositBalance) 40 41 _, err := simapp.LiquidityKeeper.CreatePool(ctx, msg) 42 require.NoError(t, err) 43 44 pools := simapp.LiquidityKeeper.GetAllPools(ctx) 45 require.Equal(t, 1, len(pools)) 46 require.Equal(t, uint64(1), pools[0].Id) 47 require.Equal(t, uint64(1), simapp.LiquidityKeeper.GetNextPoolID(ctx)-1) 48 require.Equal(t, denomA, pools[0].ReserveCoinDenoms[0]) 49 require.Equal(t, denomB, pools[0].ReserveCoinDenoms[1]) 50 51 poolCoin := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pools[0]) 52 creatorBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pools[0].PoolCoinDenom) 53 require.Equal(t, poolCoin, creatorBalance.Amount) 54 55 _, err = simapp.LiquidityKeeper.CreatePool(ctx, msg) 56 require.ErrorIs(t, err, types.ErrPoolAlreadyExists) 57 } 58 59 func TestMsgDepositWithinBatch(t *testing.T) { 60 simapp, ctx := createTestInput() 61 simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams()) 62 params := simapp.LiquidityKeeper.GetParams(ctx) 63 64 poolTypeID := types.DefaultPoolTypeID 65 addrs := app.AddTestAddrs(simapp, ctx, 4, params.PoolCreationFee) 66 67 denomA := "uETH" 68 denomB := "uUSD" 69 denomA, denomB = types.AlphabeticalDenomPair(denomA, denomB) 70 71 deposit := sdk.NewCoins(sdk.NewCoin(denomA, sdk.NewInt(100*1000000)), sdk.NewCoin(denomB, sdk.NewInt(2000*1000000))) 72 app.SaveAccount(simapp, ctx, addrs[0], deposit) 73 app.SaveAccount(simapp, ctx, addrs[1], deposit) 74 75 depositA := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomA) 76 depositB := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomB) 77 depositBalance := sdk.NewCoins(depositA, depositB) 78 79 require.Equal(t, deposit, depositBalance) 80 81 depositA = simapp.BankKeeper.GetBalance(ctx, addrs[1], denomA) 82 depositB = simapp.BankKeeper.GetBalance(ctx, addrs[1], denomB) 83 depositBalance = sdk.NewCoins(depositA, depositB) 84 85 require.Equal(t, deposit, depositBalance) 86 87 createMsg := types.NewMsgCreatePool(addrs[0], poolTypeID, depositBalance) 88 89 _, err := simapp.LiquidityKeeper.CreatePool(ctx, createMsg) 90 require.NoError(t, err) 91 92 pools := simapp.LiquidityKeeper.GetAllPools(ctx) 93 pool := pools[0] 94 95 poolCoinBefore := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool) 96 97 depositMsg := types.NewMsgDepositWithinBatch(addrs[1], pool.Id, deposit) 98 _, err = simapp.LiquidityKeeper.DepositWithinBatch(ctx, depositMsg) 99 require.NoError(t, err) 100 101 poolBatch, found := simapp.LiquidityKeeper.GetPoolBatch(ctx, depositMsg.PoolId) 102 require.True(t, found) 103 msgs := simapp.LiquidityKeeper.GetAllPoolBatchDepositMsgs(ctx, poolBatch) 104 require.Equal(t, 1, len(msgs)) 105 106 err = simapp.LiquidityKeeper.ExecuteDeposit(ctx, msgs[0], poolBatch) 107 require.NoError(t, err) 108 109 poolCoin := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool) 110 depositorBalance := simapp.BankKeeper.GetBalance(ctx, addrs[1], pool.PoolCoinDenom) 111 require.Equal(t, poolCoin.Sub(poolCoinBefore), depositorBalance.Amount) 112 } 113 114 func TestMsgWithdrawWithinBatch(t *testing.T) { 115 simapp, ctx := createTestInput() 116 simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams()) 117 params := simapp.LiquidityKeeper.GetParams(ctx) 118 119 poolTypeID := types.DefaultPoolTypeID 120 addrs := app.AddTestAddrs(simapp, ctx, 3, params.PoolCreationFee) 121 122 denomA := "uETH" 123 denomB := "uUSD" 124 denomA, denomB = types.AlphabeticalDenomPair(denomA, denomB) 125 126 deposit := sdk.NewCoins(sdk.NewCoin(denomA, sdk.NewInt(100*1000000)), sdk.NewCoin(denomB, sdk.NewInt(2000*1000000))) 127 app.SaveAccount(simapp, ctx, addrs[0], deposit) 128 129 depositA := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomA) 130 depositB := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomB) 131 depositBalance := sdk.NewCoins(depositA, depositB) 132 133 require.Equal(t, deposit, depositBalance) 134 135 createMsg := types.NewMsgCreatePool(addrs[0], poolTypeID, depositBalance) 136 137 _, err := simapp.LiquidityKeeper.CreatePool(ctx, createMsg) 138 require.NoError(t, err) 139 140 pools := simapp.LiquidityKeeper.GetAllPools(ctx) 141 pool := pools[0] 142 143 poolCoinBefore := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool) 144 withdrawerPoolCoinBefore := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom) 145 146 fmt.Println(poolCoinBefore, withdrawerPoolCoinBefore.Amount) 147 require.Equal(t, poolCoinBefore, withdrawerPoolCoinBefore.Amount) 148 withdrawMsg := types.NewMsgWithdrawWithinBatch(addrs[0], pool.Id, sdk.NewCoin(pool.PoolCoinDenom, poolCoinBefore)) 149 150 _, err = simapp.LiquidityKeeper.WithdrawWithinBatch(ctx, withdrawMsg) 151 require.NoError(t, err) 152 153 poolBatch, found := simapp.LiquidityKeeper.GetPoolBatch(ctx, withdrawMsg.PoolId) 154 require.True(t, found) 155 msgs := simapp.LiquidityKeeper.GetAllPoolBatchWithdrawMsgStates(ctx, poolBatch) 156 require.Equal(t, 1, len(msgs)) 157 158 err = simapp.LiquidityKeeper.ExecuteWithdrawal(ctx, msgs[0], poolBatch) 159 require.NoError(t, err) 160 161 poolCoinAfter := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool) 162 withdrawerPoolCoinAfter := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom) 163 require.True(t, true, poolCoinAfter.IsZero()) 164 require.True(t, true, withdrawerPoolCoinAfter.IsZero()) 165 withdrawerDenomABalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[0]) 166 withdrawerDenomBBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[1]) 167 require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]), withdrawerDenomABalance.Amount) 168 require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]), withdrawerDenomBBalance.Amount) 169 170 } 171 172 func TestMsgGetLiquidityPoolMetadata(t *testing.T) { 173 simapp, ctx := createTestInput() 174 simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams()) 175 params := simapp.LiquidityKeeper.GetParams(ctx) 176 177 poolTypeID := types.DefaultPoolTypeID 178 addrs := app.AddTestAddrs(simapp, ctx, 3, params.PoolCreationFee) 179 180 denomA := "uETH" 181 denomB := "uUSD" 182 denomA, denomB = types.AlphabeticalDenomPair(denomA, denomB) 183 184 deposit := sdk.NewCoins(sdk.NewCoin(denomA, sdk.NewInt(100*1000000)), sdk.NewCoin(denomB, sdk.NewInt(2000*1000000))) 185 app.SaveAccount(simapp, ctx, addrs[0], deposit) 186 187 depositA := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomA) 188 depositB := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomB) 189 depositBalance := sdk.NewCoins(depositA, depositB) 190 191 require.Equal(t, deposit, depositBalance) 192 193 msg := types.NewMsgCreatePool(addrs[0], poolTypeID, depositBalance) 194 195 _, err := simapp.LiquidityKeeper.CreatePool(ctx, msg) 196 require.NoError(t, err) 197 198 pools := simapp.LiquidityKeeper.GetAllPools(ctx) 199 require.Equal(t, 1, len(pools)) 200 require.Equal(t, uint64(1), pools[0].Id) 201 require.Equal(t, uint64(1), simapp.LiquidityKeeper.GetNextPoolID(ctx)-1) 202 require.Equal(t, denomA, pools[0].ReserveCoinDenoms[0]) 203 require.Equal(t, denomB, pools[0].ReserveCoinDenoms[1]) 204 205 poolCoin := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pools[0]) 206 creatorBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pools[0].PoolCoinDenom) 207 require.Equal(t, poolCoin, creatorBalance.Amount) 208 209 _, err = simapp.LiquidityKeeper.CreatePool(ctx, msg) 210 require.ErrorIs(t, err, types.ErrPoolAlreadyExists) 211 212 metaData := simapp.LiquidityKeeper.GetPoolMetaData(ctx, pools[0]) 213 require.Equal(t, pools[0].Id, metaData.PoolId) 214 215 reserveCoin := simapp.LiquidityKeeper.GetReserveCoins(ctx, pools[0]) 216 require.Equal(t, reserveCoin, metaData.ReserveCoins) 217 require.Equal(t, msg.DepositCoins, metaData.ReserveCoins) 218 219 totalSupply := sdk.NewCoin(pools[0].PoolCoinDenom, simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pools[0])) 220 require.Equal(t, totalSupply, metaData.PoolCoinTotalSupply) 221 require.Equal(t, creatorBalance, metaData.PoolCoinTotalSupply) 222 } 223 224 func TestMsgSwapWithinBatch(t *testing.T) { 225 simapp, ctx := app.CreateTestInput() 226 params := simapp.LiquidityKeeper.GetParams(ctx) 227 228 depositCoins := sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1_000_000_000)), sdk.NewCoin(DenomY, sdk.NewInt(1_000_000_000))) 229 depositorAddr := app.AddRandomTestAddr(simapp, ctx, depositCoins.Add(params.PoolCreationFee...)) 230 user := app.AddRandomTestAddr(simapp, ctx, sdk.NewCoins(sdk.NewCoin(DenomX, sdk.NewInt(1_000_000_000)), sdk.NewCoin(DenomY, sdk.NewInt(1_000_000_000)))) 231 232 pool, err := simapp.LiquidityKeeper.CreatePool(ctx, &types.MsgCreatePool{ 233 PoolCreatorAddress: depositorAddr.String(), 234 PoolTypeId: types.DefaultPoolTypeID, 235 DepositCoins: depositCoins, 236 }) 237 require.NoError(t, err) 238 239 cases := []struct { 240 expectedErr string // empty means no error expected 241 swapFeeRate sdk.Dec 242 msg *types.MsgSwapWithinBatch 243 afterBalance sdk.Coins 244 }{ 245 { 246 "", 247 types.DefaultSwapFeeRate, 248 &types.MsgSwapWithinBatch{ 249 SwapRequesterAddress: user.String(), 250 PoolId: pool.Id, 251 SwapTypeId: pool.TypeId, 252 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10000)), 253 OfferCoinFee: types.GetOfferCoinFee(sdk.NewCoin(DenomX, sdk.NewInt(10000)), params.SwapFeeRate), 254 DemandCoinDenom: DenomY, 255 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 256 }, 257 types.MustParseCoinsNormalized("999989985denomX,1000009984denomY"), 258 }, 259 { 260 // bad offer coin denom 261 "bad offer coin fee", 262 types.DefaultSwapFeeRate, 263 &types.MsgSwapWithinBatch{ 264 SwapRequesterAddress: user.String(), 265 PoolId: pool.Id, 266 SwapTypeId: pool.TypeId, 267 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10000)), 268 OfferCoinFee: sdk.NewCoin(DenomY, sdk.NewInt(15)), 269 DemandCoinDenom: DenomY, 270 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 271 }, 272 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 273 }, 274 { 275 "bad offer coin fee", 276 types.DefaultSwapFeeRate, 277 &types.MsgSwapWithinBatch{ 278 SwapRequesterAddress: user.String(), 279 PoolId: pool.Id, 280 SwapTypeId: pool.TypeId, 281 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10000)), 282 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(14)), 283 DemandCoinDenom: DenomY, 284 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 285 }, 286 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 287 }, 288 { 289 "bad offer coin fee", 290 types.DefaultSwapFeeRate, 291 &types.MsgSwapWithinBatch{ 292 SwapRequesterAddress: user.String(), 293 PoolId: pool.Id, 294 SwapTypeId: pool.TypeId, 295 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10000)), 296 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(16)), 297 DemandCoinDenom: DenomY, 298 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 299 }, 300 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 301 }, 302 { 303 "", 304 types.DefaultSwapFeeRate, 305 &types.MsgSwapWithinBatch{ 306 SwapRequesterAddress: user.String(), 307 PoolId: pool.Id, 308 SwapTypeId: pool.TypeId, 309 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10001)), 310 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(16)), 311 DemandCoinDenom: DenomY, 312 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 313 }, 314 types.MustParseCoinsNormalized("999989983denomX,1000009984denomY"), 315 }, 316 { 317 "", 318 types.DefaultSwapFeeRate, 319 &types.MsgSwapWithinBatch{ 320 SwapRequesterAddress: user.String(), 321 PoolId: pool.Id, 322 SwapTypeId: pool.TypeId, 323 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(100)), 324 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(1)), 325 DemandCoinDenom: DenomY, 326 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 327 }, 328 types.MustParseCoinsNormalized("999999899denomX,1000000098denomY"), 329 }, 330 { 331 "bad offer coin fee", 332 types.DefaultSwapFeeRate, 333 &types.MsgSwapWithinBatch{ 334 SwapRequesterAddress: user.String(), 335 PoolId: pool.Id, 336 SwapTypeId: pool.TypeId, 337 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(100)), 338 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(0)), 339 DemandCoinDenom: DenomY, 340 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 341 }, 342 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 343 }, 344 { 345 "", 346 types.DefaultSwapFeeRate, 347 &types.MsgSwapWithinBatch{ 348 SwapRequesterAddress: user.String(), 349 PoolId: pool.Id, 350 SwapTypeId: pool.TypeId, 351 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(1000)), 352 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(2)), 353 DemandCoinDenom: DenomY, 354 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 355 }, 356 types.MustParseCoinsNormalized("999998998denomX,1000000997denomY"), 357 }, 358 { 359 "bad offer coin fee", 360 types.DefaultSwapFeeRate, 361 &types.MsgSwapWithinBatch{ 362 SwapRequesterAddress: user.String(), 363 PoolId: pool.Id, 364 SwapTypeId: pool.TypeId, 365 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(1000)), 366 OfferCoinFee: sdk.NewCoin(DenomX, sdk.NewInt(1)), 367 DemandCoinDenom: DenomY, 368 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 369 }, 370 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 371 }, 372 { 373 "", 374 sdk.ZeroDec(), 375 &types.MsgSwapWithinBatch{ 376 SwapRequesterAddress: user.String(), 377 PoolId: pool.Id, 378 SwapTypeId: pool.TypeId, 379 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(1000)), 380 OfferCoinFee: sdk.NewCoin(DenomX, sdk.ZeroInt()), 381 DemandCoinDenom: DenomY, 382 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 383 }, 384 types.MustParseCoinsNormalized("999999000denomX,1000000999denomY"), 385 }, 386 { 387 "does not match the reserve coin of the pool", 388 types.DefaultSwapFeeRate, 389 &types.MsgSwapWithinBatch{ 390 SwapRequesterAddress: user.String(), 391 PoolId: pool.Id, 392 SwapTypeId: pool.TypeId, 393 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(10000)), 394 OfferCoinFee: types.GetOfferCoinFee(sdk.NewCoin(DenomX, sdk.NewInt(10000)), params.SwapFeeRate), 395 DemandCoinDenom: DenomA, 396 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 397 }, 398 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 399 }, 400 { 401 "can not exceed max order ratio of reserve coins that can be ordered at a order", 402 types.DefaultSwapFeeRate, 403 &types.MsgSwapWithinBatch{ 404 SwapRequesterAddress: user.String(), 405 PoolId: pool.Id, 406 SwapTypeId: pool.TypeId, 407 OfferCoin: sdk.NewCoin(DenomX, sdk.NewInt(100_000_001)), 408 OfferCoinFee: types.GetOfferCoinFee(sdk.NewCoin(DenomX, sdk.NewInt(100_000_001)), params.SwapFeeRate), 409 DemandCoinDenom: DenomY, 410 OrderPrice: sdk.MustNewDecFromStr("1.00002"), 411 }, 412 types.MustParseCoinsNormalized("1000000000denomX,1000000000denomY"), 413 }, 414 } 415 416 for _, tc := range cases { 417 cacheCtx, _ := ctx.CacheContext() 418 cacheCtx = cacheCtx.WithBlockHeight(1) 419 params.SwapFeeRate = tc.swapFeeRate 420 simapp.LiquidityKeeper.SetParams(cacheCtx, params) 421 _, err = simapp.LiquidityKeeper.SwapWithinBatch(cacheCtx, tc.msg, types.CancelOrderLifeSpan) 422 if tc.expectedErr == "" { 423 require.NoError(t, err) 424 liquidity.EndBlocker(cacheCtx, simapp.LiquidityKeeper) 425 } else { 426 require.EqualError(t, err, tc.expectedErr) 427 } 428 moduleAccAddress := simapp.AccountKeeper.GetModuleAddress(types.ModuleName) 429 require.True(t, simapp.BankKeeper.GetAllBalances(cacheCtx, moduleAccAddress).IsZero()) 430 require.Equal(t, tc.afterBalance, simapp.BankKeeper.GetAllBalances(cacheCtx, user)) 431 } 432 }