diff --git a/CHANGELOG.md b/CHANGELOG.md index 02af17c34..1d86e46e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - [\#800](https://github.com/cosmos/evm/pull/800) Fix denom exponent validation in virtual fee deduct in vm module. - [\#817](https://github.com/cosmos/evm/pull/817) Align GetCoinbaseAddress to handle empty proposer address in contexts like CheckTx where proposer doesn't exist. - [\#816](https://github.com/cosmos/evm/pull/816) Avoid nil pointer when RPC requests execute before evmCoinInfo initialization in PreBlock with defaultEvmCoinInfo fallback. +- [\#828](https://github.com/cosmos/evm/pull/828) Validate decimals before conversion to prevent panic when coininfo is missing in historical queries. ## v0.5.0 diff --git a/x/vm/wrappers/feemarket.go b/x/vm/wrappers/feemarket.go index f864ec3e2..04f9c6a0f 100644 --- a/x/vm/wrappers/feemarket.go +++ b/x/vm/wrappers/feemarket.go @@ -32,11 +32,21 @@ func NewFeeMarketWrapper( // GetBaseFee returns the base fee converted to 18 decimals. func (w FeeMarketWrapper) GetBaseFee(ctx sdk.Context, decimals types.Decimals) *big.Int { baseFee := w.FeeMarketKeeper.GetBaseFee(ctx) - if baseFee.IsNil() { - return nil + if baseFee.IsNil() || baseFee.BigInt() == nil { + return big.NewInt(0) } - - return baseFee.MulInt(decimals.ConversionFactor()).TruncateInt().BigInt() + if err := decimals.Validate(); err != nil { + return big.NewInt(0) + } + conv := decimals.ConversionFactor() + if conv.BigInt() == nil { + return big.NewInt(0) + } + converted := baseFee.MulInt(conv).TruncateInt().BigInt() + if converted == nil { + return big.NewInt(0) + } + return converted } // CalculateBaseFee returns the calculated base fee converted to 18 decimals. diff --git a/x/vm/wrappers/feemarket_test.go b/x/vm/wrappers/feemarket_test.go index e23bfb713..df951f7df 100644 --- a/x/vm/wrappers/feemarket_test.go +++ b/x/vm/wrappers/feemarket_test.go @@ -48,7 +48,7 @@ func TestGetBaseFee(t *testing.T) { { name: "success - nil base fee", coinInfo: testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID], - expResult: nil, + expResult: big.NewInt(0), mockSetup: func(mfk *testutil.MockFeeMarketKeeper) { mfk.EXPECT(). GetBaseFee(gomock.Any()). @@ -85,24 +85,58 @@ func TestGetBaseFee(t *testing.T) { Return(sdkmath.LegacyNewDecWithPrec(1, 13)) // multiplied by 1e12 is still less than 1 }, }, + { + name: "defensive - zero decimals returns zero without panic", + coinInfo: evmtypes.EvmCoinInfo{ + Denom: "aevmos", + ExtendedDenom: "aevmos", + DisplayDenom: "evmos", + Decimals: 0, // invalid/uninitialized + }, + expResult: big.NewInt(0), + mockSetup: func(mfk *testutil.MockFeeMarketKeeper) { + mfk.EXPECT(). + GetBaseFee(gomock.Any()). + Return(sdkmath.LegacyNewDec(1e18)) + }, + }, + { + name: "defensive - invalid decimals (19) returns zero without panic", + coinInfo: evmtypes.EvmCoinInfo{ + Denom: "aevmos", + ExtendedDenom: "aevmos", + DisplayDenom: "evmos", + Decimals: 19, // exceeds max supported + }, + expResult: big.NewInt(0), + mockSetup: func(mfk *testutil.MockFeeMarketKeeper) { + mfk.EXPECT(). + GetBaseFee(gomock.Any()). + Return(sdkmath.LegacyNewDec(1e18)) + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Setup EVM configurator to have access to the EVM coin info. - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - err := configurator.WithEVMCoinInfo(tc.coinInfo).Configure() - require.NoError(t, err, "failed to configure EVMConfigurator") - ctrl := gomock.NewController(t) mockFeeMarketKeeper := testutil.NewMockFeeMarketKeeper(ctrl) tc.mockSetup(mockFeeMarketKeeper) feeMarketWrapper := wrappers.NewFeeMarketWrapper(mockFeeMarketKeeper) - result := feeMarketWrapper.GetBaseFee(sdk.Context{}, evmtypes.Decimals(tc.coinInfo.Decimals)) - - require.Equal(t, tc.expResult, result) + // skip EVMConfigurator for defensive cases + if tc.coinInfo.Decimals == 0 || tc.coinInfo.Decimals > 18 { + result := feeMarketWrapper.GetBaseFee(sdk.Context{}, evmtypes.Decimals(tc.coinInfo.Decimals)) + require.Equal(t, tc.expResult, result) + } else { + // Setup EVM configurator to have access to the EVM coin info. + configurator := evmtypes.NewEVMConfigurator() + configurator.ResetTestConfig() + err := configurator.WithEVMCoinInfo(tc.coinInfo).Configure() + require.NoError(t, err, "failed to configure EVMConfigurator") + result := feeMarketWrapper.GetBaseFee(sdk.Context{}, evmtypes.Decimals(tc.coinInfo.Decimals)) + require.Equal(t, tc.expResult, result) + } }) } }