Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6202279

Browse files
authored
Merge pull request tendermint#91 from b-harvest/fix-empty-incorrect-pool-case
Fix empty, incorrect pool cases
2 parents 527ea2e + 52caf9d commit 6202279

File tree

5 files changed

+190
-44
lines changed

5 files changed

+190
-44
lines changed

x/liquidity/keeper/liquidity_pool.go

Lines changed: 100 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func (k Keeper) ValidateMsgCreateLiquidityPool(ctx sdk.Context, msg *types.MsgCr
4141
return types.ErrBadOrderingReserveCoin
4242
}
4343

44+
if denomA == denomB {
45+
return types.ErrEqualDenom
46+
}
47+
4448
poolKey := types.GetPoolKey(msg.ReserveCoinDenoms, msg.PoolTypeIndex)
4549
reserveAcc := types.GetPoolReserveAcc(poolKey)
4650
_, found := k.GetLiquidityPoolByReserveAccIndex(ctx, reserveAcc)
@@ -133,7 +137,7 @@ func (k Keeper) CreateLiquidityPool(ctx sdk.Context, msg *types.MsgCreateLiquidi
133137

134138
PoolCoinDenom := types.GetPoolCoinDenom(reserveAcc)
135139

136-
liquidityPool := types.LiquidityPool{
140+
pool := types.LiquidityPool{
137141
//PoolId: will set on SetLiquidityPoolAtomic
138142
PoolTypeIndex: msg.PoolTypeIndex,
139143
ReserveCoinDenoms: reserveCoinDenoms,
@@ -142,7 +146,7 @@ func (k Keeper) CreateLiquidityPool(ctx sdk.Context, msg *types.MsgCreateLiquidi
142146
}
143147

144148
batchEscrowAcc := k.accountKeeper.GetModuleAddress(types.ModuleName)
145-
mintPoolCoin := sdk.NewCoins(sdk.NewCoin(liquidityPool.PoolCoinDenom, params.InitPoolCoinMintAmount))
149+
mintPoolCoin := sdk.NewCoins(sdk.NewCoin(pool.PoolCoinDenom, params.InitPoolCoinMintAmount))
146150
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, mintPoolCoin); err != nil {
147151
return err
148152
}
@@ -166,23 +170,24 @@ func (k Keeper) CreateLiquidityPool(ctx sdk.Context, msg *types.MsgCreateLiquidi
166170
return err
167171
}
168172

169-
liquidityPool = k.SetLiquidityPoolAtomic(ctx, liquidityPool)
170-
batch := types.NewLiquidityPoolBatch(liquidityPool.PoolId, 1)
173+
pool = k.SetLiquidityPoolAtomic(ctx, pool)
174+
batch := types.NewLiquidityPoolBatch(pool.PoolId, 1)
171175

172176
k.SetLiquidityPoolBatch(ctx, batch)
173177

174178
// TODO: remove result state check, debugging
175-
reserveCoins := k.GetReserveCoins(ctx, liquidityPool)
179+
reserveCoins := k.GetReserveCoins(ctx, pool)
176180
lastReserveRatio := sdk.NewDecFromInt(reserveCoins[0].Amount).Quo(sdk.NewDecFromInt(reserveCoins[1].Amount))
177181
logger := k.Logger(ctx)
178-
logger.Info("createPool", msg, "pool", liquidityPool, "reserveCoins", reserveCoins, "lastReserveRatio", lastReserveRatio)
182+
logger.Info("createPool", msg, "pool", pool, "reserveCoins", reserveCoins, "lastReserveRatio", lastReserveRatio)
179183
return nil
180184
}
181185

182186
// Get reserve Coin from the liquidity pool
183187
func (k Keeper) GetReserveCoins(ctx sdk.Context, pool types.LiquidityPool) (reserveCoins sdk.Coins) {
188+
reserveAcc := pool.GetReserveAccount()
184189
for _, denom := range pool.ReserveCoinDenoms {
185-
reserveCoins = reserveCoins.Add(k.bankKeeper.GetBalance(ctx, pool.GetReserveAccount(), denom))
190+
reserveCoins = reserveCoins.Add(k.bankKeeper.GetBalance(ctx, reserveAcc, denom))
186191
}
187192
return
188193
}
@@ -247,12 +252,6 @@ func (k Keeper) ValidateMsgDepositLiquidityPool(ctx sdk.Context, msg types.MsgDe
247252
return types.ErrPoolNotExists
248253
}
249254

250-
for _, coin := range msg.DepositCoins {
251-
if !types.StringInSlice(coin.Denom, pool.ReserveCoinDenoms) {
252-
return types.ErrInvalidDenom
253-
}
254-
}
255-
256255
if msg.DepositCoins.Len() != len(pool.ReserveCoinDenoms) {
257256
return types.ErrNumOfReserveCoin
258257
}
@@ -264,6 +263,10 @@ func (k Keeper) ValidateMsgDepositLiquidityPool(ctx sdk.Context, msg types.MsgDe
264263
}
265264
// TODO: validate msgIndex
266265

266+
denomA, denomB := types.AlphabeticalDenomPair(msg.DepositCoins[0].Denom, msg.DepositCoins[1].Denom)
267+
if denomA != pool.ReserveCoinDenoms[0] || denomB != pool.ReserveCoinDenoms[1] {
268+
return types.ErrNotMatchedReserveCoin
269+
}
267270
return nil
268271
}
269272

@@ -282,7 +285,47 @@ func (k Keeper) DepositLiquidityPool(ctx sdk.Context, msg types.BatchPoolDeposit
282285
return types.ErrPoolNotExists
283286
}
284287

288+
var inputs []banktypes.Input
289+
var outputs []banktypes.Output
290+
291+
batchEscrowAcc := k.accountKeeper.GetModuleAddress(types.ModuleName)
292+
reserveAcc := pool.GetReserveAccount()
293+
depositor := msg.Msg.GetDepositor()
294+
params := k.GetParams(ctx)
295+
285296
reserveCoins := k.GetReserveCoins(ctx, pool)
297+
298+
// case of reserve coins has run out, ReinitializePool
299+
if reserveCoins.IsZero() {
300+
mintPoolCoin := sdk.NewCoins(sdk.NewCoin(pool.PoolCoinDenom, params.InitPoolCoinMintAmount))
301+
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, mintPoolCoin); err != nil {
302+
return err
303+
}
304+
inputs = append(inputs, banktypes.NewInput(batchEscrowAcc, msg.Msg.DepositCoins))
305+
outputs = append(outputs, banktypes.NewOutput(reserveAcc, msg.Msg.DepositCoins))
306+
307+
inputs = append(inputs, banktypes.NewInput(batchEscrowAcc, mintPoolCoin))
308+
outputs = append(outputs, banktypes.NewOutput(depositor, mintPoolCoin))
309+
310+
// execute multi-send
311+
if err := k.bankKeeper.InputOutputCoins(ctx, inputs, outputs); err != nil {
312+
return err
313+
}
314+
315+
msg.Succeed = true
316+
msg.ToDelete = true
317+
k.SetLiquidityPoolBatchDepositMsg(ctx, msg.Msg.PoolId, msg)
318+
319+
// TODO: remove result state check, debugging
320+
reserveCoins := k.GetReserveCoins(ctx, pool)
321+
lastReserveRatio := sdk.NewDecFromInt(reserveCoins[0].Amount).Quo(sdk.NewDecFromInt(reserveCoins[1].Amount))
322+
logger := k.Logger(ctx)
323+
logger.Info("ReinitializePool", msg, "pool", pool, "reserveCoins", reserveCoins, "lastReserveRatio", lastReserveRatio)
324+
return nil
325+
} else if reserveCoins.Len() != msg.Msg.DepositCoins.Len() {
326+
return types.ErrNumOfReserveCoin
327+
}
328+
286329
reserveCoins.Sort()
287330

288331
coinA := depositCoins[0]
@@ -292,12 +335,6 @@ func (k Keeper) DepositLiquidityPool(ctx sdk.Context, msg types.BatchPoolDeposit
292335
depositableAmount := coinB.Amount.ToDec().Mul(lastReserveRatio).TruncateInt()
293336
depositableAmountA := coinA.Amount
294337
depositableAmountB := coinB.Amount
295-
var inputs []banktypes.Input
296-
var outputs []banktypes.Output
297-
298-
batchEscrowAcc := k.accountKeeper.GetModuleAddress(types.ModuleName)
299-
reserveAcc := pool.GetReserveAccount()
300-
depositor := msg.Msg.GetDepositor()
301338

302339
if coinA.Amount.LT(depositableAmount) {
303340
depositableAmountB = coinA.Amount.ToDec().Quo(lastReserveRatio).TruncateInt()
@@ -330,22 +367,22 @@ func (k Keeper) DepositLiquidityPool(ctx sdk.Context, msg types.BatchPoolDeposit
330367
outputs = append(outputs, banktypes.NewOutput(reserveAcc, sdk.NewCoins(coinB)))
331368
}
332369

333-
// execute multi-send
334-
if err := k.bankKeeper.InputOutputCoins(ctx, inputs, outputs); err != nil {
335-
return err
336-
}
337-
338370
// calculate pool token mint amount
339371
poolCoinAmt := k.GetPoolCoinTotalSupply(ctx, pool).Mul(depositableAmountA).Quo(reserveCoins[0].Amount) // TODO: coinA after executed ?
340-
poolCoin := sdk.NewCoins(sdk.NewCoin(pool.PoolCoinDenom, poolCoinAmt))
372+
mintPoolCoin := sdk.NewCoins(sdk.NewCoin(pool.PoolCoinDenom, poolCoinAmt))
341373

342374
// mint pool token to Depositor
343-
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, poolCoin); err != nil {
375+
if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, mintPoolCoin); err != nil {
344376
panic(err)
345377
}
346-
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, depositor, poolCoin); err != nil {
347-
panic(err)
378+
inputs = append(inputs, banktypes.NewInput(batchEscrowAcc, mintPoolCoin))
379+
outputs = append(outputs, banktypes.NewOutput(depositor, mintPoolCoin))
380+
381+
// execute multi-send
382+
if err := k.bankKeeper.InputOutputCoins(ctx, inputs, outputs); err != nil {
383+
return err
348384
}
385+
349386
msg.Succeed = true
350387
msg.ToDelete = true
351388
k.SetLiquidityPoolBatchDepositMsg(ctx, msg.Msg.PoolId, msg)
@@ -363,7 +400,19 @@ func (k Keeper) ValidateMsgWithdrawLiquidityPool(ctx sdk.Context, msg types.MsgW
363400
if err := msg.ValidateBasic(); err != nil {
364401
return err
365402
}
366-
// TODO: add validate logic
403+
pool, found := k.GetLiquidityPool(ctx, msg.PoolId)
404+
if !found {
405+
return types.ErrPoolNotExists
406+
}
407+
408+
if msg.PoolCoin.Denom != pool.PoolCoinDenom {
409+
return types.ErrBadPoolCoinDenom
410+
}
411+
412+
poolCoinTotalSupply := k.GetPoolCoinTotalSupply(ctx, pool)
413+
if msg.PoolCoin.Amount.GT(poolCoinTotalSupply) {
414+
return types.ErrBadPoolCoinAmount
415+
}
367416
return nil
368417
}
369418

@@ -375,6 +424,12 @@ func (k Keeper) ValidateMsgSwap(ctx sdk.Context, msg types.MsgSwap) error {
375424
if !found {
376425
return types.ErrPoolNotExists
377426
}
427+
428+
denomA, denomB := types.AlphabeticalDenomPair(msg.OfferCoin.Denom, msg.DemandCoinDenom)
429+
if denomA != pool.ReserveCoinDenoms[0] || denomB != pool.ReserveCoinDenoms[1] {
430+
return types.ErrNotMatchedReserveCoin
431+
}
432+
378433
// can not exceed max order ratio of reserve coins that can be ordered at a order
379434
reserveCoinAmt := k.GetReserveCoins(ctx, pool).AmountOf(msg.OfferCoin.Denom)
380435
maximumOrderableAmt := reserveCoinAmt.ToDec().Mul(types.GetMaxOrderRatio()).TruncateInt()
@@ -408,37 +463,43 @@ func (k Keeper) WithdrawLiquidityPool(ctx sdk.Context, msg types.BatchPoolWithdr
408463
var inputs []banktypes.Input
409464
var outputs []banktypes.Output
410465

466+
reserveAcc := pool.GetReserveAccount()
467+
withdrawer := msg.Msg.GetWithdrawer()
468+
411469
for _, reserveCoin := range reserveCoins {
412470
withdrawAmt := reserveCoin.Amount.Mul(poolCoin.Amount).Quo(totalSupply)
413-
inputs = append(inputs, banktypes.NewInput(pool.GetReserveAccount(),
471+
inputs = append(inputs, banktypes.NewInput(reserveAcc,
414472
sdk.NewCoins(sdk.NewCoin(reserveCoin.Denom, withdrawAmt))))
415-
outputs = append(outputs, banktypes.NewOutput(msg.Msg.GetWithdrawer(),
473+
outputs = append(outputs, banktypes.NewOutput(withdrawer,
416474
sdk.NewCoins(sdk.NewCoin(reserveCoin.Denom, withdrawAmt))))
417475
}
418476

419477
// execute multi-send
420478
if err := k.bankKeeper.InputOutputCoins(ctx, inputs, outputs); err != nil {
421479
return err
422480
}
423-
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, k.accountKeeper.GetModuleAddress(types.ModuleName),
424-
types.ModuleName, poolCoins); err != nil {
425-
panic(err)
426-
}
481+
482+
// burn the escrowed pool coins
427483
if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, poolCoins); err != nil {
428484
panic(err)
429485
}
486+
430487
msg.Succeed = true
431488
msg.ToDelete = true
432489
k.SetLiquidityPoolBatchWithdrawMsg(ctx, msg.Msg.PoolId, msg)
433490
// TODO: add events for batch result, each err cases
434491

435492
// TODO: remove result state check, debugging
436493
reserveCoins = k.GetReserveCoins(ctx, pool)
437-
if !reserveCoins.Empty() {
438-
lastReserveRatio := sdk.NewDecFromInt(reserveCoins[0].Amount).Quo(sdk.NewDecFromInt(reserveCoins[1].Amount))
439-
logger := k.Logger(ctx)
440-
logger.Info("withdraw", msg, "pool", pool, "inputs", inputs, "outputs", outputs, "reserveCoins", reserveCoins, "lastReserveRatio", lastReserveRatio)
494+
495+
var lastReserveRatio sdk.Dec
496+
if reserveCoins.Empty() {
497+
lastReserveRatio = sdk.ZeroDec()
498+
} else {
499+
lastReserveRatio = sdk.NewDecFromInt(reserveCoins[0].Amount).Quo(sdk.NewDecFromInt(reserveCoins[1].Amount))
441500
}
501+
logger := k.Logger(ctx)
502+
logger.Info("withdraw", msg, "pool", pool, "inputs", inputs, "outputs", outputs, "reserveCoins", reserveCoins, "lastReserveRatio", lastReserveRatio)
442503
return nil
443504
}
444505

x/liquidity/keeper/liquidity_pool_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,88 @@ func TestWithdrawLiquidityPool(t *testing.T) {
185185

186186
}
187187

188+
func TestReinitializePool(t *testing.T) {
189+
simapp, ctx := createTestInput()
190+
simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams())
191+
params := simapp.LiquidityKeeper.GetParams(ctx)
192+
193+
poolTypeIndex := types.DefaultPoolTypeIndex
194+
addrs := app.AddTestAddrs(simapp, ctx, 3, params.LiquidityPoolCreationFee)
195+
196+
denomA := "uETH"
197+
denomB := "uUSD"
198+
denomA, denomB = types.AlphabeticalDenomPair(denomA, denomB)
199+
200+
denoms := []string{denomA, denomB}
201+
202+
deposit := sdk.NewCoins(sdk.NewCoin(denomA, sdk.NewInt(100*1000000)), sdk.NewCoin(denomB, sdk.NewInt(100*1000000)))
203+
app.SaveAccount(simapp, ctx, addrs[0], deposit)
204+
205+
depositA := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomA)
206+
depositB := simapp.BankKeeper.GetBalance(ctx, addrs[0], denomB)
207+
depositBalance := sdk.NewCoins(depositA, depositB)
208+
209+
require.Equal(t, deposit, depositBalance)
210+
211+
createMsg := types.NewMsgCreateLiquidityPool(addrs[0], poolTypeIndex, denoms, depositBalance)
212+
213+
err := simapp.LiquidityKeeper.CreateLiquidityPool(ctx, createMsg)
214+
require.NoError(t, err)
215+
216+
lpList := simapp.LiquidityKeeper.GetAllLiquidityPools(ctx)
217+
lp := lpList[0]
218+
219+
poolCoinBefore := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, lp)
220+
withdrawerPoolCoinBefore := simapp.BankKeeper.GetBalance(ctx, addrs[0], lp.PoolCoinDenom)
221+
222+
reserveCoins := simapp.LiquidityKeeper.GetReserveCoins(ctx, lp)
223+
require.True(t, reserveCoins.IsEqual(deposit))
224+
225+
fmt.Println(poolCoinBefore, withdrawerPoolCoinBefore.Amount)
226+
require.Equal(t, poolCoinBefore, withdrawerPoolCoinBefore.Amount)
227+
withdrawMsg := types.NewMsgWithdrawFromLiquidityPool(addrs[0], lp.PoolId, sdk.NewCoin(lp.PoolCoinDenom, poolCoinBefore))
228+
229+
err = simapp.LiquidityKeeper.WithdrawLiquidityPoolToBatch(ctx, withdrawMsg)
230+
require.NoError(t, err)
231+
232+
poolBatch, found := simapp.LiquidityKeeper.GetLiquidityPoolBatch(ctx, withdrawMsg.PoolId)
233+
require.True(t, found)
234+
msgs := simapp.LiquidityKeeper.GetAllLiquidityPoolBatchWithdrawMsgs(ctx, poolBatch)
235+
require.Equal(t, 1, len(msgs))
236+
237+
err = simapp.LiquidityKeeper.WithdrawLiquidityPool(ctx, msgs[0])
238+
require.NoError(t, err)
239+
240+
poolCoinAfter := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, lp)
241+
withdrawerPoolCoinAfter := simapp.BankKeeper.GetBalance(ctx, addrs[0], lp.PoolCoinDenom)
242+
require.True(t, true, poolCoinAfter.IsZero())
243+
require.True(t, true, withdrawerPoolCoinAfter.IsZero())
244+
withdrawerDenomAbalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], lp.ReserveCoinDenoms[0])
245+
withdrawerDenomBbalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], lp.ReserveCoinDenoms[1])
246+
require.Equal(t, deposit.AmountOf(lp.ReserveCoinDenoms[0]), withdrawerDenomAbalance.Amount)
247+
require.Equal(t, deposit.AmountOf(lp.ReserveCoinDenoms[1]), withdrawerDenomBbalance.Amount)
248+
249+
reserveCoins = simapp.LiquidityKeeper.GetReserveCoins(ctx, lp)
250+
require.True(t, reserveCoins.IsZero())
251+
252+
depositMsg := types.NewMsgDepositToLiquidityPool(addrs[0], lp.PoolId, deposit)
253+
err = simapp.LiquidityKeeper.DepositLiquidityPoolToBatch(ctx, depositMsg)
254+
require.NoError(t, err)
255+
256+
depositMsgs := simapp.LiquidityKeeper.GetAllLiquidityPoolBatchDepositMsgs(ctx, poolBatch)
257+
require.Equal(t, 1, len(depositMsgs))
258+
259+
err = simapp.LiquidityKeeper.DepositLiquidityPool(ctx, depositMsgs[0])
260+
require.NoError(t, err)
261+
262+
poolCoin := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, lp)
263+
depositorBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], lp.PoolCoinDenom)
264+
require.Equal(t, poolCoin, depositorBalance.Amount)
265+
266+
reserveCoins = simapp.LiquidityKeeper.GetReserveCoins(ctx, lp)
267+
require.True(t, reserveCoins.IsEqual(deposit))
268+
}
269+
188270
func TestGetLiquidityPoolMetaData(t *testing.T) {
189271
simapp, ctx := createTestInput()
190272
simapp.LiquidityKeeper.SetParams(ctx, types.DefaultParams())

x/liquidity/keeper/msg_server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ func (k msgServer) WithdrawFromLiquidityPool(goCtx context.Context, msg *types.M
9191
// Message server, handler for MsgSwap
9292
func (k msgServer) Swap(goCtx context.Context, msg *types.MsgSwap) (*types.MsgSwapResponse, error) {
9393
ctx := sdk.UnwrapSDKContext(goCtx)
94-
k.Keeper.SwapLiquidityPoolToBatch(ctx, msg, 0)
94+
if _, err := k.Keeper.SwapLiquidityPoolToBatch(ctx, msg, 0); err != nil {
95+
return &types.MsgSwapResponse{}, err
96+
}
9597
return &types.MsgSwapResponse{}, nil
9698
}

x/liquidity/types/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ var (
4040
ErrBadBatchMsgIndex = sdkerrors.Register(ModuleName, 32, "bad msg index of the batch")
4141
ErrSwapTypeNotExists = sdkerrors.Register(ModuleName, 33, "swap type not exists")
4242
ErrLessThanMinOfferAmount = sdkerrors.Register(ModuleName, 34, "offer amount should over 1000 micro")
43+
ErrNotMatchedReserveCoin = sdkerrors.Register(ModuleName, 35, "does not match the reserve coin of the pool")
4344
)

0 commit comments

Comments
 (0)