fix(x/staking): dust share (#18841)
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com> Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
This commit is contained in:
parent
52106a66cb
commit
adc0f75f62
@ -94,6 +94,10 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
|
||||
* (crypto) [#19371](https://github.com/cosmos/cosmos-sdk/pull/19371) Avoid cli redundant log in stdout, log to stderr instead.
|
||||
* (client) [#19393](https://github.com/cosmos/cosmos-sdk/pull/19393/) Add `ReadDefaultValuesFromDefaultClientConfig` to populate the default values from the default client config in client.Context without creating a app folder.
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) In a undelegation or redelegation if the shares being left delegated correspond to less than 1 token (in base denom) the entire delegation gets removed.
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* (x/consensus) [#19488](https://github.com/cosmos/cosmos-sdk/pull/19488) Consensus module creation takes `appmodule.Environment` instead of individual services.
|
||||
@ -1234,7 +1238,7 @@ replace github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8
|
||||
* (x/group) [#13214](https://github.com/cosmos/cosmos-sdk/pull/13214) Add `withdraw-proposal` command to group module's CLI transaction commands.
|
||||
* (x/auth) [#13048](https://github.com/cosmos/cosmos-sdk/pull/13048) Add handling of AccountNumberStoreKeyPrefix to the simulation decoder.
|
||||
* (simapp) [#13107](https://github.com/cosmos/cosmos-sdk/pull/13107) Call `SetIAVLCacheSize` with the configured value in simapp.
|
||||
* [#13301](https://github.com/cosmos/cosmos-sdk/pull/13301) Keep the balance query endpoint compatible with legacy blocks
|
||||
* [#13301](https://github.com/cosmos/cosmos-sdk/pull/13301) Keep the balance query endpoint compatible with legacy blocks
|
||||
* [#13321](https://github.com/cosmos/cosmos-sdk/pull/13321) Add flag to disable fast node migration and usage.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@ -1195,11 +1195,15 @@ func (k Keeper) ValidateUnbondAmount(
|
||||
return shares, errorsmod.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(math.OneInt())
|
||||
if err != nil {
|
||||
return shares, err
|
||||
}
|
||||
|
||||
if delShares.Sub(shares).LT(tolerance) {
|
||||
shares = delShares
|
||||
}
|
||||
|
||||
|
||||
@ -1176,3 +1176,51 @@ func (s *KeeperTestSuite) TestSetUnbondingDelegationEntry() {
|
||||
// unbondingID comes from a global counter -> gaps in unbondingIDs are OK as long as every unbondingID is unique
|
||||
require.Equal(uint64(2), resUnbonding.Entries[1].UnbondingId)
|
||||
}
|
||||
|
||||
func (s *KeeperTestSuite) TestUndelegateWithDustShare() {
|
||||
ctx, keeper := s.ctx, s.stakingKeeper
|
||||
require := s.Require()
|
||||
|
||||
addrDels, valAddrs := createValAddrs(2)
|
||||
|
||||
s.accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
|
||||
// construct the validators[0] & slash 1stake
|
||||
amt := math.NewInt(100)
|
||||
validator := testutil.NewValidator(s.T(), valAddrs[0], PKs[0])
|
||||
validator, _ = validator.AddTokensFromDel(amt)
|
||||
validator = validator.RemoveTokens(math.NewInt(1))
|
||||
validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true)
|
||||
|
||||
// first add a validators[0] to delegate too
|
||||
bond1to1 := stakingtypes.NewDelegation(addrDels[0].String(), valAddrs[0].String(), math.LegacyNewDec(100))
|
||||
require.NoError(keeper.SetDelegation(ctx, bond1to1))
|
||||
resBond, err := keeper.Delegations.Get(ctx, collections.Join(addrDels[0], valAddrs[0]))
|
||||
require.NoError(err)
|
||||
require.Equal(bond1to1, resBond)
|
||||
|
||||
// second delegators[1] add a validators[0] to delegate
|
||||
bond2to1 := stakingtypes.NewDelegation(addrDels[1].String(), valAddrs[0].String(), math.LegacyNewDec(1))
|
||||
validator, delegatorShare := validator.AddTokensFromDel(math.NewInt(1))
|
||||
bond2to1.Shares = delegatorShare
|
||||
_ = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true)
|
||||
require.NoError(keeper.SetDelegation(ctx, bond2to1))
|
||||
resBond, err = keeper.Delegations.Get(ctx, collections.Join(addrDels[1], valAddrs[0]))
|
||||
require.NoError(err)
|
||||
require.Equal(bond2to1, resBond)
|
||||
|
||||
// check delegation state
|
||||
delegations, err := keeper.GetValidatorDelegations(ctx, valAddrs[0])
|
||||
require.NoError(err)
|
||||
require.Equal(2, len(delegations))
|
||||
|
||||
// undelegate all delegator[0]'s delegate
|
||||
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0].String(), valAddrs[0].String(), sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(99))))
|
||||
require.NoError(err)
|
||||
|
||||
// remain only delegator[1]'s delegate
|
||||
delegations, err = keeper.GetValidatorDelegations(ctx, valAddrs[0])
|
||||
require.NoError(err)
|
||||
require.Equal(1, len(delegations))
|
||||
require.Equal(delegations[0].DelegatorAddress, addrDels[1].String())
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user