From 15a333fbf77e5bf9023b27659a5f6f65761a5e21 Mon Sep 17 00:00:00 2001 From: deep-quality-dev <84747155+deep-quality-dev@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:55:45 +0100 Subject: [PATCH] Update vesting integration test (#110) * Update integration test with validating balances * Optimize integration test * Add delegating portion of initial vesting coins * Track undelegation * Update integration test for undelegation --- testutil/integration.go | 19 + x/vesting/keeper/integration_test.go | 699 ++++++++++++++++++++++++--- x/vesting/types/vesting_account.go | 4 + 3 files changed, 655 insertions(+), 67 deletions(-) diff --git a/testutil/integration.go b/testutil/integration.go index a0c971ea..327138c2 100644 --- a/testutil/integration.go +++ b/testutil/integration.go @@ -60,6 +60,25 @@ func Delegate( return DeliverTx(ctx, app, priv, nil, delegateMsg) } +// Undelegate delivers a delegate tx +func Undelegate( + ctx sdk.Context, + app *app.App, + priv cryptotypes.PrivKey, + undelegateAmount sdk.Coin, + validator stakingtypes.Validator, +) (abci.ResponseDeliverTx, error) { + accountAddress := sdk.AccAddress(priv.PubKey().Address().Bytes()) + + val, err := sdk.ValAddressFromBech32(validator.OperatorAddress) + if err != nil { + return abci.ResponseDeliverTx{}, err + } + + undelegateMsg := stakingtypes.NewMsgUndelegate(accountAddress, val, undelegateAmount) + return DeliverTx(ctx, app, priv, nil, undelegateMsg) +} + // Vote delivers a vote tx with the VoteOption "yes" or "no" func Vote( ctx sdk.Context, diff --git a/x/vesting/keeper/integration_test.go b/x/vesting/keeper/integration_test.go index a09d81ee..8f228cfd 100644 --- a/x/vesting/keeper/integration_test.go +++ b/x/vesting/keeper/integration_test.go @@ -170,6 +170,89 @@ func (suite *VestingTestSuite) SetupTest() error { return nil } +func minCoins(a, b sdk.Coins) sdk.Coins { + if a.IsAllGTE(b) { + return b + } + return a +} + +func subCoins(from, sub sdk.Coins) sdk.Coins { + if from.IsAllGTE(sub) { + return from.Sub(sub...) + } + return sdk.NewCoins() +} + +func validateVestingAccountBalances( + ctx sdk.Context, + app *app.App, + address sdk.AccAddress, // address to monthly vesting account + delegating, undelegating, expVested, expUnvested, initialBalance, sentAmount, receivedAmount, consumedFee sdk.Coins, +) { + mva, err := app.VestingKeeper.GetMonthlyVestingAccount(ctx, address) + Expect(err).To(BeNil()) + Expect(mva).ToNot(BeNil()) + + var expDelegatedVesting, expDelegatedFree sdk.Coins + if delegating != nil { + expDelegatedVesting = minCoins(subCoins(expUnvested, sdk.NewCoins()), delegating) + expDelegatedFree = subCoins(delegating, expDelegatedVesting) + } + + if undelegating != nil { + X := minCoins(expDelegatedFree, undelegating) + expDelegatedVesting = subCoins(expDelegatedVesting, subCoins(undelegating, X)) + expDelegatedFree = subCoins(expDelegatedFree, X) + } + + expLocked := subCoins(expUnvested, expDelegatedFree.Add(expDelegatedVesting...)) + expBalances := subCoins(initialBalance.Sub(sentAmount...).Add(receivedAmount...).Sub(consumedFee...), delegating) + expSpendable := subCoins(expBalances, expLocked) + + balancesX := app.BankKeeper.GetAllBalances(ctx, address) + Expect(balancesX).To(Equal(expBalances)) + + lockedX := app.BankKeeper.LockedCoins(ctx, address) + if lockedX == nil { + Expect(expLocked).To(Equal(sdk.NewCoins())) + } else { + Expect(lockedX).To(Equal(expLocked)) + } + + spendableX := app.BankKeeper.SpendableCoins(ctx, address) + if spendableX == nil { + Expect(expSpendable).To(Equal(sdk.NewCoins())) + } else { + Expect(spendableX).To(Equal(expSpendable)) + } + + now := ctx.BlockTime() + vestedX := mva.GetVestedCoins(now) + if vestedX == nil { + Expect(expVested).To(Equal(sdk.NewCoins())) + } else { + Expect(vestedX).To(Equal(expVested)) + } + unvestedX := mva.GetVestingCoins(now) + if unvestedX == nil { + Expect(expUnvested).To(Equal(sdk.NewCoins())) + } else { + Expect(unvestedX).To(Equal(expUnvested)) + } + + if mva.DelegatedFree == nil { + Expect(expDelegatedFree).To(Equal(sdk.NewCoins())) + } else { + Expect(mva.DelegatedFree).To(Equal(expDelegatedFree)) + } + if mva.DelegatedVesting == nil { + Expect(expDelegatedVesting).To(Equal(sdk.NewCoins())) + } else { + Expect(mva.DelegatedVesting).To(Equal(expDelegatedVesting)) + } +} + var _ = Describe("Monthly Vesting Account", Ordered, func() { const ( cliffDays = 30 @@ -188,8 +271,10 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { extraCoins sdk.Coins // additional funds to pay gas fees for tx mva *types.MonthlyVestingAccount - unvested sdk.Coins - vested sdk.Coins + unvested sdk.Coins + vested sdk.Coins + expUnvested sdk.Coins + expVested sdk.Coins ) BeforeEach(func() { @@ -239,7 +324,7 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err = s.Commit() Expect(err).To(BeNil()) - // Check if all the tokens are unvested at beginning + // Check if all the coins are unvested at beginning unvested := mva.GetVestingCoins(now) vested := mva.GetVestedCoins(now) Expect(unvested).To(Equal(initialVesting)) @@ -252,12 +337,30 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err := s.Commit() Expect(err).To(BeNil()) - // Ensure no tokens are vested + // Ensure no coins are vested now := s.ctx.BlockTime() unvested = mva.GetVestingCoins(now) vested = mva.GetVestedCoins(now) Expect(unvested).To(Equal(initialVesting)) Expect(vested.IsZero()).To(BeTrue()) + + expUnvested = initialVesting + expVested = sdk.NewCoins() + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // vesting + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) It("can transfer spendable coins", func() { err := testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), unvested) @@ -265,13 +368,64 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err = s.app.BankKeeper.SendCoins(s.ctx, mva.GetAddress(), user, unvested) Expect(err).To(BeNil()) - spendable := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) - Expect(spendable).To(Equal(extraCoins)) + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...).Add(unvested...), // initial balance + unvested, // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) It("cannot transfer unvested coins", func() { err := s.app.BankKeeper.SendCoins(s.ctx, va, user, unvested) Expect(err).ToNot(BeNil()) }) + It("can delegate unvested coins", func() { + // Delegate unvested coins + delegating := unvested + res, err := testutil.Delegate( + s.ctx, + s.app, + vaPrivKey, + delegating[0], + s.validator, + ) + Expect(err).To(BeNil()) + Expect(res.IsOK()).To(BeTrue()) + + // Check delegation was created successfully + delegations, err := s.stkQuerier.DelegatorDelegations( + s.goCtx, + &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: mva.Address, + }, + ) + Expect(err).To(BeNil()) + Expect(delegations.DelegationResponses).To(HaveLen(1)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) + }) }) Context("after cliff, before vested", func() { @@ -281,12 +435,30 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err := s.CommitAfter(duration) Expect(err).To(BeNil()) - // Ensure no tokens are vested + // Ensure no coins are vested now := s.ctx.BlockTime() unvested = mva.GetVestingCoins(now) vested = mva.GetVestedCoins(now) Expect(unvested).To(Equal(initialVesting)) Expect(vested.IsZero()).To(BeTrue()) + + expUnvested = initialVesting + expVested = sdk.NewCoins() + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) It("can transfer spendable coins", func() { coins := sdk.NewCoins(sdk.NewCoin(utils.BaseDenom, math.NewInt(1e18))) @@ -295,24 +467,39 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err = s.app.BankKeeper.SendCoins(s.ctx, mva.GetAddress(), user, coins) Expect(err).To(BeNil()) - spendable := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) - Expect(spendable).To(Equal(extraCoins)) + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...).Add(coins...), // initial balance + coins, // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) It("cannot transfer unvested coins", func() { err := s.app.BankKeeper.SendCoins(s.ctx, va, user, unvested) Expect(err).ToNot(BeNil()) }) - It("can delegate unvested coins", func() { - // Delegate unvested tokens + It("can delegate portion of unvested coins", func() { + // Delegate portion of unvested and consume gas + delegating := unvested.Sub(extraCoins...) res, err := testutil.Delegate( s.ctx, s.app, vaPrivKey, - unvested[0], + delegating[0], s.validator, ) Expect(err).To(BeNil()) Expect(res.IsOK()).To(BeTrue()) + err = s.Commit() + Expect(err).To(BeNil()) // Check delegation was created successfully delegations, err := s.stkQuerier.DelegatorDelegations( @@ -323,18 +510,149 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { ) Expect(err).To(BeNil()) Expect(delegations.DelegationResponses).To(HaveLen(1)) - Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(unvested[0].Amount)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) - // Check delegation was tracked as delegated vesting - mva, err = s.app.VestingKeeper.GetMonthlyVestingAccount(s.ctx, mva.GetAddress()) + // Fund gas coins for next transaction + err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) Expect(err).To(BeNil()) - Expect(mva).ToNot(BeNil()) - Expect(mva.DelegatedVesting).To(Equal(unvested)) - Expect(mva.DelegatedFree).To(BeNil()) + // Undelegate portion of delegated amount and consume gas + undelegating := delegating.QuoInt(sdk.NewInt(2)) + res, err = testutil.Undelegate( + s.ctx, + s.app, + vaPrivKey, + undelegating[0], + s.validator, + ) + Expect(err).To(BeNil()) + Expect(res.IsOK()).To(BeTrue()) + + ut := s.app.StakingKeeper.UnbondingTime(s.ctx) + err = s.CommitAfter(ut) + Expect(err).To(BeNil()) + err = s.Commit() + Expect(err).To(BeNil()) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + undelegating, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + undelegating, // received amount + extraCoins, // consumed fee + ) + }) + It("can delegate all unvested coins", func() { + // Delegate coins more than unvested, and consume gas + res, err := testutil.Delegate( + s.ctx, + s.app, + vaPrivKey, + unvested[0].Add(extraCoins[0]), + s.validator, + ) + Expect(err).ToNot(BeNil()) + Expect(err).Should(MatchError(ContainSubstring("insufficient funds"))) + Expect(res.IsOK()).To(BeTrue()) - // Fund more tokens for gas consuming + // Fund gas coins for next transaction err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) Expect(err).To(BeNil()) + + // Delegate unvested coins, consume gas + delegating := unvested + res, err = testutil.Delegate( + s.ctx, + s.app, + vaPrivKey, + delegating[0], + s.validator, + ) + Expect(err).To(BeNil()) + Expect(res.IsOK()).To(BeTrue()) + + // Check delegation was created successfully + delegations, err := s.stkQuerier.DelegatorDelegations( + s.goCtx, + &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: mva.Address, + }, + ) + Expect(err).To(BeNil()) + Expect(delegations.DelegationResponses).To(HaveLen(1)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) + + // Fund gas coins for next transaction + err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) + Expect(err).To(BeNil()) + // Undelegate all unvested amount and consume gas + undelegating := delegating + res, err = testutil.Undelegate( + s.ctx, + s.app, + vaPrivKey, + undelegating[0], + s.validator, + ) + Expect(err).To(BeNil()) + Expect(res.IsOK()).To(BeTrue()) + + ut := s.app.StakingKeeper.UnbondingTime(s.ctx) + err = s.CommitAfter(ut) + Expect(err).To(BeNil()) + err = s.Commit() + Expect(err).To(BeNil()) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + undelegating, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + undelegating, // received amount + extraCoins, // consumed fee + ) }) It("can perform ethereum tx with spendable coins", func() { coins := sdk.NewCoins(sdk.NewCoin(utils.BaseDenom, math.NewInt(1e18))) @@ -366,37 +684,52 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { now := s.ctx.BlockTime() vested = mva.GetVestedCoins(now) unvested = mva.GetVestingCoins(now) - expVested := initialVesting.QuoInt(sdk.NewInt(months)) + expVested = initialVesting.QuoInt(sdk.NewInt(months)) + expUnvested = initialVesting.Sub(initialVesting.QuoInt(sdk.NewInt(months))...) Expect(vested).To(Equal(expVested)) - Expect(vested).ToNot(Equal(initialVesting)) - expUnvested := initialVesting.Sub(initialVesting.QuoInt(math.NewInt(3))...) Expect(unvested).To(Equal(expUnvested)) // Check balances of vesting account balances, err := s.querier.Balances(s.goCtx, &types.QueryBalancesRequest{Address: mva.Address}) Expect(err).To(BeNil()) - Expect(balances.Vested).To(Equal(vested)) - Expect(balances.Unvested).To(Equal(initialVesting.Sub(expVested...))) + Expect(balances.Vested).To(Equal(expVested)) + Expect(balances.Unvested).To(Equal(expUnvested)) // All coins from vesting schedule should be locked Expect(balances.Locked).To(Equal(initialVesting.Sub(expVested...))) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) - It("can delegate vested tokens", func() { + It("can delegate portion of vested coins", func() { // Verify that the total spendable coins should include vested amount. spendablePre := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePre).To(Equal(vested.Add(extraCoins...))) - // Delegate the vested coins + // Delegate the vested coins, consume gas + delegating := vested.QuoInt(sdk.NewInt(2)) _, err := testutil.Delegate( s.ctx, s.app, vaPrivKey, - vested[0], + delegating[0], s.validator, ) Expect(err).To(BeNil()) // Check spendable coins have not been reduced except gas fee. - // Delegate unvested tokens first and then vested tokens. + // Delegate unvested coins first and then vested coins. spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePost).To(Equal(spendablePre.Sub(extraCoins...))) @@ -409,32 +742,91 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { ) Expect(err).To(BeNil()) Expect(delegations.DelegationResponses).To(HaveLen(1)) - Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(vested[0].Amount)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) + + // Fund gas coins for next transaction + err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) + Expect(err).To(BeNil()) + // Undelegate more than delegated amount and consume gas + undelegating := vested + res, err := testutil.Undelegate( + s.ctx, + s.app, + vaPrivKey, + undelegating[0], + s.validator, + ) + Expect(err).ToNot(BeNil()) + Expect(err).Should(MatchError(ContainSubstring("invalid shares amount"))) + Expect(res.IsOK()).To(BeTrue()) + + // Fund gas coins for next transaction + err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) + Expect(err).To(BeNil()) + // Undelegate more than delegated amount and consume gas + undelegating = delegating + res, err = testutil.Undelegate( + s.ctx, + s.app, + vaPrivKey, + undelegating[0], + s.validator, + ) - // Check delegation was tracked as delegated vesting - mva, err = s.app.VestingKeeper.GetMonthlyVestingAccount(s.ctx, mva.GetAddress()) + ut := s.app.StakingKeeper.UnbondingTime(s.ctx) + err = s.CommitAfter(ut) + Expect(err).To(BeNil()) + err = s.Commit() Expect(err).To(BeNil()) - Expect(mva).ToNot(BeNil()) - Expect(mva.DelegatedVesting).To(Equal(vested)) - Expect(mva.DelegatedFree).To(BeNil()) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + undelegating, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + undelegating, // received amount + extraCoins, // consumed fee + ) }) - It("can delegate unvested + vested tokens", func() { + It("can delegate unvested + vested coins", func() { // Verify that the total spendable coins should include vested amount. spendablePre := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePre).To(Equal(vested.Add(extraCoins...))) - // Delegate the vested coins + // Delegate the vested coins, consume gas + delegating := initialVesting _, err := testutil.Delegate( s.ctx, s.app, vaPrivKey, - initialVesting[0], + delegating[0], s.validator, ) Expect(err).To(BeNil()) - // Delegate unvested tokens first and then vested tokens. - // Check spendable coins have been reduced vested tokens as well. + // Delegate unvested coins first and then vested coins. + // Check spendable coins have been reduced vested coins as well. spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePost).To(Equal(spendablePre.Sub(extraCoins...).Sub(vested...))) @@ -447,16 +839,60 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { ) Expect(err).To(BeNil()) Expect(delegations.DelegationResponses).To(HaveLen(1)) - Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(initialVesting[0].Amount)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) - // Check delegation was tracked as delegated vesting/free - mva, err = s.app.VestingKeeper.GetMonthlyVestingAccount(s.ctx, mva.GetAddress()) + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) + + // Fund gas coins for next transaction + err = testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), extraCoins) + Expect(err).To(BeNil()) + // Undelegate portion of delegated amount and consume gas + undelegating := delegating + res, err := testutil.Undelegate( + s.ctx, + s.app, + vaPrivKey, + undelegating[0], + s.validator, + ) + Expect(err).To(BeNil()) + Expect(res.IsOK()).To(BeTrue()) + + ut := s.app.StakingKeeper.UnbondingTime(s.ctx) + err = s.CommitAfter(ut) + Expect(err).To(BeNil()) + err = s.Commit() Expect(err).To(BeNil()) - Expect(mva).ToNot(BeNil()) - Expect(mva.DelegatedVesting).To(Equal(unvested)) - Expect(mva.DelegatedFree).To(Equal(vested)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + undelegating, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + undelegating, // received amount + extraCoins, // consumed fee + ) }) - It("can delegate tokens from account balance and initial vesting", func() { + It("can delegate coins from account balance and initial vesting", func() { // Funds some coins to delegate coinsToDelegate := sdk.NewCoins(sdk.NewCoin(utils.BaseDenom, math.NewInt(1e18))) err := testutil.FundAccount(s.ctx, s.app.BankKeeper, mva.GetAddress(), coinsToDelegate) @@ -467,11 +903,12 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { Expect(spendablePre).To(Equal(vested.Add(extraCoins...).Add(coinsToDelegate...))) // Delegate funds not in vesting schedule + delegating := sdk.NewCoins(coinsToDelegate.Add(initialVesting...)...) res, err := testutil.Delegate( s.ctx, s.app, vaPrivKey, - coinsToDelegate[0].Add(initialVesting[0]), + delegating[0], s.validator, ) Expect(err).To(BeNil()) @@ -479,13 +916,54 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { // Check spendable balance is updated properly spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) - Expect(spendablePost).To(Equal(sdk.Coins{})) + Expect(spendablePost).To(Equal(sdk.NewCoins())) + + // Check delegation was created successfully + delegations, err := s.stkQuerier.DelegatorDelegations( + s.goCtx, + &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: mva.Address, + }, + ) + Expect(err).To(BeNil()) + Expect(delegations.DelegationResponses).To(HaveLen(1)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) }) - It("can transfer vested tokens", func() { + It("can transfer vested coins", func() { err := s.app.BankKeeper.SendCoins(s.ctx, mva.GetAddress(), user, vested) Expect(err).To(BeNil()) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + vested, // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) - It("cannot transfer unvested tokens", func() { + It("cannot transfer unvested coins", func() { err := s.app.BankKeeper.SendCoins(s.ctx, mva.GetAddress(), user, unvested) Expect(err).ToNot(BeNil()) }) @@ -526,21 +1004,38 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { err := s.CommitAfter(duration) Expect(err).To(BeNil()) - // Check if all the tokens of initial vesting were vested + // Check if all the coins of initial vesting were vested now := s.ctx.BlockTime() vested = mva.GetVestedCoins(now) unvested = mva.GetVestingCoins(now) - Expect(vested).To(Equal(initialVesting)) - Expect(unvested).To(Equal(sdk.Coins{})) + expVested = initialVesting + expUnvested = sdk.NewCoins() + Expect(vested).To(Equal(expVested)) + Expect(unvested).To(Equal(expUnvested)) // Check balances of vesting account balances, err := s.querier.Balances(s.goCtx, &types.QueryBalancesRequest{Address: mva.Address}) Expect(err).To(BeNil()) Expect(balances.Vested).To(Equal(vested)) Expect(balances.Unvested).To(Equal(unvested)) - Expect(balances.Locked).To(Equal(sdk.Coins{})) // no tokens were locked + Expect(balances.Locked).To(Equal(sdk.Coins{})) // no coins were locked + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) - It("can send entire initial vesting tokens", func() { + It("can send entire initial vesting coins", func() { spendablePre := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePre).To(Equal(initialVesting.Add(extraCoins...))) @@ -549,24 +1044,40 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePost).To(Equal(extraCoins)) + + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + sdk.NewCoins(), // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + initialVesting, // sent amount + sdk.NewCoins(), // received amount + sdk.NewCoins(), // consumed fee + ) }) - It("can delegate initial vesting tokens", func() { - // Verify that the total spendable coins should include initial vesting tokens. + It("can delegate portion of initial vesting coins", func() { + // Verify that the total spendable coins should include initial vesting coins. spendablePre := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) Expect(spendablePre).To(Equal(initialVesting.Add(extraCoins...))) - // Delegate the initial vesting tokens + // Delegate the initial vesting coins + delegating := initialVesting.QuoInt(sdk.NewInt(3)) _, err := testutil.Delegate( s.ctx, s.app, vaPrivKey, - initialVesting[0], + delegating[0], s.validator, ) Expect(err).To(BeNil()) spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) - Expect(spendablePost).To(Equal(spendablePre.Sub(extraCoins...).Sub(initialVesting...))) + Expect(spendablePost).To(Equal(spendablePre.Sub(extraCoins...).Sub(delegating...))) // Check delegation was created successfully delegations, err := s.stkQuerier.DelegatorDelegations( @@ -577,15 +1088,69 @@ var _ = Describe("Monthly Vesting Account", Ordered, func() { ) Expect(err).To(BeNil()) Expect(delegations.DelegationResponses).To(HaveLen(1)) - Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(initialVesting[0].Amount)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // No vesting coins after vesting period. + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + sdk.NewCoins(), // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) + }) + It("can delegate initial vesting coins", func() { + // Verify that the total spendable coins should include initial vesting coins. + spendablePre := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) + Expect(spendablePre).To(Equal(initialVesting.Add(extraCoins...))) - // No vesting tokens after vesting period. - // Check delegation was tracked as delegated free - mva, err = s.app.VestingKeeper.GetMonthlyVestingAccount(s.ctx, mva.GetAddress()) + // Delegate the initial vesting coins + delegating := initialVesting + _, err := testutil.Delegate( + s.ctx, + s.app, + vaPrivKey, + delegating[0], + s.validator, + ) Expect(err).To(BeNil()) - Expect(mva).ToNot(BeNil()) - Expect(mva.DelegatedFree).To(Equal(initialVesting)) - Expect(mva.DelegatedVesting).To(BeNil()) + + spendablePost := s.app.BankKeeper.SpendableCoins(s.ctx, mva.GetAddress()) + Expect(spendablePost).To(Equal(spendablePre.Sub(extraCoins...).Sub(delegating...))) + + // Check delegation was created successfully + delegations, err := s.stkQuerier.DelegatorDelegations( + s.goCtx, + &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: mva.Address, + }, + ) + Expect(err).To(BeNil()) + Expect(delegations.DelegationResponses).To(HaveLen(1)) + Expect(delegations.DelegationResponses[0].Balance.Amount).To(Equal(delegating[0].Amount)) + + // No vesting coins after vesting period. + // Check all the balances of vesting account + validateVestingAccountBalances( + s.ctx, + s.app, + va, + delegating, // delegating + nil, // undelegating + expVested, // vested + expUnvested, // unvested + initialVesting.Add(extraCoins...), // initial balance + initialVesting, // sent amount + sdk.NewCoins(), // received amount + extraCoins, // consumed fee + ) }) It("can perform ethereum tx with initial vesting coins", func() { amount := initialVesting.AmountOf(utils.BaseDenom) diff --git a/x/vesting/types/vesting_account.go b/x/vesting/types/vesting_account.go index cc56a6c8..cf811233 100644 --- a/x/vesting/types/vesting_account.go +++ b/x/vesting/types/vesting_account.go @@ -110,6 +110,10 @@ func (m *MonthlyVestingAccount) TrackDelegation(blockTime time.Time, balance, am m.BaseVestingAccount.TrackDelegation(balance, m.GetVestingCoins(blockTime), amount) } +func (m *MonthlyVestingAccount) TrackUndelegation(amount sdk.Coins) { + m.BaseVestingAccount.TrackUndelegation(amount) +} + // GetStartTime returns the time when vesting period starts func (m MonthlyVestingAccount) GetStartTime() int64 { return m.StartTime