Skip to content

Commit

Permalink
Fix: add overflow checking and test codes for cover edge cases (#458)
Browse files Browse the repository at this point in the history
* test: add testcase for cover small withdrawal case

* test: add test case for CreatePool

* fix: refactor and optimize depleted pool validation

* feat: add overflow checking logic

* chore: add testcase and remove comments

* test: add test code for big deposit

* fix: apply PR suggestions

* fix: add overflow checking logic and test cases

Co-authored-by: Hanjun Kim <[email protected]>
Co-authored-by: typark391 <[email protected]>
  • Loading branch information
3 people authored Oct 25, 2021
1 parent 616985f commit e0def69
Show file tree
Hide file tree
Showing 10 changed files with 467 additions and 44 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## v1.4.x - 2021.10.xx
## [Unreleased]

## [v1.4.1](https://github.com/tendermint/liquidity/releases) - 2021.10.25

* [\#455](https://github.com/tendermint/liquidity/pull/455) (sdk) Bump SDK version to [v0.44.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.2)
* [\#446](https://github.com/tendermint/liquidity/pull/446) Fix: Pool Coin Decimal Truncation During Deposit
* [\#448](https://github.com/tendermint/liquidity/pull/448) Fix: add overflow checking and test codes for cover edge cases

## [Unreleased]

## [v1.4.0](https://github.com/tendermint/liquidity/releases/tag/v1.4.0) - 2021.09.07

Expand Down
9 changes: 8 additions & 1 deletion x/liquidity/keeper/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,14 @@ func (k Keeper) WithdrawWithinBatch(ctx sdk.Context, msg *types.MsgWithdrawWithi

// In order to deal with the batch at the same time, the coins of msgs are deposited in escrow.
func (k Keeper) SwapWithinBatch(ctx sdk.Context, msg *types.MsgSwapWithinBatch, orderExpirySpanHeight int64) (*types.SwapMsgState, error) {
if err := k.ValidateMsgSwapWithinBatch(ctx, *msg); err != nil {
pool, found := k.GetPool(ctx, msg.PoolId)
if !found {
return nil, types.ErrPoolNotExists
}
if k.IsDepletedPool(ctx, pool) {
return nil, types.ErrDepletedPool
}
if err := k.ValidateMsgSwapWithinBatch(ctx, *msg, pool); err != nil {
return nil, err
}
poolBatch, found := k.GetPoolBatch(ctx, msg.PoolId)
Expand Down
37 changes: 22 additions & 15 deletions x/liquidity/keeper/liquidity_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,18 @@ func (k Keeper) ExecuteDeposit(ctx sdk.Context, msg types.DepositMsgState, batch
depositCoinA := depositCoins[0]
depositCoinB := depositCoins[1]

poolCoinTotalSupply := k.GetPoolCoinTotalSupply(ctx, pool)
poolCoinTotalSupply := k.GetPoolCoinTotalSupply(ctx, pool).ToDec()
if err := types.CheckOverflowWithDec(poolCoinTotalSupply, depositCoinA.Amount.ToDec()); err != nil {
return err
}
if err := types.CheckOverflowWithDec(poolCoinTotalSupply, depositCoinB.Amount.ToDec()); err != nil {
return err
}
poolCoinMintAmt := sdk.MinDec(
poolCoinTotalSupply.ToDec().MulTruncate(depositCoinA.Amount.ToDec()).QuoTruncate(lastReserveCoinA.Amount.ToDec()),
poolCoinTotalSupply.ToDec().MulTruncate(depositCoinB.Amount.ToDec()).QuoTruncate(lastReserveCoinB.Amount.ToDec()),
poolCoinTotalSupply.MulTruncate(depositCoinA.Amount.ToDec()).QuoTruncate(lastReserveCoinA.Amount.ToDec()),
poolCoinTotalSupply.MulTruncate(depositCoinB.Amount.ToDec()).QuoTruncate(lastReserveCoinB.Amount.ToDec()),
)
mintRate := poolCoinMintAmt.TruncateDec().QuoTruncate(poolCoinTotalSupply.ToDec())
mintRate := poolCoinMintAmt.TruncateDec().QuoTruncate(poolCoinTotalSupply)
acceptedCoins := sdk.NewCoins(
sdk.NewCoin(depositCoins[0].Denom, lastReserveCoinA.Amount.ToDec().Mul(mintRate).TruncateInt()),
sdk.NewCoin(depositCoins[1].Denom, lastReserveCoinB.Amount.ToDec().Mul(mintRate).TruncateInt()),
Expand Down Expand Up @@ -301,7 +307,7 @@ func (k Keeper) ExecuteDeposit(ctx sdk.Context, msg types.DepositMsgState, batch
afterReserveCoinA := afterReserveCoins[0].Amount
afterReserveCoinB := afterReserveCoins[1].Amount

MintingPoolCoinsInvariant(poolCoinTotalSupply, mintPoolCoin.Amount, depositCoinA.Amount, depositCoinB.Amount,
MintingPoolCoinsInvariant(poolCoinTotalSupply.TruncateInt(), mintPoolCoin.Amount, depositCoinA.Amount, depositCoinB.Amount,
lastReserveCoinA.Amount, lastReserveCoinB.Amount, refundedCoinA.Amount, refundedCoinB.Amount)
DepositInvariant(lastReserveCoinA.Amount, lastReserveCoinB.Amount, depositCoinA.Amount, depositCoinB.Amount,
afterReserveCoinA, afterReserveCoinB, refundedCoinA.Amount, refundedCoinB.Amount)
Expand Down Expand Up @@ -378,6 +384,12 @@ func (k Keeper) ExecuteWithdrawal(ctx sdk.Context, msg types.WithdrawMsgState, b
} else {
// Calculate withdraw amount of respective reserve coin considering fees and pool coin's totally supply
for _, reserveCoin := range reserveCoins {
if err := types.CheckOverflow(reserveCoin.Amount, msg.Msg.PoolCoin.Amount); err != nil {
return err
}
if err := types.CheckOverflow(reserveCoin.Amount.Mul(msg.Msg.PoolCoin.Amount).ToDec().TruncateInt(), poolCoinTotalSupply); err != nil {
return err
}
// WithdrawAmount = ReserveAmount * PoolCoinAmount * WithdrawFeeProportion / TotalSupply
withdrawAmtWithFee := reserveCoin.Amount.Mul(msg.Msg.PoolCoin.Amount).ToDec().TruncateInt().Quo(poolCoinTotalSupply)
withdrawAmt := reserveCoin.Amount.Mul(msg.Msg.PoolCoin.Amount).ToDec().MulTruncate(withdrawProportion).TruncateInt().Quo(poolCoinTotalSupply)
Expand Down Expand Up @@ -770,21 +782,12 @@ func (k Keeper) ValidateMsgWithdrawWithinBatch(ctx sdk.Context, msg types.MsgWit
}

// ValidateMsgSwapWithinBatch validates MsgSwapWithinBatch.
func (k Keeper) ValidateMsgSwapWithinBatch(ctx sdk.Context, msg types.MsgSwapWithinBatch) error {
pool, found := k.GetPool(ctx, msg.PoolId)
if !found {
return types.ErrPoolNotExists
}

func (k Keeper) ValidateMsgSwapWithinBatch(ctx sdk.Context, msg types.MsgSwapWithinBatch, pool types.Pool) error {
denomA, denomB := types.AlphabeticalDenomPair(msg.OfferCoin.Denom, msg.DemandCoinDenom)
if denomA != pool.ReserveCoinDenoms[0] || denomB != pool.ReserveCoinDenoms[1] {
return types.ErrNotMatchedReserveCoin
}

if k.IsDepletedPool(ctx, pool) {
return types.ErrDepletedPool
}

params := k.GetParams(ctx)

// can not exceed max order ratio of reserve coins that can be ordered at a order
Expand All @@ -800,6 +803,10 @@ func (k Keeper) ValidateMsgSwapWithinBatch(ctx sdk.Context, msg types.MsgSwapWit
return types.ErrBadOfferCoinFee
}

if err := types.CheckOverflowWithDec(msg.OfferCoin.Amount.ToDec(), msg.OrderPrice); err != nil {
return err
}

if !msg.OfferCoinFee.Equal(types.GetOfferCoinFee(msg.OfferCoin, params.SwapFeeRate)) {
return types.ErrBadOfferCoinFee
}
Expand Down
Loading

0 comments on commit e0def69

Please sign in to comment.