Skip to content

Commit 307b519

Browse files
javiersuweijieemidev98
authored andcommitted
fix: donate all vesting token to work with dust delegations
1 parent b854247 commit 307b519

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

x/auth/vesting/handler_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package vesting_test
22

33
import (
4+
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
45
"testing"
6+
"time"
57

68
"github.com/stretchr/testify/suite"
79
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@@ -108,8 +110,22 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() {
108110
addr1 := sdk.AccAddress([]byte("addr1_______________"))
109111
addr2 := sdk.AccAddress([]byte("addr2_______________"))
110112
addr3 := sdk.AccAddress([]byte("addr3_______________"))
113+
addr4 := sdk.AccAddress([]byte("addr4_______________"))
111114

112115
valAddr := sdk.ValAddress([]byte("validator___________"))
116+
suite.app.StakingKeeper.SetValidator(ctx, stakingtypes.Validator{
117+
OperatorAddress: valAddr.String(),
118+
ConsensusPubkey: nil,
119+
Jailed: false,
120+
Status: 0,
121+
Tokens: sdk.NewInt(2),
122+
DelegatorShares: sdk.MustNewDecFromStr("1.1"),
123+
Description: stakingtypes.Description{},
124+
UnbondingHeight: 0,
125+
UnbondingTime: time.Time{},
126+
Commission: stakingtypes.Commission{},
127+
MinSelfDelegation: sdk.NewInt(1),
128+
})
113129

114130
acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1)
115131
suite.app.AccountKeeper.SetAccount(ctx, acc1)
@@ -133,6 +149,18 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() {
133149
suite.app.AccountKeeper.SetAccount(ctx, acc3)
134150
suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr3, balances))
135151

152+
acc4 := types.NewPermanentLockedAccount(
153+
suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr4).(*authtypes.BaseAccount), balances,
154+
)
155+
acc4.DelegatedVesting = balances
156+
suite.app.AccountKeeper.SetAccount(ctx, acc4)
157+
suite.app.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{
158+
DelegatorAddress: addr4.String(),
159+
ValidatorAddress: valAddr.String(),
160+
Shares: sdk.MustNewDecFromStr("0.1"),
161+
})
162+
suite.Require().NoError(simapp.FundAccount(suite.app.BankKeeper, ctx, addr4, balances))
163+
136164
testCases := []struct {
137165
name string
138166
msg *types.MsgDonateAllVestingTokens
@@ -153,12 +181,19 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() {
153181
msg: types.NewMsgDonateAllVestingTokens(addr3),
154182
expectErr: false,
155183
},
184+
{
185+
name: "donate from vesting account with dust delegation",
186+
msg: types.NewMsgDonateAllVestingTokens(addr4),
187+
expectErr: false,
188+
},
156189
}
157190

158191
for _, tc := range testCases {
159192
tc := tc
160193

161194
suite.Run(tc.name, func() {
195+
// Rollback context after every test case
196+
ctx, _ := ctx.CacheContext()
162197
res, err := suite.handler(ctx, tc.msg)
163198
if tc.expectErr {
164199
suite.Require().Error(err)
@@ -178,6 +213,9 @@ func (suite *HandlerTestSuite) TestMsgDonateVestingToken() {
178213
suite.Require().True(ok)
179214
balance := suite.app.BankKeeper.GetAllBalances(ctx, fromAddr)
180215
suite.Require().Empty(balance)
216+
217+
_, broken := stakingkeeper.DelegatorSharesInvariant(suite.app.StakingKeeper)(ctx)
218+
suite.Require().False(broken)
181219
}
182220
})
183221
}

x/auth/vesting/msg_server.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package vesting
22

33
import (
44
"context"
5+
"fmt"
6+
"math"
57

68
"github.com/armon/go-metrics"
79

@@ -219,9 +221,33 @@ func (s msgServer) DonateAllVestingTokens(goCtx context.Context, msg *types.MsgD
219221
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s not exists", msg.FromAddress)
220222
}
221223

222-
// check whether an account has any type of staking entry
223-
if len(sk.GetDelegatorDelegations(ctx, acc.GetAddress(), 1)) != 0 ||
224-
len(sk.GetUnbondingDelegations(ctx, acc.GetAddress(), 1)) != 0 ||
224+
// get all delegations of an account and undust those that have less than 1 uluna
225+
delegations := sk.GetDelegatorDelegations(ctx, acc.GetAddress(), math.MaxUint16)
226+
for _, delegation := range delegations {
227+
validatorAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress)
228+
if err != nil {
229+
return nil, err
230+
}
231+
validator, found := sk.GetValidator(ctx, validatorAddr)
232+
if !found {
233+
return nil, fmt.Errorf("validator not found")
234+
}
235+
// Try to delete the dust delegation
236+
_, removedTokens := sk.RemoveValidatorTokensAndShares(ctx, validator, delegation.Shares)
237+
// If the delegation is not dust, return an error and stop the donation flow
238+
if !removedTokens.IsZero() {
239+
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has a non-zero staking entry", msg.FromAddress)
240+
}
241+
242+
// Remove the dust delegation shares from the validator
243+
err = sk.RemoveDelegation(ctx, delegation)
244+
if err != nil {
245+
return nil, err
246+
}
247+
}
248+
249+
// check whether an account has any other type of staking entries
250+
if len(sk.GetUnbondingDelegations(ctx, acc.GetAddress(), 1)) != 0 ||
225251
len(sk.GetRedelegations(ctx, acc.GetAddress(), 1)) != 0 {
226252
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s has staking entry", msg.FromAddress)
227253
}

x/auth/vesting/types/expected_keepers.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package types
22

33
import (
4+
"cosmossdk.io/math"
45
sdk "github.com/cosmos/cosmos-sdk/types"
56
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
67
)
@@ -23,4 +24,7 @@ type StakingKeeper interface {
2324
GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []stakingtypes.Delegation)
2425
GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (unbondingDelegations []stakingtypes.UnbondingDelegation)
2526
GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (redelegations []stakingtypes.Redelegation)
27+
RemoveValidatorTokensAndShares(ctx sdk.Context, validator stakingtypes.Validator, sharesToRemove sdk.Dec) (valOut stakingtypes.Validator, removedTokens math.Int)
28+
GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool)
29+
RemoveDelegation(ctx sdk.Context, delegation stakingtypes.Delegation) error
2630
}

0 commit comments

Comments
 (0)