Skip to content

Commit

Permalink
fix: delegations with less than the minimum share removed (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoowonYun authored Jan 3, 2024
1 parent e40f08b commit f8f5517
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 5 deletions.
14 changes: 9 additions & 5 deletions x/staking/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -1008,11 +1008,15 @@ func (k Keeper) ValidateUnbondAmount(
return shares, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid shares amount")
}

// Cap the shares at the delegation's shares. Shares being greater could occur
// due to rounding, however we don't want to truncate the shares or take the
// minimum because we want to allow for the full withdraw of shares from a
// delegation.
if shares.GT(delShares) {
// Depending on the share, amount can be smaller than unit amount(1stake).
// If the remain amount after unbonding is smaller than the minimum share,
// it's completely unbonded to avoid leaving dust shares.
tolerance, err := validator.SharesFromTokens(sdk.OneInt())
if err != nil {
return shares, err
}

if delShares.Sub(shares).LT(tolerance) {
shares = delShares
}

Expand Down
46 changes: 46 additions & 0 deletions x/staking/keeper/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
"github.com/cosmos/cosmos-sdk/x/staking/types"
Expand Down Expand Up @@ -990,3 +991,48 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
red, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.False(t, found, "%v", red)
}

func TestUndelegateWithDustShare(t *testing.T) {
_, app, ctx := createTestInput()
sh := staking.NewHandler(app.StakingKeeper)

addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0))
addrVals := simapp.ConvertAddrsToValAddrs(addrDels)

// construct the validators[0] & slash 1stake
amt := sdk.NewInt(100)
validator := teststaking.NewValidator(t, addrVals[0], PKs[0])
validator, _ = validator.AddTokensFromDel(amt)
validator = validator.RemoveTokens(sdk.NewInt(1))
validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)

// first add a validators[0] to delegate too
bond1to1 := types.NewDelegation(addrDels[0], addrVals[0], sdk.NewDec(100))
app.StakingKeeper.SetDelegation(ctx, bond1to1)
resBond, found := app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
require.True(t, found)
require.Equal(t, bond1to1, resBond)

// second delegators[1] add a validators[0] to delegate
bond2to1 := types.NewDelegation(addrDels[1], addrVals[0], sdk.NewDec(1))
validator, delegatorShare := validator.AddTokensFromDel(sdk.NewInt(1))
bond2to1.Shares = delegatorShare
_ = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)
app.StakingKeeper.SetDelegation(ctx, bond2to1)
resBond, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], addrVals[0])
require.True(t, found)
require.Equal(t, bond2to1, resBond)

// check delegation state
delegations := app.StakingKeeper.GetValidatorDelegations(ctx, addrVals[0])
require.Equal(t, 2, len(delegations))

// undelegate all delegator[0]'s delegate
_, err := sh(ctx, types.NewMsgUndelegate(addrDels[0], addrVals[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(99))))
require.NoError(t, err)

// remain only delegator[1]'s delegate
delegations = app.StakingKeeper.GetValidatorDelegations(ctx, addrVals[0])
require.Equal(t, 1, len(delegations))
require.Equal(t, delegations[0].DelegatorAddress, addrDels[1].String())
}

0 comments on commit f8f5517

Please sign in to comment.