diff --git a/ante/cosmos.go b/ante/cosmos.go index 1820bd79b..0573afd27 100644 --- a/ante/cosmos.go +++ b/ante/cosmos.go @@ -16,7 +16,7 @@ func newCosmosAnteHandler(ctx sdk.Context, options HandlerOptions) sdk.AnteHandl feemarketParams := options.FeeMarketKeeper.GetParams(ctx) var txFeeChecker ante.TxFeeChecker if options.DynamicFeeChecker { - txFeeChecker = evmante.NewDynamicFeeChecker(&feemarketParams) + txFeeChecker = evmante.NewDynamicFeeChecker(options.EvmKeeper, &feemarketParams) } return sdk.ChainAnteDecorators( @@ -30,7 +30,7 @@ func newCosmosAnteHandler(ctx sdk.Context, options HandlerOptions) sdk.AnteHandl ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), - cosmosante.NewMinGasPriceDecorator(&feemarketParams), + cosmosante.NewMinGasPriceDecorator(&feemarketParams, options.EvmKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, txFeeChecker), // SetPubKeyDecorator must be called before all signature verification decorators diff --git a/ante/cosmos/authz_test.go b/ante/cosmos/authz_test.go index aee72f910..b57986695 100644 --- a/ante/cosmos/authz_test.go +++ b/ante/cosmos/authz_test.go @@ -21,11 +21,6 @@ import ( ) func TestAuthzLimiterDecorator(t *testing.T) { - evmConfigurator := evmtypes.NewEVMConfigurator(). - WithEVMCoinInfo(constants.ExampleChainCoinInfo[constants.ExampleChainID]) - err := evmConfigurator.Configure() - require.NoError(t, err) - encodingCfg := encoding.MakeConfig(constants.ExampleChainID.EVMChainID) txCfg := encodingCfg.TxConfig testPrivKeys, testAddresses, err := testutil.GeneratePrivKeyAddressPairs(5) diff --git a/ante/cosmos/min_gas_price.go b/ante/cosmos/min_gas_price.go index b4c69b11b..3f6aadbb7 100644 --- a/ante/cosmos/min_gas_price.go +++ b/ante/cosmos/min_gas_price.go @@ -5,8 +5,8 @@ import ( "math/big" "slices" + anteinterfaces "github.com/cosmos/evm/ante/interfaces" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" - evmtypes "github.com/cosmos/evm/x/vm/types" errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" @@ -22,12 +22,16 @@ import ( // CONTRACT: Tx must implement FeeTx to use MinGasPriceDecorator type MinGasPriceDecorator struct { feemarketParams *feemarkettypes.Params + evmKeeper anteinterfaces.EVMKeeper } // NewMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for // Cosmos transactions. -func NewMinGasPriceDecorator(feemarketParams *feemarkettypes.Params) MinGasPriceDecorator { - return MinGasPriceDecorator{feemarketParams} +func NewMinGasPriceDecorator(feemarketParams *feemarkettypes.Params, evmKeeper anteinterfaces.EVMKeeper) MinGasPriceDecorator { + return MinGasPriceDecorator{ + feemarketParams: feemarketParams, + evmKeeper: evmKeeper, + } } func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { @@ -39,7 +43,7 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate minGasPrice := mpd.feemarketParams.MinGasPrice feeCoins := feeTx.GetFee() - evmDenom := evmtypes.GetEVMCoinDenom() + evmDenom := mpd.evmKeeper.EvmCoinInfo().Denom // only allow user to pass in aatom and stake native token as transaction fees // allow use stake native tokens for fees is just for unit tests to pass diff --git a/ante/evm/05_signature_verification.go b/ante/evm/05_signature_verification.go index 4c2dd9146..f76efbd09 100644 --- a/ante/evm/05_signature_verification.go +++ b/ante/evm/05_signature_verification.go @@ -32,7 +32,7 @@ func NewEthSigVerificationDecorator(ek anteinterfaces.EVMKeeper) EthSigVerificat // Failure in RecheckTx will prevent tx to be included into block, especially when CheckTx succeed, in which case user // won't see the error message. func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - ethCfg := evmtypes.GetEthChainConfig() + ethCfg := esvd.evmKeeper.EthChainConfig() blockNum := big.NewInt(ctx.BlockHeight()) signer := ethtypes.MakeSigner(ethCfg, blockNum, uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here diff --git a/ante/evm/10_gas_wanted.go b/ante/evm/10_gas_wanted.go index ef07c0ec7..3ce8a9d83 100644 --- a/ante/evm/10_gas_wanted.go +++ b/ante/evm/10_gas_wanted.go @@ -6,7 +6,6 @@ import ( anteinterfaces "github.com/cosmos/evm/ante/interfaces" "github.com/cosmos/evm/ante/types" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" - evmtypes "github.com/cosmos/evm/x/vm/types" errorsmod "cosmossdk.io/errors" @@ -37,7 +36,7 @@ func NewGasWantedDecorator( } func (gwd GasWantedDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - ethCfg := evmtypes.GetEthChainConfig() + ethCfg := gwd.evmKeeper.EthChainConfig() blockHeight := big.NewInt(ctx.BlockHeight()) isLondon := ethCfg.IsLondon(blockHeight) diff --git a/ante/evm/fee_checker.go b/ante/evm/fee_checker.go index 4a3266859..395c91130 100644 --- a/ante/evm/fee_checker.go +++ b/ante/evm/fee_checker.go @@ -3,8 +3,7 @@ package evm import ( "math" - "github.com/ethereum/go-ethereum/params" - + anteinterfaces "github.com/cosmos/evm/ante/interfaces" cosmosevmtypes "github.com/cosmos/evm/ante/types" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" evmtypes "github.com/cosmos/evm/x/vm/types" @@ -15,6 +14,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + gethparams "github.com/ethereum/go-ethereum/params" ) // NewDynamicFeeChecker returns a `TxFeeChecker` that applies a dynamic fee to @@ -25,7 +25,7 @@ import ( // - when `ExtensionOptionDynamicFeeTx` is omitted, `tipFeeCap` defaults to `MaxInt64`. // - when london hardfork is not enabled, it falls back to SDK default behavior (validator min-gas-prices). // - Tx priority is set to `effectiveGasPrice / DefaultPriorityReduction`. -func NewDynamicFeeChecker(feemarketParams *feemarkettypes.Params) authante.TxFeeChecker { +func NewDynamicFeeChecker(evmKeeper anteinterfaces.EVMKeeper, feemarketParams *feemarkettypes.Params) authante.TxFeeChecker { return func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) { feeTx, ok := tx.(sdk.FeeTx) if !ok { @@ -37,8 +37,12 @@ func NewDynamicFeeChecker(feemarketParams *feemarkettypes.Params) authante.TxFee // genesis transactions: fallback to min-gas-price logic return checkTxFeeWithValidatorMinGasPrices(ctx, feeTx) } - denom := evmtypes.GetEVMCoinDenom() - ethCfg := evmtypes.GetEthChainConfig() + ethCfg := evmKeeper.EthChainConfig() + coinInfo := evmKeeper.EvmCoinInfo() + denom := coinInfo.Denom + if denom == "" { + denom = evmtypes.DefaultEVMDenom + } return FeeChecker(ctx, feemarketParams, denom, ethCfg, feeTx) } @@ -49,7 +53,7 @@ func FeeChecker( ctx sdk.Context, feemarketParams *feemarkettypes.Params, denom string, - ethConfig *params.ChainConfig, + ethConfig *gethparams.ChainConfig, feeTx sdk.FeeTx, ) (sdk.Coins, int64, error) { if !evmtypes.IsLondon(ethConfig, ctx.BlockHeight()) { diff --git a/ante/evm/fee_checker_test.go b/ante/evm/fee_checker_test.go index b942b8e17..30bd3cdf1 100644 --- a/ante/evm/fee_checker_test.go +++ b/ante/evm/fee_checker_test.go @@ -36,23 +36,6 @@ func TestSDKTxFeeChecker(t *testing.T) { chainID := uint64(testconstants.EighteenDecimalsChainID) encodingConfig := encoding.MakeConfig(chainID) //nolint:staticcheck // this is used - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - // set global chain config - ethCfg := evmtypes.DefaultChainConfig(chainID) - if err := evmtypes.SetChainConfig(ethCfg); err != nil { - panic(err) - } - err := configurator. - WithExtendedEips(evmtypes.DefaultCosmosEVMActivators). - // NOTE: we're using the 18 decimals default for the example chain - WithEVMCoinInfo(testconstants.ChainsCoinInfo[chainID]). - Configure() - require.NoError(t, err) - if err != nil { - panic(err) - } - evmDenom := evmtypes.GetEVMCoinDenom() minGasPrices := sdk.NewDecCoins(sdk.NewDecCoin(evmDenom, math.NewInt(10))) @@ -251,14 +234,17 @@ func TestSDKTxFeeChecker(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - cfg := evmtypes.GetEthChainConfig() + evmKeeper := NewExtendedEVMKeeper() + chainCfg := evmKeeper.ChainConfig() + ethCfg := chainCfg.EthereumConfig(nil) if !tc.londonEnabled { - cfg.LondonBlock = big.NewInt(10000) + ethCfg.LondonBlock = big.NewInt(10000) } else { - cfg.LondonBlock = big.NewInt(0) + ethCfg.LondonBlock = big.NewInt(0) } + feemarketParams := tc.feemarketParamsFn() - fees, priority, err := evm.NewDynamicFeeChecker(&feemarketParams)(tc.ctx, tc.buildTx()) + fees, priority, err := evm.NewDynamicFeeChecker(evmKeeper, &feemarketParams)(tc.ctx, tc.buildTx()) if tc.expSuccess { require.Equal(t, tc.expFees, fees.String()) require.Equal(t, tc.expPriority, priority) diff --git a/ante/evm/mono_decorator.go b/ante/evm/mono_decorator.go index ba4601557..793545214 100644 --- a/ante/evm/mono_decorator.go +++ b/ante/evm/mono_decorator.go @@ -76,7 +76,10 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne } } - evmDenom := evmtypes.GetEVMCoinDenom() + evmDenom := md.evmKeeper.EvmCoinInfo().Denom + if evmDenom == "" { + evmDenom = evmtypes.DefaultEVMDenom + } // 1. setup ctx ctx, err = SetupContextAndResetTransientGas(ctx, tx, md.evmKeeper) @@ -112,7 +115,7 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne Difficulty: big.NewInt(0), } - chainConfig := evmtypes.GetEthChainConfig() + chainConfig := md.evmKeeper.EthChainConfig() if err := txpool.ValidateTransaction(ethTx, &header, decUtils.Signer, &txpool.ValidationOptions{ Config: chainConfig, diff --git a/ante/evm/mono_decorator_test.go b/ante/evm/mono_decorator_test.go index 20d3e1358..9cb85beb6 100644 --- a/ante/evm/mono_decorator_test.go +++ b/ante/evm/mono_decorator_test.go @@ -121,7 +121,8 @@ func signMsgEthereumTx(t *testing.T, privKey *ethsecp256k1.PrivKey, args *evmsdk msg := evmsdktypes.NewTx(args) fromAddr := common.BytesToAddress(privKey.PubKey().Address().Bytes()) msg.From = fromAddr.Bytes() - ethSigner := ethtypes.LatestSignerForChainID(evmsdktypes.GetEthChainConfig().ChainID) + chainID := evmsdktypes.DefaultEVMChainID + ethSigner := ethtypes.LatestSignerForChainID(big.NewInt(int64(chainID))) require.NoError(t, msg.Sign(ethSigner, utiltx.NewSigner(privKey))) return msg } @@ -196,23 +197,6 @@ func TestMonoDecorator(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - configurator := evmsdktypes.NewEVMConfigurator() - configurator.ResetTestConfig() - chainConfig := evmsdktypes.DefaultChainConfig(evmsdktypes.DefaultEVMChainID) - err := evmsdktypes.SetChainConfig(chainConfig) - require.NoError(t, err) - coinInfo := evmsdktypes.EvmCoinInfo{ - Denom: evmsdktypes.DefaultEVMExtendedDenom, - ExtendedDenom: evmsdktypes.DefaultEVMExtendedDenom, - DisplayDenom: evmsdktypes.DefaultEVMDisplayDenom, - Decimals: 18, - } - err = configurator. - WithExtendedEips(evmsdktypes.DefaultCosmosEVMActivators). - // NOTE: we're using the 18 decimals default for the example chain - WithEVMCoinInfo(coinInfo). - Configure() - require.NoError(t, err) privKey, _ := ethsecp256k1.GenerateKey() keeper, cosmosAddr := setupFundedKeeper(t, privKey) accountKeeper := MockAccountKeeper{FundedAddr: cosmosAddr} @@ -223,8 +207,9 @@ func TestMonoDecorator(t *testing.T) { ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger()) ctx = ctx.WithBlockGasMeter(storetypes.NewGasMeter(1e19)) + evmChainID := big.NewInt(int64(evmsdktypes.DefaultEVMChainID)) msgs := tc.buildMsgs(privKey) - tx, err := utiltx.PrepareEthTx(cfg.TxConfig, nil, toMsgSlice(msgs)...) + tx, err := utiltx.PrepareEthTx(cfg.TxConfig, nil, evmChainID, toMsgSlice(msgs)...) require.NoError(t, err) newCtx, err := monoDec.AnteHandle(ctx, tx, tc.simulate, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil }) diff --git a/ante/evm/utils.go b/ante/evm/utils.go index 48e45ebee..b02e20171 100644 --- a/ante/evm/utils.go +++ b/ante/evm/utils.go @@ -6,6 +6,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + gethparams "github.com/ethereum/go-ethereum/params" anteinterfaces "github.com/cosmos/evm/ante/interfaces" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" @@ -33,6 +34,11 @@ type DecoratorUtils struct { TxFee *big.Int } +type chainConfigProvider interface { + EthChainConfig() *gethparams.ChainConfig + ChainConfig() *evmtypes.ChainConfig +} + // NewMonoDecoratorUtils returns a new DecoratorUtils instance. // // These utilities are extracted once at the beginning of the ante handle process, @@ -47,8 +53,9 @@ func NewMonoDecoratorUtils( evmParams *evmtypes.Params, feemarketParams *feemarkettypes.Params, ) (*DecoratorUtils, error) { - ethCfg := evmtypes.GetEthChainConfig() - evmDenom := evmtypes.GetEVMCoinDenom() + ethCfg := ek.EthChainConfig() + evmDenom := ek.EvmCoinInfo().Denom + blockHeight := big.NewInt(ctx.BlockHeight()) rules := ethCfg.Rules(blockHeight, true, uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here baseFee := evmtypes.GetBaseFee(ctx.BlockHeight(), ethCfg, feemarketParams) diff --git a/ante/interfaces/evm.go b/ante/interfaces/evm.go index d296e7b7d..0e1329409 100644 --- a/ante/interfaces/evm.go +++ b/ante/interfaces/evm.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/vm" + gethparams "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" @@ -26,6 +27,9 @@ type EVMKeeper interface { ResetTransientGasUsed(ctx sdk.Context) GetTxIndexTransient(ctx sdk.Context) uint64 GetParams(ctx sdk.Context) evmtypes.Params + ChainConfig() *evmtypes.ChainConfig + EthChainConfig() *gethparams.ChainConfig + EvmCoinInfo() evmtypes.EvmCoinInfo } // FeeMarketKeeper exposes the required feemarket keeper interface required for ante handlers diff --git a/ethereum/eip712/preprocess_test.go b/ethereum/eip712/preprocess_test.go index 6cb298ec5..c7f1233a7 100644 --- a/ethereum/eip712/preprocess_test.go +++ b/ethereum/eip712/preprocess_test.go @@ -55,10 +55,6 @@ type TestCaseStruct struct { func TestLedgerPreprocessing(t *testing.T) { // Update bech32 prefix sdk.GetConfig().SetBech32PrefixForAccount(constants.ExampleBech32Prefix, "") - evmConfigurator := evmtypes.NewEVMConfigurator(). - WithEVMCoinInfo(constants.ExampleChainCoinInfo[constants.ExampleChainID]) - err := evmConfigurator.Configure() - require.NoError(t, err) testCases := []TestCaseStruct{ createBasicTestCase(t), diff --git a/evmd/app.go b/evmd/app.go index 732c25e52..4417a09ca 100644 --- a/evmd/app.go +++ b/evmd/app.go @@ -7,7 +7,6 @@ import ( "io" "os" - "github.com/spf13/cast" // Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes @@ -470,6 +469,8 @@ func NewExampleApp( ), ) + app.PreciseBankKeeper.SetCoinInfo(app.EVMKeeper.EvmCoinInfo()) + app.Erc20Keeper = erc20keeper.NewKeeper( keys[erc20types.StoreKey], appCodec, @@ -803,6 +804,10 @@ func (app *EVMD) RegisterPendingTxListener(listener func(common.Hash)) { app.pendingTxListeners = append(app.pendingTxListeners, listener) } +func (app *EVMD) RuntimeConfig() *evmtypes.RuntimeConfig { + return app.EVMKeeper.RuntimeConfig() +} + func (app *EVMD) setPostHandler() { postHandler, err := posthandler.NewPostHandler( posthandler.HandlerOptions{}, diff --git a/evmd/mempool.go b/evmd/mempool.go index fbdecb082..7e1eefc80 100644 --- a/evmd/mempool.go +++ b/evmd/mempool.go @@ -2,6 +2,7 @@ package evmd import ( "fmt" + "github.com/cosmos/evm/server" "cosmossdk.io/log" @@ -11,12 +12,11 @@ import ( sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" evmmempool "github.com/cosmos/evm/mempool" - evmtypes "github.com/cosmos/evm/x/vm/types" ) // configureEVMMempool sets up the EVM mempool and related handlers using viper configuration. func (app *EVMD) configureEVMMempool(appOpts servertypes.AppOptions, logger log.Logger) error { - if evmtypes.GetChainConfig() == nil { + if app.EVMKeeper.ChainConfig() == nil { logger.Debug("evm chain config is not set, skipping mempool configuration") return nil } diff --git a/evmd/tests/integration/x_vm_test.go b/evmd/tests/integration/x_vm_test.go index e0ce08f09..129645e2f 100644 --- a/evmd/tests/integration/x_vm_test.go +++ b/evmd/tests/integration/x_vm_test.go @@ -32,28 +32,6 @@ func BenchmarkGasEstimation(b *testing.B) { // gh := grpc.NewIntegrationHandler(nw) // tf := factory.New(nw, gh) - chainConfig := types.DefaultChainConfig(nw.GetEIP155ChainID().Uint64()) - // get the denom and decimals set on chain initialization - // because we'll need to set them again when resetting the chain config - denom := types.GetEVMCoinDenom() - extendedDenom := types.GetEVMCoinExtendedDenom() - displayDenom := types.GetEVMCoinDisplayDenom() - decimals := types.GetEVMCoinDecimals() - - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() - err := types.SetChainConfig(chainConfig) - require.NoError(b, err) - err = configurator. - WithEVMCoinInfo(types.EvmCoinInfo{ - Denom: denom, - ExtendedDenom: extendedDenom, - DisplayDenom: displayDenom, - Decimals: decimals.Uint32(), - }). - Configure() - require.NoError(b, err) - // Use simple transaction args for consistent benchmarking args := types.TransactionArgs{ To: &common.Address{}, diff --git a/evmd/testutil/abci.go b/evmd/testutil/abci.go index 5c364af07..b900b2815 100644 --- a/evmd/testutil/abci.go +++ b/evmd/testutil/abci.go @@ -94,8 +94,9 @@ func DeliverEthTx( msgs ...sdk.Msg, ) (abci.ExecTxResult, error) { txConfig := exampleApp.GetTxConfig() + evmChainID := exampleApp.GetEVMKeeper().EvmChainID() - tx, err := tx.PrepareEthTx(txConfig, priv, msgs...) + tx, err := tx.PrepareEthTx(txConfig, priv, evmChainID, msgs...) if err != nil { return abci.ExecTxResult{}, err } @@ -121,8 +122,9 @@ func DeliverEthTxWithoutCheck( msgs ...sdk.Msg, ) (abci.ExecTxResult, error) { txConfig := exampleApp.GetTxConfig() + evmChainID := exampleApp.GetEVMKeeper().EvmChainID() - tx, err := tx.PrepareEthTx(txConfig, priv, msgs...) + tx, err := tx.PrepareEthTx(txConfig, priv, evmChainID, msgs...) if err != nil { return abci.ExecTxResult{}, err } @@ -170,8 +172,9 @@ func CheckEthTx( msgs ...sdk.Msg, ) (abci.ResponseCheckTx, error) { txConfig := exampleApp.GetTxConfig() + evmChainID := exampleApp.GetEVMKeeper().EvmChainID() - tx, err := tx.PrepareEthTx(txConfig, priv, msgs...) + tx, err := tx.PrepareEthTx(txConfig, priv, evmChainID, msgs...) if err != nil { return abci.ResponseCheckTx{}, err } diff --git a/mempool/blockchain.go b/mempool/blockchain.go index 5b907028c..853ee2726 100644 --- a/mempool/blockchain.go +++ b/mempool/blockchain.go @@ -75,7 +75,13 @@ func NewBlockchain(ctx func(height int64, prove bool) (sdk.Context, error), logg // Config returns the Ethereum chain configuration. It should only be called after the chain is initialized. // This provides the necessary parameters for EVM execution and transaction validation. func (b *Blockchain) Config() *params.ChainConfig { - return evmtypes.GetEthChainConfig() + if cfg := b.vmKeeper.EthChainConfig(); cfg != nil { + return cfg + } + if chainCfg := b.vmKeeper.ChainConfig(); chainCfg != nil { + return chainCfg.EthereumConfig(nil) + } + return evmtypes.DefaultChainConfig(0).EthereumConfig(nil) } // CurrentBlock returns the current block header for the app. @@ -109,7 +115,7 @@ func (b *Blockchain) CurrentBlock() *types.Header { Difficulty: big.NewInt(0), // 0 difficulty on PoS } - chainConfig := evmtypes.GetEthChainConfig() + chainConfig := b.Config() if chainConfig.IsLondon(header.Number) { baseFee := b.vmKeeper.GetBaseFee(ctx) if baseFee != nil { diff --git a/mempool/blockchain_test.go b/mempool/blockchain_test.go index 32f04999b..116e4bee7 100644 --- a/mempool/blockchain_test.go +++ b/mempool/blockchain_test.go @@ -48,14 +48,13 @@ func TestBlockchainRaceCondition(t *testing.T) { mockFeeMarketKeeper := mocks.NewFeeMarketKeeper(t) ethCfg := vmtypes.DefaultChainConfig(constants.EighteenDecimalsChainID) - if err := vmtypes.SetChainConfig(ethCfg); err != nil { - panic(err) - } // Set up mock expectations for methods that will be called mockVMKeeper.On("GetBaseFee", mock.Anything).Return(big.NewInt(1000000000)).Maybe() // 1 gwei mockFeeMarketKeeper.On("GetBlockGasWanted", mock.Anything).Return(uint64(10000000)).Maybe() // 10M gas mockVMKeeper.On("GetParams", mock.Anything).Return(vmtypes.DefaultParams()).Maybe() + mockVMKeeper.On("ChainConfig").Return(ethCfg).Maybe() + mockVMKeeper.On("EthChainConfig").Return(ethCfg.EthereumConfig(nil)).Maybe() mockVMKeeper.On("GetAccount", mock.Anything, common.Address{}).Return(&statedb.Account{}).Maybe() mockVMKeeper.On("GetState", mock.Anything, common.Address{}, common.Hash{}).Return(common.Hash{}).Maybe() mockVMKeeper.On("GetCode", mock.Anything, common.Hash{}).Return([]byte{}).Maybe() @@ -63,9 +62,6 @@ func TestBlockchainRaceCondition(t *testing.T) { mockVMKeeper.On("ForEachStorage", mock.Anything, common.Address{}, mock.AnythingOfType("func(common.Hash, common.Hash) bool")).Maybe() mockVMKeeper.On("KVStoreKeys").Return(make(map[string]*storetypes.KVStoreKey)).Maybe() - err := vmtypes.NewEVMConfigurator().WithEVMCoinInfo(constants.ChainsCoinInfo[constants.EighteenDecimalsChainID]).Configure() - require.NoError(t, err) - // Mock context callback that returns a valid context getCtxCallback := func(height int64, prove bool) (sdk.Context, error) { return createMockContext(), nil diff --git a/mempool/interface.go b/mempool/interface.go index b0c56673b..3e3f92b4e 100644 --- a/mempool/interface.go +++ b/mempool/interface.go @@ -4,6 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + gethparams "github.com/ethereum/go-ethereum/params" "github.com/cosmos/evm/x/vm/statedb" vmtypes "github.com/cosmos/evm/x/vm/types" @@ -17,6 +18,8 @@ type VMKeeperI interface { GetBaseFee(ctx sdk.Context) *big.Int GetParams(ctx sdk.Context) (params vmtypes.Params) GetEvmCoinInfo(ctx sdk.Context) (coinInfo vmtypes.EvmCoinInfo) + ChainConfig() *vmtypes.ChainConfig + EthChainConfig() *gethparams.ChainConfig GetAccount(ctx sdk.Context, addr common.Address) *statedb.Account GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash GetCode(ctx sdk.Context, codeHash common.Hash) []byte diff --git a/mempool/mocks/VMKeeper.go b/mempool/mocks/VMKeeper.go index 403807154..03f1a9079 100644 --- a/mempool/mocks/VMKeeper.go +++ b/mempool/mocks/VMKeeper.go @@ -7,6 +7,7 @@ import ( mempool "github.com/cosmos/evm/mempool" common "github.com/ethereum/go-ethereum/common" + gethparams "github.com/ethereum/go-ethereum/params" mock "github.com/stretchr/testify/mock" @@ -173,6 +174,46 @@ func (_m *VMKeeper) GetParams(ctx types.Context) vmtypes.Params { return r0 } +// ChainConfig provides a mock function with no fields +func (_m *VMKeeper) ChainConfig() *vmtypes.ChainConfig { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ChainConfig") + } + + var r0 *vmtypes.ChainConfig + if rf, ok := ret.Get(0).(func() *vmtypes.ChainConfig); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*vmtypes.ChainConfig) + } + } + + return r0 +} + +// EthChainConfig provides a mock function with no fields +func (_m *VMKeeper) EthChainConfig() *gethparams.ChainConfig { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for EthChainConfig") + } + + var r0 *gethparams.ChainConfig + if rf, ok := ret.Get(0).(func() *gethparams.ChainConfig); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*gethparams.ChainConfig) + } + } + + return r0 +} + // GetState provides a mock function with given fields: ctx, addr, key func (_m *VMKeeper) GetState(ctx types.Context, addr common.Address, key common.Hash) common.Hash { ret := _m.Called(ctx, addr, key) diff --git a/precompiles/common/balance_handler_test.go b/precompiles/common/balance_handler_test.go index 0e6c79930..41edd685d 100644 --- a/precompiles/common/balance_handler_test.go +++ b/precompiles/common/balance_handler_test.go @@ -28,11 +28,7 @@ import ( func setupBalanceHandlerTest(t *testing.T) { t.Helper() - sdk.GetConfig().SetBech32PrefixForAccount(testconstants.ExampleBech32Prefix, "") - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - require.NoError(t, configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]).Configure()) } func TestParseAddress(t *testing.T) { diff --git a/precompiles/testutil/contracts/contracts.go b/precompiles/testutil/contracts/contracts.go index 09f4d3ebc..8495835fe 100644 --- a/precompiles/testutil/contracts/contracts.go +++ b/precompiles/testutil/contracts/contracts.go @@ -71,7 +71,7 @@ func Call(ctx sdk.Context, app evm.EvmApp, args CallArgs) (res abci.ExecTxResult // create MsgEthereumTx that calls the contract msg := evmtypes.NewTx(&evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: app.GetEVMKeeper().EvmChainID(), Nonce: nonce, To: &args.ContractAddr, Amount: args.Amount, diff --git a/rpc/apis.go b/rpc/apis.go index 2ea7100ef..0b89ab866 100644 --- a/rpc/apis.go +++ b/rpc/apis.go @@ -3,6 +3,7 @@ package rpc import ( "fmt" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" evmmempool "github.com/cosmos/evm/mempool" @@ -17,6 +18,7 @@ import ( "github.com/cosmos/evm/rpc/namespaces/ethereum/web3" "github.com/cosmos/evm/rpc/stream" servertypes "github.com/cosmos/evm/server/types" + evmtypes "github.com/cosmos/evm/x/vm/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -49,6 +51,8 @@ type APICreator = func( allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API // apiCreators defines the JSON-RPC API namespaces. @@ -62,8 +66,10 @@ func init() { allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { - evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool) + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo) return []rpc.API{ { Namespace: EthNamespace, @@ -79,7 +85,7 @@ func init() { }, } }, - Web3Namespace: func(*server.Context, client.Context, *stream.RPCStream, bool, servertypes.EVMTxIndexer, *evmmempool.ExperimentalEVMMempool) []rpc.API { + Web3Namespace: func(*server.Context, client.Context, *stream.RPCStream, bool, servertypes.EVMTxIndexer, *evmmempool.ExperimentalEVMMempool, *params.ChainConfig, evmtypes.EvmCoinInfo) []rpc.API { return []rpc.API{ { Namespace: Web3Namespace, @@ -89,7 +95,7 @@ func init() { }, } }, - NetNamespace: func(ctx *server.Context, clientCtx client.Context, _ *stream.RPCStream, _ bool, _ servertypes.EVMTxIndexer, _ *evmmempool.ExperimentalEVMMempool) []rpc.API { + NetNamespace: func(ctx *server.Context, clientCtx client.Context, _ *stream.RPCStream, _ bool, _ servertypes.EVMTxIndexer, _ *evmmempool.ExperimentalEVMMempool, _ *params.ChainConfig, _ evmtypes.EvmCoinInfo) []rpc.API { return []rpc.API{ { Namespace: NetNamespace, @@ -105,8 +111,10 @@ func init() { allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { - evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool) + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo) return []rpc.API{ { Namespace: PersonalNamespace, @@ -122,8 +130,10 @@ func init() { allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { - evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool) + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo) return []rpc.API{ { Namespace: TxPoolNamespace, @@ -139,8 +149,10 @@ func init() { allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { - evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool) + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo) return []rpc.API{ { Namespace: DebugNamespace, @@ -156,8 +168,10 @@ func init() { allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { - evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool) + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo) return []rpc.API{ { Namespace: MinerNamespace, @@ -178,12 +192,14 @@ func GetRPCAPIs(ctx *server.Context, indexer servertypes.EVMTxIndexer, selectedAPIs []string, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) []rpc.API { var apis []rpc.API for _, ns := range selectedAPIs { if creator, ok := apiCreators[ns]; ok { - apis = append(apis, creator(ctx, clientCtx, stream, allowUnprotectedTxs, indexer, mempool)...) + apis = append(apis, creator(ctx, clientCtx, stream, allowUnprotectedTxs, indexer, mempool, ethChainConfig, evmCoinInfo)...) } else { ctx.Logger.Error("invalid namespace value", "namespace", ns) } diff --git a/rpc/backend/backend.go b/rpc/backend/backend.go index 06079229d..796fcfa75 100644 --- a/rpc/backend/backend.go +++ b/rpc/backend/backend.go @@ -173,12 +173,20 @@ type Backend struct { Indexer servertypes.EVMTxIndexer ProcessBlocker ProcessBlocker Mempool *evmmempool.ExperimentalEVMMempool + // ethChainCfg carries the runtime (keeper-derived) Ethereum chain configuration and is + // intentionally kept separate from the static app.toml-backed config in Cfg. + ethChainCfg *params.ChainConfig + coinInfo evmtypes.EvmCoinInfo } func (b *Backend) GetConfig() config.Config { return b.Cfg } +func (b *Backend) evmDenom() string { + return b.coinInfo.Denom +} + // NewBackend creates a new Backend instance for cosmos and ethereum namespaces func NewBackend( ctx *server.Context, @@ -187,6 +195,8 @@ func NewBackend( allowUnprotectedTxs bool, indexer servertypes.EVMTxIndexer, mempool *evmmempool.ExperimentalEVMMempool, + ethChainConfig *params.ChainConfig, + evmCoinInfo evmtypes.EvmCoinInfo, ) *Backend { appConf, err := config.GetConfig(ctx.Viper) if err != nil { @@ -209,7 +219,10 @@ func NewBackend( AllowUnprotectedTxs: allowUnprotectedTxs, Indexer: indexer, Mempool: mempool, + ethChainCfg: ethChainConfig, + coinInfo: evmCoinInfo, } + b.ProcessBlocker = b.ProcessBlock return b } diff --git a/rpc/backend/call_tx.go b/rpc/backend/call_tx.go index 61dfc0b34..33cd01e65 100644 --- a/rpc/backend/call_tx.go +++ b/rpc/backend/call_tx.go @@ -131,7 +131,7 @@ func (b *Backend) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { return common.Hash{}, fmt.Errorf("failed to validate transaction: %w", err) } - baseDenom := evmtypes.GetEVMCoinDenom() + baseDenom := b.evmDenom() cosmosTx, err := ethereumTx.BuildTx(b.ClientCtx.TxConfig.NewTxBuilder(), baseDenom) if err != nil { diff --git a/rpc/backend/chain_info.go b/rpc/backend/chain_info.go index 3686a3250..c1e8c6525 100644 --- a/rpc/backend/chain_info.go +++ b/rpc/backend/chain_info.go @@ -44,7 +44,11 @@ func (b *Backend) ChainID() (*hexutil.Big, error) { // ChainConfig returns the latest ethereum chain configuration func (b *Backend) ChainConfig() *params.ChainConfig { - return evmtypes.GetEthChainConfig() + return b.ethChainCfg +} + +func (b *Backend) EvmCoinDenom() string { + return b.coinInfo.Denom } // GlobalMinGasPrice returns MinGasPrice param from FeeMarket diff --git a/rpc/backend/comet_to_eth.go b/rpc/backend/comet_to_eth.go index 8b4847726..243e12c74 100644 --- a/rpc/backend/comet_to_eth.go +++ b/rpc/backend/comet_to_eth.go @@ -151,7 +151,7 @@ func (b *Backend) EthBlockFromCometBlock( } // 4. create blockHeader without transactions, receipts, withdrawals, ... - ethHeader := rpctypes.MakeHeader(cmtBlock.Header, gasLimit, miner, baseFee) + ethHeader := rpctypes.MakeHeader(cmtBlock.Header, gasLimit, miner, baseFee, b.ChainConfig()) // 5. get MsgEthereumTxs msgs := b.EthMsgsFromCometBlock(resBlock, blockRes) diff --git a/rpc/backend/node_info.go b/rpc/backend/node_info.go index dcdf28a68..0d9896c8f 100644 --- a/rpc/backend/node_info.go +++ b/rpc/backend/node_info.go @@ -283,7 +283,7 @@ func (b *Backend) GenerateMinGasCoin(gasPrice hexutil.Big, appConf config.Config // fetch the base denom from the sdk Config in case it's not currently defined on the node config if len(minGasPrices) == 0 || minGasPrices.Empty() { - unit = evmtypes.GetEVMCoinDenom() + unit = b.evmDenom() } else { unit = minGasPrices[0].Denom } @@ -340,7 +340,7 @@ func (b *Backend) RPCBlockRangeCap() int32 { // RPCMinGasPrice returns the minimum gas price for a transaction obtained from // the node config. If set value is 0, it will default to 20. func (b *Backend) RPCMinGasPrice() *big.Int { - baseDenom := evmtypes.GetEVMCoinDenom() + baseDenom := b.evmDenom() minGasPrice := b.Cfg.GetMinGasPrices() amt := minGasPrice.AmountOf(baseDenom) diff --git a/rpc/backend/sign_tx.go b/rpc/backend/sign_tx.go index 859c2c3a8..e8b831149 100644 --- a/rpc/backend/sign_tx.go +++ b/rpc/backend/sign_tx.go @@ -74,7 +74,7 @@ func (b *Backend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, e return common.Hash{}, err } - baseDenom := evmtypes.GetEVMCoinDenom() + baseDenom := b.evmDenom() // Assemble transaction from fields tx, err := msg.BuildTx(b.ClientCtx.TxConfig.NewTxBuilder(), baseDenom) diff --git a/rpc/backend/tx_info_test.go b/rpc/backend/tx_info_test.go index 01bc77791..8a24b6104 100644 --- a/rpc/backend/tx_info_test.go +++ b/rpc/backend/tx_info_test.go @@ -65,10 +65,16 @@ func setupMockBackend(t *testing.T) *Backend { WithClient(mocks.NewClient(t)). WithCodec(encodingConfig.Codec) + chainCfg := evmtypes.DefaultChainConfig(constants.ExampleChainID.EVMChainID) + coinInfo := constants.ChainsCoinInfo[constants.ExampleChainID.EVMChainID] + allowUnprotectedTxs := false idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, clientCtx) - backend := NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer, nil) + evmChainID := constants.ExampleChainID.EVMChainID + ethChainConfig := *chainCfg.EthereumConfig(big.NewInt(int64(evmChainID))) //nolint:gosec // Won't exceed int64 + + backend := NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer, nil, ðChainConfig, coinInfo) backend.Cfg.JSONRPC.GasCap = 25000000 backend.Cfg.JSONRPC.EVMTimeout = 0 backend.Cfg.JSONRPC.AllowInsecureUnlock = true diff --git a/rpc/types/utils.go b/rpc/types/utils.go index d2554f7be..f189ac603 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -127,7 +127,12 @@ func BlockMaxGasFromConsensusParams(goCtx context.Context, clientCtx client.Cont func MakeHeader( cmtHeader cmttypes.Header, gasLimit int64, validatorAddr common.Address, baseFee *big.Int, + ethCfg *ethparams.ChainConfig, ) *ethtypes.Header { + if ethCfg == nil { + ethCfg = evmtypes.DefaultChainConfig(evmtypes.DefaultEVMChainID).EthereumConfig(nil) + } + header := ðtypes.Header{ Root: common.BytesToHash(hexutil.Bytes(cmtHeader.AppHash)), ParentHash: common.BytesToHash(cmtHeader.LastBlockID.Hash.Bytes()), @@ -138,15 +143,15 @@ func MakeHeader( Time: uint64(cmtHeader.Time.UTC().Unix()), //nolint:gosec // G115 // timestamp won't exceed uint64 } - if evmtypes.GetEthChainConfig().IsLondon(header.Number) { + if ethCfg.IsLondon(header.Number) { header.BaseFee = baseFee } - if evmtypes.GetEthChainConfig().IsCancun(header.Number, header.Time) { + if ethCfg.IsCancun(header.Number, header.Time) { header.ExcessBlobGas = new(uint64) header.BlobGasUsed = new(uint64) header.ParentBeaconRoot = new(common.Hash) } - if evmtypes.GetEthChainConfig().IsPrague(header.Number, header.Time) { + if ethCfg.IsPrague(header.Number, header.Time) { header.RequestsHash = ðtypes.EmptyRequestsHash } return header diff --git a/server/json_rpc.go b/server/json_rpc.go index c490d342d..5de5f5c4b 100644 --- a/server/json_rpc.go +++ b/server/json_rpc.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/evm/rpc/stream" serverconfig "github.com/cosmos/evm/server/config" "github.com/cosmos/evm/server/types" + evmtypes "github.com/cosmos/evm/x/vm/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" @@ -29,6 +30,7 @@ const shutdownTimeout = 200 * time.Millisecond type AppWithPendingTxStream interface { RegisterPendingTxListener(listener func(common.Hash)) + RuntimeConfig() *evmtypes.RuntimeConfig } // StartJSONRPC starts the JSON-RPC server @@ -61,8 +63,10 @@ func StartJSONRPC( rpcServer.SetBatchLimits(config.JSONRPC.BatchRequestLimit, config.JSONRPC.BatchResponseMaxSize) allowUnprotectedTxs := config.JSONRPC.AllowUnprotectedTxs rpcAPIArr := config.JSONRPC.API + ethChainConfig := app.RuntimeConfig().EthChainConfig() + evmCoinInfo := app.RuntimeConfig().EvmCoinInfo() - apis := rpc.GetRPCAPIs(srvCtx, clientCtx, stream, allowUnprotectedTxs, indexer, rpcAPIArr, mempool) + apis := rpc.GetRPCAPIs(srvCtx, clientCtx, stream, allowUnprotectedTxs, indexer, rpcAPIArr, mempool, ethChainConfig, evmCoinInfo) for _, api := range apis { if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { diff --git a/tests/integration/ante/ante_test_suite.go b/tests/integration/ante/ante_test_suite.go index 320d4a3be..ebf9e8829 100644 --- a/tests/integration/ante/ante_test_suite.go +++ b/tests/integration/ante/ante_test_suite.go @@ -110,26 +110,6 @@ func (s *AnteTestSuite) SetupTest() { chainConfig.CancunTime = &maxInt chainConfig.PragueTime = &maxInt } - - // get the denom and decimals set when initialized the chain - // to set them again - // when resetting the chain config - denom := evmtypes.GetEVMCoinDenom() - extendedDenom := evmtypes.GetEVMCoinExtendedDenom() - displayDenom := evmtypes.GetEVMCoinDisplayDenom() - decimals := evmtypes.GetEVMCoinDecimals() - - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - err := configurator. - WithEVMCoinInfo(evmtypes.EvmCoinInfo{ - Denom: denom, - ExtendedDenom: extendedDenom, - DisplayDenom: displayDenom, - Decimals: decimals.Uint32(), - }). - Configure() - s.Require().NoError(err) } func (s *AnteTestSuite) WithFeemarketEnabled(enabled bool) { diff --git a/tests/integration/ante/test_bench_evm_ante.go b/tests/integration/ante/test_bench_evm_ante.go index fc7919d6d..636267caf 100644 --- a/tests/integration/ante/test_bench_evm_ante.go +++ b/tests/integration/ante/test_bench_evm_ante.go @@ -26,8 +26,9 @@ func RunBenchmarkEthGasConsumeDecorator(b *testing.B, create network.CreateEvmAp s.SetT(&testing.T{}) s.SetupTest() ctx := s.network.GetContext() + evmChainID := s.network.App.GetEVMKeeper().EvmChainID() args := &evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: evmChainID, Nonce: 1, Amount: big.NewInt(10), GasLimit: uint64(1_000_000), diff --git a/tests/integration/ante/test_evm_01_setup_ctx.go b/tests/integration/ante/test_evm_01_setup_ctx.go index 259e601f5..1c76a179d 100644 --- a/tests/integration/ante/test_evm_01_setup_ctx.go +++ b/tests/integration/ante/test_evm_01_setup_ctx.go @@ -15,8 +15,9 @@ import ( func (s *EvmAnteTestSuite) TestEthSetupContextDecorator() { dec := evmante.NewEthSetUpContextDecorator(s.GetNetwork().App.GetEVMKeeper()) + evmChainID := s.GetNetwork().App.GetEVMKeeper().EvmChainID() ethContractCreationTxParams := &evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: evmChainID, Nonce: 1, Amount: big.NewInt(10), GasLimit: 1000, diff --git a/tests/integration/ante/test_evm_ante.go b/tests/integration/ante/test_evm_ante.go index be0fec752..2762cd0fa 100644 --- a/tests/integration/ante/test_evm_ante.go +++ b/tests/integration/ante/test_evm_ante.go @@ -48,7 +48,7 @@ func (s *EvmAnteTestSuite) TestAnteHandler() { ctx = s.GetNetwork().GetContext() } - ethCfg := evmtypes.GetEthChainConfig() + ethCfg := s.network.App.GetEVMKeeper().EthChainConfig() ethContractCreationTxParams := evmtypes.EvmTxArgs{ ChainID: ethCfg.ChainID, Nonce: 0, @@ -947,8 +947,7 @@ func (s *EvmAnteTestSuite) TestAnteHandlerWithDynamicTxFee() { addr, privKey := utiltx.NewAddrKey() to := utiltx.GenerateAddress() - evmChainID := evmtypes.GetEthChainConfig().ChainID - + evmChainID := s.network.App.GetEVMKeeper().EvmChainID() ethContractCreationTxParams := evmtypes.EvmTxArgs{ ChainID: evmChainID, Nonce: 0, @@ -1112,8 +1111,7 @@ func (s *EvmAnteTestSuite) TestAnteHandlerWithParams() { addr, privKey := utiltx.NewAddrKey() to := utiltx.GenerateAddress() - ethCfg := evmtypes.GetEthChainConfig() - + ethCfg := s.network.App.GetEVMKeeper().EthChainConfig() ethContractCreationTxParams := evmtypes.EvmTxArgs{ ChainID: ethCfg.ChainID, Nonce: 0, @@ -1237,7 +1235,7 @@ func (s *EvmAnteTestSuite) TestAnteHandlerWithParams() { func (s *EvmAnteTestSuite) TestEthSigVerificationDecorator() { addr, privKey := utiltx.NewAddrKey() - ethCfg := evmtypes.GetEthChainConfig() + ethCfg := s.network.App.GetEVMKeeper().EthChainConfig() ethSigner := types.LatestSignerForChainID(ethCfg.ChainID) ethContractCreationTxParams := &evmtypes.EvmTxArgs{ @@ -1309,9 +1307,10 @@ func (s *EvmAnteTestSuite) TestSignatures() { privKey := s.GetKeyring().GetPrivKey(0) to := utiltx.GenerateAddress() + chainID := s.network.App.GetEVMKeeper().EvmChainID() txArgs := evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: chainID, Nonce: 0, To: &to, Amount: big.NewInt(10), diff --git a/tests/integration/ante/test_evm_unit_04_validate.go b/tests/integration/ante/test_evm_unit_04_validate.go index 8e87308cd..6b72509be 100644 --- a/tests/integration/ante/test_evm_unit_04_validate.go +++ b/tests/integration/ante/test_evm_unit_04_validate.go @@ -242,12 +242,6 @@ func (s *EvmUnitAnteTestSuite) TestCheckTxFee() { } { for _, tc := range testCases { s.Run(fmt.Sprintf("%s, %s", chainID.ChainID, tc.name), func() { - // Call the configurator to set the EVM coin required for the - // function to be tested. - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - s.Require().NoError(configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[chainID]).Configure()) - // If decimals is not 18 decimals, we have to convert txFeeInfo to original // decimals representation. evmExtendedDenom := evmtypes.GetEVMCoinExtendedDenom() diff --git a/tests/integration/ante/test_min_gas_price.go b/tests/integration/ante/test_min_gas_price.go index 1255cd7af..5a83e53ef 100644 --- a/tests/integration/ante/test_min_gas_price.go +++ b/tests/integration/ante/test_min_gas_price.go @@ -192,7 +192,8 @@ func (s *AnteTestSuite) TestMinGasPriceDecorator() { s.Run(et.name+"_"+tc.name, func() { ctx := ctx.WithIsReCheckTx(et.isCheckTx) params := nw.App.GetFeeMarketKeeper().GetParams(ctx) - dec := cosmosante.NewMinGasPriceDecorator(¶ms) + evmKeeper := nw.App.GetEVMKeeper() + dec := cosmosante.NewMinGasPriceDecorator(¶ms, evmKeeper) _, err := dec.AnteHandle(ctx, tc.malleate(), et.simulate, testutil.NoOpNextFn) if (et.name == "deliverTx" && tc.expPass) || (et.name == "deliverTxSimulate" && et.simulate && tc.allowPassOnSimulate) { diff --git a/tests/integration/eip7702/test_integration.go b/tests/integration/eip7702/test_integration.go index f80886b78..53647a12d 100644 --- a/tests/integration/eip7702/test_integration.go +++ b/tests/integration/eip7702/test_integration.go @@ -15,7 +15,6 @@ import ( "github.com/cosmos/evm/testutil/integration/evm/network" "github.com/cosmos/evm/testutil/keyring" utiltx "github.com/cosmos/evm/testutil/tx" - evmtypes "github.com/cosmos/evm/x/vm/types" ) func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, options ...network.ConfigOption) { @@ -34,7 +33,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, s = NewIntegrationTestSuite(create, options...) s.SetupTest() - validChainID = evmtypes.GetChainConfig().GetChainId() + validChainID = s.network.App.GetEVMKeeper().ChainConfig().ChainId invalidChainID = 1234 user0 = s.keyring.GetKey(0) @@ -217,6 +216,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, Describe("test simple user operation using smart wallet set by eip7702 SetCode", func() { var ( + chainID *big.Int user0Balance *big.Int user1Balance *big.Int user2Balance *big.Int @@ -225,6 +225,8 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, BeforeEach(func() { s.SetupSmartWallet() + chainID = s.network.App.GetEVMKeeper().EvmChainID() + user0Balance = s.getERC20Balance(user0.Addr) user1Balance = s.getERC20Balance(user1.Addr) user2Balance = s.getERC20Balance(user2.Addr) @@ -266,7 +268,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, // Make UserOperation userOp := NewUserOperation(user0.Addr, acc.GetNonce(), swCalldata) - userOp, err = SignUserOperation(userOp, s.entryPointAddr, user0.Priv) + userOp, err = SignUserOperation(userOp, s.entryPointAddr, user0.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") return []UserOperation{*userOp} @@ -301,7 +303,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, // Make UserOperation userOp := NewUserOperation(user1.Addr, acc1.GetNonce(), swCalldata) - userOp, err = SignUserOperation(userOp, s.entryPointAddr, user1.Priv) + userOp, err = SignUserOperation(userOp, s.entryPointAddr, user1.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") return []UserOperation{*userOp} @@ -337,12 +339,12 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, // Make UserOperations userOp1 := NewUserOperation(user0.Addr, nonce, swCalldata) - userOp1, err = SignUserOperation(userOp1, s.entryPointAddr, user0.Priv) + userOp1, err = SignUserOperation(userOp1, s.entryPointAddr, user0.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") nonce++ userOp2 := NewUserOperation(user0.Addr, nonce, swCalldata) - userOp2, err = SignUserOperation(userOp2, s.entryPointAddr, user0.Priv) + userOp2, err = SignUserOperation(userOp2, s.entryPointAddr, user0.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") return []UserOperation{*userOp1, *userOp2} @@ -380,7 +382,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, Expect(err).To(BeNil(), "failed to get account") userOp1 := NewUserOperation(user1.Addr, acc1.GetNonce(), swCalldata) - userOp1, err = SignUserOperation(userOp1, s.entryPointAddr, user1.Priv) + userOp1, err = SignUserOperation(userOp1, s.entryPointAddr, user1.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") // user2 -> user0 @@ -388,7 +390,7 @@ func TestEIP7702IntegrationTestSuite(t *testing.T, create network.CreateEvmApp, Expect(err).To(BeNil(), "failed to get account") userOp2 := NewUserOperation(user2.Addr, acc2.GetNonce(), swCalldata) - userOp2, err = SignUserOperation(userOp2, s.entryPointAddr, user2.Priv) + userOp2, err = SignUserOperation(userOp2, s.entryPointAddr, user2.Priv, chainID) Expect(err).To(BeNil(), "failed to sign UserOperation") return []UserOperation{*userOp1, *userOp2} diff --git a/tests/integration/eip7702/test_setup.go b/tests/integration/eip7702/test_setup.go index d8da8e9ad..b1f60e8f8 100644 --- a/tests/integration/eip7702/test_setup.go +++ b/tests/integration/eip7702/test_setup.go @@ -80,7 +80,7 @@ func (s *IntegrationTestSuite) setupSmartWalletForKey(key testkeyring.Key) { if s.walletConfigured[key.Addr] { return } - chainID := evmtypes.GetChainConfig().GetChainId() + chainID := s.network.App.GetEVMKeeper().ChainConfig().ChainId acc, err := s.grpcHandler.GetEvmAccount(key.Addr) Expect(err).To(BeNil()) diff --git a/tests/integration/eip7702/test_utils.go b/tests/integration/eip7702/test_utils.go index ac34903bc..3289956a5 100644 --- a/tests/integration/eip7702/test_utils.go +++ b/tests/integration/eip7702/test_utils.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/cosmos/evm/crypto/ethsecp256k1" - evmtypes "github.com/cosmos/evm/x/vm/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) @@ -44,9 +43,7 @@ func NewUserOperation(sender common.Address, nonce uint64, calldata []byte) *Use } } -func SignUserOperation(userOp *UserOperation, entryPointAddr common.Address, privKey cryptotypes.PrivKey) (*UserOperation, error) { - chainID := new(big.Int).SetUint64(evmtypes.GetChainConfig().GetChainId()) - +func SignUserOperation(userOp *UserOperation, entryPointAddr common.Address, privKey cryptotypes.PrivKey, chainID *big.Int) (*UserOperation, error) { addressType, _ := abi.NewType("address", "", nil) uint256Type, _ := abi.NewType("uint256", "", nil) bytes32Type, _ := abi.NewType("bytes32", "", nil) diff --git a/tests/integration/mempool/test_setup.go b/tests/integration/mempool/test_setup.go index 5deefe1d7..07d0e6332 100644 --- a/tests/integration/mempool/test_setup.go +++ b/tests/integration/mempool/test_setup.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/evm/testutil/integration/evm/grpc" "github.com/cosmos/evm/testutil/integration/evm/network" "github.com/cosmos/evm/testutil/keyring" + evmtypes "github.com/cosmos/evm/x/vm/types" sdkmath "cosmossdk.io/math" @@ -122,6 +123,17 @@ func (s *IntegrationTestSuite) SetupTestWithChainID(chainID testconstants.ChainI s.network = nw s.factory = tf + + // Ensure runtime config is aligned with the network chain ID and coin info. + params := s.network.App.GetEVMKeeper().GetParams(s.network.GetContext()) + chainCfg := evmtypes.DefaultChainConfig(chainID.EVMChainID) + coinInfo := testconstants.ChainsCoinInfo[chainID.EVMChainID] + chainCfg.Denom = coinInfo.Denom + chainCfg.Decimals = uint64(coinInfo.Decimals) + runtimeCfg, err := evmtypes.NewRuntimeConfig(chainCfg, coinInfo, params.ExtraEIPs) + s.Require().NoError(err) + s.Require().NoError(s.network.App.GetEVMKeeper().SetRuntimeConfig(runtimeCfg)) + s.Require().Equal(chainID.EVMChainID, s.network.App.GetEVMKeeper().EthChainConfig().ChainID.Uint64()) } // FundAccount funds an account with a specific amount of a given denomination. diff --git a/tests/integration/precompiles/distribution/test_distribution.go b/tests/integration/precompiles/distribution/test_distribution.go index c749828b5..1f1a53536 100644 --- a/tests/integration/precompiles/distribution/test_distribution.go +++ b/tests/integration/precompiles/distribution/test_distribution.go @@ -247,7 +247,7 @@ func (s *PrecompileTestSuite) TestRun() { contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: s.network.App.GetEVMKeeper().EvmChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -472,7 +472,7 @@ func (s *PrecompileTestSuite) TestCMS() { // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ Input: input, - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: s.network.App.GetEVMKeeper().EvmChainID(), Nonce: 0, To: &contractAddr, Amount: nil, diff --git a/tests/integration/precompiles/gov/test_gov.go b/tests/integration/precompiles/gov/test_gov.go index b2a2aee96..645fdd750 100644 --- a/tests/integration/precompiles/gov/test_gov.go +++ b/tests/integration/precompiles/gov/test_gov.go @@ -88,7 +88,7 @@ func (s *PrecompileTestSuite) TestRun() { contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: s.network.App.GetEVMKeeper().EvmChainID(), Nonce: 0, To: &contractAddr, Amount: nil, diff --git a/tests/integration/precompiles/staking/test_staking.go b/tests/integration/precompiles/staking/test_staking.go index 9bc4c7155..23359965a 100644 --- a/tests/integration/precompiles/staking/test_staking.go +++ b/tests/integration/precompiles/staking/test_staking.go @@ -1,6 +1,7 @@ package staking import ( + "errors" "math/big" "time" @@ -381,7 +382,7 @@ func (s *PrecompileTestSuite) TestRun() { s.Require().NoError(err, "failed to pack input") return input }, - 21295, // use enough gas to avoid out of gas error + 1000000, // use enough gas to avoid out of gas error true, false, "write protection", @@ -391,7 +392,7 @@ func (s *PrecompileTestSuite) TestRun() { func(_ keyring.Key) []byte { return []byte("invalid") }, - 21295, // use enough gas to avoid out of gas error + 1000000, // use enough gas to avoid out of gas error false, false, "no method with id", @@ -416,7 +417,8 @@ func (s *PrecompileTestSuite) TestRun() { // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: s.network.App.GetEVMKeeper().EvmChainID(), + Input: contract.Input, Nonce: 0, To: &contractAddr, Amount: nil, @@ -462,9 +464,11 @@ func (s *PrecompileTestSuite) TestRun() { s.Require().NotNil(bz, "expected returned bytes to be nil") execRevertErr := evmtypes.NewExecErrorWithReason(bz) s.Require().ErrorContains(execRevertErr, tc.errContains) - consumed := ctx.GasMeter().GasConsumed() - // LessThanOrEqual because the gas is consumed before the error is returned - s.Require().LessOrEqual(tc.gas, consumed, "expected gas consumed to be equal to gas limit") + if errors.Is(execRevertErr, vm.ErrOutOfGas) { + consumed := ctx.GasMeter().GasConsumed() + // LessThanOrEqual because the gas is consumed before the error is returned + s.Require().LessOrEqual(tc.gas, consumed, "expected gas consumed to be equal to gas limit") + } } }) } @@ -754,7 +758,7 @@ func (s *PrecompileTestSuite) TestCMS() { // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ Input: input, - ChainID: evmtypes.GetEthChainConfig().ChainID, + ChainID: s.network.App.GetEVMKeeper().EvmChainID(), Nonce: 0, To: &contractAddr, Amount: nil, diff --git a/tests/integration/precompiles/staking/test_utils.go b/tests/integration/precompiles/staking/test_utils.go index d1504a16b..a4693737a 100644 --- a/tests/integration/precompiles/staking/test_utils.go +++ b/tests/integration/precompiles/staking/test_utils.go @@ -167,7 +167,7 @@ func (s *PrecompileTestSuite) delegateAccountToContract(privKey cryptotypes.Priv ecdsaPriv, err := ethPriv.ToECDSA() Expect(err).To(BeNil(), "error converting to ECDSA private key") - chainID := evmtypes.GetChainConfig().GetChainId() + chainID := s.network.GetEvmChainID() accResp, err := s.grpcHandler.GetEvmAccount(accountAddr) Expect(err).To(BeNil(), "error while getting the EVM account") diff --git a/tests/integration/precompiles/werc20/test_integration.go b/tests/integration/precompiles/werc20/test_integration.go index ea5e1a0e5..89ef5bc48 100644 --- a/tests/integration/precompiles/werc20/test_integration.go +++ b/tests/integration/precompiles/werc20/test_integration.go @@ -24,7 +24,6 @@ import ( testutiltypes "github.com/cosmos/evm/testutil/types" erc20types "github.com/cosmos/evm/x/erc20/types" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" - precisebanktypes "github.com/cosmos/evm/x/precisebank/types" evmtypes "github.com/cosmos/evm/x/vm/types" "cosmossdk.io/math" @@ -107,13 +106,6 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp feemarketGenesis.Params.NoBaseFee = true customGenesis[feemarkettypes.ModuleName] = feemarketGenesis - // Reset evm config here for the standard case - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - Expect(configurator. - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[chainId]). - Configure()).To(BeNil(), "expected no error setting the evm configurator") - opts := []network.ConfigOption{ network.WithChainID(chainId), network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), @@ -321,9 +313,10 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp When("the method is withdraw", func() { It("it should fail if user doesn't have enough funds", func() { newUserAcc, newUserPriv := utiltx.NewAccAddressAndKey() + conversionFactor := is.network.App.GetEVMKeeper().EvmConversionFactor() newUserBalance := sdk.Coins{sdk.Coin{ Denom: evmtypes.GetEVMCoinDenom(), - Amount: math.NewIntFromBigInt(withdrawAmount).Quo(precisebanktypes.ConversionFactor()).SubRaw(1), + Amount: math.NewIntFromBigInt(withdrawAmount).Quo(conversionFactor).SubRaw(1), }} err := is.network.App.GetBankKeeper().SendCoins(is.network.GetContext(), user.AccAddr, newUserAcc, newUserBalance) Expect(err).ToNot(HaveOccurred(), "expected no error sending tokens") diff --git a/tests/integration/rpc/backend/test_backend_suite.go b/tests/integration/rpc/backend/test_backend_suite.go index c056d6d6a..20d0e34a6 100644 --- a/tests/integration/rpc/backend/test_backend_suite.go +++ b/tests/integration/rpc/backend/test_backend_suite.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" abci "github.com/cometbft/cometbft/abci/types" @@ -95,7 +96,29 @@ func (s *TestSuite) SetupTest() { allowUnprotectedTxs := false idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, clientCtx) - s.backend = rpcbackend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer, nil) + chainCfg := evmtypes.DefaultChainConfig(ChainID.EVMChainID) + coinInfo, ok := constants.ChainsCoinInfo[ChainID.EVMChainID] + if !ok { + coinInfo = evmtypes.EvmCoinInfo{ + Denom: chainCfg.Denom, + ExtendedDenom: chainCfg.Denom, + DisplayDenom: chainCfg.Denom, + Decimals: uint32(chainCfg.Decimals), + } + } + if coinInfo.Denom != "" { + chainCfg.Denom = coinInfo.Denom + } + if coinInfo.Decimals != 0 { + chainCfg.Decimals = uint64(coinInfo.Decimals) + } + runtimeCfg, err := evmtypes.NewRuntimeConfig(chainCfg, coinInfo, evmtypes.DefaultExtraEIPs) + s.Require().NoError(err) + + ethChainConfig := runtimeCfg.EthChainConfig() + evmCoinInfo := runtimeCfg.EvmCoinInfo() + + s.backend = rpcbackend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer, nil, ethChainConfig, evmCoinInfo) s.backend.Cfg.JSONRPC.GasCap = 0 s.backend.Cfg.JSONRPC.EVMTimeout = 0 s.backend.Cfg.JSONRPC.AllowInsecureUnlock = true @@ -104,6 +127,11 @@ func (s *TestSuite) SetupTest() { s.backend.QueryClient.FeeMarket = mocks.NewFeeMarketQueryClient(s.T()) s.backend.Ctx = rpctypes.ContextWithHeight(1) + s.backend.QueryClient.QueryClient.(*mocks.EVMQueryClient). + On("Config", mock.Anything, mock.AnythingOfType("*types.QueryConfigRequest")). + Return(&evmtypes.QueryConfigResponse{Config: chainCfg}, nil). + Maybe() + // Add codec s.backend.ClientCtx.Codec = encodingConfig.Codec } @@ -199,7 +227,7 @@ func (s *TestSuite) buildEthBlock( miner := common.BytesToAddress(validator.Bytes()) // 3) Build ethereum header - ethHeader := rpctypes.MakeHeader(cmtHeader, gasLimit, miner, baseFee) + ethHeader := rpctypes.MakeHeader(cmtHeader, gasLimit, miner, baseFee, s.backend.ChainConfig()) // 4) Prepare msgs and txs txs := make([]*ethtypes.Transaction, len(msgs)) diff --git a/tests/integration/rpc/backend/test_node_info.go b/tests/integration/rpc/backend/test_node_info.go index 270a3c8a1..12ae26b1c 100644 --- a/tests/integration/rpc/backend/test_node_info.go +++ b/tests/integration/rpc/backend/test_node_info.go @@ -15,7 +15,6 @@ import ( "github.com/cosmos/evm/rpc/backend/mocks" "github.com/cosmos/evm/server/config" "github.com/cosmos/evm/testutil/constants" - evmtypes "github.com/cosmos/evm/x/vm/types" "cosmossdk.io/math" @@ -61,6 +60,7 @@ func (s *TestSuite) TestRPCMinGasPrice() { func (s *TestSuite) TestGenerateMinGasCoin() { defaultGasPrice := (*hexutil.Big)(big.NewInt(1)) + evmCoinDenom := s.backend.EvmCoinDenom() testCases := []struct { name string gasPrice hexutil.Big @@ -72,7 +72,7 @@ func (s *TestSuite) TestGenerateMinGasCoin() { *defaultGasPrice, sdk.DecCoins{}, sdk.DecCoin{ - Denom: evmtypes.GetEVMCoinDenom(), + Denom: evmCoinDenom, Amount: math.LegacyNewDecFromBigInt(defaultGasPrice.ToInt()), }, }, diff --git a/tests/integration/x/precisebank/test_burn_integration.go b/tests/integration/x/precisebank/test_burn_integration.go index 65b57f5da..355275a7c 100644 --- a/tests/integration/x/precisebank/test_burn_integration.go +++ b/tests/integration/x/precisebank/test_burn_integration.go @@ -6,8 +6,6 @@ import ( "math/rand" "testing" - "github.com/stretchr/testify/require" - testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/x/precisebank/types" evmtypes "github.com/cosmos/evm/x/vm/types" @@ -498,12 +496,6 @@ func (s *KeeperIntegrationTestSuite) TestBurnCoinsRandomValueMultiDecimals() { } func FuzzBurnCoins(f *testing.F) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - err := configurator.Configure() - require.NoError(f, err) - f.Add(int64(0)) f.Add(int64(100)) f.Add(types.ConversionFactor().Int64()) diff --git a/tests/integration/x/precisebank/test_mint_integration.go b/tests/integration/x/precisebank/test_mint_integration.go index 0458f1299..d3f4d4708 100644 --- a/tests/integration/x/precisebank/test_mint_integration.go +++ b/tests/integration/x/precisebank/test_mint_integration.go @@ -6,8 +6,6 @@ import ( "math/rand" "testing" - "github.com/stretchr/testify/require" - testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/x/precisebank/types" evmtypes "github.com/cosmos/evm/x/vm/types" @@ -415,12 +413,6 @@ func (s *KeeperIntegrationTestSuite) TestMintCoinsRandomValueMultiDecimals() { } func FuzzMintCoins(f *testing.F) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - err := configurator.Configure() - require.NoError(f, err) - f.Add(int64(0)) f.Add(int64(100)) f.Add(types.ConversionFactor().Int64()) diff --git a/tests/integration/x/precisebank/test_send_integration.go b/tests/integration/x/precisebank/test_send_integration.go index d819bf650..d2e310d32 100644 --- a/tests/integration/x/precisebank/test_send_integration.go +++ b/tests/integration/x/precisebank/test_send_integration.go @@ -9,7 +9,6 @@ import ( "testing" corevm "github.com/ethereum/go-ethereum/core/vm" - "github.com/stretchr/testify/require" testconstants "github.com/cosmos/evm/testutil/constants" cosmosevmutils "github.com/cosmos/evm/utils" @@ -796,12 +795,6 @@ func (s *KeeperIntegrationTestSuite) TestSendCoinsRandomValueMultiDecimals() { } func FuzzSendCoins(f *testing.F) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - err := configurator.Configure() - require.NoError(f, err) - f.Add(uint64(100), uint64(0), uint64(2)) f.Add(uint64(100), uint64(100), uint64(5)) f.Add(types.ConversionFactor().Uint64(), uint64(0), uint64(500)) diff --git a/tests/integration/x/vm/keeper_test_suite.go b/tests/integration/x/vm/keeper_test_suite.go index 5f4aa1c52..bd4c0b2f8 100644 --- a/tests/integration/x/vm/keeper_test_suite.go +++ b/tests/integration/x/vm/keeper_test_suite.go @@ -35,6 +35,13 @@ type KeeperTestSuite struct { MintFeeCollector bool } +func (s *KeeperTestSuite) setRuntimeConfig(chainConfig *evmtypes.ChainConfig, coinInfo evmtypes.EvmCoinInfo) { + params := s.Network.App.GetEVMKeeper().GetParams(s.Network.GetContext()) + runtimeCfg, err := evmtypes.NewRuntimeConfig(chainConfig, coinInfo, params.ExtraEIPs) + s.Require().NoError(err) + s.Require().NoError(s.Network.App.GetEVMKeeper().SetRuntimeConfig(runtimeCfg)) +} + func NewKeeperTestSuite(create network.CreateEvmApp, options ...network.ConfigOption) *KeeperTestSuite { return &KeeperTestSuite{ Create: create, @@ -106,17 +113,10 @@ func (s *KeeperTestSuite) SetupTest() { displayDenom := evmtypes.GetEVMCoinDisplayDenom() decimals := evmtypes.GetEVMCoinDecimals() - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - err := evmtypes.SetChainConfig(chainConfig) - s.Require().NoError(err) - err = configurator. - WithEVMCoinInfo(evmtypes.EvmCoinInfo{ - Denom: denom, - ExtendedDenom: extendedDenom, - DisplayDenom: displayDenom, - Decimals: decimals.Uint32(), - }). - Configure() - s.Require().NoError(err) + s.setRuntimeConfig(chainConfig, evmtypes.EvmCoinInfo{ + Denom: denom, + ExtendedDenom: extendedDenom, + DisplayDenom: displayDenom, + Decimals: decimals.Uint32(), + }) } diff --git a/tests/integration/x/vm/state_transition_benchmark.go b/tests/integration/x/vm/state_transition_benchmark.go index e3e0a2c93..bbb404cf8 100644 --- a/tests/integration/x/vm/state_transition_benchmark.go +++ b/tests/integration/x/vm/state_transition_benchmark.go @@ -179,7 +179,8 @@ func BenchmarkApplyTransaction(b *testing.B) { //nolint:dupl suite := KeeperTestSuite{EnableLondonHF: true} suite.SetupTest() - ethSigner := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + ethSigner := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() @@ -208,7 +209,8 @@ func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) { //nolint:dupl suite := KeeperTestSuite{EnableLondonHF: true} suite.SetupTest() - ethSigner := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + ethSigner := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() @@ -237,7 +239,8 @@ func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) { suite := KeeperTestSuite{EnableFeemarket: true, EnableLondonHF: true} suite.SetupTest() - ethSigner := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + ethSigner := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() @@ -266,8 +269,8 @@ func BenchmarkApplyMessage(b *testing.B) { suite := KeeperTestSuite{EnableLondonHF: true} suite.SetupTest() - ethCfg := evmtypes.GetEthChainConfig() - signer := ethtypes.LatestSignerForChainID(ethCfg.ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + signer := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() @@ -300,8 +303,8 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) { suite := KeeperTestSuite{EnableLondonHF: true} suite.SetupTest() - ethCfg := evmtypes.GetEthChainConfig() - signer := ethtypes.LatestSignerForChainID(ethCfg.ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + signer := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() @@ -334,8 +337,8 @@ func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) { suite := KeeperTestSuite{EnableFeemarket: true, EnableLondonHF: true} suite.SetupTest() - ethCfg := evmtypes.GetEthChainConfig() - signer := ethtypes.LatestSignerForChainID(ethCfg.ChainID) + evmChainID := suite.Network.App.GetEVMKeeper().EvmChainID() + signer := ethtypes.LatestSignerForChainID(evmChainID) b.ResetTimer() b.ReportAllocs() diff --git a/tests/integration/x/vm/test_benchmark.go b/tests/integration/x/vm/test_benchmark.go index 77847f070..8adf0a434 100644 --- a/tests/integration/x/vm/test_benchmark.go +++ b/tests/integration/x/vm/test_benchmark.go @@ -64,7 +64,8 @@ func DoBenchmark(b *testing.B, txBuilder TxBuilder) { krSigner := utiltx.NewSigner(suite.Keyring.GetPrivKey(0)) msg := txBuilder(suite, contractAddr) msg.From = suite.Keyring.GetAddr(0).Bytes() - err := msg.Sign(ethtypes.LatestSignerForChainID(types.GetEthChainConfig().ChainID), krSigner) + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() + err := msg.Sign(ethtypes.LatestSignerForChainID(chainID), krSigner) require.NoError(b, err) b.ResetTimer() @@ -90,8 +91,9 @@ func BenchmarkTokenTransfer(b *testing.B) { input, err := erc20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) require.NoError(b, err) nonce := suite.Network.App.GetEVMKeeper().GetNonce(suite.Network.GetContext(), suite.Keyring.GetAddr(0)) + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() ethTxParams := &types.EvmTxArgs{ - ChainID: types.GetEthChainConfig().ChainID, + ChainID: chainID, Nonce: nonce, To: &contract, Amount: big.NewInt(0), @@ -111,8 +113,9 @@ func BenchmarkEmitLogs(b *testing.B) { input, err := erc20Contract.ABI.Pack("benchmarkLogs", big.NewInt(1000)) require.NoError(b, err) nonce := suite.Network.App.GetEVMKeeper().GetNonce(suite.Network.GetContext(), suite.Keyring.GetAddr(0)) + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() ethTxParams := &types.EvmTxArgs{ - ChainID: types.GetEthChainConfig().ChainID, + ChainID: chainID, Nonce: nonce, To: &contract, Amount: big.NewInt(0), @@ -132,8 +135,9 @@ func BenchmarkTokenTransferFrom(b *testing.B) { input, err := erc20Contract.ABI.Pack("transferFrom", suite.Keyring.GetAddr(0), common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(0)) require.NoError(b, err) nonce := suite.Network.App.GetEVMKeeper().GetNonce(suite.Network.GetContext(), suite.Keyring.GetAddr(0)) + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() ethTxParams := &types.EvmTxArgs{ - ChainID: types.GetEthChainConfig().ChainID, + ChainID: chainID, Nonce: nonce, To: &contract, Amount: big.NewInt(0), @@ -153,8 +157,9 @@ func BenchmarkTokenMint(b *testing.B) { input, err := erc20Contract.ABI.Pack("mint", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) require.NoError(b, err) nonce := suite.Network.App.GetEVMKeeper().GetNonce(suite.Network.GetContext(), suite.Keyring.GetAddr(0)) + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() ethTxParams := &types.EvmTxArgs{ - ChainID: types.GetEthChainConfig().ChainID, + ChainID: chainID, Nonce: nonce, To: &contract, Amount: big.NewInt(0), @@ -175,9 +180,9 @@ func BenchmarkMessageCall(b *testing.B) { input, err := messageCallContract.ABI.Pack("benchmarkMessageCall", big.NewInt(10000)) require.NoError(b, err) nonce := suite.Network.App.GetEVMKeeper().GetNonce(suite.Network.GetContext(), suite.Keyring.GetAddr(0)) - ethCfg := types.GetEthChainConfig() + chainID := suite.Network.App.GetEVMKeeper().EvmChainID() ethTxParams := &types.EvmTxArgs{ - ChainID: ethCfg.ChainID, + ChainID: chainID, Nonce: nonce, To: &contract, Amount: big.NewInt(0), @@ -189,7 +194,7 @@ func BenchmarkMessageCall(b *testing.B) { msg.From = suite.Keyring.GetAddr(0).Bytes() krSigner := utiltx.NewSigner(suite.Keyring.GetPrivKey(0)) - err = msg.Sign(ethtypes.LatestSignerForChainID(ethCfg.ChainID), krSigner) + err = msg.Sign(ethtypes.LatestSignerForChainID(chainID), krSigner) require.NoError(b, err) b.ResetTimer() diff --git a/tests/integration/x/vm/test_genesis.go b/tests/integration/x/vm/test_genesis.go index 06b266f54..b9b04c3ef 100644 --- a/tests/integration/x/vm/test_genesis.go +++ b/tests/integration/x/vm/test_genesis.go @@ -139,9 +139,6 @@ func (s *GenesisTestSuite) TestInitGenesis() { err := vmdb.Commit() s.Require().NoError(err) - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() - if tc.expPanic { s.Require().Panics(func() { _ = vm.InitGenesis( diff --git a/tests/integration/x/vm/test_grpc_query.go b/tests/integration/x/vm/test_grpc_query.go index f58715242..7334ef1a1 100644 --- a/tests/integration/x/vm/test_grpc_query.go +++ b/tests/integration/x/vm/test_grpc_query.go @@ -2033,14 +2033,7 @@ func (s *KeeperTestSuite) TestQueryBaseFee() { chainConfig.CancunTime = &maxInt chainConfig.PragueTime = &maxInt - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() - err := types.SetChainConfig(chainConfig) - s.Require().NoError(err) - err = configurator. - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - Configure() - s.Require().NoError(err) + s.setRuntimeConfig(chainConfig, testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]) }, true, }, @@ -2090,14 +2083,9 @@ func (s *KeeperTestSuite) TestQueryBaseFee() { s.Require().Error(err) } s.Require().NoError(s.Network.NextBlock()) - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() - err = types.SetChainConfig(chainConfig) - s.Require().NoError(err) - err = configurator. - WithEVMCoinInfo(coinInfo). - Configure() - s.Require().NoError(err) + + // Restore runtime configs + s.setRuntimeConfig(chainConfig, coinInfo) }) } } diff --git a/tests/integration/x/vm/test_msg_server.go b/tests/integration/x/vm/test_msg_server.go index 8f776f9c3..bf191ea8a 100644 --- a/tests/integration/x/vm/test_msg_server.go +++ b/tests/integration/x/vm/test_msg_server.go @@ -59,7 +59,7 @@ func (s *KeeperTestSuite) TestEthereumTx() { s.Require().NoError(err) auth := ethtypes.SetCodeAuthorization{ - ChainID: *uint256.NewInt(types.GetChainConfig().GetChainId()), + ChainID: *uint256.NewInt(s.Network.App.GetEVMKeeper().ChainConfig().ChainId), Address: target, Nonce: accResp.GetNonce(), } @@ -94,7 +94,7 @@ func (s *KeeperTestSuite) TestEthereumTx() { s.Require().NoError(err) auth := ethtypes.SetCodeAuthorization{ - ChainID: *uint256.NewInt(types.GetChainConfig().GetChainId()), + ChainID: *uint256.NewInt(s.Network.GetEvmChainID()), Address: target, Nonce: accResp.GetNonce(), } diff --git a/tests/integration/x/vm/test_state_transition.go b/tests/integration/x/vm/test_state_transition.go index f44f5d5e6..8634c636a 100644 --- a/tests/integration/x/vm/test_state_transition.go +++ b/tests/integration/x/vm/test_state_transition.go @@ -278,7 +278,7 @@ func (s *KeeperTestSuite) TestGetEthIntrinsicGas() { for _, tc := range testCases { s.Run(fmt.Sprintf("Case %s", tc.name), func() { - ethCfg := types.GetEthChainConfig() + ethCfg := s.Network.App.GetEVMKeeper().EthChainConfig() ethCfg.HomesteadBlock = big.NewInt(2) ethCfg.IstanbulBlock = big.NewInt(3) signer := gethtypes.LatestSignerForChainID(ethCfg.ChainID) @@ -813,10 +813,11 @@ func (s *KeeperTestSuite) TestApplyMessage() { ) s.Require().NoError(err) + ethChainConfig := s.Network.App.GetEVMKeeper().EthChainConfig() tracer := s.Network.App.GetEVMKeeper().Tracer( s.Network.GetContext(), *coreMsg, - types.GetEthChainConfig(), + ethChainConfig, ) res, err := s.Network.App.GetEVMKeeper().ApplyMessage(s.Network.GetContext(), *coreMsg, tracer, true, false) s.Require().NoError(err) @@ -884,7 +885,7 @@ func (s *KeeperTestSuite) TestApplyMessageWithConfig() { s.Require().NoError(err) auth := gethtypes.SetCodeAuthorization{ - ChainID: *uint256.NewInt(types.GetChainConfig().GetChainId()), + ChainID: *uint256.NewInt(s.Network.GetEvmChainID()), Address: target, Nonce: accResp.GetNonce(), } @@ -929,7 +930,7 @@ func (s *KeeperTestSuite) TestApplyMessageWithConfig() { s.Require().NoError(err) auth := gethtypes.SetCodeAuthorization{ - ChainID: *uint256.NewInt(types.GetChainConfig().GetChainId()), + ChainID: *uint256.NewInt(s.Network.App.GetEVMKeeper().ChainConfig().ChainId), Address: target, Nonce: accResp.GetNonce(), } @@ -1213,10 +1214,11 @@ func (s *KeeperTestSuite) TestApplyMessageWithNegativeAmount() { ) s.Require().NoError(err) + ethChainConfig := s.Network.App.GetEVMKeeper().EthChainConfig() tracer := s.Network.App.GetEVMKeeper().Tracer( s.Network.GetContext(), *coreMsg, - types.GetEthChainConfig(), + ethChainConfig, ) ctx := s.Network.GetContext() diff --git a/tests/integration/x/vm/utils.go b/tests/integration/x/vm/utils.go index 837e80745..bea67e7c9 100644 --- a/tests/integration/x/vm/utils.go +++ b/tests/integration/x/vm/utils.go @@ -31,7 +31,7 @@ func (s *KeeperTestSuite) StateDB() *statedb.StateDB { // DeployTestContract deploy a test erc20 contract and returns the contract address func (s *KeeperTestSuite) DeployTestContract(t require.TestingT, ctx sdk.Context, owner common.Address, supply *big.Int) common.Address { - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := s.Network.App.GetEVMKeeper().EvmChainID() erc20Contract, err := testdata.LoadERC20Contract() require.NoError(t, err, "failed to load contract") @@ -93,7 +93,7 @@ func (s *KeeperTestSuite) DeployTestContract(t require.TestingT, ctx sdk.Context func (s *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAddr, from, to common.Address, amount *big.Int) *evmtypes.MsgEthereumTx { ctx := s.Network.GetContext() - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := s.Network.App.GetEVMKeeper().EvmChainID() erc20Contract, err := testdata.LoadERC20Contract() require.NoError(t, err, "failed to load contract") @@ -151,7 +151,7 @@ func (s *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAddr, f // DeployTestMessageCall deploy a test erc20 contract and returns the contract address func (s *KeeperTestSuite) DeployTestMessageCall(t require.TestingT) common.Address { ctx := s.Network.GetContext() - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := s.Network.App.GetEVMKeeper().EvmChainID() testMsgCall, err := testdata.LoadMessageCallContract() require.NoError(t, err) diff --git a/testutil/ibc/chain.go b/testutil/ibc/chain.go index 1f8d29163..bb1acd39a 100644 --- a/testutil/ibc/chain.go +++ b/testutil/ibc/chain.go @@ -422,7 +422,8 @@ func (chain *TestChain) SendEvmTx( require.NoError(chain.TB, err) txConfig := app.GetTxConfig() - tx, err := tx.PrepareEthTx(txConfig, senderAcc.SenderPrivKey, msgEthereumTx) + chainID := app.GetEVMKeeper().EvmChainID() + tx, err := tx.PrepareEthTx(txConfig, senderAcc.SenderPrivKey, chainID, msgEthereumTx) require.NoError(chain.TB, err) // bz are bytes to be broadcasted over the network diff --git a/testutil/ibc/coordinator.go b/testutil/ibc/coordinator.go index 0d235d324..bd7cb4d54 100644 --- a/testutil/ibc/coordinator.go +++ b/testutil/ibc/coordinator.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/evm/x/vm/types" ibctesting "github.com/cosmos/ibc-go/v10/testing" ) @@ -39,8 +38,6 @@ func NewCoordinator(t *testing.T, nEVMChains, mCosmosChains int, evmAppCreator i ibctesting.DefaultTestingAppInit = evmAppCreator for i := 1; i <= nEVMChains; i++ { //nolint: staticcheck // this variable does change when the number of evmchains is 2 - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() chainID := GetChainID(i) evmChainID, err := strconv.ParseUint(GetEvmChainID(i), 10, 64) require.NoError(t, err) diff --git a/testutil/integration/contract.go b/testutil/integration/contract.go index 8797d3234..0ccb50208 100644 --- a/testutil/integration/contract.go +++ b/testutil/integration/contract.go @@ -57,7 +57,7 @@ func DeployContract( contract evmtypes.CompiledContract, constructorArgs ...interface{}, ) (common.Address, error) { - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := app.GetEVMKeeper().EvmChainID() from := common.BytesToAddress(priv.PubKey().Address().Bytes()) nonce := app.GetEVMKeeper().GetNonce(ctx, from) @@ -108,7 +108,7 @@ func DeployContractWithFactory( priv cryptotypes.PrivKey, factoryAddress common.Address, ) (common.Address, abci.ExecTxResult, error) { - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := exampleApp.GetEVMKeeper().EvmChainID() from := common.BytesToAddress(priv.PubKey().Address().Bytes()) factoryNonce := exampleApp.GetEVMKeeper().GetNonce(ctx, factoryAddress) nonce := exampleApp.GetEVMKeeper().GetNonce(ctx, from) diff --git a/testutil/integration/evm/network/network.go b/testutil/integration/evm/network/network.go index 907af9894..2ee9dcb41 100644 --- a/testutil/integration/evm/network/network.go +++ b/testutil/integration/evm/network/network.go @@ -16,6 +16,7 @@ import ( "github.com/cometbft/cometbft/version" "github.com/cosmos/evm" + testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/testutil/integration" basenetwork "github.com/cosmos/evm/testutil/integration/base/network" erc20types "github.com/cosmos/evm/x/erc20/types" @@ -76,8 +77,6 @@ type IntegrationNetwork struct { // // It panics if an error occurs. func New(createEvmApp CreateEvmApp, opts ...ConfigOption) *IntegrationNetwork { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() cfg := DefaultConfig() // Modify the default config with the given options for _, opt := range opts { @@ -257,6 +256,20 @@ func (n *IntegrationNetwork) configureAndInitChain(evmApp evm.EvmApp) error { n.valSet = valSet n.valSigners = valSigners + params := n.app.GetEVMKeeper().GetParams(n.ctx) + coinInfo := testconstants.ChainsCoinInfo[n.cfg.eip155ChainID.Uint64()] + chainCfg := evmtypes.DefaultChainConfig(n.cfg.eip155ChainID.Uint64()) + chainCfg.Denom = coinInfo.Denom + chainCfg.Decimals = uint64(coinInfo.Decimals) + + runtimeCfg, err := evmtypes.NewRuntimeConfig(chainCfg, coinInfo, params.ExtraEIPs) + if err != nil { + return err + } + if err := n.app.GetEVMKeeper().SetRuntimeConfig(runtimeCfg); err != nil { + return err + } + return nil } @@ -301,7 +314,7 @@ func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { // GetEVMChainConfig returns the network's EVM chain config func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { - return evmtypes.GetEthChainConfig() + return n.app.GetEVMKeeper().EthChainConfig() } // GetBaseDenom returns the network's base denom diff --git a/testutil/integration/evm/network/unit_network.go b/testutil/integration/evm/network/unit_network.go index ff068b8f5..b93d10f56 100644 --- a/testutil/integration/evm/network/unit_network.go +++ b/testutil/integration/evm/network/unit_network.go @@ -41,6 +41,11 @@ func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { ) } +// GetEvmChainID returns evm chain id +func (n *UnitTestNetwork) GetEvmChainID() uint64 { + return n.app.GetEVMKeeper().ChainConfig().ChainId +} + // FundAccount funds the given account with the given amount of coins. func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { ctx := n.GetContext() diff --git a/testutil/integration/utils.go b/testutil/integration/utils.go index 7ec44baa4..242551a35 100644 --- a/testutil/integration/utils.go +++ b/testutil/integration/utils.go @@ -24,8 +24,8 @@ func DeliverEthTx( msgs ...sdk.Msg, ) (abci.ExecTxResult, error) { txConfig := evmApp.GetTxConfig() - - tx, err := tx.PrepareEthTx(txConfig, priv, msgs...) + evmChainID := evmApp.GetEVMKeeper().EvmChainID() + tx, err := tx.PrepareEthTx(txConfig, priv, evmChainID, msgs...) if err != nil { return abci.ExecTxResult{}, err } diff --git a/testutil/tx/eth.go b/testutil/tx/eth.go index 9a6b65540..b1f400567 100644 --- a/testutil/tx/eth.go +++ b/testutil/tx/eth.go @@ -29,11 +29,12 @@ import ( func PrepareEthTx( txCfg client.TxConfig, priv cryptotypes.PrivKey, + evmChainID *big.Int, msgs ...sdk.Msg, ) (authsigning.Tx, error) { txBuilder := txCfg.NewTxBuilder() - signer := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + signer := ethtypes.LatestSignerForChainID(evmChainID) txFee := sdk.Coins{} txGasLimit := uint64(0) @@ -109,7 +110,7 @@ func CreateEthTx( copy(toAddr[:], dest) } fromAddr := common.BytesToAddress(privKey.PubKey().Address().Bytes()) - chainID := evmtypes.GetEthChainConfig().ChainID + chainID := evmApp.GetEVMKeeper().EvmChainID() baseFeeRes, err := evmApp.GetEVMKeeper().BaseFee(ctx, &evmtypes.QueryBaseFeeRequest{}) if err != nil { @@ -139,7 +140,8 @@ func CreateEthTx( // If we are creating multiple eth Tx's with different senders, we need to sign here rather than later. if privKey != nil { - signer := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + evmChainID := evmApp.GetEVMKeeper().ChainConfig().GetChainId() + signer := ethtypes.LatestSignerForChainID(big.NewInt(int64(evmChainID))) //nolint:gosec // won't exceed int64 err := msgEthereumTx.Sign(signer, NewSigner(privKey)) if err != nil { return nil, err diff --git a/utils/utils_test.go b/utils/utils_test.go index f3f152ae7..a82712d28 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -538,12 +538,6 @@ func TestAccountEquivalence(t *testing.T) { func TestCalcBaseFee(t *testing.T) { for _, chainID := range []constants.ChainID{constants.ExampleChainID, constants.TwelveDecimalsChainID, constants.SixDecimalsChainID} { t.Run(chainID.ChainID, func(t *testing.T) { - evmConfigurator := evmtypes.NewEVMConfigurator(). - WithEVMCoinInfo(constants.ExampleChainCoinInfo[chainID]) - evmConfigurator.ResetTestConfig() - err := evmConfigurator.Configure() - require.NoError(t, err) - config := ¶ms.ChainConfig{ LondonBlock: big.NewInt(0), } diff --git a/x/precisebank/genesis.go b/x/precisebank/genesis.go index 171dd89e1..a32b4d801 100644 --- a/x/precisebank/genesis.go +++ b/x/precisebank/genesis.go @@ -20,7 +20,11 @@ func InitGenesis( gs *types.GenesisState, ) { // Ensure the genesis state is valid - if err := gs.Validate(); err != nil { + conversionFactor := keeper.ConversionFactor() + integerDenom := keeper.IntegerDenom() + extendedDenom := keeper.ExtendedDenom() + + if err := gs.Validate(conversionFactor); err != nil { panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err)) } @@ -35,8 +39,8 @@ func InitGenesis( totalAmt := gs.TotalAmountWithRemainder() moduleAddr := ak.GetModuleAddress(types.ModuleName) - moduleBal := bk.GetBalance(ctx, moduleAddr, types.IntegerCoinDenom()) - moduleBalExtended := moduleBal.Amount.Mul(types.ConversionFactor()) + moduleBal := bk.GetBalance(ctx, moduleAddr, integerDenom) + moduleBalExtended := moduleBal.Amount.Mul(conversionFactor) // Compare balances in full precise extended amounts if !totalAmt.Equal(moduleBalExtended) { @@ -46,15 +50,15 @@ func InitGenesis( fmt.Printf( "WARNING: module account balance does not match sum of fractional balances and remainder, balance is %s but expected %v%s (%v%s). This is expected during testing with default genesis state.\n", moduleBal, - totalAmt, types.ExtendedCoinDenom(), - totalAmt.Quo(types.ConversionFactor()), types.IntegerCoinDenom(), + totalAmt, extendedDenom, + totalAmt.Quo(conversionFactor), integerDenom, ) } else { // For non-default genesis states, enforce strict validation panic(fmt.Sprintf("module account balance does not match sum of fractional balances and remainder, balance is %s but expected %v%s (%v%s)", moduleBal, - totalAmt, types.ExtendedCoinDenom(), - totalAmt.Quo(types.ConversionFactor()), types.IntegerCoinDenom(), + totalAmt, extendedDenom, + totalAmt.Quo(conversionFactor), integerDenom, )) } } diff --git a/x/precisebank/keeper/burn.go b/x/precisebank/keeper/burn.go index f971f4d26..a504d012d 100644 --- a/x/precisebank/keeper/burn.go +++ b/x/precisebank/keeper/burn.go @@ -19,6 +19,8 @@ import ( func (k Keeper) BurnCoins(goCtx context.Context, moduleName string, amt sdk.Coins) error { ctx := sdk.UnwrapSDKContext(goCtx) + extendedDenom := k.ExtendedDenom() + // Custom protection for x/precisebank, no external module should be able to // affect reserves. if moduleName == types.ModuleName { @@ -43,10 +45,10 @@ func (k Keeper) BurnCoins(goCtx context.Context, moduleName string, amt sdk.Coin // Get non-ExtendedCoinDenom coins passthroughCoins := amt - extendedAmount := amt.AmountOf(types.ExtendedCoinDenom()) + extendedAmount := amt.AmountOf(extendedDenom) if extendedAmount.IsPositive() { // Remove ExtendedCoinDenom from the coins as it is managed by x/precisebank - removeCoin := sdk.NewCoin(types.ExtendedCoinDenom(), extendedAmount) + removeCoin := sdk.NewCoin(extendedDenom, extendedAmount) passthroughCoins = amt.Sub(removeCoin) } @@ -73,6 +75,9 @@ func (k Keeper) burnExtendedCoin( moduleName string, amt sdkmath.Int, ) error { + conversionFactor := k.ConversionFactor() + integerDenom := k.IntegerDenom() + // Get the module address moduleAddr := k.ak.GetModuleAddress(moduleName) @@ -80,9 +85,9 @@ func (k Keeper) burnExtendedCoin( // The precisebank module account is the reserve and should not have fractional balances if moduleName == types.ModuleName { // For the precisebank module account, just burn the integer coins directly - integerBurnAmount := amt.Quo(types.ConversionFactor()) + integerBurnAmount := amt.Quo(conversionFactor) if integerBurnAmount.IsPositive() { - integerBurnCoin := sdk.NewCoin(types.IntegerCoinDenom(), integerBurnAmount) + integerBurnCoin := sdk.NewCoin(integerDenom, integerBurnAmount) if err := k.bk.BurnCoins(ctx, moduleName, sdk.NewCoins(integerBurnCoin)); err != nil { return err } @@ -100,8 +105,8 @@ func (k Keeper) burnExtendedCoin( // ------------------------------------------------------------------------- // Pure stateless calculations - integerBurnAmount := amt.Quo(types.ConversionFactor()) - fractionalBurnAmount := amt.Mod(types.ConversionFactor()) + integerBurnAmount := amt.Quo(conversionFactor) + fractionalBurnAmount := amt.Mod(conversionFactor) // newFractionalBalance can be negative if fractional balance is insufficient. newFractionalBalance := prevFractionalBalance.Sub(fractionalBurnAmount) @@ -115,7 +120,7 @@ func (k Keeper) burnExtendedCoin( // If true, remainder has accumulated enough fractional amounts to burn 1 // integer coin. - overflowingRemainder := newRemainder.GTE(types.ConversionFactor()) + overflowingRemainder := newRemainder.GTE(conversionFactor) // ------------------------------------------------------------------------- // Stateful operations for burn @@ -131,8 +136,8 @@ func (k Keeper) burnExtendedCoin( // Case #1: (optimization) direct burn instead of borrow (reserve transfer) // & reserve burn. No additional reserve burn would be necessary after this. if requiresBorrow && overflowingRemainder { - newFractionalBalance = newFractionalBalance.Add(types.ConversionFactor()) - newRemainder = newRemainder.Sub(types.ConversionFactor()) + newFractionalBalance = newFractionalBalance.Add(conversionFactor) + newRemainder = newRemainder.Sub(conversionFactor) integerBurnAmount = integerBurnAmount.AddRaw(1) } @@ -140,13 +145,13 @@ func (k Keeper) burnExtendedCoin( // Case #2: Transfer 1 integer coin to reserve for integer borrow to ensure // reserve fully backs the fractional amount. if requiresBorrow && !overflowingRemainder { - newFractionalBalance = newFractionalBalance.Add(types.ConversionFactor()) + newFractionalBalance = newFractionalBalance.Add(conversionFactor) // Transfer 1 integer coin to reserve to cover the borrowed fractional // amount. SendCoinsFromModuleToModule will return an error if the // module account has insufficient funds and an error with the full // extended balance will be returned. - borrowCoin := sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.OneInt()) + borrowCoin := sdk.NewCoin(integerDenom, sdkmath.OneInt()) if err := k.bk.SendCoinsFromModuleToModule( ctx, moduleName, @@ -160,12 +165,12 @@ func (k Keeper) burnExtendedCoin( // Case #3: Does not require borrow, but remainder has accumulated enough // fractional amounts to burn 1 integer coin. if !requiresBorrow && overflowingRemainder { - reserveBurnCoins := sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.OneInt())) + reserveBurnCoins := sdk.NewCoins(sdk.NewCoin(integerDenom, sdkmath.OneInt())) if err := k.bk.BurnCoins(ctx, types.ModuleName, reserveBurnCoins); err != nil { return fmt.Errorf("failed to burn %s for reserve: %w", reserveBurnCoins, err) } - newRemainder = newRemainder.Sub(types.ConversionFactor()) + newRemainder = newRemainder.Sub(conversionFactor) } // Case #4: No additional work required, no borrow needed and no additional @@ -174,7 +179,7 @@ func (k Keeper) burnExtendedCoin( // Burn the integer amount - this may include the extra optimization burn // from case #1 if !integerBurnAmount.IsZero() { - coin := sdk.NewCoin(types.IntegerCoinDenom(), integerBurnAmount) + coin := sdk.NewCoin(integerDenom, integerBurnAmount) if err := k.bk.BurnCoins(ctx, moduleName, sdk.NewCoins(coin)); err != nil { return k.updateInsufficientFundsError(ctx, moduleAddr, amt, err) } diff --git a/x/precisebank/keeper/burn_test.go b/x/precisebank/keeper/burn_test.go index 4905b916b..f19991ba5 100644 --- a/x/precisebank/keeper/burn_test.go +++ b/x/precisebank/keeper/burn_test.go @@ -38,7 +38,7 @@ func TestBurnCoins_PanicValidations(t *testing.T) { Return(nil). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "module account notamodule does not exist: unknown address", }, { @@ -54,7 +54,7 @@ func TestBurnCoins_PanicValidations(t *testing.T) { )). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), fmt.Sprintf("module account %s does not have permissions to burn tokens: unauthorized", burnerModuleName), }, { @@ -73,11 +73,11 @@ func TestBurnCoins_PanicValidations(t *testing.T) { // Will call x/bank BurnCoins coins td.bk.EXPECT(). - BurnCoins(td.ctx, burnerModuleName, cs(c(types.IntegerCoinDenom(), 1000))). + BurnCoins(td.ctx, burnerModuleName, cs(c(testIntegerDenom, 1000))). Return(nil). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "", }, { @@ -87,7 +87,7 @@ func TestBurnCoins_PanicValidations(t *testing.T) { // No mock setup needed since this is checked before module // account checks }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "module account precisebank cannot be burned from: unauthorized", }, } @@ -137,10 +137,10 @@ func TestBurnCoins_Errors(t *testing.T) { Once() }, sdk.Coins{sdk.Coin{ - Denom: types.IntegerCoinDenom(), + Denom: testIntegerDenom, Amount: sdkmath.NewInt(-1000), }}, - fmt.Sprintf("-1000%s: invalid coins", types.IntegerCoinDenom()), + fmt.Sprintf("-1000%s: invalid coins", testIntegerDenom), }, } diff --git a/x/precisebank/keeper/coin_info.go b/x/precisebank/keeper/coin_info.go new file mode 100644 index 000000000..ff64b7db2 --- /dev/null +++ b/x/precisebank/keeper/coin_info.go @@ -0,0 +1,52 @@ +package keeper + +import ( + sdkmath "cosmossdk.io/math" + + evmtypes "github.com/cosmos/evm/x/vm/types" +) + +// SetCoinInfo wires the runtime EVM coin information into the precisebank keeper. +// It must be called before any operations that require denomination or decimal +// conversions. +func (k *Keeper) SetCoinInfo(info evmtypes.EvmCoinInfo) { + if info.Denom == "" { + panic("precisebank: coin info must include denom") + } + k.coinInfo = info + k.coinInfoSet = true +} + +func (k Keeper) ensureCoinInfoSet() { + if !k.coinInfoSet { + panic("precisebank: coin info not initialized") + } +} + +func (k Keeper) evmCoinInfo() evmtypes.EvmCoinInfo { + k.ensureCoinInfoSet() + return k.coinInfo +} + +func (k Keeper) IntegerDenom() string { + info := k.evmCoinInfo() + if info.Denom == "" { + panic("precisebank: coin info missing denom") + } + return info.Denom +} + +func (k Keeper) ExtendedDenom() string { + info := k.evmCoinInfo() + if info.ExtendedDenom != "" { + return info.ExtendedDenom + } + return info.DenomOrDefault() +} + +func (k Keeper) ConversionFactor() sdkmath.Int { + info := k.evmCoinInfo() + return info.DecimalsOrDefault().ConversionFactor() +} + +// describeCoinInfo is used in panic messages to aid debugging. diff --git a/x/precisebank/keeper/fractional_balance.go b/x/precisebank/keeper/fractional_balance.go index 5f54c5a75..7ad4c877a 100644 --- a/x/precisebank/keeper/fractional_balance.go +++ b/x/precisebank/keeper/fractional_balance.go @@ -46,7 +46,7 @@ func (k *Keeper) SetFractionalBalance( // Ensure the fractional balance is valid before setting it. Use the // ValidateFractionalAmount function to validate the amount. - if err := types.ValidateFractionalAmount(amount); err != nil { + if err := types.ValidateFractionalAmount(amount, k.ConversionFactor()); err != nil { panic(fmt.Errorf("amount is invalid: %w", err)) } diff --git a/x/precisebank/keeper/fractional_balance_test.go b/x/precisebank/keeper/fractional_balance_test.go index 89b7afd04..1938e4a2a 100644 --- a/x/precisebank/keeper/fractional_balance_test.go +++ b/x/precisebank/keeper/fractional_balance_test.go @@ -37,7 +37,7 @@ func TestSetGetFractionalBalance(t *testing.T) { { "valid - max amount", addr, - types.ConversionFactor().SubRaw(1), + testConversionFactor.SubRaw(1), "", }, { @@ -55,7 +55,7 @@ func TestSetGetFractionalBalance(t *testing.T) { { "invalid - over max amount", addr, - types.ConversionFactor(), + testConversionFactor, "amount is invalid: amount 1000000000000 exceeds max of 999999999999", }, } diff --git a/x/precisebank/keeper/grpc_query.go b/x/precisebank/keeper/grpc_query.go index c95b6b4ec..ebd04bc5d 100644 --- a/x/precisebank/keeper/grpc_query.go +++ b/x/precisebank/keeper/grpc_query.go @@ -27,7 +27,7 @@ func (s queryServer) Remainder( ctx := sdk.UnwrapSDKContext(goCtx) remainder := s.keeper.GetRemainderAmount(ctx) - remainderCoin := sdk.NewCoin(types.ExtendedCoinDenom(), remainder) + remainderCoin := sdk.NewCoin(s.keeper.ExtendedDenom(), remainder) return &types.QueryRemainderResponse{ Remainder: remainderCoin, @@ -47,7 +47,7 @@ func (s queryServer) FractionalBalance( } amt := s.keeper.GetFractionalBalance(ctx, address) - fractionalBalance := sdk.NewCoin(types.ExtendedCoinDenom(), amt) + fractionalBalance := sdk.NewCoin(s.keeper.ExtendedDenom(), amt) return &types.QueryFractionalBalanceResponse{ FractionalBalance: fractionalBalance, diff --git a/x/precisebank/keeper/keeper.go b/x/precisebank/keeper/keeper.go index 1185ea518..96f1e6e92 100644 --- a/x/precisebank/keeper/keeper.go +++ b/x/precisebank/keeper/keeper.go @@ -22,6 +22,9 @@ type Keeper struct { bk types.BankKeeper ak types.AccountKeeper + // coinInfo stores the runtime coin metadata used for denomination and decimal conversions. + coinInfo evmtypes.EvmCoinInfo + coinInfoSet bool } // NewKeeper creates a new keeper diff --git a/x/precisebank/keeper/keeper_test.go b/x/precisebank/keeper/keeper_test.go index b1ac4b235..70be16335 100644 --- a/x/precisebank/keeper/keeper_test.go +++ b/x/precisebank/keeper/keeper_test.go @@ -3,14 +3,11 @@ package keeper_test import ( "testing" - "github.com/stretchr/testify/require" - evmencoding "github.com/cosmos/evm/encoding" testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/x/precisebank/keeper" "github.com/cosmos/evm/x/precisebank/types" "github.com/cosmos/evm/x/precisebank/types/mocks" - vmtypes "github.com/cosmos/evm/x/vm/types" sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" @@ -19,6 +16,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +var ( + // Test coin info constants - matches the setup in newMockedTestData + testCoinInfo = testconstants.ChainsCoinInfo[testconstants.SixDecimalsChainID.EVMChainID] + testConversionFactor = testCoinInfo.DecimalsOrDefault().ConversionFactor() + testIntegerDenom = testCoinInfo.DenomOrDefault() + testExtendedDenom = testCoinInfo.ExtendedDenomOrDefault() +) + // testData defines necessary fields for testing keeper store methods and mocks // for unit tests without full app setup. type testData struct { @@ -46,11 +51,7 @@ func newMockedTestData(t *testing.T) testData { cfg := evmencoding.MakeConfig(chainID) cdc := cfg.Codec k := keeper.NewKeeper(cdc, storeKey, bk, ak) //nolint: staticcheck // this variable is used - evmConfigurator := vmtypes.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - evmConfigurator.ResetTestConfig() - err := evmConfigurator.Configure() - require.NoError(t, err) + k.SetCoinInfo(testconstants.ChainsCoinInfo[chainID]) return testData{ ctx: ctx, diff --git a/x/precisebank/keeper/mint.go b/x/precisebank/keeper/mint.go index 2b3ca4496..b4ff5d9e8 100644 --- a/x/precisebank/keeper/mint.go +++ b/x/precisebank/keeper/mint.go @@ -21,6 +21,8 @@ import ( func (k Keeper) MintCoins(goCtx context.Context, moduleName string, amt sdk.Coins) error { ctx := sdk.UnwrapSDKContext(goCtx) + extendedDenom := k.ExtendedDenom() + // Disallow minting to x/precisebank module if moduleName == types.ModuleName { panic(errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "module account %s cannot be minted to", moduleName)) @@ -45,10 +47,10 @@ func (k Keeper) MintCoins(goCtx context.Context, moduleName string, amt sdk.Coin // Get non-ExtendedCoinDenom coins passthroughCoins := amt - extendedAmount := amt.AmountOf(types.ExtendedCoinDenom()) + extendedAmount := amt.AmountOf(extendedDenom) if extendedAmount.IsPositive() { // Remove ExtendedCoinDenom from the coins as it is managed by x/precisebank - removeCoin := sdk.NewCoin(types.ExtendedCoinDenom(), extendedAmount) + removeCoin := sdk.NewCoin(extendedDenom, extendedAmount) passthroughCoins = amt.Sub(removeCoin) } @@ -90,15 +92,18 @@ func (k Keeper) mintExtendedCoin( recipientModuleName string, amt sdkmath.Int, ) error { + conversionFactor := k.ConversionFactor() + integerDenom := k.IntegerDenom() + moduleAddr := k.ak.GetModuleAddress(recipientModuleName) // Don't create fractional balances for the precisebank module account itself // The precisebank module account is the reserve and should not have fractional balances if recipientModuleName == types.ModuleName { // For the precisebank module account, just mint the integer coins directly - integerMintAmount := amt.Quo(types.ConversionFactor()) + integerMintAmount := amt.Quo(conversionFactor) if integerMintAmount.IsPositive() { - integerMintCoin := sdk.NewCoin(types.IntegerCoinDenom(), integerMintAmount) + integerMintCoin := sdk.NewCoin(integerDenom, integerMintAmount) if err := k.bk.MintCoins(ctx, recipientModuleName, sdk.NewCoins(integerMintCoin)); err != nil { return err } @@ -110,8 +115,8 @@ func (k Keeper) mintExtendedCoin( fractionalAmount := k.GetFractionalBalance(ctx, moduleAddr) // Get separated mint amounts - integerMintAmount := amt.Quo(types.ConversionFactor()) - fractionalMintAmount := amt.Mod(types.ConversionFactor()) + integerMintAmount := amt.Quo(conversionFactor) + fractionalMintAmount := amt.Mod(conversionFactor) // Get previous remainder amount, as we need to it before carry calculation // for the optimization path. @@ -133,11 +138,11 @@ func (k Keeper) mintExtendedCoin( newFractionalBalance := fractionalAmount.Add(fractionalMintAmount) // Case #3 - Integer carry, remainder is sufficient (0 or positive) - if newFractionalBalance.GTE(types.ConversionFactor()) && newRemainder.GTE(sdkmath.ZeroInt()) { + if newFractionalBalance.GTE(conversionFactor) && newRemainder.GTE(sdkmath.ZeroInt()) { // Carry should send from reserve -> account, instead of minting an // extra integer coin. Otherwise doing an extra mint will require a burn // from reserves to maintain exact backing. - carryCoin := sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.OneInt()) + carryCoin := sdk.NewCoin(integerDenom, sdkmath.OneInt()) // SendCoinsFromModuleToModule allows for sending coins even if the // recipient module account is blocked. @@ -154,7 +159,7 @@ func (k Keeper) mintExtendedCoin( // Case #4 - Integer carry, remainder is insufficient // This is the optimization path where the integer mint amount is increased // by 1, instead of doing both a reserve -> account transfer and reserve mint. - if newFractionalBalance.GTE(types.ConversionFactor()) && newRemainder.IsNegative() { + if newFractionalBalance.GTE(conversionFactor) && newRemainder.IsNegative() { integerMintAmount = integerMintAmount.AddRaw(1) } @@ -163,16 +168,16 @@ func (k Keeper) mintExtendedCoin( // fractional amounts x and y where both x and y < ConversionFactor // x + y < (2 * ConversionFactor) - 2 // x + y < 1 integer amount + fractional amount - if newFractionalBalance.GTE(types.ConversionFactor()) { + if newFractionalBalance.GTE(conversionFactor) { // Subtract 1 integer equivalent amount of fractional balance. Same // behavior as using .Mod() in this case. - newFractionalBalance = newFractionalBalance.Sub(types.ConversionFactor()) + newFractionalBalance = newFractionalBalance.Sub(conversionFactor) } // Mint new integer amounts in x/bank - including carry over from fractional // amount if any. if integerMintAmount.IsPositive() { - integerMintCoin := sdk.NewCoin(types.IntegerCoinDenom(), integerMintAmount) + integerMintCoin := sdk.NewCoin(integerDenom, integerMintAmount) if err := k.bk.MintCoins( ctx, @@ -197,10 +202,10 @@ func (k Keeper) mintExtendedCoin( // Optimization: This is only done when the integer amount does NOT carry, // as a direct account mint is done instead of integer carry transfer + // insufficient remainder reserve mint. - wasCarried := fractionalAmount.Add(fractionalMintAmount).GTE(types.ConversionFactor()) + wasCarried := fractionalAmount.Add(fractionalMintAmount).GTE(conversionFactor) if prevRemainder.LT(fractionalMintAmount) && !wasCarried { // Always only 1 integer coin, as fractionalMintAmount < ConversionFactor - reserveMintCoins := sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.OneInt())) + reserveMintCoins := sdk.NewCoins(sdk.NewCoin(integerDenom, sdkmath.OneInt())) if err := k.bk.MintCoins(ctx, types.ModuleName, reserveMintCoins); err != nil { return fmt.Errorf("failed to mint %s for reserve: %w", reserveMintCoins, err) } @@ -210,7 +215,7 @@ func (k Keeper) mintExtendedCoin( // This needs to be adjusted back to the corresponding positive value. The // remainder will be always < conversionFactor after add if it is negative. if newRemainder.IsNegative() { - newRemainder = newRemainder.Add(types.ConversionFactor()) + newRemainder = newRemainder.Add(conversionFactor) } k.SetRemainderAmount(ctx, newRemainder) diff --git a/x/precisebank/keeper/mint_test.go b/x/precisebank/keeper/mint_test.go index 3c0f9f93f..e4a9c2cef 100644 --- a/x/precisebank/keeper/mint_test.go +++ b/x/precisebank/keeper/mint_test.go @@ -35,7 +35,7 @@ func TestMintCoins_PanicValidations(t *testing.T) { Return(nil). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "module account notamodule does not exist: unknown address", }, { @@ -51,7 +51,7 @@ func TestMintCoins_PanicValidations(t *testing.T) { )). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "module account mint does not have permissions to mint tokens: unauthorized", }, { @@ -70,11 +70,11 @@ func TestMintCoins_PanicValidations(t *testing.T) { // Will call x/bank MintCoins coins td.bk.EXPECT(). - MintCoins(td.ctx, minttypes.ModuleName, cs(c(types.IntegerCoinDenom(), 1000))). + MintCoins(td.ctx, minttypes.ModuleName, cs(c(testIntegerDenom, 1000))). Return(nil). Once() }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "", }, { @@ -84,7 +84,7 @@ func TestMintCoins_PanicValidations(t *testing.T) { // No mock setup needed since this is checked before module // account checks }, - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), "module account precisebank cannot be minted to: unauthorized", }, } @@ -135,10 +135,10 @@ func TestMintCoins_Errors(t *testing.T) { Once() }, sdk.Coins{sdk.Coin{ - Denom: types.IntegerCoinDenom(), + Denom: testIntegerDenom, Amount: sdkmath.NewInt(-1000), }}, - fmt.Sprintf("-1000%s: invalid coins", types.IntegerCoinDenom()), + fmt.Sprintf("-1000%s: invalid coins", testIntegerDenom), }, } @@ -177,7 +177,7 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { { "passthrough mint - integer denom", sdkmath.ZeroInt(), - cs(c(types.IntegerCoinDenom(), 1000)), + cs(c(testIntegerDenom, 1000)), sdkmath.ZeroInt(), }, @@ -190,41 +190,41 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { { "no carry - 0 starting fractional", sdkmath.ZeroInt(), - cs(c(types.ExtendedCoinDenom(), 1000)), + cs(c(testExtendedDenom, 1000)), sdkmath.NewInt(1000), }, { "no carry - non-zero fractional", sdkmath.NewInt(1_000_000), - cs(c(types.ExtendedCoinDenom(), 1000)), + cs(c(testExtendedDenom, 1000)), sdkmath.NewInt(1_001_000), }, { "fractional carry", // max fractional amount - types.ConversionFactor().SubRaw(1), - cs(c(types.ExtendedCoinDenom(), 1)), // +1 to carry + testConversionFactor.SubRaw(1), + cs(c(testExtendedDenom, 1)), // +1 to carry sdkmath.ZeroInt(), }, { "fractional carry max", // max fractional amount + max fractional amount - types.ConversionFactor().SubRaw(1), - cs(ci(types.ExtendedCoinDenom(), types.ConversionFactor().SubRaw(1))), - types.ConversionFactor().SubRaw(2), + testConversionFactor.SubRaw(1), + cs(ci(testExtendedDenom, testConversionFactor.SubRaw(1))), + testConversionFactor.SubRaw(2), }, { "integer with fractional no carry", sdkmath.NewInt(1234), // mint 100 fractional - cs(c(types.ExtendedCoinDenom(), 100)), + cs(c(testExtendedDenom, 100)), sdkmath.NewInt(1234 + 100), }, { "integer with fractional carry", - types.ConversionFactor().SubRaw(100), + testConversionFactor.SubRaw(100), // mint 105 fractional to carry - cs(c(types.ExtendedCoinDenom(), 105)), + cs(c(testExtendedDenom, 105)), sdkmath.NewInt(5), }, } @@ -264,20 +264,20 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { // Determine how much is passed through to x/bank passthroughCoins := tt.mintAmount - found, extCoins := tt.mintAmount.Find(types.ExtendedCoinDenom()) + found, extCoins := tt.mintAmount.Find(testExtendedDenom) if found { // Remove extended coin from passthrough coins passthroughCoins = passthroughCoins.Sub(extCoins) } else { - extCoins = sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.ZeroInt()) + extCoins = sdk.NewCoin(testExtendedDenom, sdkmath.ZeroInt()) } require.Equalf( t, sdkmath.ZeroInt(), - passthroughCoins.AmountOf(types.ExtendedCoinDenom()), + passthroughCoins.AmountOf(testExtendedDenom), "expected pass through coins should not include %v", - types.ExtendedCoinDenom(), + testExtendedDenom, ) // ---------------------------------------- @@ -295,15 +295,15 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { // ---------------------------------------- // Set expectations for reserve minting when fractional amounts // are minted & remainder is insufficient - mintFractionalAmount := extCoins.Amount.Mod(types.ConversionFactor()) + mintFractionalAmount := extCoins.Amount.Mod(testConversionFactor) currentRemainder := td.keeper.GetRemainderAmount(td.ctx) - causesIntegerCarry := fBal.Add(mintFractionalAmount).GTE(types.ConversionFactor()) + causesIntegerCarry := fBal.Add(mintFractionalAmount).GTE(testConversionFactor) remainderEnough := currentRemainder.GTE(mintFractionalAmount) // Optimization: Carry & insufficient remainder is directly minted if causesIntegerCarry && !remainderEnough { - extCoins = extCoins.AddAmount(types.ConversionFactor()) + extCoins = extCoins.AddAmount(testConversionFactor) } // ---------------------------------------- @@ -315,10 +315,10 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { Once() // Initial integer balance is always 0 for this test - mintIntegerAmount := extCoins.Amount.Quo(types.ConversionFactor()) + mintIntegerAmount := extCoins.Amount.Quo(testConversionFactor) // Minted coins does NOT include roll-over, simply excludes - mintCoins := cs(ci(types.IntegerCoinDenom(), mintIntegerAmount)) + mintCoins := cs(ci(testIntegerDenom, mintIntegerAmount)) // Only expect MintCoins to be called with mint coins with // non-zero amount. @@ -339,14 +339,14 @@ func TestMintCoins_ExpectedCalls(t *testing.T) { td.ctx, types.ModuleName, minttypes.ModuleName, - cs(c(types.IntegerCoinDenom(), 1)), + cs(c(testIntegerDenom, 1)), ). Return(nil). Once() } if !remainderEnough && !causesIntegerCarry { - reserveMintCoins := cs(c(types.IntegerCoinDenom(), 1)) + reserveMintCoins := cs(c(testIntegerDenom, 1)) td.bk.EXPECT(). // Mints to x/precisebank MintCoins(td.ctx, types.ModuleName, reserveMintCoins). diff --git a/x/precisebank/keeper/remainder_amount.go b/x/precisebank/keeper/remainder_amount.go index 0c4eacdfd..55c8f0ca1 100644 --- a/x/precisebank/keeper/remainder_amount.go +++ b/x/precisebank/keeper/remainder_amount.go @@ -44,7 +44,7 @@ func (k *Keeper) SetRemainderAmount( // Ensure the remainder is valid before setting it. Follows the same // validation as FractionalBalance with the same value range. - if err := types.ValidateFractionalAmount(amount); err != nil { + if err := types.ValidateFractionalAmount(amount, k.ConversionFactor()); err != nil { panic(fmt.Errorf("remainder amount is invalid: %w", err)) } diff --git a/x/precisebank/keeper/remainder_amount_test.go b/x/precisebank/keeper/remainder_amount_test.go index 98e1c3c37..cb755a087 100644 --- a/x/precisebank/keeper/remainder_amount_test.go +++ b/x/precisebank/keeper/remainder_amount_test.go @@ -43,7 +43,7 @@ func TestInvalidRemainderAmount(t *testing.T) { // Set amount over max require.PanicsWithError(t, "remainder amount is invalid: amount 1000000000000 exceeds max of 999999999999", func() { - k.SetRemainderAmount(ctx, types.ConversionFactor()) + k.SetRemainderAmount(ctx, testConversionFactor) }) } diff --git a/x/precisebank/keeper/send.go b/x/precisebank/keeper/send.go index 938622925..8ef7b56e5 100644 --- a/x/precisebank/keeper/send.go +++ b/x/precisebank/keeper/send.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-metrics" "github.com/cosmos/evm/x/precisebank/types" - evmtypes "github.com/cosmos/evm/x/vm/types" errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" @@ -43,8 +42,8 @@ func (k Keeper) IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error // it also checks for the SendEnabled status on the EVM denom. The rest pass through the // regular bank keeper implementation. func (k Keeper) IsSendEnabledCoin(ctx context.Context, coin sdk.Coin) bool { - if coin.Denom == evmtypes.GetEVMCoinExtendedDenom() { - return k.bk.IsSendEnabledCoin(ctx, sdk.NewCoin(evmtypes.GetEVMCoinDenom(), coin.Amount.Quo(types.ConversionFactor()))) + if coin.Denom == k.ExtendedDenom() { + return k.bk.IsSendEnabledCoin(ctx, sdk.NewCoin(k.IntegerDenom(), coin.Amount.Quo(k.ConversionFactor()))) } return k.bk.IsSendEnabledCoin(ctx, coin) } @@ -58,6 +57,7 @@ func (k Keeper) SendCoins( from, to sdk.AccAddress, amt sdk.Coins, ) error { + extendedDenom := k.ExtendedDenom() ctx := sdk.UnwrapSDKContext(goCtx) // IsSendEnabledCoins() is only used in x/bank in msg server, not in keeper, @@ -68,11 +68,11 @@ func (k Keeper) SendCoins( } passthroughCoins := amt - extendedCoinAmount := amt.AmountOf(types.ExtendedCoinDenom()) + extendedCoinAmount := amt.AmountOf(extendedDenom) // Remove the extended coin amount from the passthrough coins if extendedCoinAmount.IsPositive() { - subCoin := sdk.NewCoin(types.ExtendedCoinDenom(), extendedCoinAmount) + subCoin := sdk.NewCoin(extendedDenom, extendedCoinAmount) passthroughCoins = amt.Sub(subCoin) } @@ -132,9 +132,9 @@ func (k Keeper) sendExtendedCoins( moduleAddr := k.ak.GetModuleAddress(types.ModuleName) if from.Equals(moduleAddr) || to.Equals(moduleAddr) { // For transfers involving the precisebank module account, just do the integer transfer - integerAmt := amt.Quo(types.ConversionFactor()) + integerAmt := amt.Quo(k.ConversionFactor()) if integerAmt.IsPositive() { - transferCoin := sdk.NewCoin(types.IntegerCoinDenom(), integerAmt) + transferCoin := sdk.NewCoin(k.IntegerDenom(), integerAmt) if err := k.bk.SendCoins(ctx, from, to, sdk.NewCoins(transferCoin)); err != nil { return k.updateInsufficientFundsError(ctx, from, amt, err) } @@ -166,12 +166,13 @@ func (k Keeper) sendExtendedCoins( // ------------------------------------------------------------------------- // Pure stateless calculations - integerAmt := amt.Quo(types.ConversionFactor()) - fractionalAmt := amt.Mod(types.ConversionFactor()) + cf := k.ConversionFactor() + integerAmt := amt.Quo(cf) + fractionalAmt := amt.Mod(cf) // Account new fractional balances - senderNewFracBal, senderNeedsBorrow := subFromFractionalBalance(senderFracBal, fractionalAmt) - recipientNewFracBal, recipientNeedsCarry := addToFractionalBalance(recipientFracBal, fractionalAmt) + senderNewFracBal, senderNeedsBorrow := k.subFromFractionalBalance(senderFracBal, fractionalAmt) + recipientNewFracBal, recipientNeedsCarry := k.addToFractionalBalance(recipientFracBal, fractionalAmt) // Case #1: Sender borrow, recipient carry if senderNeedsBorrow && recipientNeedsCarry { @@ -186,7 +187,7 @@ func (k Keeper) sendExtendedCoins( // Full integer amount transfer, including direct transfer of borrow/carry // if any. if integerAmt.IsPositive() { - transferCoin := sdk.NewCoin(types.IntegerCoinDenom(), integerAmt) + transferCoin := sdk.NewCoin(k.IntegerDenom(), integerAmt) if err := k.bk.SendCoins(ctx, from, to, sdk.NewCoins(transferCoin)); err != nil { return k.updateInsufficientFundsError(ctx, from, amt, err) } @@ -196,7 +197,7 @@ func (k Keeper) sendExtendedCoins( // Sender borrows by transferring 1 integer amount to reserve to account for // lack of fractional balance. if senderNeedsBorrow && !recipientNeedsCarry { - borrowCoin := sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1)) + borrowCoin := sdk.NewCoin(k.IntegerDenom(), sdkmath.NewInt(1)) if err := k.bk.SendCoinsFromAccountToModule( ctx, from, // sender borrowing @@ -219,7 +220,7 @@ func (k Keeper) sendExtendedCoins( // a SendCoins operation. Only SendCoinsFromModuleToAccount should check // blocked addrs which is done by the parent SendCoinsFromModuleToAccount // method. - carryCoin := sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1)) + carryCoin := sdk.NewCoin(k.IntegerDenom(), sdkmath.NewInt(1)) if err := k.bk.SendCoins( ctx, reserveAddr, @@ -252,16 +253,17 @@ func (k Keeper) sendExtendedCoins( // subFromFractionalBalance subtracts a fractional amount from the provided // current fractional balance, returning the new fractional balance and true if // an integer borrow is required. -func subFromFractionalBalance( +func (k Keeper) subFromFractionalBalance( currentFractionalBalance sdkmath.Int, amountToSub sdkmath.Int, ) (sdkmath.Int, bool) { + cf := k.ConversionFactor() // Enforce that currentFractionalBalance is not a full balance. - if currentFractionalBalance.GTE(types.ConversionFactor()) { + if currentFractionalBalance.GTE(cf) { panic("currentFractionalBalance must be less than ConversionFactor") } - if amountToSub.GTE(types.ConversionFactor()) { + if amountToSub.GTE(cf) { panic("amountToSub must be less than ConversionFactor") } @@ -274,7 +276,7 @@ func subFromFractionalBalance( // Borrowing 1 integer equivalent amount of fractional coins. We need to // add 1 integer equivalent amount to the fractional balance otherwise // the new fractional balance will be negative. - newFractionalBalance = newFractionalBalance.Add(types.ConversionFactor()) + newFractionalBalance = newFractionalBalance.Add(cf) } return newFractionalBalance, borrowRequired @@ -283,13 +285,14 @@ func subFromFractionalBalance( // addToFractionalBalance adds a fractional amount to the provided current // fractional balance, returning the new fractional balance and true if a carry // is required. -func addToFractionalBalance(currentFractionalBalance sdkmath.Int, amountToAdd sdkmath.Int) (sdkmath.Int, bool) { +func (k Keeper) addToFractionalBalance(currentFractionalBalance sdkmath.Int, amountToAdd sdkmath.Int) (sdkmath.Int, bool) { + cf := k.ConversionFactor() // Enforce that currentFractionalBalance is not a full balance. - if currentFractionalBalance.GTE(types.ConversionFactor()) { + if currentFractionalBalance.GTE(cf) { panic("currentFractionalBalance must be less than ConversionFactor") } - if amountToAdd.GTE(types.ConversionFactor()) { + if amountToAdd.GTE(cf) { panic("amountToAdd must be less than ConversionFactor") } @@ -297,11 +300,11 @@ func addToFractionalBalance(currentFractionalBalance sdkmath.Int, amountToAdd sd // New balance exceeds max fractional balance, so we need to carry it over // to the integer balance. - carryRequired := newFractionalBalance.GTE(types.ConversionFactor()) + carryRequired := newFractionalBalance.GTE(cf) if carryRequired { // Carry over to integer amount - newFractionalBalance = newFractionalBalance.Sub(types.ConversionFactor()) + newFractionalBalance = newFractionalBalance.Sub(cf) } return newFractionalBalance, carryRequired @@ -411,8 +414,9 @@ func (k Keeper) updateInsufficientFundsError( } // Check balance is sufficient - bal := k.SpendableCoin(ctx, addr, types.ExtendedCoinDenom()) - coin := sdk.NewCoin(types.ExtendedCoinDenom(), amt) + extendedDenom := k.ExtendedDenom() + bal := k.SpendableCoin(ctx, addr, extendedDenom) + coin := sdk.NewCoin(extendedDenom, amt) // TODO: This checks spendable coins and returns error with spendable // coins, not full balance. If GetBalance() is modified to return the diff --git a/x/precisebank/keeper/view.go b/x/precisebank/keeper/view.go index b83f6ca4e..2a75f660b 100644 --- a/x/precisebank/keeper/view.go +++ b/x/precisebank/keeper/view.go @@ -18,23 +18,25 @@ func (k Keeper) GetBalance( addr sdk.AccAddress, denom string, ) sdk.Coin { + extendedDenom := k.ExtendedDenom() ctx := sdk.UnwrapSDKContext(goCtx) // Module balance should display as empty for extended denom. Module // balances are **only** for the reserve which backs the fractional // balances. Returning the backing balances if querying extended denom would // result in a double counting of the fractional balances. - if denom == types.ExtendedCoinDenom() && addr.Equals(k.ak.GetModuleAddress(types.ModuleName)) { + if denom == extendedDenom && addr.Equals(k.ak.GetModuleAddress(types.ModuleName)) { return sdk.NewCoin(denom, sdkmath.ZeroInt()) } // Pass through to x/bank for denoms except ExtendedCoinDenom - if denom != types.ExtendedCoinDenom() { + if denom != extendedDenom { return k.bk.GetBalance(ctx, addr, denom) } + integerDenom := k.IntegerDenom() // x/bank for integer balance - full balance, including locked - integerCoins := k.bk.GetBalance(ctx, addr, types.IntegerCoinDenom()) + integerCoins := k.bk.GetBalance(ctx, addr, integerDenom) // x/precisebank for fractional balance fractionalAmount := k.GetFractionalBalance(ctx, addr) @@ -42,10 +44,10 @@ func (k Keeper) GetBalance( // (Integer * ConversionFactor) + Fractional fullAmount := integerCoins. Amount. - Mul(types.ConversionFactor()). + Mul(k.ConversionFactor()). Add(fractionalAmount) - return sdk.NewCoin(types.ExtendedCoinDenom(), fullAmount) + return sdk.NewCoin(extendedDenom, fullAmount) } func (k Keeper) IterateAccountBalances(ctx context.Context, account sdk.AccAddress, cb func(coin sdk.Coin) bool) { @@ -60,28 +62,30 @@ func (k Keeper) SpendableCoin( addr sdk.AccAddress, denom string, ) sdk.Coin { + extendedDenom := k.ExtendedDenom() ctx := sdk.UnwrapSDKContext(goCtx) // Same as GetBalance, extended denom balances are transparent to consumers. - if denom == types.ExtendedCoinDenom() && addr.Equals(k.ak.GetModuleAddress(types.ModuleName)) { + if denom == extendedDenom && addr.Equals(k.ak.GetModuleAddress(types.ModuleName)) { return sdk.NewCoin(denom, sdkmath.ZeroInt()) } // Pass through to x/bank for denoms except ExtendedCoinDenom - if denom != types.ExtendedCoinDenom() { + if denom != extendedDenom { return k.bk.SpendableCoin(ctx, addr, denom) } + integerDenom := k.IntegerDenom() // x/bank for integer balance - excluding locked - integerCoin := k.bk.SpendableCoin(ctx, addr, types.IntegerCoinDenom()) + integerCoin := k.bk.SpendableCoin(ctx, addr, integerDenom) // x/precisebank for fractional balance fractionalAmount := k.GetFractionalBalance(ctx, addr) // Spendable = (Integer * ConversionFactor) + Fractional fullAmount := integerCoin.Amount. - Mul(types.ConversionFactor()). + Mul(k.ConversionFactor()). Add(fractionalAmount) - return sdk.NewCoin(types.ExtendedCoinDenom(), fullAmount) + return sdk.NewCoin(extendedDenom, fullAmount) } diff --git a/x/precisebank/keeper/view_test.go b/x/precisebank/keeper/view_test.go index 6172a80dc..4796c249d 100644 --- a/x/precisebank/keeper/view_test.go +++ b/x/precisebank/keeper/view_test.go @@ -28,50 +28,50 @@ func TestKeeper_GetBalance(t *testing.T) { }{ { "extended denom - no fractional balance", - types.ExtendedCoinDenom(), + testExtendedDenom, // queried bank balance in uatom when querying for aatom - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.ZeroInt(), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_000_000_000_000)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_000_000_000_000)), }, { "extended denom - with fractional balance", - types.ExtendedCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testExtendedDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.NewInt(100), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_000_000_000_100)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_000_000_000_100)), }, { "extended denom - only fractional balance", - types.ExtendedCoinDenom(), + testExtendedDenom, // no coins in bank, only fractional balance sdk.NewCoins(), sdkmath.NewInt(100), - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(100)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(100)), }, { "extended denom - max fractional balance", - types.ExtendedCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), - types.ConversionFactor().SubRaw(1), + testExtendedDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), + testConversionFactor.SubRaw(1), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_999_999_999_999)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_999_999_999_999)), }, { "non-extended denom - uatom returns uatom", - types.IntegerCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testIntegerDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.ZeroInt(), - sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000)), + sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000)), }, { "non-extended denom - unaffected by fractional balance", - types.IntegerCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testIntegerDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.NewInt(100), - sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000)), + sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000)), }, { "unrelated denom - no fractional", @@ -97,19 +97,19 @@ func TestKeeper_GetBalance(t *testing.T) { tk.keeper.SetFractionalBalance(tk.ctx, addr, tt.giveFractionalBal) // Checks address if its a reserve denom - if tt.giveDenom == types.ExtendedCoinDenom() { + if tt.giveDenom == testExtendedDenom { tk.ak.EXPECT().GetModuleAddress(types.ModuleName). Return(authtypes.NewModuleAddress(types.ModuleName)). Once() } - if tt.giveDenom == types.ExtendedCoinDenom() { + if tt.giveDenom == testExtendedDenom { // No balance pass through tk.bk.EXPECT(). - GetBalance(tk.ctx, addr, types.IntegerCoinDenom()). + GetBalance(tk.ctx, addr, testIntegerDenom). RunAndReturn(func(_ context.Context, _ sdk.AccAddress, _ string) sdk.Coin { - amt := tt.giveBankBal.AmountOf(types.IntegerCoinDenom()) - return sdk.NewCoin(types.IntegerCoinDenom(), amt) + amt := tt.giveBankBal.AmountOf(testIntegerDenom) + return sdk.NewCoin(testIntegerDenom, amt) }). Once() } else { @@ -142,50 +142,50 @@ func TestKeeper_SpendableCoin(t *testing.T) { }{ { "extended denom - no fractional balance", - types.ExtendedCoinDenom(), + testExtendedDenom, // queried bank balance in uatom when querying for aatom - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.ZeroInt(), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_000_000_000_000)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_000_000_000_000)), }, { "extended denom - with fractional balance", - types.ExtendedCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testExtendedDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.NewInt(100), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_000_000_000_100)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_000_000_000_100)), }, { "extended denom - only fractional balance", - types.ExtendedCoinDenom(), + testExtendedDenom, // no coins in bank, only fractional balance sdk.NewCoins(), sdkmath.NewInt(100), - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(100)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(100)), }, { "extended denom - max fractional balance", - types.ExtendedCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), - types.ConversionFactor().SubRaw(1), + testExtendedDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), + testConversionFactor.SubRaw(1), // integer + fractional - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(1000_999_999_999_999)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(1000_999_999_999_999)), }, { "non-extended denom - uatom returns uatom", - types.IntegerCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testIntegerDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.ZeroInt(), - sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000)), + sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000)), }, { "non-extended denom - unaffected by fractional balance", - types.IntegerCoinDenom(), - sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000))), + testIntegerDenom, + sdk.NewCoins(sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000))), sdkmath.NewInt(100), - sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1000)), + sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1000)), }, { "unrelated denom - no fractional", @@ -212,19 +212,19 @@ func TestKeeper_SpendableCoin(t *testing.T) { tk.keeper.SetFractionalBalance(tk.ctx, addr, tt.giveFractionalBal) // If its a reserve denom, module address is checked - if tt.giveDenom == types.ExtendedCoinDenom() { + if tt.giveDenom == testExtendedDenom { tk.ak.EXPECT().GetModuleAddress(types.ModuleName). Return(authtypes.NewModuleAddress(types.ModuleName)). Once() } - if tt.giveDenom == types.ExtendedCoinDenom() { + if tt.giveDenom == testExtendedDenom { // No balance pass through tk.bk.EXPECT(). - SpendableCoin(tk.ctx, addr, types.IntegerCoinDenom()). + SpendableCoin(tk.ctx, addr, testIntegerDenom). RunAndReturn(func(_ context.Context, _ sdk.AccAddress, _ string) sdk.Coin { - amt := tt.giveBankBal.AmountOf(types.IntegerCoinDenom()) - return sdk.NewCoin(types.IntegerCoinDenom(), amt) + amt := tt.giveBankBal.AmountOf(testIntegerDenom) + return sdk.NewCoin(testIntegerDenom, amt) }). Once() } else { @@ -264,13 +264,13 @@ func TestHiddenReserve(t *testing.T) { }{ { "aatom", - types.ExtendedCoinDenom(), - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.ZeroInt()), + testExtendedDenom, + sdk.NewCoin(testExtendedDenom, sdkmath.ZeroInt()), }, { "uatom", - types.IntegerCoinDenom(), - sdk.NewCoin(types.IntegerCoinDenom(), sdkmath.NewInt(1)), + testIntegerDenom, + sdk.NewCoin(testIntegerDenom, sdkmath.NewInt(1)), }, { "unrelated denom", @@ -282,7 +282,7 @@ func TestHiddenReserve(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 2 calls for GetBalance and SpendableCoin, only for reserve coins - if tt.denom == types.ExtendedCoinDenom() { + if tt.denom == testExtendedDenom { tk.ak.EXPECT().GetModuleAddress(types.ModuleName). Return(moduleAddr). Twice() diff --git a/x/precisebank/testutil/fractional_balances.go b/x/precisebank/testutil/fractional_balances.go index 927183af3..d06fe71a4 100644 --- a/x/precisebank/testutil/fractional_balances.go +++ b/x/precisebank/testutil/fractional_balances.go @@ -44,6 +44,7 @@ func randAccAddress() sdk.AccAddress { func GenerateEqualFractionalBalances( t *testing.T, count int, + conversionFactor sdkmath.Int, ) types.FractionalBalances { t.Helper() @@ -69,11 +70,11 @@ func GenerateEqualFractionalBalances( // If it's 0, Validate() will error. // Why start at 2 instead of 1? We want to make sure its divisible // for the last account, more details below. - amt := randRange(2, types.ConversionFactor().Int64()) + amt := randRange(2, conversionFactor.Int64()) amtInt := sdkmath.NewInt(amt) fb := types.NewFractionalBalance(addr, amtInt) - require.NoError(t, fb.Validate()) + require.NoError(t, fb.Validate(conversionFactor)) fbs[i] = fb @@ -96,9 +97,9 @@ func GenerateEqualFractionalBalances( // Note that we only have this issue in tests since we want to calculate a // new valid remainder, but we only validate in the actual code. - amt := types.ConversionFactor(). - Sub(sum.Mod(types.ConversionFactor())). - Mod(types.ConversionFactor()) + amt := conversionFactor. + Sub(sum.Mod(conversionFactor)). + Mod(conversionFactor) // We only want to generate VALID FractionalBalances - zero would not be // valid, so let's just borrow half of the previous amount. We generated @@ -110,7 +111,7 @@ func GenerateEqualFractionalBalances( } fb := types.NewFractionalBalance(addr, amt) - require.NoError(t, fb.Validate()) + require.NoError(t, fb.Validate(conversionFactor)) fbs[count-1] = fb @@ -119,10 +120,10 @@ func GenerateEqualFractionalBalances( for _, fb := range fbs { verificationSum = verificationSum.Add(fb.Amount) } - require.True(t, verificationSum.Mod(types.ConversionFactor()).IsZero()) + require.True(t, verificationSum.Mod(conversionFactor).IsZero()) // Also make sure no duplicate addresses - require.NoError(t, fbs.Validate()) + require.NoError(t, fbs.Validate(conversionFactor)) return fbs } @@ -135,6 +136,7 @@ func GenerateEqualFractionalBalances( func GenerateEqualFractionalBalancesWithRemainder( t *testing.T, count int, + conversionFactor sdkmath.Int, ) (types.FractionalBalances, sdkmath.Int) { t.Helper() @@ -143,7 +145,7 @@ func GenerateEqualFractionalBalancesWithRemainder( countWithRemainder := count + 1 // Generate 1 additional FractionalBalance so we can use one as remainder - fbs := GenerateEqualFractionalBalances(t, countWithRemainder) + fbs := GenerateEqualFractionalBalances(t, countWithRemainder, conversionFactor) // Use the last one as remainder remainder := fbs[countWithRemainder-1].Amount diff --git a/x/precisebank/types/extended_balance.go b/x/precisebank/types/extended_balance.go index 50825f103..0de7853c3 100644 --- a/x/precisebank/types/extended_balance.go +++ b/x/precisebank/types/extended_balance.go @@ -1,25 +1,26 @@ package types import ( + evmtypes "github.com/cosmos/evm/x/vm/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -// SumExtendedCoin returns a sdk.Coin of extended coin denomination -// with all integer and fractional amounts combined. e.g. if amount contains -// both coins of integer denom and extended denom, this will return the total -// amount in extended coins. This is intended to get the full value to emit in -// events. -func SumExtendedCoin(amt sdk.Coins) sdk.Coin { - // uatom converted to aatom - integerAmount := amt.AmountOf(IntegerCoinDenom()).Mul(ConversionFactor()) - // aatom as is - extendedAmount := amt.AmountOf(ExtendedCoinDenom()) +// SumExtendedCoin returns a sdk.Coin of extended coin denomination with all +// integer and fractional amounts combined. Callers must supply the coin info +// describing the EVM denom configuration. +func SumExtendedCoin(amt sdk.Coins, info evmtypes.EvmCoinInfo) sdk.Coin { + conversionFactor := info.DecimalsOrDefault().ConversionFactor() + integerDenom := info.DenomOrDefault() + extendedDenom := info.ExtendedDenomOrDefault() + + integerAmount := amt.AmountOf(integerDenom).Mul(conversionFactor) + extendedAmount := amt.AmountOf(extendedDenom) - // total of uatom and aatom amounts fullEmissionAmount := integerAmount.Add(extendedAmount) return sdk.NewCoin( - ExtendedCoinDenom(), + extendedDenom, fullEmissionAmount, ) } diff --git a/x/precisebank/types/extended_balance_test.go b/x/precisebank/types/extended_balance_test.go index d0a013f1b..3f9edc641 100644 --- a/x/precisebank/types/extended_balance_test.go +++ b/x/precisebank/types/extended_balance_test.go @@ -5,9 +5,7 @@ import ( "github.com/stretchr/testify/require" - testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/x/precisebank/types" - evmtypes "github.com/cosmos/evm/x/vm/types" sdkmath "cosmossdk.io/math" @@ -15,13 +13,6 @@ import ( ) func TestSumExtendedCoin(t *testing.T) { - coinInfo := testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID] - configurator := evmtypes.NewEVMConfigurator() - err := configurator. - WithEVMCoinInfo(coinInfo). - Configure() - require.NoError(t, err) - tests := []struct { name string amt sdk.Coins @@ -30,31 +21,31 @@ func TestSumExtendedCoin(t *testing.T) { { "empty", sdk.NewCoins(), - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.ZeroInt()), + sdk.NewCoin(testExtendedDenom, sdkmath.ZeroInt()), }, { "only integer", - sdk.NewCoins(sdk.NewInt64Coin(types.IntegerCoinDenom(), 100)), - sdk.NewCoin(types.ExtendedCoinDenom(), types.ConversionFactor().MulRaw(100)), + sdk.NewCoins(sdk.NewInt64Coin(testIntegerDenom, 100)), + sdk.NewCoin(testExtendedDenom, testConversionFactor.MulRaw(100)), }, { "only extended", - sdk.NewCoins(sdk.NewInt64Coin(types.ExtendedCoinDenom(), 100)), - sdk.NewCoin(types.ExtendedCoinDenom(), sdkmath.NewInt(100)), + sdk.NewCoins(sdk.NewInt64Coin(testExtendedDenom, 100)), + sdk.NewCoin(testExtendedDenom, sdkmath.NewInt(100)), }, { "integer and extended", sdk.NewCoins( - sdk.NewInt64Coin(types.IntegerCoinDenom(), 100), - sdk.NewInt64Coin(types.ExtendedCoinDenom(), 100), + sdk.NewInt64Coin(testIntegerDenom, 100), + sdk.NewInt64Coin(testExtendedDenom, 100), ), - sdk.NewCoin(types.ExtendedCoinDenom(), types.ConversionFactor().MulRaw(100).AddRaw(100)), + sdk.NewCoin(testExtendedDenom, testConversionFactor.MulRaw(100).AddRaw(100)), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - extVal := types.SumExtendedCoin(tt.amt) + extVal := types.SumExtendedCoin(tt.amt, testCoinInfo) require.Equal(t, tt.want, extVal) }) } diff --git a/x/precisebank/types/fractional_balance.go b/x/precisebank/types/fractional_balance.go index 6b2f883ff..6b3ae7aae 100644 --- a/x/precisebank/types/fractional_balance.go +++ b/x/precisebank/types/fractional_balance.go @@ -3,65 +3,35 @@ package types import ( "fmt" - evmtypes "github.com/cosmos/evm/x/vm/types" - sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) -// ConversionFactor returns a copy of the conversionFactor used to convert the -// fractional balance to integer balances. This is also 1 greater than the max -// valid fractional amount (999_999_999_999): -// 0 < FractionalBalance < conversionFactor -func ConversionFactor() sdkmath.Int { - return sdkmath.NewIntFromBigInt(evmtypes.GetEVMCoinDecimals().ConversionFactor().BigInt()) -} - -// IntegerCoinDenom is the denomination for integer coins that are managed by -// x/bank. This is the "true" denomination of the coin, and is also used for -// the reserve to back all fractional coins. -func IntegerCoinDenom() string { - return evmtypes.GetEVMCoinDenom() -} - -// ExtendedCoinDenom is the denomination for the extended IntegerCoinDenom. This -// not only represents the fractional balance, but the total balance of -// integer + fractional balances. -func ExtendedCoinDenom() string { - return evmtypes.GetEVMCoinExtendedDenom() -} - -// IsExtendedDenomSameAsIntegerDenom returns true if the extended denom is the same as the integer denom -// This happens in 18-decimal chains where both denoms are identical -func IsExtendedDenomSameAsIntegerDenom() bool { - return IntegerCoinDenom() == ExtendedCoinDenom() -} - // FractionalBalance returns a new FractionalBalance with the given address and // amount. func NewFractionalBalance(address string, amount sdkmath.Int) FractionalBalance { - return FractionalBalance{ - Address: address, + return FractionalBalance{ + Address: address, Amount: amount, } } // Validate returns an error if the FractionalBalance has an invalid address or // negative amount. -func (fb FractionalBalance) Validate() error { +func (fb FractionalBalance) Validate(conversionFactor sdkmath.Int) error { if _, err := sdk.AccAddressFromBech32(fb.Address); err != nil { return err } // Validate the amount with the FractionalAmount wrapper - return ValidateFractionalAmount(fb.Amount) + return ValidateFractionalAmount(fb.Amount, conversionFactor) } // ValidateFractionalAmount checks if an sdkmath.Int is a valid fractional // amount, ensuring it is positive and less than or equal to the maximum // fractional amount. -func ValidateFractionalAmount(amt sdkmath.Int) error { +func ValidateFractionalAmount(amt, conversionFactor sdkmath.Int) error { if amt.IsNil() { return fmt.Errorf("nil amount") } @@ -70,8 +40,8 @@ func ValidateFractionalAmount(amt sdkmath.Int) error { return fmt.Errorf("non-positive amount %v", amt) } - if amt.GTE(ConversionFactor()) { - return fmt.Errorf("amount %v exceeds max of %v", amt, ConversionFactor().SubRaw(1)) + if amt.GTE(conversionFactor) { + return fmt.Errorf("amount %v exceeds max of %v", amt, conversionFactor.SubRaw(1)) } return nil diff --git a/x/precisebank/types/fractional_balance_test.go b/x/precisebank/types/fractional_balance_test.go index 8bfc8c730..bcec32a1f 100644 --- a/x/precisebank/types/fractional_balance_test.go +++ b/x/precisebank/types/fractional_balance_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "math/big" "testing" @@ -12,7 +13,7 @@ import ( ) func TestConversionFactor_Immutable(t *testing.T) { - cf1 := types.ConversionFactor() + cf1 := testCoinInfo.DecimalsOrDefault().ConversionFactor() origInt64 := cf1.Int64() // Get the internal pointer to the big.Int without copying @@ -24,14 +25,14 @@ func TestConversionFactor_Immutable(t *testing.T) { require.Equal(t, origInt64+5, internalBigInt.Int64()) // Fetch the max amount again - cf2 := types.ConversionFactor() + cf2 := testCoinInfo.DecimalsOrDefault().ConversionFactor() require.Equal(t, origInt64, cf2.Int64(), "conversion factor should be immutable") } func TestConversionFactor_Copied(t *testing.T) { - max1 := types.ConversionFactor().BigIntMut() - max2 := types.ConversionFactor().BigIntMut() + max1 := testCoinInfo.DecimalsOrDefault().ConversionFactor().BigIntMut() + max2 := testCoinInfo.DecimalsOrDefault().ConversionFactor().BigIntMut() // Checks that the returned two pointers do not reference the same object require.NotSame(t, max1, max2, "max fractional amount should be copied") @@ -41,7 +42,7 @@ func TestConversionFactor(t *testing.T) { require.Equal( t, sdkmath.NewInt(1_000_000_000_000), - types.ConversionFactor(), + testCoinInfo.DecimalsOrDefault().ConversionFactor(), "conversion factor should have 12 decimal points", ) } @@ -97,7 +98,7 @@ func TestFractionalBalance_Validate(t *testing.T) { { "valid - max balance", "cosmos1gpxd677pp8zr97xvy3pmgk70a9vcpagsprcjap", - types.ConversionFactor().SubRaw(1), + testMaxFractionalAmount(), "", }, { @@ -133,21 +134,21 @@ func TestFractionalBalance_Validate(t *testing.T) { { "invalid - max amount + 1", "cosmos1gpxd677pp8zr97xvy3pmgk70a9vcpagsprcjap", - types.ConversionFactor(), - "amount 1000000000000 exceeds max of 999999999999", + testConversionFactor, + fmt.Sprintf("amount %s exceeds max of %s", testConversionFactor.String(), testMaxFractionalAmount().String()), }, { "invalid - much more than max amount", "cosmos1gpxd677pp8zr97xvy3pmgk70a9vcpagsprcjap", - sdkmath.NewInt(100000000000_000), - "amount 100000000000000 exceeds max of 999999999999", + testConversionFactor.MulRaw(100), + fmt.Sprintf("amount %s exceeds max of %s", testConversionFactor.MulRaw(100).String(), testMaxFractionalAmount().String()), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fb := types.NewFractionalBalance(tt.giveAddress, tt.giveAmount) - err := fb.Validate() + err := fb.Validate(testConversionFactor) if tt.wantErr == "" { require.NoError(t, err) diff --git a/x/precisebank/types/fractional_balances.go b/x/precisebank/types/fractional_balances.go index 656e47b32..331e6f3fb 100644 --- a/x/precisebank/types/fractional_balances.go +++ b/x/precisebank/types/fractional_balances.go @@ -11,12 +11,12 @@ import ( type FractionalBalances []FractionalBalance // Validate returns an error if any FractionalBalance in the slice is invalid. -func (fbs FractionalBalances) Validate() error { +func (fbs FractionalBalances) Validate(conversionFactor sdkmath.Int) error { seenAddresses := make(map[string]struct{}) for _, fb := range fbs { // Individual FractionalBalance validation - if err := fb.Validate(); err != nil { + if err := fb.Validate(conversionFactor); err != nil { return fmt.Errorf("invalid fractional balance for %s: %w", fb.Address, err) } diff --git a/x/precisebank/types/fractional_balances_test.go b/x/precisebank/types/fractional_balances_test.go index 00ea4ac4d..abf3ad3d0 100644 --- a/x/precisebank/types/fractional_balances_test.go +++ b/x/precisebank/types/fractional_balances_test.go @@ -75,7 +75,7 @@ func TestFractionalBalances_Validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.fbs.Validate() + err := tt.fbs.Validate(testConversionFactor) if tt.wantErr == "" { require.NoError(t, err) return diff --git a/x/precisebank/types/genesis.go b/x/precisebank/types/genesis.go index 167d94b58..406ddbd3b 100644 --- a/x/precisebank/types/genesis.go +++ b/x/precisebank/types/genesis.go @@ -24,9 +24,13 @@ func DefaultGenesisState() *GenesisState { // Validate performs basic validation of genesis data returning an error for // any failed validation criteria. -func (gs *GenesisState) Validate() error { +func (gs *GenesisState) Validate(conversionFactor sdkmath.Int) error { + if !conversionFactor.IsPositive() { + return fmt.Errorf("invalid conversion factor: %s", conversionFactor) + } + // Validate all FractionalBalances - if err := gs.Balances.Validate(); err != nil { + if err := gs.Balances.Validate(conversionFactor); err != nil { return fmt.Errorf("invalid balances: %w", err) } @@ -39,8 +43,8 @@ func (gs *GenesisState) Validate() error { return fmt.Errorf("negative remainder amount %s", gs.Remainder) } - if gs.Remainder.GTE(ConversionFactor()) { - return fmt.Errorf("remainder %v exceeds max of %v", gs.Remainder, ConversionFactor().SubRaw(1)) + if gs.Remainder.GTE(conversionFactor) { + return fmt.Errorf("remainder %v exceeds max of %v", gs.Remainder, conversionFactor.SubRaw(1)) } // Determine if sum(fractionalBalances) + remainder = whole integer value @@ -48,14 +52,14 @@ func (gs *GenesisState) Validate() error { sum := gs.Balances.SumAmount() sumWithRemainder := sum.Add(gs.Remainder) - offBy := sumWithRemainder.Mod(ConversionFactor()) + offBy := sumWithRemainder.Mod(conversionFactor) if !offBy.IsZero() { return fmt.Errorf( "sum of fractional balances %v + remainder %v is not a multiple of %v", sum, gs.Remainder, - ConversionFactor(), + conversionFactor, ) } diff --git a/x/precisebank/types/genesis_test.go b/x/precisebank/types/genesis_test.go index bd92f3e07..4e565635c 100644 --- a/x/precisebank/types/genesis_test.go +++ b/x/precisebank/types/genesis_test.go @@ -5,10 +5,8 @@ import ( "github.com/stretchr/testify/require" - testconstants "github.com/cosmos/evm/testutil/constants" "github.com/cosmos/evm/x/precisebank/testutil" "github.com/cosmos/evm/x/precisebank/types" - evmtypes "github.com/cosmos/evm/x/vm/types" sdkmath "cosmossdk.io/math" @@ -44,7 +42,7 @@ func TestGenesisStateValidate_Basic(t *testing.T) { types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), }, - types.ConversionFactor().SubRaw(1), + testConversionFactor.SubRaw(1), ), "", }, @@ -104,7 +102,7 @@ func TestGenesisStateValidate_Basic(t *testing.T) { types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), sdkmath.NewInt(1)), }, - types.ConversionFactor(), + testConversionFactor, ), "remainder 1000000000000 exceeds max of 999999999999", }, @@ -112,7 +110,7 @@ func TestGenesisStateValidate_Basic(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(tt *testing.T) { - err := tc.genesisState.Validate() + err := tc.genesisState.Validate(testConversionFactor) if tc.wantErr == "" { require.NoError(tt, err) @@ -140,7 +138,7 @@ func TestGenesisStateValidate_Total(t *testing.T) { { "valid - non-zero balances, zero remainder", func() *types.GenesisState { - fbs := testutil.GenerateEqualFractionalBalances(t, 100) + fbs := testutil.GenerateEqualFractionalBalances(t, 100, testConversionFactor) require.Len(t, fbs, 100) return types.NewGenesisState(fbs, sdkmath.ZeroInt()) @@ -150,7 +148,7 @@ func TestGenesisStateValidate_Total(t *testing.T) { { "valid - non-zero balances, non-zero remainder", func() *types.GenesisState { - fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, 100) + fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, 100, testConversionFactor) require.Len(t, fbs, 100) require.NotZero(t, remainder.Int64()) @@ -164,7 +162,7 @@ func TestGenesisStateValidate_Total(t *testing.T) { { "invalid - non-zero balances, invalid remainder", func() *types.GenesisState { - fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, 100) + fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, 100, testConversionFactor) require.Len(t, fbs, 100) require.NotZero(t, remainder.Int64()) @@ -187,7 +185,7 @@ func TestGenesisStateValidate_Total(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(tt *testing.T) { - err := tc.buildGenesisState().Validate() + err := tc.buildGenesisState().Validate(testConversionFactor) if tc.containsErr == "" { require.NoError(tt, err) @@ -215,28 +213,28 @@ func TestGenesisState_TotalAmountWithRemainder(t *testing.T) { { "non-empty balances, zero remainder", types.FractionalBalances{ - types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().QuoRaw(2)), - types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().QuoRaw(2)), + types.NewFractionalBalance(sdk.AccAddress{1}.String(), testConversionFactor.QuoRaw(2)), + types.NewFractionalBalance(sdk.AccAddress{2}.String(), testConversionFactor.QuoRaw(2)), }, sdkmath.ZeroInt(), - types.ConversionFactor(), + testConversionFactor, }, { "non-empty balances, 1 remainder", types.FractionalBalances{ - types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().QuoRaw(2)), - types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().QuoRaw(2).SubRaw(1)), + types.NewFractionalBalance(sdk.AccAddress{1}.String(), testConversionFactor.QuoRaw(2)), + types.NewFractionalBalance(sdk.AccAddress{2}.String(), testConversionFactor.QuoRaw(2).SubRaw(1)), }, sdkmath.OneInt(), - types.ConversionFactor(), + testConversionFactor, }, { "non-empty balances, max remainder", types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.OneInt()), }, - types.ConversionFactor().SubRaw(1), - types.ConversionFactor(), + testConversionFactor.SubRaw(1), + testConversionFactor, }, } @@ -247,7 +245,7 @@ func TestGenesisState_TotalAmountWithRemainder(t *testing.T) { tt.giveRemainder, ) - require.NoError(t, gs.Validate(), "genesis state should be valid before testing total amount") + require.NoError(t, gs.Validate(testConversionFactor), "genesis state should be valid before testing total amount") totalAmt := gs.TotalAmountWithRemainder() require.Equal(t, tt.wantTotalAmountWithRemainder, totalAmt, "total amount should be balances + remainder") @@ -256,12 +254,6 @@ func TestGenesisState_TotalAmountWithRemainder(t *testing.T) { } func FuzzGenesisStateValidate_NonZeroRemainder(f *testing.F) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - err := configurator.Configure() - require.NoError(f, err) - f.Add(5) f.Add(100) f.Add(30) @@ -272,23 +264,17 @@ func FuzzGenesisStateValidate_NonZeroRemainder(f *testing.F) { t.Skip("count < 2") } - fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, count) + fbs, remainder := testutil.GenerateEqualFractionalBalancesWithRemainder(t, count, testConversionFactor) t.Logf("count: %v", count) t.Logf("remainder: %v", remainder) gs := types.NewGenesisState(fbs, remainder) - require.NoError(t, gs.Validate()) + require.NoError(t, gs.Validate(testConversionFactor)) }) } func FuzzGenesisStateValidate_ZeroRemainder(f *testing.F) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.SixDecimalsChainID]) - err := configurator.Configure() - require.NoError(f, err) - f.Add(5) f.Add(100) f.Add(30) @@ -299,9 +285,9 @@ func FuzzGenesisStateValidate_ZeroRemainder(f *testing.F) { t.Skip("count < 2") } - fbs := testutil.GenerateEqualFractionalBalances(t, count) + fbs := testutil.GenerateEqualFractionalBalances(t, count, testConversionFactor) gs := types.NewGenesisState(fbs, sdkmath.ZeroInt()) - require.NoError(t, gs.Validate()) + require.NoError(t, gs.Validate(testConversionFactor)) }) } diff --git a/x/precisebank/types/test_helpers_test.go b/x/precisebank/types/test_helpers_test.go new file mode 100644 index 000000000..a8a369b4a --- /dev/null +++ b/x/precisebank/types/test_helpers_test.go @@ -0,0 +1,18 @@ +package types_test + +import ( + testconstants "github.com/cosmos/evm/testutil/constants" + + sdkmath "cosmossdk.io/math" +) + +var ( + testCoinInfo = testconstants.ChainsCoinInfo[testconstants.TwelveDecimalsChainID.EVMChainID] + testConversionFactor = testCoinInfo.DecimalsOrDefault().ConversionFactor() + testIntegerDenom = testCoinInfo.DenomOrDefault() + testExtendedDenom = testCoinInfo.ExtendedDenomOrDefault() +) + +func testMaxFractionalAmount() sdkmath.Int { + return testConversionFactor.SubRaw(1) +} diff --git a/x/vm/genesis.go b/x/vm/genesis.go index ffcaafb30..013be1224 100644 --- a/x/vm/genesis.go +++ b/x/vm/genesis.go @@ -66,7 +66,7 @@ func InitGenesis( coinInfo := k.GetEvmCoinInfo(ctx) initializer.Do(func() { - SetGlobalConfigVariables(coinInfo) + SetGlobalConfigVariables(ctx, k, coinInfo) }) if err := k.AddPreinstalls(ctx, data.Preinstalls); err != nil { diff --git a/x/vm/keeper/chain_config.go b/x/vm/keeper/chain_config.go new file mode 100644 index 000000000..41bf07468 --- /dev/null +++ b/x/vm/keeper/chain_config.go @@ -0,0 +1,48 @@ +package keeper + +import ( + "math/big" + + ethparams "github.com/ethereum/go-ethereum/params" + + "github.com/cosmos/evm/x/vm/types" +) + +// ChainConfig returns the x/vm ChainConfig kept in runtime config. +func (k Keeper) ChainConfig() *types.ChainConfig { + cfg := k.RuntimeConfig() + if cfg == nil { + return nil + } + return cfg.ChainConfig() +} + +// EthChainConfig returns the go-ethereum ChainConfig kept in runtime config. +func (k Keeper) EthChainConfig() *ethparams.ChainConfig { + cfg := k.RuntimeConfig() + if cfg == nil { + return nil + } + return cfg.EthChainConfig() +} + +// EvmChainID returns the chain ID as a big.Int derived from the runtime config. +func (k Keeper) EvmChainID() *big.Int { + chainCfg := k.ChainConfig() + if chainCfg == nil { + return big.NewInt(int64(types.DefaultEVMChainID)) //nolint:gosec // won't exceed int64 + } + return big.NewInt(int64(chainCfg.ChainId)) //nolint:gosec // won't exceed int64 +} + +// effectiveEthChainConfig returns the runtime chain config if present, or falls back to the +// default configuration. This helper is intended for internal use. +func (k Keeper) effectiveEthChainConfig() *ethparams.ChainConfig { + if cfg := k.EthChainConfig(); cfg != nil { + return cfg + } + if chainCfg := k.ChainConfig(); chainCfg != nil { + return chainCfg.EthereumConfig(nil) + } + return types.DefaultChainConfig(types.DefaultEVMChainID).EthereumConfig(nil) +} diff --git a/x/vm/keeper/coin_info.go b/x/vm/keeper/coin_info.go index 78399afad..ea346ca15 100644 --- a/x/vm/keeper/coin_info.go +++ b/x/vm/keeper/coin_info.go @@ -5,6 +5,8 @@ import ( "github.com/cosmos/evm/x/vm/types" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -73,3 +75,49 @@ func (k Keeper) SetEvmCoinInfo(ctx sdk.Context, coinInfo types.EvmCoinInfo) erro store.Set(types.KeyPrefixEvmCoinInfo, bz) return nil } + +// EvmCoinInfo returns the EvmCoinInfo kept in runtime config. +func (k Keeper) EvmCoinInfo() types.EvmCoinInfo { + cfg := k.RuntimeConfig() + if cfg == nil { + return types.EvmCoinInfo{} + } + return cfg.EvmCoinInfo() +} + +// EvmDenom returns the base EVM denom configured for the chain. +func (k Keeper) EvmDenom() string { + return k.EvmCoinInfo().DenomOrDefault() +} + +// EvmExtendedDenom returns the extended denom used for on-chain accounting. +func (k Keeper) EvmExtendedDenom() string { + info := k.EvmCoinInfo() + if info.ExtendedDenom != "" { + return info.ExtendedDenom + } + return info.DenomOrDefault() +} + +// EvmDisplayDenom returns the display denom associated with the EVM coin. +func (k Keeper) EvmDisplayDenom() string { + info := k.EvmCoinInfo() + if info.DisplayDenom != "" { + return info.DisplayDenom + } + if info.Denom == "" || info.Denom == types.DefaultEVMDenom { + return types.DefaultEVMDisplayDenom + } + return info.Denom +} + +// EvmDecimals returns the decimals configured for the EVM coin. +func (k Keeper) EvmDecimals() types.Decimals { + return k.EvmCoinInfo().DecimalsOrDefault() +} + +// EvmConversionFactor returns the conversion factor between the configured +// decimals and the canonical 18-decimal representation. +func (k Keeper) EvmConversionFactor() sdkmath.Int { + return k.EvmDecimals().ConversionFactor() +} diff --git a/x/vm/keeper/config.go b/x/vm/keeper/config.go index 530050869..faf4a3074 100644 --- a/x/vm/keeper/config.go +++ b/x/vm/keeper/config.go @@ -48,7 +48,8 @@ func (k *Keeper) TxConfig(ctx sdk.Context, txHash common.Hash) statedb.TxConfig // module parameters. The config generated uses the default JumpTable from the EVM. func (k Keeper) VMConfig(ctx sdk.Context, _ core.Message, cfg *statedb.EVMConfig, tracer *tracing.Hooks) vm.Config { noBaseFee := true - if types.IsLondon(types.GetEthChainConfig(), ctx.BlockHeight()) { + ethCfg := k.effectiveEthChainConfig() + if types.IsLondon(ethCfg, ctx.BlockHeight()) { noBaseFee = cfg.FeeMarketParams.NoBaseFee } diff --git a/x/vm/keeper/grpc_query.go b/x/vm/keeper/grpc_query.go index b9d1cc5a6..cbf58c1c9 100644 --- a/x/vm/keeper/grpc_query.go +++ b/x/vm/keeper/grpc_query.go @@ -255,7 +255,8 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms nonce := k.GetNonce(ctx, args.GetFrom()) args.Nonce = (*hexutil.Uint64)(&nonce) - if err := args.CallDefaults(req.GasCap, cfg.BaseFee, types.GetEthChainConfig().ChainID); err != nil { + chainCfg := k.effectiveEthChainConfig() + if err := args.CallDefaults(req.GasCap, cfg.BaseFee, chainCfg.ChainID); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -346,7 +347,8 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest if args.Gas == nil { args.Gas = new(hexutil.Uint64) } - if err := args.CallDefaults(req.GasCap, cfg.BaseFee, types.GetEthChainConfig().ChainID); err != nil { + chainCfg := k.effectiveEthChainConfig() + if err := args.CallDefaults(req.GasCap, cfg.BaseFee, chainCfg.ChainID); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -355,7 +357,10 @@ func (k Keeper) EstimateGasInternal(c context.Context, req *types.EthCallRequest // Recap the highest gas limit with account's available balance. if msg.GasFeeCap.BitLen() != 0 { - baseDenom := types.GetEVMCoinDenom() + baseDenom := k.EvmCoinInfo().Denom + if baseDenom == "" { + baseDenom = types.DefaultEVMDenom + } balance := k.bankWrapper.SpendableCoin(ctx, sdk.AccAddress(args.From.Bytes()), baseDenom) available := balance.Amount @@ -541,7 +546,8 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ cfg.BaseFee = baseFee } - signer := ethtypes.MakeSigner(types.GetEthChainConfig(), big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here + chainCfg := k.effectiveEthChainConfig() + signer := ethtypes.MakeSigner(chainCfg, big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here txConfig := statedb.NewEmptyTxConfig() // gas used at this point corresponds to GetProposerAddress & CalculateBaseFee @@ -633,7 +639,8 @@ func (k Keeper) TraceBlock(c context.Context, req *types.QueryTraceBlockRequest) cfg.BaseFee = baseFee } - signer := ethtypes.MakeSigner(types.GetEthChainConfig(), big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here + chainCfg := k.effectiveEthChainConfig() + signer := ethtypes.MakeSigner(chainCfg, big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here txsLength := len(req.Txs) results := make([]*types.TxTraceResult, 0, txsLength) @@ -717,7 +724,8 @@ func (k Keeper) TraceCall(c context.Context, req *types.QueryTraceCallRequest) ( nonce := k.GetNonce(ctx, args.GetFrom()) args.Nonce = (*hexutil.Uint64)(&nonce) // Fill in default values for missing transaction fields (gas, gasPrice, value, etc.) - if err := args.CallDefaults(req.GasCap, baseFee, types.GetEthChainConfig().ChainID); err != nil { + chainCfg := k.effectiveEthChainConfig() + if err := args.CallDefaults(req.GasCap, baseFee, chainCfg.ChainID); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } msg := args.ToMessage(baseFee, true, true) @@ -774,6 +782,7 @@ func (k *Keeper) traceTxWithMsg( err error timeout = defaultTraceTimeout ) + chainCfg := k.effectiveEthChainConfig() if traceConfig == nil { traceConfig = &types.TraceConfig{} @@ -785,7 +794,7 @@ func (k *Keeper) traceTxWithMsg( } if traceConfig.Overrides != nil { - overrides = traceConfig.Overrides.EthereumConfig(types.GetEthChainConfig().ChainID) + overrides = traceConfig.Overrides.EthereumConfig(chainCfg.ChainID) } logConfig := logger.Config{ @@ -815,8 +824,7 @@ func (k *Keeper) traceTxWithMsg( if traceConfig.TracerJsonConfig != "" { cfg = json.RawMessage(traceConfig.TracerJsonConfig) } - if tracer, err = tracers.DefaultDirectory.New(traceConfig.Tracer, tCtx, cfg, - types.GetEthChainConfig()); err != nil { + if tracer, err = tracers.DefaultDirectory.New(traceConfig.Tracer, tCtx, cfg, chainCfg); err != nil { return nil, 0, status.Error(codes.Internal, err.Error()) } } @@ -879,11 +887,23 @@ func (k Keeper) GlobalMinGasPrice(c context.Context, _ *types.QueryGlobalMinGasP // Config implements the Query/Config gRPC method func (k Keeper) Config(_ context.Context, _ *types.QueryConfigRequest) (*types.QueryConfigResponse, error) { - config := types.GetChainConfig() - config.Denom = types.GetEVMCoinDenom() - config.Decimals = uint64(types.GetEVMCoinDecimals()) + chainCfg := k.ChainConfig() + if chainCfg == nil { + chainCfg = types.DefaultChainConfig(types.DefaultEVMChainID) + } else { + copied := *chainCfg + chainCfg = &copied + } + + coinInfo := k.EvmCoinInfo() + if coinInfo.Denom != "" { + chainCfg.Denom = coinInfo.Denom + } + if coinInfo.Decimals != 0 { + chainCfg.Decimals = uint64(coinInfo.Decimals) + } - return &types.QueryConfigResponse{Config: config}, nil + return &types.QueryConfigResponse{Config: chainCfg}, nil } // buildTraceCtx builds a context for simulating or tracing transactions by: diff --git a/x/vm/keeper/keeper.go b/x/vm/keeper/keeper.go index c93bee600..50a41056a 100644 --- a/x/vm/keeper/keeper.go +++ b/x/vm/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/binary" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -20,7 +21,7 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" "cosmossdk.io/store/prefix" storetypes "cosmossdk.io/store/types" @@ -53,11 +54,13 @@ type Keeper struct { // bankWrapper is used to convert the Cosmos SDK coin used in the EVM to the // proper decimal representation. + bankKeeper types.BankKeeper bankWrapper types.BankWrapper // access historical headers for EVM state transition execution stakingKeeper types.StakingKeeper // fetch EIP1559 base fee and parameters + feeMarketKeeper types.FeeMarketKeeper feeMarketWrapper *wrappers.FeeMarketWrapper // optional erc20Keeper interface needed to instantiate erc20 precompiles erc20Keeper types.Erc20Keeper @@ -81,6 +84,10 @@ type Keeper struct { // evmMempool is the custom EVM appside mempool // if it is nil, the default comet mempool will be used evmMempool *evmmempool.ExperimentalEVMMempool + + // runtimeCfg keeps runtime-wide configuration (chain config, coin info, + // extra EIPs) to avoid using package-level global variables. + runtimeCfg *types.RuntimeConfig } // NewKeeper generates new evm module keeper @@ -108,34 +115,36 @@ func NewKeeper( panic(err) } - bankWrapper := wrappers.NewBankWrapper(bankKeeper) - feeMarketWrapper := wrappers.NewFeeMarketWrapper(fmk) + effectiveChainID := evmChainID + if effectiveChainID == 0 { + effectiveChainID = types.DefaultEVMChainID + } + + defaultChainCfg := types.DefaultChainConfig(effectiveChainID) + storeKeys := make(map[string]storetypes.StoreKey) for _, k := range keys { storeKeys[k.Name()] = k } - // set global chain config - ethCfg := types.DefaultChainConfig(evmChainID) - if err := types.SetChainConfig(ethCfg); err != nil { - panic(err) + keeper := &Keeper{ + cdc: cdc, + authority: authority, + accountKeeper: ak, + bankKeeper: bankKeeper, + stakingKeeper: sk, + feeMarketKeeper: fmk, + storeKey: storeKey, + transientKey: transientKey, + tracer: tracer, + consensusKeeper: consensusKeeper, + erc20Keeper: erc20Keeper, + storeKeys: storeKeys, } - // NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations - return &Keeper{ - cdc: cdc, - authority: authority, - accountKeeper: ak, - bankWrapper: bankWrapper, - stakingKeeper: sk, - feeMarketWrapper: feeMarketWrapper, - storeKey: storeKey, - transientKey: transientKey, - tracer: tracer, - consensusKeeper: consensusKeeper, - erc20Keeper: erc20Keeper, - storeKeys: storeKeys, - } + keeper.WithChainConfig(defaultChainCfg) + + return keeper } // Logger returns a module-specific logger. @@ -158,6 +167,79 @@ func (k Keeper) EmitBlockBloomEvent(ctx sdk.Context, bloom ethtypes.Bloom) { ) } +// ---------------------------------------------------------------------------- +// Runtime configuration +// ---------------------------------------------------------------------------- + +// SetRuntimeConfig stores the runtime configuration shared across components. +func (k *Keeper) SetRuntimeConfig(cfg *types.RuntimeConfig) error { + if cfg == nil { + return errors.New("runtime config cannot be nil") + } + + k.runtimeCfg = cfg + coinInfo := cfg.EvmCoinInfo() + if k.bankKeeper != nil { + k.bankWrapper = wrappers.NewBankWrapper(k.bankKeeper, coinInfo) + } + if k.feeMarketKeeper != nil { + k.feeMarketWrapper = wrappers.NewFeeMarketWrapper(k.feeMarketKeeper, coinInfo) + } + + return nil +} + +// RuntimeConfig returns the stored runtime configuration. +func (k Keeper) RuntimeConfig() *types.RuntimeConfig { + return k.runtimeCfg +} + +func coinInfoFromChainConfig(chainCfg *types.ChainConfig) types.EvmCoinInfo { + denom := chainCfg.Denom + if denom == "" { + denom = types.DefaultEVMDenom + } + extended := denom + display := denom + if denom == types.DefaultEVMDenom { + display = types.DefaultEVMDisplayDenom + } + decimals := uint32(types.DefaultEVMDecimals) + if chainCfg.Decimals != 0 { + decimals = uint32(chainCfg.Decimals) + } + + return types.EvmCoinInfo{ + Denom: denom, + ExtendedDenom: extended, + DisplayDenom: display, + Decimals: decimals, + } +} + +// WithChainConfig sets the keeper runtime configuration using the provided chain config. +// If chainCfg is nil, the default configuration is used. The method updates both the global +// chain configuration (for legacy consumers) and the keeper's runtime configuration. +func (k *Keeper) WithChainConfig(chainCfg *types.ChainConfig) *Keeper { + if chainCfg == nil { + chainCfg = types.DefaultChainConfig(types.DefaultEVMChainID) + } + + coinInfo := coinInfoFromChainConfig(chainCfg) + if err := types.EnsureEVMCoinInfo(coinInfo); err != nil { + panic(err) + } + runtimeCfg, err := types.NewRuntimeConfig(chainCfg, coinInfo, types.DefaultExtraEIPs) + if err != nil { + panic(err) + } + if err := k.SetRuntimeConfig(runtimeCfg); err != nil { + panic(err) + } + + return k +} + // GetAuthority returns the x/evm module authority address func (k Keeper) GetAuthority() sdk.AccAddress { return k.authority @@ -322,7 +404,11 @@ func (k *Keeper) SpendableCoin(ctx sdk.Context, addr common.Address) *uint256.In cosmosAddr := sdk.AccAddress(addr.Bytes()) // Get the balance via bank wrapper to convert it to 18 decimals if needed. - coin := k.bankWrapper.SpendableCoin(ctx, cosmosAddr, types.GetEVMCoinDenom()) + denom := k.EvmCoinInfo().Denom + if denom == "" { + denom = types.DefaultEVMDenom + } + coin := k.bankWrapper.SpendableCoin(ctx, cosmosAddr, denom) result, err := utils.Uint256FromBigInt(coin.Amount.BigInt()) if err != nil { @@ -337,7 +423,11 @@ func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *uint256.Int { cosmosAddr := sdk.AccAddress(addr.Bytes()) // Get the balance via bank wrapper to convert it to 18 decimals if needed. - coin := k.bankWrapper.GetBalance(ctx, cosmosAddr, types.GetEVMCoinDenom()) + denom := k.EvmCoinInfo().Denom + if denom == "" { + denom = types.DefaultEVMDenom + } + coin := k.bankWrapper.GetBalance(ctx, cosmosAddr, denom) result, err := utils.Uint256FromBigInt(coin.Amount.BigInt()) if err != nil { @@ -352,12 +442,11 @@ func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *uint256.Int { // - `0`: london hardfork enabled but feemarket is not enabled. // - `n`: both london hardfork and feemarket are enabled. func (k Keeper) GetBaseFee(ctx sdk.Context) *big.Int { - ethCfg := types.GetEthChainConfig() + ethCfg := k.effectiveEthChainConfig() if !types.IsLondon(ethCfg, ctx.BlockHeight()) { return nil } - coinInfo := k.GetEvmCoinInfo(ctx) - baseFee := k.feeMarketWrapper.GetBaseFee(ctx, types.Decimals(coinInfo.Decimals)) + baseFee := k.feeMarketWrapper.GetBaseFee(ctx) if baseFee == nil { // return 0 if feemarket not enabled. baseFee = big.NewInt(0) @@ -367,7 +456,7 @@ func (k Keeper) GetBaseFee(ctx sdk.Context) *big.Int { // GetMinGasPrice returns the MinGasPrice param from the fee market module // adapted according to the evm denom decimals -func (k Keeper) GetMinGasPrice(ctx sdk.Context) math.LegacyDec { +func (k Keeper) GetMinGasPrice(ctx sdk.Context) sdkmath.LegacyDec { return k.feeMarketWrapper.GetParams(ctx).MinGasPrice } diff --git a/x/vm/keeper/state_transition.go b/x/vm/keeper/state_transition.go index eb8975cf2..49bf3ffc3 100644 --- a/x/vm/keeper/state_transition.go +++ b/x/vm/keeper/state_transition.go @@ -55,7 +55,7 @@ func (k *Keeper) NewEVMWithOverridePrecompiles( Random: &common.MaxHash, // need to be different than nil to signal it is after the merge and pick up the right opcodes } - ethCfg := types.GetEthChainConfig() + ethCfg := k.effectiveEthChainConfig() txCtx := core.NewEVMTxContext(&msg) if tracer == nil { tracer = k.Tracer(ctx, msg, ethCfg) @@ -202,7 +202,8 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t txConfig := k.TxConfig(ctx, tx.Hash()) // get the signer according to the chain rules from the config and block height - signer := ethtypes.MakeSigner(types.GetEthChainConfig(), big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here + ethCfg := k.effectiveEthChainConfig() + signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here msg, err := core.TransactionToMessage(tx, signer, cfg.BaseFee) if err != nil { return nil, errorsmod.Wrap(err, "failed to return ethereum transaction as core message") @@ -311,7 +312,11 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t if msg.GasLimit > res.GasUsed { remainingGas = msg.GasLimit - res.GasUsed } - if err = k.RefundGas(ctx, *msg, remainingGas, types.GetEVMCoinDenom()); err != nil { + refundDenom := k.EvmCoinInfo().Denom + if refundDenom == "" { + refundDenom = types.DefaultEVMDenom + } + if err = k.RefundGas(ctx, *msg, remainingGas, refundDenom); err != nil { return nil, errorsmod.Wrapf(err, "failed to refund gas leftover gas to sender %s", msg.From) } @@ -398,7 +403,7 @@ func (k *Keeper) ApplyMessageWithConfig( ) stateDB := statedb.New(ctx, k, txConfig) - ethCfg := types.GetEthChainConfig() + ethCfg := k.effectiveEthChainConfig() evm := k.NewEVMWithOverridePrecompiles(ctx, msg, cfg, tracer, stateDB, overrides == nil) // Gas limit suffices for the floor data cost (EIP-7623) rules := ethCfg.Rules(evm.Context.BlockNumber, true, evm.Context.Time) diff --git a/x/vm/keeper/statedb.go b/x/vm/keeper/statedb.go index 828e0da60..99a15204b 100644 --- a/x/vm/keeper/statedb.go +++ b/x/vm/keeper/statedb.go @@ -114,7 +114,11 @@ func (k *Keeper) SetBalance(ctx sdk.Context, addr common.Address, amount *uint25 return nil } cosmosAddr := sdk.AccAddress(addr.Bytes()) - coin := k.bankWrapper.SpendableCoin(ctx, cosmosAddr, types.GetEVMCoinDenom()) + denom := k.EvmCoinInfo().Denom + if denom == "" { + denom = types.DefaultEVMDenom + } + coin := k.bankWrapper.SpendableCoin(ctx, cosmosAddr, denom) balance := coin.Amount.BigInt() delta := new(big.Int).Sub(amount.ToBig(), balance) diff --git a/x/vm/module.go b/x/vm/module.go index ac01dae48..8faacc7b6 100644 --- a/x/vm/module.go +++ b/x/vm/module.go @@ -140,7 +140,7 @@ func (am AppModule) PreBlock(goCtx context.Context) (appmodule.ResponsePreBlock, ctx := sdk.UnwrapSDKContext(goCtx) coinInfo := am.keeper.GetEvmCoinInfo(ctx) am.initializer.Do(func() { - SetGlobalConfigVariables(coinInfo) + SetGlobalConfigVariables(ctx, am.keeper, coinInfo) }) return &sdk.ResponsePreBlock{ConsensusParamsChanged: false}, nil } @@ -212,19 +212,44 @@ func setBaseDenom(ci types.EvmCoinInfo) (err error) { return sdk.RegisterDenom(ci.Denom, math.LegacyNewDecWithPrec(1, int64(ci.Decimals))) } -func SetGlobalConfigVariables(coinInfo types.EvmCoinInfo) { +func SetGlobalConfigVariables(ctx sdk.Context, k *keeper.Keeper, coinInfo types.EvmCoinInfo) { // set the denom info for the chain if err := setBaseDenom(coinInfo); err != nil { panic(err) } - configurator := types.NewEVMConfigurator() - err := configurator. - WithExtendedEips(types.DefaultCosmosEVMActivators). - // NOTE: we're using the 18 decimals default for the example chain - WithEVMCoinInfo(coinInfo). - Configure() + // configurator := types.NewEVMConfigurator() + // err := configurator. + // WithExtendedEips(types.DefaultCosmosEVMActivators). + // // NOTE: we're using the 18 decimals default for the example chain + // WithEVMCoinInfo(coinInfo). + // Configure() + // if err != nil { + // panic(err) + // } + + params := k.GetParams(ctx) + + chainCfg := k.ChainConfig() + if chainCfg == nil { + chainCfg = types.DefaultChainConfig(types.DefaultEVMChainID) + } else { + copied := *chainCfg + chainCfg = &copied + } + if coinInfo.Denom != "" { + chainCfg.Denom = coinInfo.Denom + } + if coinInfo.Decimals != 0 { + chainCfg.Decimals = uint64(coinInfo.Decimals) + } + + runtimeCfg, err := types.NewRuntimeConfig(chainCfg, coinInfo, params.ExtraEIPs) if err != nil { panic(err) } + + if err := k.SetRuntimeConfig(runtimeCfg); err != nil { + panic(err) + } } diff --git a/x/vm/types/chain_config.go b/x/vm/types/chain_config.go index 930ffdcf8..8a8842f49 100644 --- a/x/vm/types/chain_config.go +++ b/x/vm/types/chain_config.go @@ -9,10 +9,6 @@ import ( sdkmath "cosmossdk.io/math" ) -// chainConfig is the chain configuration used in the EVM to defined which -// opcodes are active based on Ethereum upgrades. -var chainConfig *ChainConfig - // EthereumConfig returns an Ethereum ChainConfig for EVM state transitions. // All the negative or nil values are converted to nil func (cc ChainConfig) EthereumConfig(chainID *big.Int) *gethparams.ChainConfig { diff --git a/x/vm/types/coin_info_helpers.go b/x/vm/types/coin_info_helpers.go new file mode 100644 index 000000000..843dfa5df --- /dev/null +++ b/x/vm/types/coin_info_helpers.go @@ -0,0 +1,28 @@ +package types + +// DenomOrDefault returns the configured EVM denom if set, otherwise the +// module default denom. +func (info EvmCoinInfo) DenomOrDefault() string { + if info.Denom == "" { + return DefaultEVMDenom + } + return info.Denom +} + +// ExtendedDenomOrDefault returns the configured extended denom if set, +// otherwise it falls back to the primary denom. +func (info EvmCoinInfo) ExtendedDenomOrDefault() string { + if info.ExtendedDenom != "" { + return info.ExtendedDenom + } + return info.DenomOrDefault() +} + +// DecimalsOrDefault returns the configured decimals or the default EVM +// decimals (18) when unset. +func (info EvmCoinInfo) DecimalsOrDefault() Decimals { + if info.Decimals == 0 { + return EighteenDecimals + } + return Decimals(info.Decimals) +} diff --git a/x/vm/types/config.go b/x/vm/types/config.go index 4fc952a76..ab77e6403 100644 --- a/x/vm/types/config.go +++ b/x/vm/types/config.go @@ -8,11 +8,9 @@ package types import ( - "errors" "fmt" "github.com/ethereum/go-ethereum/core/vm" - geth "github.com/ethereum/go-ethereum/params" ) // Configure applies the changes to the virtual machine configuration. @@ -45,32 +43,3 @@ func (ec *EVMConfigurator) Configure() error { func (ec *EVMConfigurator) ResetTestConfig() { panic("this is only implemented with the 'test' build flag. Make sure you're running your tests using the '-tags=test' flag.") } - -// GetEthChainConfig returns the `chainConfig` used in the EVM (geth type). -func GetEthChainConfig() *geth.ChainConfig { - return chainConfig.EthereumConfig(nil) -} - -// GetChainConfig returns the `chainConfig`. -func GetChainConfig() *ChainConfig { - return chainConfig -} - -// SetChainConfig allows to set the `chainConfig` variable modifying the -// default values. The method is private because it should only be called once -// in the EVMConfigurator. -func SetChainConfig(cc *ChainConfig) error { - if chainConfig != nil && chainConfig.ChainId != DefaultEVMChainID { - return errors.New("chainConfig already set. Cannot set again the chainConfig") - } - config := DefaultChainConfig(0) - if cc != nil { - config = cc - } - if err := config.Validate(); err != nil { - return err - } - chainConfig = config - - return nil -} diff --git a/x/vm/types/config_testing.go b/x/vm/types/config_testing.go deleted file mode 100644 index fe16fab17..000000000 --- a/x/vm/types/config_testing.go +++ /dev/null @@ -1,96 +0,0 @@ -// -// The config package provides a convenient way to modify x/evm params and values. -// Its primary purpose is to be used during application initialization. - -//go:build test -// +build test - -package types - -import ( - "errors" - "fmt" - "github.com/ethereum/go-ethereum/core/vm" - geth "github.com/ethereum/go-ethereum/params" -) - -// testChainConfig is the chain configuration used in the EVM to defined which -// opcodes are active based on Ethereum upgrades. -var testChainConfig *ChainConfig - -// Configure applies the changes to the virtual machine configuration. -func (ec *EVMConfigurator) Configure() error { - // If Configure method has been already used in the object, return - // an error to avoid overriding configuration. - if ec.sealed { - return fmt.Errorf("error configuring EVMConfigurator: already sealed and cannot be modified") - } - - if err := setTestingEVMCoinInfo(ec.evmCoinInfo); err != nil { - return err - } - - if err := extendDefaultExtraEIPs(ec.extendedDefaultExtraEIPs); err != nil { - return err - } - - if err := vm.ExtendActivators(ec.extendedEIPs); err != nil { - return err - } - - // After applying modifications, the configurator is sealed. This way, it is not possible - // to call the configure method twice. - ec.sealed = true - - return nil -} - -func (ec *EVMConfigurator) ResetTestConfig() { - vm.ResetActivators() - resetEVMCoinInfo() - testChainConfig = nil -} - -func setTestChainConfig(cc *ChainConfig) error { - if testChainConfig != nil { - return errors.New("chainConfig already set. Cannot set again the chainConfig. Call the configurators ResetTestConfig method before configuring a new chain.") - } - config := DefaultChainConfig(0) - if cc != nil { - config = cc - } - if err := config.Validate(); err != nil { - return err - } - testChainConfig = config - return nil -} - -// SetChainConfig allows to set the `chainConfig` variable modifying the -// default values. The method is private because it should only be called once -// in the EVMConfigurator. -func SetChainConfig(cc *ChainConfig) error { - if chainConfig != nil && chainConfig.ChainId != DefaultEVMChainID { - return errors.New("chainConfig already set. Cannot set again the chainConfig") - } - config := DefaultChainConfig(0) - if cc != nil { - config = cc - } - if err := config.Validate(); err != nil { - return err - } - testChainConfig = config - - return nil -} - -// GetEthChainConfig returns the `chainConfig` used in the EVM (geth type). -func GetEthChainConfig() *geth.ChainConfig { - return testChainConfig.EthereumConfig(nil) -} - -// GetChainConfig returns the `chainConfig`. -func GetChainConfig() *ChainConfig { - return testChainConfig -} diff --git a/x/vm/types/configurator.go b/x/vm/types/configurator.go deleted file mode 100644 index b661edf71..000000000 --- a/x/vm/types/configurator.go +++ /dev/null @@ -1,59 +0,0 @@ -// -// The config package provides a convenient way to modify x/evm params and values. -// Its primary purpose is to be used during application initialization. - -package types - -import ( - "fmt" - "slices" - - "github.com/ethereum/go-ethereum/core/vm" -) - -// EVMConfigurator allows to extend x/evm module configurations. The configurator modifies -// the EVM before starting the node. This means that all init genesis validations will be -// applied to each change. -type EVMConfigurator struct { - sealed bool - extendedEIPs map[int]func(*vm.JumpTable) - extendedDefaultExtraEIPs []int64 - evmCoinInfo EvmCoinInfo -} - -// NewEVMConfigurator returns a pointer to a new EVMConfigurator object. -func NewEVMConfigurator() *EVMConfigurator { - return &EVMConfigurator{} -} - -// WithExtendedEips allows to add to the go-ethereum activators map the provided -// EIP activators. -func (ec *EVMConfigurator) WithExtendedEips(extendedEIPs map[int]func(*vm.JumpTable)) *EVMConfigurator { - ec.extendedEIPs = extendedEIPs - return ec -} - -// WithExtendedDefaultExtraEIPs update the x/evm DefaultExtraEIPs params -// by adding provided EIP numbers. -func (ec *EVMConfigurator) WithExtendedDefaultExtraEIPs(eips ...int64) *EVMConfigurator { - ec.extendedDefaultExtraEIPs = eips - return ec -} - -// WithEVMCoinInfo allows to define the denom and decimals of the token used as the -// EVM token. -func (ec *EVMConfigurator) WithEVMCoinInfo(coinInfo EvmCoinInfo) *EVMConfigurator { - ec.evmCoinInfo = coinInfo - return ec -} - -func extendDefaultExtraEIPs(extraEIPs []int64) error { - for _, eip := range extraEIPs { - if slices.Contains(DefaultExtraEIPs, eip) { - return fmt.Errorf("error configuring EVMConfigurator: EIP %d is already present in the default list: %v", eip, DefaultExtraEIPs) - } - - DefaultExtraEIPs = append(DefaultExtraEIPs, eip) - } - return nil -} diff --git a/x/vm/types/configurator_test.go b/x/vm/types/configurator_test.go deleted file mode 100644 index a278fa62d..000000000 --- a/x/vm/types/configurator_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/ethereum/go-ethereum/core/vm" - "github.com/stretchr/testify/require" - - testconstants "github.com/cosmos/evm/testutil/constants" - "github.com/cosmos/evm/x/vm/types" -) - -func TestEVMConfigurator(t *testing.T) { - evmConfigurator := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]) - err := evmConfigurator.Configure() - require.NoError(t, err) - - err = evmConfigurator.Configure() - require.Error(t, err) - require.Contains(t, err.Error(), "already sealed", "expected different error") -} - -func TestExtendedEips(t *testing.T) { - testCases := []struct { - name string - malleate func() *types.EVMConfigurator - expPass bool - errContains string - }{ - { - "fail - eip already present in activators return an error", - func() *types.EVMConfigurator { - extendedEIPs := map[int]func(*vm.JumpTable){ - 3855: func(_ *vm.JumpTable) {}, - } - ec := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - WithExtendedEips(extendedEIPs) - return ec - }, - false, - "duplicate activation", - }, - { - "success - new default extra eips without duplication added", - func() *types.EVMConfigurator { - extendedEIPs := map[int]func(*vm.JumpTable){ - 0o000: func(_ *vm.JumpTable) {}, - } - ec := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - WithExtendedEips(extendedEIPs) - return ec - }, - true, - "", - }, - } - - for _, tc := range testCases { - ec := tc.malleate() - ec.ResetTestConfig() - err := ec.Configure() - - if tc.expPass { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), tc.errContains, "expected different error") - } - } -} - -func TestExtendedDefaultExtraEips(t *testing.T) { - defaultExtraEIPsSnapshot := types.DefaultExtraEIPs - testCases := []struct { - name string - malleate func() *types.EVMConfigurator - postCheck func() - expPass bool - errContains string - }{ - { - "fail - duplicate default EIP entiries", - func() *types.EVMConfigurator { - extendedDefaultExtraEIPs := []int64{1000} - types.DefaultExtraEIPs = append(types.DefaultExtraEIPs, 1000) - ec := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - WithExtendedDefaultExtraEIPs(extendedDefaultExtraEIPs...) - return ec - }, - func() { - require.ElementsMatch(t, append(defaultExtraEIPsSnapshot, 1000), types.DefaultExtraEIPs) - types.DefaultExtraEIPs = defaultExtraEIPsSnapshot - }, - false, - "EIP 1000 is already present", - }, - { - "success - empty default extra eip", - func() *types.EVMConfigurator { - var extendedDefaultExtraEIPs []int64 - ec := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - WithExtendedDefaultExtraEIPs(extendedDefaultExtraEIPs...) - return ec - }, - func() { - require.ElementsMatch(t, defaultExtraEIPsSnapshot, types.DefaultExtraEIPs) - }, - true, - "", - }, - { - "success - extra default eip added", - func() *types.EVMConfigurator { - extendedDefaultExtraEIPs := []int64{1001} - ec := types.NewEVMConfigurator(). - WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID]). - WithExtendedDefaultExtraEIPs(extendedDefaultExtraEIPs...) - return ec - }, - func() { - require.ElementsMatch(t, append(defaultExtraEIPsSnapshot, 1001), types.DefaultExtraEIPs) - types.DefaultExtraEIPs = defaultExtraEIPsSnapshot - }, - true, - "", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ec := tc.malleate() - ec.ResetTestConfig() - err := ec.Configure() - - if tc.expPass { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), tc.errContains, "expected different error") - } - - tc.postCheck() - }) - } -} diff --git a/x/vm/types/denom_config.go b/x/vm/types/denom_config.go index ca731fc3a..af02ccdc1 100644 --- a/x/vm/types/denom_config.go +++ b/x/vm/types/denom_config.go @@ -78,17 +78,14 @@ func GetEVMCoinDisplayDenom() string { // setEVMCoinInfo allows to define denom and decimals of the coin used in the EVM. func setEVMCoinInfo(eci EvmCoinInfo) error { - if evmCoinInfo != nil { - return errors.New("EVM coin info already set") - } - if Decimals(eci.Decimals) == EighteenDecimals { if eci.Denom != eci.ExtendedDenom { return errors.New("EVM coin denom and extended denom must be the same for 18 decimals") } } - - evmCoinInfo = new(EvmCoinInfo) + if evmCoinInfo == nil { + evmCoinInfo = new(EvmCoinInfo) + } if err := setEVMCoinDenom(eci.Denom); err != nil { return err @@ -101,3 +98,8 @@ func setEVMCoinInfo(eci EvmCoinInfo) error { } return setEVMCoinDecimals(Decimals(eci.Decimals)) } + +// EnsureEVMCoinInfo initializes or updates the globally configured EVM coin info. +func EnsureEVMCoinInfo(info EvmCoinInfo) error { + return setEVMCoinInfo(info) +} diff --git a/x/vm/types/denom_config_testing.go b/x/vm/types/denom_config_testing.go index fede6ffdb..005e8dea9 100644 --- a/x/vm/types/denom_config_testing.go +++ b/x/vm/types/denom_config_testing.go @@ -78,17 +78,14 @@ func GetEVMCoinDisplayDenom() string { // setTestingEVMCoinInfo allows to define denom and decimals of the coin used in the EVM. func setTestingEVMCoinInfo(eci EvmCoinInfo) error { - if testingEvmCoinInfo != nil { - return errors.New("testing EVM coin info already set. Make sure you run the configurator's ResetTestConfig before trying to set a new evm coin info") - } - if eci.Decimals == EighteenDecimals.Uint32() { if eci.Denom != eci.ExtendedDenom { return errors.New("EVM coin denom and extended denom must be the same for 18 decimals") } } - - testingEvmCoinInfo = new(EvmCoinInfo) + if testingEvmCoinInfo == nil { + testingEvmCoinInfo = new(EvmCoinInfo) + } if err := setEVMCoinDenom(eci.Denom); err != nil { return err @@ -106,3 +103,8 @@ func setTestingEVMCoinInfo(eci EvmCoinInfo) error { func resetEVMCoinInfo() { testingEvmCoinInfo = nil } + +// EnsureEVMCoinInfo initializes or updates the testing EVM coin info. +func EnsureEVMCoinInfo(info EvmCoinInfo) error { + return setTestingEVMCoinInfo(info) +} diff --git a/x/vm/types/mocks/EVMKeeper.go b/x/vm/types/mocks/EVMKeeper.go index a6ed186e2..4508a96b4 100644 --- a/x/vm/types/mocks/EVMKeeper.go +++ b/x/vm/types/mocks/EVMKeeper.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + gethparams "github.com/ethereum/go-ethereum/params" "github.com/cosmos/evm/x/vm/statedb" "github.com/cosmos/evm/x/vm/types" @@ -31,6 +32,9 @@ type EVMKeeper struct { accounts map[common.Address]Account codes map[common.Hash][]byte storeKeys map[string]*storetypes.KVStoreKey + chainCfg *types.ChainConfig + ethCfg *gethparams.ChainConfig + coinInfo types.EvmCoinInfo } func NewEVMKeeper() *EVMKeeper { @@ -38,6 +42,8 @@ func NewEVMKeeper() *EVMKeeper { accounts: make(map[common.Address]Account), codes: make(map[common.Hash][]byte), storeKeys: make(map[string]*storetypes.KVStoreKey), + ethCfg: types.DefaultChainConfig(types.DefaultEVMChainID).EthereumConfig(nil), + coinInfo: types.EvmCoinInfo{}, } } @@ -118,7 +124,14 @@ func (k EVMKeeper) Clone() *EVMKeeper { accounts := maps.Clone(k.accounts) codes := maps.Clone(k.codes) storeKeys := maps.Clone(k.storeKeys) - return &EVMKeeper{accounts, codes, storeKeys} + return &EVMKeeper{ + accounts: accounts, + codes: codes, + storeKeys: storeKeys, + chainCfg: k.chainCfg, + ethCfg: k.ethCfg, + coinInfo: k.coinInfo, + } } func (k EVMKeeper) KVStoreKeys() map[string]storetypes.StoreKey { @@ -129,6 +142,18 @@ func (k EVMKeeper) KVStoreKeys() map[string]storetypes.StoreKey { return result } +func (k EVMKeeper) ChainConfig() *types.ChainConfig { + return k.chainCfg +} + +func (k EVMKeeper) EthChainConfig() *gethparams.ChainConfig { + return k.ethCfg +} + +func (k EVMKeeper) EvmCoinInfo() types.EvmCoinInfo { + return k.coinInfo +} + func (k EVMKeeper) GetCodeHash(_ sdk.Context, _ common.Address) common.Hash { return common.Hash{} } diff --git a/x/vm/types/msg_test.go b/x/vm/types/msg_test.go index 0e2ca6498..fb48a3440 100644 --- a/x/vm/types/msg_test.go +++ b/x/vm/types/msg_test.go @@ -52,9 +52,6 @@ func (suite *MsgsTestSuite) SetupTest() { encodingConfig := encoding.MakeConfig(suite.chainID.Uint64()) suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) - - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() } func (suite *MsgsTestSuite) TestMsgEthereumTx_Constructor() { @@ -115,14 +112,9 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_BuildTx() { testconstants.ExampleChainCoinInfo[testconstants.ExampleChainID], } { for _, tc := range testCases { - configurator := types.NewEVMConfigurator() - configurator.ResetTestConfig() - suite.Require().NoError(configurator.WithEVMCoinInfo(coinInfo).Configure()) - - baseDenom := types.GetEVMCoinDenom() extendedDenom := types.GetEVMCoinExtendedDenom() - tx, err := tc.msg.BuildTx(suite.clientCtx.TxConfig.NewTxBuilder(), baseDenom) + tx, err := tc.msg.BuildTx(suite.clientCtx.TxConfig.NewTxBuilder(), coinInfo.Denom) if tc.expError { suite.Require().Error(err) } else { diff --git a/x/vm/types/runtime_config.go b/x/vm/types/runtime_config.go new file mode 100644 index 000000000..130ce50fe --- /dev/null +++ b/x/vm/types/runtime_config.go @@ -0,0 +1,66 @@ +package types + +import ( + "errors" + + gethparams "github.com/ethereum/go-ethereum/params" +) + +// RuntimeConfig keeps the runtime configuration for the EVM module. +type RuntimeConfig struct { + chainConfig *ChainConfig + ethChainConfig *gethparams.ChainConfig + coinInfo EvmCoinInfo + extraEIPs []int64 +} + +// NewRuntimeConfig builds a new runtime configuration from the provided values. +// Copies are made where necessary to avoid accidental mutation. +func NewRuntimeConfig( + chainCfg *ChainConfig, + coinInfo EvmCoinInfo, + extraEIPs []int64, +) (*RuntimeConfig, error) { + if chainCfg == nil { + return nil, errors.New("runtime config requires chain config") + } + + cfg := &RuntimeConfig{ + chainConfig: chainCfg, + ethChainConfig: chainCfg.EthereumConfig(nil), + coinInfo: coinInfo, + } + + if len(extraEIPs) > 0 { + copied := make([]int64, len(extraEIPs)) + copy(copied, extraEIPs) + cfg.extraEIPs = copied + } + + return cfg, nil +} + +// ChainConfig returns the x/vm chain configuration. +func (rc *RuntimeConfig) ChainConfig() *ChainConfig { + return rc.chainConfig +} + +// EthChainConfig returns the go-ethereum chain configuration. +func (rc *RuntimeConfig) EthChainConfig() *gethparams.ChainConfig { + return rc.ethChainConfig +} + +// CoinInfo returns the EVM coin info. +func (rc *RuntimeConfig) EvmCoinInfo() EvmCoinInfo { + return rc.coinInfo +} + +// ExtraEIPs returns the additional EIPs configured for the EVM. +func (rc *RuntimeConfig) ExtraEIPs() []int64 { + if len(rc.extraEIPs) == 0 { + return nil + } + copied := make([]int64, len(rc.extraEIPs)) + copy(copied, rc.extraEIPs) + return copied +} diff --git a/x/vm/types/scaling.go b/x/vm/types/scaling.go index a3274826d..62697521c 100644 --- a/x/vm/types/scaling.go +++ b/x/vm/types/scaling.go @@ -14,9 +14,8 @@ import ( // ConvertAmountTo18DecimalsLegacy convert the given amount into a 18 decimals // representation. func ConvertAmountTo18DecimalsLegacy(amt sdkmath.LegacyDec) sdkmath.LegacyDec { - evmCoinDecimal := GetEVMCoinDecimals() - - return amt.MulInt(evmCoinDecimal.ConversionFactor()) + eighteenDecimals := Decimals(18) + return amt.MulInt(eighteenDecimals.ConversionFactor()) } // ConvertAmountTo18DecimalsBigInt convert the given amount into a 18 decimals diff --git a/x/vm/types/scaling_test.go b/x/vm/types/scaling_test.go index 46657beec..1b76bd391 100644 --- a/x/vm/types/scaling_test.go +++ b/x/vm/types/scaling_test.go @@ -75,10 +75,6 @@ func TestConvertEvmCoinFrom18Decimals(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - require.NoError(t, configurator.WithEVMCoinInfo(tc.evmCoinInfo).Configure()) - coinConverted, err := evmtypes.ConvertEvmCoinDenomToExtendedDenom(tc.coin) if !tc.expErr { @@ -139,10 +135,6 @@ func TestConvertCoinsFrom18Decimals(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - require.NoError(t, configurator.WithEVMCoinInfo(tc.evmCoinInfo).Configure()) - coinConverted := evmtypes.ConvertCoinsDenomToExtendedDenom(tc.coins) require.Equal(t, tc.expCoins, coinConverted, "expected a different coin") }) @@ -188,9 +180,6 @@ func TestConvertAmountTo18DecimalsLegacy(t *testing.T) { } { for _, tc := range testCases { t.Run(fmt.Sprintf("%d dec - %s", coinInfo.Decimals, tc.name), func(t *testing.T) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - require.NoError(t, configurator.WithEVMCoinInfo(coinInfo).Configure()) res := evmtypes.ConvertBigIntFrom18DecimalsToLegacyDec(tc.amt.ToBig()) exp := math.LegacyNewDecFromBigInt(tc.amt.ToBig()) if coinInfo.Decimals == evmtypes.SixDecimals.Uint32() { @@ -226,9 +215,6 @@ func TestConvertAmountTo18DecimalsBigInt(t *testing.T) { } { for _, tc := range testCases { t.Run(fmt.Sprintf("%d dec - %s", coinInfo.Decimals, tc.name), func(t *testing.T) { - configurator := evmtypes.NewEVMConfigurator() - configurator.ResetTestConfig() - require.NoError(t, configurator.WithEVMCoinInfo(coinInfo).Configure()) res := evmtypes.ConvertAmountTo18DecimalsBigInt(tc.amt) exp := tc.amt if coinInfo.Decimals == evmtypes.SixDecimals.Uint32() { diff --git a/x/vm/wrappers/bank.go b/x/vm/wrappers/bank.go index ac50e3089..c827263aa 100644 --- a/x/vm/wrappers/bank.go +++ b/x/vm/wrappers/bank.go @@ -13,23 +13,34 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var _ types.BankWrapper = BankWrapper{} +var _ types.BankWrapper = (*BankWrapper)(nil) // BankWrapper is a wrapper around the Cosmos SDK bank keeper // that is used to manage an evm denom with a custom decimal representation. type BankWrapper struct { types.BankKeeper + coinInfo types.EvmCoinInfo } // NewBankWrapper creates a new BankWrapper instance. func NewBankWrapper( bk types.BankKeeper, + coinInfo types.EvmCoinInfo, ) *BankWrapper { return &BankWrapper{ - bk, + BankKeeper: bk, + coinInfo: coinInfo, } } +func (w BankWrapper) evmDenom() string { + return w.coinInfo.DenomOrDefault() +} + +func (w BankWrapper) extendedDenom() string { + return w.coinInfo.ExtendedDenomOrDefault() +} + // ------------------------------------------------------------------------------------------ // Bank wrapper own methods // ------------------------------------------------------------------------------------------ @@ -37,9 +48,9 @@ func NewBankWrapper( // MintAmountToAccount converts the given amount into the evm coin scaling // the amount to the original decimals, then mints that amount to the provided account. func (w BankWrapper) MintAmountToAccount(ctx context.Context, recipientAddr sdk.AccAddress, amt *big.Int) error { - coin := sdk.Coin{Denom: types.GetEVMCoinDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} + coin := sdk.Coin{Denom: w.evmDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} - convertedCoin, err := types.ConvertEvmCoinDenomToExtendedDenom(coin) + convertedCoin, err := w.convertCoinToExtended(coin) if err != nil { return errors.Wrap(err, "failed to mint coin to account in bank wrapper") } @@ -55,9 +66,9 @@ func (w BankWrapper) MintAmountToAccount(ctx context.Context, recipientAddr sdk. // BurnAmountFromAccount converts the given amount into the evm coin scaling // the amount to the original decimals, then burns that quantity from the provided account. func (w BankWrapper) BurnAmountFromAccount(ctx context.Context, account sdk.AccAddress, amt *big.Int) error { - coin := sdk.Coin{Denom: types.GetEVMCoinDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} + coin := sdk.Coin{Denom: w.evmDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} - convertedCoin, err := types.ConvertEvmCoinDenomToExtendedDenom(coin) + convertedCoin, err := w.convertCoinToExtended(coin) if err != nil { return errors.Wrap(err, "failed to burn coins from account in bank wrapper") } @@ -76,27 +87,27 @@ func (w BankWrapper) BurnAmountFromAccount(ctx context.Context, account sdk.AccA // GetBalance returns the balance of the given account. func (w BankWrapper) GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin { - if denom != types.GetEVMCoinDenom() { - panic(fmt.Sprintf("expected evm denom %s, received %s", types.GetEVMCoinDenom(), denom)) + if denom != w.evmDenom() { + panic(fmt.Sprintf("expected evm denom %s, received %s", w.evmDenom(), denom)) } - return w.BankKeeper.GetBalance(ctx, addr, types.GetEVMCoinExtendedDenom()) + return w.BankKeeper.GetBalance(ctx, addr, w.extendedDenom()) } // SpendableCoin returns the balance of the given account. func (w BankWrapper) SpendableCoin(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin { - if denom != types.GetEVMCoinDenom() { - panic(fmt.Sprintf("expected evm denom %s, received %s", types.GetEVMCoinDenom(), denom)) + if denom != w.evmDenom() { + panic(fmt.Sprintf("expected evm denom %s, received %s", w.evmDenom(), denom)) } - return w.BankKeeper.SpendableCoin(ctx, addr, types.GetEVMCoinExtendedDenom()) + return w.BankKeeper.SpendableCoin(ctx, addr, w.extendedDenom()) } // SendCoinsFromAccountToModule wraps around the Cosmos SDK x/bank module's // SendCoinsFromAccountToModule method to convert the evm coin, if present in // the input, to its original representation. func (w BankWrapper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, coins sdk.Coins) error { - convertedCoins := types.ConvertCoinsDenomToExtendedDenom(coins) + convertedCoins := w.convertCoinsToExtended(coins) if convertedCoins.IsZero() { // if after scaling the coins the amt is zero // then is a no-op. @@ -112,10 +123,30 @@ func (w BankWrapper) SendCoinsFromAccountToModule(ctx context.Context, senderAdd // SendCoinsFromModuleToAccount method to convert the evm coin, if present in // the input, to its original representation. func (w BankWrapper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, coins sdk.Coins) error { - convertedCoins := types.ConvertCoinsDenomToExtendedDenom(coins) + convertedCoins := w.convertCoinsToExtended(coins) if convertedCoins.IsZero() { return nil } return w.BankKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, convertedCoins) } + +func (w BankWrapper) convertCoinToExtended(coin sdk.Coin) (sdk.Coin, error) { + if coin.Denom != w.evmDenom() { + return sdk.Coin{}, fmt.Errorf("expected coin denom %s, received %s", w.evmDenom(), coin.Denom) + } + + return sdk.Coin{Denom: w.extendedDenom(), Amount: coin.Amount}, nil +} + +func (w BankWrapper) convertCoinsToExtended(coins sdk.Coins) sdk.Coins { + convertedCoins := make(sdk.Coins, len(coins)) + for i, coin := range coins { + if coin.Denom == w.evmDenom() { + convertedCoins[i] = sdk.Coin{Denom: w.extendedDenom(), Amount: coin.Amount} + } else { + convertedCoins[i] = coin + } + } + return convertedCoins.Sort() +} diff --git a/x/vm/wrappers/bank_test.go b/x/vm/wrappers/bank_test.go index 590da9908..4325405a8 100644 --- a/x/vm/wrappers/bank_test.go +++ b/x/vm/wrappers/bank_test.go @@ -103,20 +103,14 @@ func TestMintAmountToAccount(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) - err = bankWrapper.MintAmountToAccount(context.Background(), tc.recipient, tc.amount) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) + err := bankWrapper.MintAmountToAccount(context.Background(), tc.recipient, tc.amount) if tc.expectErr != "" { require.ErrorContains(t, err, tc.expectErr) @@ -224,20 +218,14 @@ func TestBurnAmountFromAccount(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) - err = bankWrapper.BurnAmountFromAccount(context.Background(), account, tc.amount) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) + err := bankWrapper.BurnAmountFromAccount(context.Background(), account, tc.amount) if tc.expectErr != "" { require.ErrorContains(t, err, tc.expectErr) @@ -385,20 +373,14 @@ func TestSendCoinsFromModuleToAccount(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) - err = bankWrapper.SendCoinsFromModuleToAccount(context.Background(), evmtypes.ModuleName, account, tc.coins()) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) + err := bankWrapper.SendCoinsFromModuleToAccount(context.Background(), evmtypes.ModuleName, account, tc.coins()) if tc.expectErr != "" { require.ErrorContains(t, err, tc.expectErr) @@ -546,20 +528,14 @@ func TestSendCoinsFromAccountToModule(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) - err = bankWrapper.SendCoinsFromAccountToModule(context.Background(), account, evmtypes.ModuleName, tc.coins()) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) + err := bankWrapper.SendCoinsFromAccountToModule(context.Background(), account, evmtypes.ModuleName, tc.coins()) if tc.expectErr != "" { require.ErrorContains(t, err, tc.expectErr) @@ -584,7 +560,6 @@ func TestGetBalance(t *testing.T) { coinInfo evmtypes.EvmCoinInfo evmDenom string expCoin sdk.Coin - expErr string expPanic string mockSetup func(*testutil.MockBankWrapper) }{ @@ -593,7 +568,6 @@ func TestGetBalance(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e18)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e18)) @@ -610,7 +584,6 @@ func TestGetBalance(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e12).MulRaw(maxInt64)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e12).MulRaw(maxInt64)) @@ -627,7 +600,6 @@ func TestGetBalance(t *testing.T) { coinInfo: eighteenDecimalsCoinInfo, evmDenom: eighteenDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(eighteenDecimalsCoinInfo.Denom, sdkmath.NewInt(1e18)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(eighteenDecimalsCoinInfo.Denom, sdkmath.NewInt(1e18)) @@ -644,7 +616,6 @@ func TestGetBalance(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(0)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(0)) @@ -661,7 +632,6 @@ func TestGetBalance(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e14)), // 0.0001 token in 18 decimals - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e14)) // 0.0001 token in 6 decimals @@ -684,19 +654,13 @@ func TestGetBalance(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) // When calling the function with a denom different than the evm one, it should panic defer func() { @@ -707,12 +671,7 @@ func TestGetBalance(t *testing.T) { balance := bankWrapper.GetBalance(context.Background(), account, tc.evmDenom) - if tc.expErr != "" { - require.ErrorContains(t, err, tc.expErr) - } else { - require.NoError(t, err) - require.Equal(t, tc.expCoin, balance, "expected a different balance") - } + require.Equal(t, tc.expCoin, balance, "expected a different balance") }) } } @@ -731,7 +690,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo evmtypes.EvmCoinInfo evmDenom string expCoin sdk.Coin - expErr string expPanic string mockSetup func(*testutil.MockBankWrapper) }{ @@ -740,7 +698,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e18)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e18)) @@ -757,7 +714,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e12).MulRaw(maxInt64)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e12).MulRaw(maxInt64)) @@ -774,7 +730,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo: eighteenDecimalsCoinInfo, evmDenom: eighteenDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(eighteenDecimalsCoinInfo.Denom, sdkmath.NewInt(1e18)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(eighteenDecimalsCoinInfo.Denom, sdkmath.NewInt(1e18)) @@ -791,7 +746,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(0)), - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(0)) @@ -808,7 +762,6 @@ func TestSppendableCoin(t *testing.T) { coinInfo: sixDecimalsCoinInfo, evmDenom: sixDecimalsCoinInfo.Denom, expCoin: sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e14)), // 0.0001 token in 18 decimals - expErr: "", mockSetup: func(mbk *testutil.MockBankWrapper) { returnedCoin := sdk.NewCoin(sixDecimalsCoinInfo.ExtendedDenom, sdkmath.NewInt(1e14)) // 0.0001 token in 6 decimals @@ -831,19 +784,13 @@ func TestSppendableCoin(t *testing.T) { 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") - // Setup mock controller ctrl := gomock.NewController(t) mockBankKeeper := testutil.NewMockBankWrapper(ctrl) tc.mockSetup(mockBankKeeper) - bankWrapper := wrappers.NewBankWrapper(mockBankKeeper) + bankWrapper := wrappers.NewBankWrapper(mockBankKeeper, tc.coinInfo) // When calling the function with a denom different than the evm one, it should panic defer func() { @@ -854,12 +801,7 @@ func TestSppendableCoin(t *testing.T) { balance := bankWrapper.SpendableCoin(context.Background(), account, tc.evmDenom) - if tc.expErr != "" { - require.ErrorContains(t, err, tc.expErr) - } else { - require.NoError(t, err) - require.Equal(t, tc.expCoin, balance, "expected a different balance") - } + require.Equal(t, tc.expCoin, balance, "expected a different balance") }) } } diff --git a/x/vm/wrappers/feemarket.go b/x/vm/wrappers/feemarket.go index f864ec3e2..bb55906ee 100644 --- a/x/vm/wrappers/feemarket.go +++ b/x/vm/wrappers/feemarket.go @@ -10,33 +10,39 @@ import ( ) // FeeMarketWrapper is a wrapper around the feemarket keeper -// that is used to manage an evm denom with 6 or 18 decimals. +// that is used to manage an evm denom with a configurable decimal precision. // The wrapper makes the corresponding conversions to achieve: // - With the EVM, the wrapper works always with 18 decimals. // - With the feemarket module, the wrapper works always -// with the bank module decimals (either 6 or 18). +// with the bank module decimals. type FeeMarketWrapper struct { types.FeeMarketKeeper + coinInfo types.EvmCoinInfo } // NewFeeMarketWrapper creates a new feemarket Keeper wrapper instance. -// The BankWrapper is used to manage an evm denom with 6 or 18 decimals. func NewFeeMarketWrapper( fk types.FeeMarketKeeper, + coinInfo types.EvmCoinInfo, ) *FeeMarketWrapper { return &FeeMarketWrapper{ - fk, + FeeMarketKeeper: fk, + coinInfo: coinInfo, } } +func (w FeeMarketWrapper) decimals() types.Decimals { + return w.coinInfo.DecimalsOrDefault() +} + // GetBaseFee returns the base fee converted to 18 decimals. -func (w FeeMarketWrapper) GetBaseFee(ctx sdk.Context, decimals types.Decimals) *big.Int { +func (w FeeMarketWrapper) GetBaseFee(ctx sdk.Context) *big.Int { baseFee := w.FeeMarketKeeper.GetBaseFee(ctx) if baseFee.IsNil() { return nil } - return baseFee.MulInt(decimals.ConversionFactor()).TruncateInt().BigInt() + return baseFee.MulInt(w.decimals().ConversionFactor()).TruncateInt().BigInt() } // CalculateBaseFee returns the calculated base fee converted to 18 decimals. @@ -45,14 +51,15 @@ func (w FeeMarketWrapper) CalculateBaseFee(ctx sdk.Context) *big.Int { if baseFee.IsNil() { return nil } - return types.ConvertAmountTo18DecimalsLegacy(baseFee).TruncateInt().BigInt() + return baseFee.MulInt(w.decimals().ConversionFactor()).TruncateInt().BigInt() } // GetParams returns the params with associated fees values converted to 18 decimals. func (w FeeMarketWrapper) GetParams(ctx sdk.Context) feemarkettypes.Params { params := w.FeeMarketKeeper.GetParams(ctx) + decimals := w.decimals() if !params.BaseFee.IsNil() { - params.BaseFee = types.ConvertAmountTo18DecimalsLegacy(params.BaseFee) + params.BaseFee = params.BaseFee.MulInt(decimals.ConversionFactor()) } params.MinGasPrice = types.ConvertAmountTo18DecimalsLegacy(params.MinGasPrice) return params diff --git a/x/vm/wrappers/feemarket_test.go b/x/vm/wrappers/feemarket_test.go index e23bfb713..134bce210 100644 --- a/x/vm/wrappers/feemarket_test.go +++ b/x/vm/wrappers/feemarket_test.go @@ -89,18 +89,12 @@ func TestGetBaseFee(t *testing.T) { 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)) + feeMarketWrapper := wrappers.NewFeeMarketWrapper(mockFeeMarketKeeper, tc.coinInfo) + result := feeMarketWrapper.GetBaseFee(sdk.Context{}) require.Equal(t, tc.expResult, result) }) @@ -179,17 +173,11 @@ func TestCalculateBaseFee(t *testing.T) { 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) + feeMarketWrapper := wrappers.NewFeeMarketWrapper(mockFeeMarketKeeper, tc.coinInfo) result := feeMarketWrapper.CalculateBaseFee(sdk.Context{}) require.Equal(t, tc.expResult, result) @@ -254,17 +242,11 @@ func TestGetParams(t *testing.T) { 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) + feeMarketWrapper := wrappers.NewFeeMarketWrapper(mockFeeMarketKeeper, tc.coinInfo) result := feeMarketWrapper.GetParams(sdk.Context{}) require.Equal(t, tc.expParams, result)