From 29809a36314f43b6ad93a79fcd32576353747fc9 Mon Sep 17 00:00:00 2001 From: leekyungun Date: Fri, 21 Aug 2026 16:13:18 +0900 Subject: [PATCH] fix(statedb): guard SubBalance against underflow; make ParseAmount denom-aware Backports cosmos/evm#1176. Adds an underflow guard to stateObject.SubBalance so a subtraction larger than the current balance panics (reverting the tx) instead of wrapping the uint256 balance to a near-max value. Also makes precompile bank-event amount parsing account for both the base and extended EVM-coin denoms. This closes the class of issue used to drain MANTRA (delegate() with a tiny amount triggering a balance underflow). Full fix is upstream cosmos/evm v0.6.2; recommend rebasing this fork onto v0.6.2 for the complete set of changes. Ref: https://github.com/cosmos/evm/pull/1176 --- precompiles/common/utils.go | 9 +++++++-- x/vm/statedb/state_object.go | 12 +++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/precompiles/common/utils.go b/precompiles/common/utils.go index 520dc5e0..0db1efc7 100644 --- a/precompiles/common/utils.go +++ b/precompiles/common/utils.go @@ -42,8 +42,13 @@ func ParseAmount(event sdk.Event) (*uint256.Int, error) { return nil, fmt.Errorf("failed to parse coins from %q: %w", amountAttr.Value, err) } - amountBigInt := amountCoins.AmountOf(evmtypes.GetEVMCoinDenom()).BigInt() - amount, err := utils.Uint256FromBigInt(evmtypes.ConvertAmountTo18DecimalsBigInt(amountBigInt)) + baseAmount := amountCoins.AmountOf(evmtypes.GetEVMCoinDenom()).BigInt() + amountBigInt := evmtypes.ConvertAmountTo18DecimalsBigInt(baseAmount) + if evmtypes.GetEVMCoinExtendedDenom() != evmtypes.GetEVMCoinDenom() { + extendedAmount := amountCoins.AmountOf(evmtypes.GetEVMCoinExtendedDenom()).BigInt() + amountBigInt = new(big.Int).Add(amountBigInt, extendedAmount) + } + amount, err := utils.Uint256FromBigInt(amountBigInt) if err != nil { return nil, fmt.Errorf("failed to convert coin amount to Uint256: %w", err) } diff --git a/x/vm/statedb/state_object.go b/x/vm/statedb/state_object.go index 0b06e64e..a93f3dcd 100644 --- a/x/vm/statedb/state_object.go +++ b/x/vm/statedb/state_object.go @@ -2,6 +2,7 @@ package statedb import ( "bytes" + "fmt" "math/big" "sort" @@ -147,7 +148,16 @@ func (s *stateObject) SubBalance(amount *uint256.Int) uint256.Int { if amount.IsZero() { return *(s.Balance()) } - return s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount)) + balance := s.Balance() + if balance.Lt(amount) { + panic(fmt.Sprintf( + "state balance underflow for %s: have=%s sub=%s", + s.address.Hex(), + balance.String(), + amount.String(), + )) + } + return s.SetBalance(new(uint256.Int).Sub(balance, amount)) } // SetBalance updates account balance.