diff --git a/client/x/evmstaking/keeper/abci.go b/client/x/evmstaking/keeper/abci.go index da647b45..bbe4a64c 100644 --- a/client/x/evmstaking/keeper/abci.go +++ b/client/x/evmstaking/keeper/abci.go @@ -90,17 +90,39 @@ func (k *Keeper) EndBlock(ctx context.Context) (abci.ValidatorUpdates, error) { } for _, entry := range unbondedEntries { - log.Debug(ctx, "Adding undelegation to withdrawal queue", - "delegator", entry.delegatorAddress, - "validator", entry.validatorAddress, - "amount", entry.amount.String()) - delegatorAddr, err := k.authKeeper.AddressCodec().StringToBytes(entry.delegatorAddress) if err != nil { return nil, errors.Wrap(err, "delegator address from bech32") } + + maxAmount := k.bankKeeper.SpendableCoin(ctx, delegatorAddr, sdk.DefaultBondDenom).Amount + if maxAmount.IsZero() { + log.Warn(ctx, "No spendable coins for undelegation", + errors.New("no spendable coins for undelegation"), + "delegator", entry.delegatorAddress, + "validator", entry.validatorAddress, + "original_amount", entry.amount.String()) + + continue + } + + if entry.amount.LT(maxAmount) { + maxAmount = entry.amount + log.Warn(ctx, "Undelegation amount is less than max amount", + errors.New("undelegation amount is less than max amount"), + "delegator", entry.delegatorAddress, + "validator", entry.validatorAddress, + "original_amount", entry.amount.String(), + "max_amount", maxAmount.String()) + } + + log.Debug(ctx, "Adding undelegation to withdrawal queue", + "delegator", entry.delegatorAddress, + "validator", entry.validatorAddress, + "max_amount", maxAmount.String()) + // Burn tokens from the delegator - _, coins := IPTokenToBondCoin(entry.amount.BigInt()) + _, coins := IPTokenToBondCoin(maxAmount.BigInt()) err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, delegatorAddr, types.ModuleName, coins) if err != nil { return nil, errors.Wrap(err, "send coins from account to module") @@ -123,7 +145,7 @@ func (k *Keeper) EndBlock(ctx context.Context) (abci.ValidatorUpdates, error) { entry.delegatorAddress, entry.validatorAddress, delEvmAddr, - entry.amount.Uint64(), + maxAmount.Uint64(), )) if err != nil { return nil, err diff --git a/client/x/evmstaking/keeper/abci_test.go b/client/x/evmstaking/keeper/abci_test.go index ccf26b94..d300159f 100644 --- a/client/x/evmstaking/keeper/abci_test.go +++ b/client/x/evmstaking/keeper/abci_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + sdkmath "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" dtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -363,6 +365,7 @@ func compareValUpdates(t *testing.T, expected, actual abcitypes.ValidatorUpdates // setupMaturedUnbonding creates matured unbondings for testing. func (s *TestSuite) setupMatureUnbondingDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt string, duration time.Duration) { + require := s.Require() pastHeader := ctx.BlockHeader() pastHeader.Time = pastHeader.Time.Add(-duration).Add(-time.Minute) pastCtx := ctx.WithBlockHeader(pastHeader) @@ -372,6 +375,9 @@ func (s *TestSuite) setupMatureUnbondingDelegation(ctx sdk.Context, delAddr sdk. // Mock staking.EndBlocker s.BankKeeper.EXPECT().UndelegateCoinsFromModuleToAccount(gomock.Any(), stypes.NotBondedPoolName, delAddr, gomock.Any()).Return(nil) // Mock evmstaking.EndBlocker + amtInt, ok := sdkmath.NewIntFromString(amt) + require.True(ok) + s.BankKeeper.EXPECT().SpendableCoin(gomock.Any(), delAddr, sdk.DefaultBondDenom).Return(sdk.NewCoin(sdk.DefaultBondDenom, amtInt)) s.BankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), delAddr, types.ModuleName, gomock.Any()).Return(nil) s.BankKeeper.EXPECT().BurnCoins(gomock.Any(), types.ModuleName, gomock.Any()).Return(nil) } diff --git a/client/x/evmstaking/testutil/expected_keepers_mocks.go b/client/x/evmstaking/testutil/expected_keepers_mocks.go index ab85d653..c2360617 100644 --- a/client/x/evmstaking/testutil/expected_keepers_mocks.go +++ b/client/x/evmstaking/testutil/expected_keepers_mocks.go @@ -155,15 +155,15 @@ func (mr *MockAccountKeeperMockRecorder) SetAccount(ctx, acc any) *gomock.Call { } // SetModuleAccount mocks base method. -func (m *MockAccountKeeper) SetModuleAccount(arg0 context.Context, arg1 types0.ModuleAccountI) { +func (m *MockAccountKeeper) SetModuleAccount(ctx context.Context, modAcc types0.ModuleAccountI) { m.ctrl.T.Helper() - m.ctrl.Call(m, "SetModuleAccount", arg0, arg1) + m.ctrl.Call(m, "SetModuleAccount", ctx, modAcc) } // SetModuleAccount indicates an expected call of SetModuleAccount. -func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 any) *gomock.Call { +func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(ctx, modAcc any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), ctx, modAcc) } // MockBankKeeper is a mock of BankKeeper interface. @@ -329,6 +329,20 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderPoo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), ctx, senderPool, recipientPool, amt) } +// SpendableCoin mocks base method. +func (m *MockBankKeeper) SpendableCoin(ctx context.Context, addr types0.AccAddress, denom string) types0.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SpendableCoin", ctx, addr, denom) + ret0, _ := ret[0].(types0.Coin) + return ret0 +} + +// SpendableCoin indicates an expected call of SpendableCoin. +func (mr *MockBankKeeperMockRecorder) SpendableCoin(ctx, addr, denom any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoin", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoin), ctx, addr, denom) +} + // SpendableCoins mocks base method. func (m *MockBankKeeper) SpendableCoins(ctx context.Context, addr types0.AccAddress) types0.Coins { m.ctrl.T.Helper() @@ -395,21 +409,6 @@ func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), ctx) } -// CompleteRedelegation mocks base method. -func (m *MockStakingKeeper) CompleteRedelegation(ctx context.Context, delAddr types0.AccAddress, valSrcAddr, valDstAddr types0.ValAddress) (types0.Coins, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CompleteRedelegation", ctx, delAddr, valSrcAddr, valDstAddr) - ret0, _ := ret[0].(types0.Coins) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CompleteRedelegation indicates an expected call of CompleteRedelegation. -func (mr *MockStakingKeeperMockRecorder) CompleteRedelegation(ctx, delAddr, valSrcAddr, valDstAddr any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteRedelegation", reflect.TypeOf((*MockStakingKeeper)(nil).CompleteRedelegation), ctx, delAddr, valSrcAddr, valDstAddr) -} - // DeleteUnbondingIndex mocks base method. func (m *MockStakingKeeper) DeleteUnbondingIndex(ctx context.Context, id uint64) error { m.ctrl.T.Helper() @@ -424,21 +423,6 @@ func (mr *MockStakingKeeperMockRecorder) DeleteUnbondingIndex(ctx, id any) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUnbondingIndex", reflect.TypeOf((*MockStakingKeeper)(nil).DeleteUnbondingIndex), ctx, id) } -// DequeueAllMatureRedelegationQueue mocks base method. -func (m *MockStakingKeeper) DequeueAllMatureRedelegationQueue(ctx context.Context, currTime time.Time) ([]types2.DVVTriplet, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DequeueAllMatureRedelegationQueue", ctx, currTime) - ret0, _ := ret[0].([]types2.DVVTriplet) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DequeueAllMatureRedelegationQueue indicates an expected call of DequeueAllMatureRedelegationQueue. -func (mr *MockStakingKeeperMockRecorder) DequeueAllMatureRedelegationQueue(ctx, currTime any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DequeueAllMatureRedelegationQueue", reflect.TypeOf((*MockStakingKeeper)(nil).DequeueAllMatureRedelegationQueue), ctx, currTime) -} - // EndBlocker mocks base method. func (m *MockStakingKeeper) EndBlocker(ctx context.Context) ([]types.ValidatorUpdate, error) { m.ctrl.T.Helper() diff --git a/client/x/evmstaking/types/expected_keepers.go b/client/x/evmstaking/types/expected_keepers.go index e4f5e20d..d3317890 100644 --- a/client/x/evmstaking/types/expected_keepers.go +++ b/client/x/evmstaking/types/expected_keepers.go @@ -40,6 +40,7 @@ type BankKeeper interface { GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins LockedCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins + SpendableCoin(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin GetSupply(ctx context.Context, denom string) sdk.Coin SendCoinsFromModuleToModule(ctx context.Context, senderPool, recipientPool string, amt sdk.Coins) error } diff --git a/lib/buildinfo/buildinfo.go b/lib/buildinfo/buildinfo.go index a7c3808b..4bf5301e 100644 --- a/lib/buildinfo/buildinfo.go +++ b/lib/buildinfo/buildinfo.go @@ -15,7 +15,7 @@ import ( const ( VersionMajor = 0 // Major version component of the current release VersionMinor = 10 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release + VersionPatch = 1 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string )