Skip to content

Commit

Permalink
Add logics for withdrawing all reserve coins
Browse files Browse the repository at this point in the history
  • Loading branch information
dongsam committed Mar 30, 2021
1 parent 56d43e7 commit fb15225
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 18 deletions.
4 changes: 2 additions & 2 deletions x/liquidity/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ func TestMsgServerWithdrawLiquidityPool(t *testing.T) {
require.True(t, true, withdrawerPoolCoinAfter.IsZero())
withdrawerDenomABalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[0])
withdrawerDenomBBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[1])
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomBBalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]), withdrawerDenomBBalance.Amount)

}

Expand Down
6 changes: 6 additions & 0 deletions x/liquidity/keeper/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ func (k Keeper) ExecutePoolBatch(ctx sdk.Context) {
}

k.IterateAllPoolBatchDepositMsgStates(ctx, poolBatch, func(batchMsg types.DepositMsgState) bool {
if batchMsg.Executed || batchMsg.ToBeDeleted || batchMsg.Succeeded {
return false
}
executedMsgCount++
if err := k.DepositLiquidityPool(ctx, batchMsg, poolBatch); err != nil {
if err := k.RefundDepositLiquidityPool(ctx, batchMsg, poolBatch); err != nil {
Expand All @@ -101,6 +104,9 @@ func (k Keeper) ExecutePoolBatch(ctx sdk.Context) {
})

k.IterateAllPoolBatchWithdrawMsgStates(ctx, poolBatch, func(batchMsg types.WithdrawMsgState) bool {
if batchMsg.Executed || batchMsg.ToBeDeleted || batchMsg.Succeeded {
return false
}
executedMsgCount++
if err := k.WithdrawLiquidityPool(ctx, batchMsg, poolBatch); err != nil {
if err := k.RefundWithdrawLiquidityPool(ctx, batchMsg, poolBatch); err != nil {
Expand Down
36 changes: 29 additions & 7 deletions x/liquidity/keeper/liquidity_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ func (k Keeper) CreatePool(ctx sdk.Context, msg *types.MsgCreatePool) (types.Poo
}

func (k Keeper) DepositLiquidityPool(ctx sdk.Context, msg types.DepositMsgState, batch types.PoolBatch) error {
if msg.Executed || msg.ToBeDeleted || msg.Succeeded {
return fmt.Errorf("cannot process already executed batch msg")
}
msg.Executed = true
k.SetPoolBatchDepositMsgState(ctx, msg.Msg.PoolId, msg)

Expand Down Expand Up @@ -335,14 +338,15 @@ func (k Keeper) DepositLiquidityPool(ctx sdk.Context, msg types.DepositMsgState,

// WithdrawLiquidityPool withdraws pool coin from the liquidity pool
func (k Keeper) WithdrawLiquidityPool(ctx sdk.Context, msg types.WithdrawMsgState, batch types.PoolBatch) error {
if msg.Executed || msg.ToBeDeleted || msg.Succeeded {
return fmt.Errorf("cannot process already executed batch msg")
}
msg.Executed = true
k.SetPoolBatchWithdrawMsgState(ctx, msg.Msg.PoolId, msg)

if err := k.ValidateMsgWithdrawLiquidityPool(ctx, *msg.Msg); err != nil {
return err
}
// TODO: validate reserveCoin balance

poolCoins := sdk.NewCoins(msg.Msg.PoolCoin)

pool, found := k.GetPool(ctx, msg.Msg.PoolId)
Expand All @@ -364,16 +368,25 @@ func (k Keeper) WithdrawLiquidityPool(ctx sdk.Context, msg types.WithdrawMsgStat
withdrawProportion := sdk.OneDec().Sub(params.WithdrawFeeRate)
withdrawCoins := sdk.NewCoins()

// calculate withdraw amount of respective reserve coin considering fees and pool coin's totally supply
for _, reserveCoin := range reserveCoins {
// WithdrawAmount = ReserveAmount * PoolCoinAmount * WithdrawFeeProportion / TotalSupply
withdrawAmt := reserveCoin.Amount.Mul(msg.Msg.PoolCoin.Amount).ToDec().MulTruncate(withdrawProportion).TruncateInt().Quo(poolCoinTotalSupply)
withdrawCoins = withdrawCoins.Add(sdk.NewCoin(reserveCoin.Denom, withdrawAmt))
// Case for withdrawing all reserve coins
if msg.Msg.PoolCoin.Amount.Equal(poolCoinTotalSupply) {
withdrawCoins = reserveCoins
} else {
// Calculate withdraw amount of respective reserve coin considering fees and pool coin's totally supply
for _, reserveCoin := range reserveCoins {
// WithdrawAmount = ReserveAmount * PoolCoinAmount * WithdrawFeeProportion / TotalSupply
withdrawAmt := reserveCoin.Amount.Mul(msg.Msg.PoolCoin.Amount).ToDec().MulTruncate(withdrawProportion).TruncateInt().Quo(poolCoinTotalSupply)
if withdrawAmt.IsPositive() {
withdrawCoins = withdrawCoins.Add(sdk.NewCoin(reserveCoin.Denom, withdrawAmt))
}
}
}

if withdrawCoins.IsValid() {
inputs = append(inputs, banktypes.NewInput(reserveAcc, withdrawCoins))
outputs = append(outputs, banktypes.NewOutput(withdrawer, withdrawCoins))
} else {
return types.ErrBadPoolCoinAmount
}

// send withdrawing coins to the withdrawer
Expand Down Expand Up @@ -840,6 +853,11 @@ func (k Keeper) ValidateMsgWithdrawLiquidityPool(ctx sdk.Context, msg types.MsgW
}

poolCoinTotalSupply := k.GetPoolCoinTotalSupply(ctx, pool)

if !poolCoinTotalSupply.IsPositive() || !k.GetReserveCoins(ctx, pool).IsAllPositive() {
return types.ErrDepletedPool
}

if msg.PoolCoin.Amount.GT(poolCoinTotalSupply) {
return types.ErrBadPoolCoinAmount
}
Expand Down Expand Up @@ -867,6 +885,10 @@ func (k Keeper) ValidateMsgSwapWithinBatch(ctx sdk.Context, msg types.MsgSwapWit
// can not exceed max order ratio of reserve coins that can be ordered at a order
reserveCoinAmt := k.GetReserveCoins(ctx, pool).AmountOf(msg.OfferCoin.Denom)

if !reserveCoinAmt.IsPositive() {
return types.ErrDepletedPool
}

// Decimal Error, Multiply the Int coin amount by the Decimal Rate and erase the decimal point to order a lower value
maximumOrderableAmt := reserveCoinAmt.ToDec().MulTruncate(params.MaxOrderAmountRatio).TruncateInt()
if msg.OfferCoin.Amount.GT(maximumOrderableAmt) {
Expand Down
51 changes: 44 additions & 7 deletions x/liquidity/keeper/liquidity_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"

lapp "github.com/tendermint/liquidity/app"
"github.com/tendermint/liquidity/x/liquidity"
"github.com/tendermint/liquidity/x/liquidity/types"
)

Expand Down Expand Up @@ -275,11 +276,12 @@ func TestWithdrawLiquidityPool(t *testing.T) {
pools := simapp.LiquidityKeeper.GetAllPools(ctx)
pool := pools[0]

// Case for normal withdrawing
poolCoinBefore := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool)
withdrawerPoolCoinBefore := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom)

require.Equal(t, poolCoinBefore, withdrawerPoolCoinBefore.Amount)
withdrawMsg := types.NewMsgWithdrawWithinBatch(addrs[0], pool.Id, sdk.NewCoin(pool.PoolCoinDenom, poolCoinBefore))
withdrawMsg := types.NewMsgWithdrawWithinBatch(addrs[0], pool.Id, sdk.NewCoin(pool.PoolCoinDenom, withdrawerPoolCoinBefore.Amount.QuoRaw(2)))

_, err = simapp.LiquidityKeeper.WithdrawLiquidityPoolToBatch(ctx, withdrawMsg)
require.NoError(t, err)
Expand All @@ -289,17 +291,46 @@ func TestWithdrawLiquidityPool(t *testing.T) {
msgs := simapp.LiquidityKeeper.GetAllPoolBatchWithdrawMsgStates(ctx, poolBatch)
require.Equal(t, 1, len(msgs))

err = simapp.LiquidityKeeper.WithdrawLiquidityPool(ctx, msgs[0], poolBatch)
require.NoError(t, err)
liquidity.EndBlocker(ctx, simapp.LiquidityKeeper)
liquidity.BeginBlocker(ctx, simapp.LiquidityKeeper)

poolCoinAfter := simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool)
withdrawerPoolCoinAfter := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom)
require.True(t, true, poolCoinAfter.IsZero())
require.True(t, true, withdrawerPoolCoinAfter.IsZero())

require.Equal(t, poolCoinAfter, poolCoinBefore.QuoRaw(2))
require.Equal(t, withdrawerPoolCoinAfter.Amount, withdrawerPoolCoinBefore.Amount.QuoRaw(2))
withdrawerDenomABalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[0])
withdrawerDenomBBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[1])
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomBBalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]).QuoRaw(2).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]).QuoRaw(2).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomBBalance.Amount)

// Case for withdrawing all reserve coins
poolCoinBefore = simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool)
withdrawerPoolCoinBefore = simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom)

require.Equal(t, poolCoinBefore, withdrawerPoolCoinBefore.Amount)
withdrawMsg = types.NewMsgWithdrawWithinBatch(addrs[0], pool.Id, sdk.NewCoin(pool.PoolCoinDenom, poolCoinBefore))

_, err = simapp.LiquidityKeeper.WithdrawLiquidityPoolToBatch(ctx, withdrawMsg)
require.NoError(t, err)

poolBatch, found = simapp.LiquidityKeeper.GetPoolBatch(ctx, withdrawMsg.PoolId)
require.True(t, found)
msgs = simapp.LiquidityKeeper.GetAllPoolBatchWithdrawMsgStates(ctx, poolBatch)
require.Equal(t, 1, len(msgs))

err = simapp.LiquidityKeeper.WithdrawLiquidityPool(ctx, msgs[0], poolBatch)
require.NoError(t, err)

poolCoinAfter = simapp.LiquidityKeeper.GetPoolCoinTotalSupply(ctx, pool)
withdrawerPoolCoinAfter = simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.PoolCoinDenom)

require.True(t, true, poolCoinAfter.IsZero())
require.True(t, true, withdrawerPoolCoinAfter.IsZero())
withdrawerDenomABalance = simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[0])
withdrawerDenomBBalance = simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[1])
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]), withdrawerDenomBBalance.Amount)
}

func TestReinitializePool(t *testing.T) {
Expand Down Expand Up @@ -365,6 +396,12 @@ func TestReinitializePool(t *testing.T) {
reserveCoins = simapp.LiquidityKeeper.GetReserveCoins(ctx, pool)
require.True(t, reserveCoins.IsZero())

// error when swap request to depleted pool
offerCoin := sdk.NewCoin(denomA, withdrawerDenomABalance.Amount.QuoRaw(2))
swapMsg := types.NewMsgSwapWithinBatch(addrs[0], pool.Id, types.DefaultSwapTypeId, offerCoin, denomB, sdk.MustNewDecFromStr("0.1"), params.SwapFeeRate)
_, err = simapp.LiquidityKeeper.SwapLiquidityPoolToBatch(ctx, swapMsg, 0)
require.Error(t, err, types.ErrDepletedPool)

depositMsg := types.NewMsgDepositWithinBatch(addrs[0], pool.Id, deposit)
_, err = simapp.LiquidityKeeper.DepositLiquidityPoolToBatch(ctx, depositMsg)
require.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions x/liquidity/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ func TestMsgWithdrawLiquidityPool(t *testing.T) {
require.True(t, true, withdrawerPoolCoinAfter.IsZero())
withdrawerDenomABalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[0])
withdrawerDenomBBalance := simapp.BankKeeper.GetBalance(ctx, addrs[0], pool.ReserveCoinDenoms[1])
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]).ToDec().Mul(sdk.OneDec().Sub(params.WithdrawFeeRate)).TruncateInt(), withdrawerDenomBBalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[0]), withdrawerDenomABalance.Amount)
require.Equal(t, deposit.AmountOf(pool.ReserveCoinDenoms[1]), withdrawerDenomBBalance.Amount)

}

Expand Down
1 change: 1 addition & 0 deletions x/liquidity/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ var (
ErrNotMatchedReserveCoin = sdkerrors.Register(ModuleName, 36, "does not match the reserve coin of the pool")
ErrBadPoolTypeId = sdkerrors.Register(ModuleName, 37, "invalid index of the pool type")
ErrExceededReserveCoinLimit = sdkerrors.Register(ModuleName, 38, "can not exceed reserve coin limit amount")
ErrDepletedPool = sdkerrors.Register(ModuleName, 39, "the pool is depleted of reserve coin, reinitializing is required by deposit")
)

0 comments on commit fb15225

Please sign in to comment.