Skip to content

Commit

Permalink
Merge pull request #135 from cosmosquad-labs/134-fix-withdraw-re-stak…
Browse files Browse the repository at this point in the history
…ing-logic

fix: withdraw, re-staking logic
  • Loading branch information
dongsam authored Feb 6, 2022
2 parents 9318023 + 633a07c commit f172ee0
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 144 deletions.
112 changes: 66 additions & 46 deletions x/liquidstaking/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ package keeper_test
import (
"fmt"
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
squadtypes "github.com/cosmosquad-labs/squad/types"
farmingtypes "github.com/cosmosquad-labs/squad/x/farming/types"
liquiditytypes "github.com/cosmosquad-labs/squad/x/liquidity/types"
"github.com/cosmosquad-labs/squad/x/liquidstaking"
"github.com/cosmosquad-labs/squad/x/liquidstaking/types"
"github.com/cosmosquad-labs/squad/x/mint"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
Expand All @@ -19,13 +27,7 @@ import (
)

var (
//initialBalances = sdk.NewCoins(
// sdk.NewInt64Coin(sdk.DefaultBondDenom, 1_000_000_000),
// sdk.NewInt64Coin(denom1, 1_000_000_000),
// sdk.NewInt64Coin(denom2, 1_000_000_000),
// sdk.NewInt64Coin(denom3, 1_000_000_000))
//smallBalances = mustParseCoinsNormalized("1denom1,2denom2,3denom3,1000000000stake")
PKs = simapp.CreateTestPubKeys(500)
BlockTime = 10 * time.Second
)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -66,46 +68,15 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.addrs = simapp.AddTestAddrs(suite.app, suite.ctx, 10, sdk.NewInt(1_000_000_000))
suite.delAddrs = simapp.AddTestAddrs(suite.app, suite.ctx, 10, sdk.NewInt(1_000_000_000))
suite.valAddrs = simapp.ConvertAddrsToValAddrs(suite.delAddrs)
//dAddr1 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "destinationAddr1")
//dAddr2 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "destinationAddr2")
//dAddr3 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "destinationAddr3")
//dAddr4 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "destinationAddr4")
//dAddr5 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "destinationAddr5")
//dAddr6 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, "farming", "GravityDEXFarmingWhitelistedValidator")
//sAddr1 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "sourceAddr1")
//sAddr2 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "sourceAddr2")
//sAddr3 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "sourceAddr3")
//sAddr4 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "sourceAddr4")
//sAddr5 := liquiditytypes.DeriveAddress(liquiditytypes.AddressType32Bytes, liquiditytypes.ModuleName, "sourceAddr5")
//sAddr6 := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, authtypes.FeeCollectorName).GetAddress()
//suite.destinationAddrs = []sdk.AccAddress{dAddr1, dAddr2, dAddr3, dAddr4, dAddr5, dAddr6}
//suite.sourceAddrs = []sdk.AccAddress{sAddr1, sAddr2, sAddr3, sAddr4, sAddr5, sAddr6}
//for _, addr := range append(suite.addrs, suite.sourceAddrs[:3]...) {
// err := simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr, initialBalances)
// suite.Require().NoError(err)
//}
//err := simapp.FundAccount(suite.app.BankKeeper, suite.ctx, suite.sourceAddrs[3], smallBalances)
//suite.Require().NoError(err)

//suite.whitelistedValidators = []liquiditytypes.WhitelistedValidator{
// {
// ValidatorAddress: "cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv",
// Weight: sdk.MustNewDecFromStr("0.5"),
// },
//}
}

//func coinsEq(exp, got sdk.Coins) (bool, string, string, string) {
// return exp.IsEqual(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
//}
//
//func mustParseCoinsNormalized(coinStr string) (coins sdk.Coins) {
// coins, err := sdk.ParseCoinsNormalized(coinStr)
// if err != nil {
// panic(err)
// }
// return coins
//}
suite.ctx = suite.ctx.WithBlockHeight(100).WithBlockTime(squadtypes.MustParseRFC3339("2022-03-01T00:00:00Z"))
params := suite.keeper.GetParams(suite.ctx)
params.UnstakeFeeRate = sdk.ZeroDec()
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.EndBlocker(suite.ctx)
// call mint.BeginBlocker for init k.SetLastBlockTime(ctx, ctx.BlockTime())
mint.BeginBlocker(suite.ctx, suite.app.MintKeeper)
}

func (suite *KeeperTestSuite) CreateValidators(powers []int64) ([]sdk.AccAddress, []sdk.ValAddress) {
suite.app.BeginBlocker(suite.ctx, abci.RequestBeginBlock{})
Expand All @@ -131,6 +102,55 @@ func (suite *KeeperTestSuite) CreateValidators(powers []int64) ([]sdk.AccAddress
return addrs, valAddrs
}

func (suite *KeeperTestSuite) liquidStaking(liquidStaker sdk.AccAddress, stakingAmt sdk.Int) {
params := suite.keeper.GetParams(suite.ctx)
btokenBalanceBefore := suite.app.BankKeeper.GetBalance(suite.ctx, liquidStaker, params.BondedBondDenom).Amount
newShares, bTokenMintAmt, err := suite.keeper.LiquidStaking(suite.ctx, types.LiquidStakingProxyAcc, liquidStaker, sdk.NewCoin(sdk.DefaultBondDenom, stakingAmt))
btokenBalanceAfter := suite.app.BankKeeper.GetBalance(suite.ctx, liquidStaker, params.BondedBondDenom).Amount
suite.Require().NoError(err)
suite.NotEqualValues(newShares, sdk.ZeroDec())
suite.Require().EqualValues(bTokenMintAmt, btokenBalanceAfter.Sub(btokenBalanceBefore))
}

func (s *KeeperTestSuite) advanceHeight(height int, withEndBlock bool) {
feeCollector := s.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName)
for i := 0; i < height; i++ {
s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(s.ctx.BlockTime().Add(BlockTime))
mint.BeginBlocker(s.ctx, s.app.MintKeeper)
feeCollectorBalance := s.app.BankKeeper.GetAllBalances(s.ctx, feeCollector)
rewardsToBeDistributed := feeCollectorBalance.AmountOf(sdk.DefaultBondDenom)

// mimic AllocateTokens, get rewards from feeCollector, AllocateTokensToValidator, add remaining to feePool
err := s.app.BankKeeper.SendCoinsFromModuleToModule(s.ctx, authtypes.FeeCollectorName, distrtypes.ModuleName, feeCollectorBalance)
s.Require().NoError(err)
totalRewards := sdk.ZeroDec()
totalPower := int64(0)
s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
consPower := validator.GetConsensusPower(s.app.StakingKeeper.PowerReduction(s.ctx))
totalPower = totalPower + consPower
return false
})
s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
consPower := validator.GetConsensusPower(s.app.StakingKeeper.PowerReduction(s.ctx))
powerFraction := sdk.NewDec(consPower).QuoTruncate(sdk.NewDec(totalPower))
reward := rewardsToBeDistributed.ToDec().MulTruncate(powerFraction)
s.app.DistrKeeper.AllocateTokensToValidator(s.ctx, validator, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: reward}})
totalRewards = totalRewards.Add(reward)
return false
})
remaining := rewardsToBeDistributed.ToDec().Sub(totalRewards)
s.Require().False(remaining.GT(sdk.NewDec(1)))
feePool := s.app.DistrKeeper.GetFeePool(s.ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: remaining}}...)
s.app.DistrKeeper.SetFeePool(s.ctx, feePool)
staking.BeginBlocker(s.ctx, *s.app.StakingKeeper)
staking.EndBlocker(s.ctx, *s.app.StakingKeeper)
if withEndBlock {
liquidstaking.EndBlocker(s.ctx, s.app.LiquidStakingKeeper)
}
}
}

func (s *KeeperTestSuite) fundAddr(addr sdk.AccAddress, amt sdk.Coins) {
err := s.app.BankKeeper.MintCoins(s.ctx, liquiditytypes.ModuleName, amt)
s.Require().NoError(err)
Expand Down
20 changes: 7 additions & 13 deletions x/liquidstaking/keeper/liquidstaking.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,14 @@ func (k Keeper) NetAmount(ctx sdk.Context) sdk.Dec {

func (k Keeper) LiquidDelegate(ctx sdk.Context, proxyAcc sdk.AccAddress, activeVals types.LiquidValidators, stakingAmt sdk.Int, whitelistedValMap types.WhitelistedValMap) (newShares sdk.Dec, err error) {
totalNewShares := sdk.ZeroDec()
// crumb would be small micro token, drop it
// crumb may occur due to a decimal point error in dividing the staking amount into the weight of liquid validators, which is also accumulated in the netAmount value
weightedShares, _ := types.DivideByWeight(activeVals, stakingAmt, whitelistedValMap)
for i, val := range activeVals {
validator, found := k.stakingKeeper.GetValidator(ctx, val.GetOperator())
if !found {
panic("validator not founded")
}
validator, _ := k.stakingKeeper.GetValidator(ctx, val.GetOperator())
newShares, err = k.stakingKeeper.Delegate(ctx, proxyAcc, weightedShares[i], stakingtypes.Unbonded, validator, true)
if err != nil {
return sdk.ZeroDec(), err
}
//val.LiquidTokens = val.GetDelShares(ctx, k.stakingKeeper).Add(weightedShares[i])
//k.SetLiquidValidator(ctx, val)
totalNewShares = totalNewShares.Add(newShares)
}
return totalNewShares, nil
Expand Down Expand Up @@ -146,6 +141,7 @@ func (k Keeper) LiquidStaking(
if err != nil {
return sdk.ZeroDec(), bTokenMintAmount, err
}
// TODO: need to re-calc mintAmount for actual delegation amount using sum of divided with truncation
newShares, err = k.LiquidDelegate(ctx, proxyAcc, activeVals, stakingCoin.Amount, whitelistedValMap)
return newShares, bTokenMintAmount, err
}
Expand Down Expand Up @@ -189,8 +185,8 @@ func (k Keeper) LiquidUnstaking(
return time.Time{}, sdk.ZeroDec(), []stakingtypes.UnbondingDelegation{}, err
}

// crumb could small micro token, drop it
unbondingShares, _ := k.DivideByCurrentWeightDec(ctx, activeVals, unbondingAmount)
// crumb may occur due to a decimal error in dividing the amount into the weight of liquid validators, which is also accumulated in the netAmount value
unbondingShares, _ := k.DivideByCurrentWeight(ctx, activeVals, unbondingAmount)
var ubdTime time.Time
var ubds []stakingtypes.UnbondingDelegation
for i, val := range activeVals {
Expand All @@ -201,12 +197,10 @@ func (k Keeper) LiquidUnstaking(
weightedShare, err = k.stakingKeeper.ValidateUnbondAmount(ctx, proxyAcc, val.GetOperator(), weightedShare.TruncateInt())
fmt.Println("[liquid UBD]", weightedShare.String(), del.Shares.String(), found, unbondingShares[i], activeVals.Len(), unbondingAmount.String())
if err != nil {
// TODO: add custom error
return time.Time{}, sdk.ZeroDec(), []stakingtypes.UnbondingDelegation{}, err
}
ubdTime, returnAmount, ubd, err = k.LiquidUnbond(ctx, proxyAcc, liquidStaker, val.GetOperator(), weightedShare)
if err != nil {
// TODO: add custom error
return time.Time{}, sdk.ZeroDec(), []stakingtypes.UnbondingDelegation{}, err
}
ubds = append(ubds, ubd)
Expand Down Expand Up @@ -246,15 +240,15 @@ func (k Keeper) LiquidUnbond(
return completionTime, returnAmount, ubd, nil
}

func (k Keeper) WithdrawLiquidRewards(ctx sdk.Context, proxyAcc sdk.AccAddress) (totalRewards sdk.Int) {
func (k Keeper) WithdrawLiquidRewards(ctx sdk.Context, proxyAcc sdk.AccAddress) sdk.Int {
totalRewards := sdk.ZeroInt()
bondDenom := k.stakingKeeper.BondDenom(ctx)
k.stakingKeeper.IterateDelegations(
ctx, proxyAcc,
func(_ int64, del stakingtypes.DelegationI) (stop bool) {
valAddr := del.GetValidatorAddr()
reward, err := k.distrKeeper.WithdrawDelegationRewards(ctx, proxyAcc, valAddr)
if err != nil {
// TODO: tmp panic for debugging
panic(err)
}
totalRewards = totalRewards.Add(reward.AmountOf(bondDenom))
Expand Down
15 changes: 5 additions & 10 deletions x/liquidstaking/keeper/liquidstaking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/crisis"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
squadtypes "github.com/cosmosquad-labs/squad/types"
Expand All @@ -14,10 +15,7 @@ import (
// tests LiquidStaking, LiquidUnstaking
func (suite *KeeperTestSuite) TestLiquidStaking() {
_, valOpers := suite.CreateValidators([]int64{1000000, 2000000, 3000000})
suite.ctx = suite.ctx.WithBlockHeight(100).WithBlockTime(squadtypes.MustParseRFC3339("2022-03-01T00:00:00Z"))
params := suite.keeper.GetParams(suite.ctx)
params.UnstakeFeeRate = sdk.ZeroDec()
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.EndBlocker(suite.ctx)

stakingAmt := sdk.NewInt(50000)
Expand Down Expand Up @@ -141,14 +139,16 @@ func (suite *KeeperTestSuite) TestLiquidStaking() {
suite.Require().Equal(types.ValidatorStatusActive, res[2].Status)
suite.Require().Equal(sdk.NewInt(13333), res[2].DelShares)

// test withdraw liquid reward and re-staking
suite.advanceHeight(100, true)
// invariant check
crisis.EndBlocker(suite.ctx, suite.app.CrisisKeeper)
// TODO: add cases for different weight
}

// test Liquid Staking gov power
func (suite *KeeperTestSuite) TestLiquidStakingGov() {
suite.SetupTest()
params := types.DefaultParams()
params.UnstakeFeeRate = sdk.ZeroDec()
bondedBondDenom := suite.keeper.BondedBondDenom(suite.ctx)

// v1, v2, v3, v4
Expand All @@ -160,7 +160,6 @@ func (suite *KeeperTestSuite) TestLiquidStakingGov() {
{ValidatorAddress: valOpers[3].String(), TargetWeight: sdk.NewInt(10)},
}
suite.keeper.SetParams(suite.ctx, params)
suite.ctx = suite.ctx.WithBlockHeight(100).WithBlockTime(squadtypes.MustParseRFC3339("2022-03-01T00:00:00Z"))
suite.keeper.EndBlocker(suite.ctx)

liquidValidators := suite.keeper.GetAllLiquidValidators(suite.ctx)
Expand Down Expand Up @@ -323,17 +322,13 @@ func (suite *KeeperTestSuite) TestLiquidStakingGov() {

// test Liquid Staking gov power
func (suite *KeeperTestSuite) TestLiquidStakingGov2() {
suite.SetupTest()
params := types.DefaultParams()
params.UnstakeFeeRate = sdk.ZeroDec()
suite.keeper.SetParams(suite.ctx, params)

vals, valOpers := suite.CreateValidators([]int64{10000000})
params.WhitelistedValidators = []types.WhitelistedValidator{
{ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)},
}
suite.keeper.SetParams(suite.ctx, params)
suite.ctx = suite.ctx.WithBlockHeight(100).WithBlockTime(squadtypes.MustParseRFC3339("2022-03-01T00:00:00Z"))
suite.keeper.EndBlocker(suite.ctx)

val1, _ := suite.app.StakingKeeper.GetValidator(suite.ctx, valOpers[0])
Expand Down
2 changes: 1 addition & 1 deletion x/liquidstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ func (k msgServer) LiquidStake(goCtx context.Context, msg *types.MsgLiquidStake)
ctx := sdk.UnwrapSDKContext(goCtx)
params := k.GetParams(ctx)
if msg.Amount.Amount.LT(params.MinLiquidStakingAmount) {
// TODO: consider newShares on MsgLiquidStakeResponse
return nil, types.ErrLessThanMinLiquidStakingAmount
}

Expand All @@ -52,6 +51,7 @@ func (k msgServer) LiquidStake(goCtx context.Context, msg *types.MsgLiquidStake)
sdk.NewAttribute(types.AttributeKeyBTokenMintedAmount, bTokenMintAmount.String()),
),
})
// TODO: consider newShares on MsgLiquidStakeResponse
return &types.MsgLiquidStakeResponse{}, nil
}

Expand Down
Loading

0 comments on commit f172ee0

Please sign in to comment.