From 6d60aec810fa2a05e091b243e5e54debd3cdb1ee Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:56:53 +0000 Subject: [PATCH 1/5] feat(precompile): add `getClientChains` function This function can be used by the `ExocoreGateway` to fetch all currently registered client chains. This list is used to send a `markBootstrapped` transaction to all the destination chains. TODO: at this point, the function to mark bootstrap on all chains must be triggered manually. This call should be automated in the future. --- precompiles/clientchains/abi.json | 21 +++ precompiles/clientchains/clientchains.go | 120 ++++++++++++++++ precompiles/clientchains/clientchains.sol | 19 +++ precompiles/clientchains/clientchains_test.go | 131 ++++++++++++++++++ precompiles/clientchains/setup_test.go | 32 +++++ precompiles/clientchains/tx.go | 39 ++++++ x/assets/types/errors.go | 6 + x/evm/keeper/precompiles.go | 10 ++ x/evm/types/params.go | 1 + 9 files changed, 379 insertions(+) create mode 100644 precompiles/clientchains/abi.json create mode 100644 precompiles/clientchains/clientchains.go create mode 100644 precompiles/clientchains/clientchains.sol create mode 100644 precompiles/clientchains/clientchains_test.go create mode 100644 precompiles/clientchains/setup_test.go create mode 100644 precompiles/clientchains/tx.go diff --git a/precompiles/clientchains/abi.json b/precompiles/clientchains/abi.json new file mode 100644 index 000000000..eeb2160b7 --- /dev/null +++ b/precompiles/clientchains/abi.json @@ -0,0 +1,21 @@ +[ + { + "name": "getClientChains", + "type": "function", + "inputs": [], + "outputs": [ + { + "name": "success", + "type": "bool", + "internalType": "bool" + }, + { + "name": "chainIds", + "type": "uint16[]", + "internalType": "uint16[]" + } + ], + "stateMutability": "view", + "payable": false + } +] \ No newline at end of file diff --git a/precompiles/clientchains/clientchains.go b/precompiles/clientchains/clientchains.go new file mode 100644 index 000000000..57d67f333 --- /dev/null +++ b/precompiles/clientchains/clientchains.go @@ -0,0 +1,120 @@ +package clientchains + +import ( + "bytes" + "embed" + "fmt" + + assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + "github.com/ethereum/go-ethereum/accounts/abi" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + cmn "github.com/evmos/evmos/v14/precompiles/common" +) + +var _ vm.PrecompiledContract = &Precompile{} + +// Embed abi json file to the executable binary. Needed when importing as dependency. +// +//go:embed abi.json +var f embed.FS + +// Precompile defines the precompiled contract for deposit. +type Precompile struct { + cmn.Precompile + assetsKeeper assetskeeper.Keeper +} + +// NewPrecompile creates a new deposit Precompile instance as a +// PrecompiledContract interface. +func NewPrecompile( + assetsKeeper assetskeeper.Keeper, + authzKeeper authzkeeper.Keeper, +) (*Precompile, error) { + abiBz, err := f.ReadFile("abi.json") + if err != nil { + return nil, fmt.Errorf("error loading the client chains ABI %s", err) + } + + newAbi, err := abi.JSON(bytes.NewReader(abiBz)) + if err != nil { + return nil, fmt.Errorf(cmn.ErrInvalidABI, err) + } + + return &Precompile{ + Precompile: cmn.Precompile{ + ABI: newAbi, + AuthzKeeper: authzKeeper, + KvGasConfig: storetypes.KVGasConfig(), + TransientKVGasConfig: storetypes.TransientGasConfig(), + // should be configurable in the future. + ApprovalExpiration: cmn.DefaultExpirationDuration, + }, + assetsKeeper: assetsKeeper, + }, nil +} + +// Address defines the address of the client chains precompile contract. +// address: 0x0000000000000000000000000000000000000801 +func (p Precompile) Address() common.Address { + return common.HexToAddress("0x0000000000000000000000000000000000000801") +} + +// RequiredGas calculates the precompiled contract's base gas rate. +func (p Precompile) RequiredGas(input []byte) uint64 { + methodID := input[:4] + + method, err := p.MethodById(methodID) + if err != nil { + // This should never happen since this method is going to fail during Run + return 0 + } + return p.Precompile.RequiredGas(input, p.IsTransaction(method.Name)) +} + +// Run executes the precompiled contract client chain methods defined in the ABI. +func (p Precompile) Run( + evm *vm.EVM, contract *vm.Contract, readOnly bool, +) (bz []byte, err error) { + // if the user calls instead of staticcalls, it is their problem. we don't validate that. + ctx, _, method, initialGas, args, err := p.RunSetup( + evm, contract, readOnly, p.IsTransaction, + ) + if err != nil { + return nil, err + } + + // This handles any out of gas errors that may occur during the execution of a precompile tx + // or query. It avoids panics and returns the out of gas error so the EVM can continue + // gracefully. + defer cmn.HandleGasError(ctx, contract, initialGas, &err)() + + bz, err = p.GetClientChains(ctx, method, args) + + if err != nil { + ctx.Logger().Error( + "call client chains precompile error", + "module", "client chains precompile", + "err", err, + ) + return nil, err + } + + cost := ctx.GasMeter().GasConsumed() - initialGas + + if !contract.UseGas(cost) { + return nil, vm.ErrOutOfGas + } + + return bz, nil +} + +// IsTransaction checks if the given methodID corresponds to a transaction (true) +// or query (false). +func (Precompile) IsTransaction(string) bool { + // there are no transaction methods in this precompile, only queries. + return false +} diff --git a/precompiles/clientchains/clientchains.sol b/precompiles/clientchains/clientchains.sol new file mode 100644 index 000000000..e687ff062 --- /dev/null +++ b/precompiles/clientchains/clientchains.sol @@ -0,0 +1,19 @@ +pragma solidity >=0.8.17; + +/// @dev The CLIENT_CHAINS contract's address. +address constant CLIENT_CHAINS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + +/// @dev The CLIENT_CHAINS contract's instance. +IClientChains constant CLIENT_CHAINS_CONTRACT = IClientChains( + CLIENT_CHAINS_PRECOMPILE_ADDRESS +); + +/// @author Exocore Team +/// @title Client Chains Precompile Contract +/// @dev The interface through which solidity contracts will interact with ClientChains +/// @custom:address 0x0000000000000000000000000000000000000801 +interface IClientChains { + /// @dev Returns the chain indices of the client chains. + function getClientChains() external view returns (bool, uint16[] memory); +} + diff --git a/precompiles/clientchains/clientchains_test.go b/precompiles/clientchains/clientchains_test.go new file mode 100644 index 000000000..8315f3d72 --- /dev/null +++ b/precompiles/clientchains/clientchains_test.go @@ -0,0 +1,131 @@ +package clientchains_test + +import ( + "math/big" + + "github.com/ExocoreNetwork/exocore/app" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + evmtypes "github.com/evmos/evmos/v14/x/evm/types" +) + +func (s *ClientChainsPrecompileSuite) TestIsTransaction() { + testCases := []struct { + name string + method string + isTx bool + }{ + { + "non existant method", + "HelloFakeMethod", + false, + }, + { + "actual method", + "getClientChains", + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.Require().Equal(s.precompile.IsTransaction(tc.method), tc.isTx) + }) + } +} + +func paddingClientChainAddress(input []byte, outputLength int) []byte { + if len(input) < outputLength { + padding := make([]byte, outputLength-len(input)) + return append(input, padding...) + } + return input +} + +func (s *ClientChainsPrecompileSuite) TestGetClientChains() { + input, err := s.precompile.Pack("getClientChains") + s.Require().NoError(err, "failed to pack input") + output, err := s.precompile.Methods["getClientChains"].Outputs.Pack(true, []uint16{101}) + s.Require().NoError(err, "failed to pack output") + testcases := []struct { + name string + malleate func() []byte + readOnly bool + expPass bool + errContains string + returnBytes []byte + }{ + { + name: "get client chains", + malleate: func() []byte { + return input + }, + readOnly: true, + expPass: true, + returnBytes: output, + }, + } + for _, tc := range testcases { + tc := tc + s.Run(tc.name, func() { + s.SetupTest() + baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + contract := vm.NewPrecompile( + vm.AccountRef(s.Address), + s.precompile, + big.NewInt(0), + uint64(1e6), + ) + contract.Input = tc.malleate() + contractAddr := contract.Address() + txArgs := evmtypes.EvmTxArgs{ + ChainID: s.App.EvmKeeper.ChainID(), + Nonce: 0, + To: &contractAddr, + Amount: nil, + GasLimit: 100000, + GasPrice: app.MainnetMinGasPrices.BigInt(), + GasFeeCap: baseFee, + GasTipCap: big.NewInt(1), + Accesses: ðtypes.AccessList{}, + } + msgEthereumTx := evmtypes.NewTx(&txArgs) + msgEthereumTx.From = s.Address.String() + err := msgEthereumTx.Sign(s.EthSigner, s.Signer) + s.Require().NoError(err, "failed to sign Ethereum message") + proposerAddress := s.Ctx.BlockHeader().ProposerAddress + cfg, err := s.App.EvmKeeper.EVMConfig( + s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID(), + ) + s.Require().NoError(err, "failed to instantiate EVM config") + msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) + s.Require().NoError(err, "failed to instantiate Ethereum message") + evm := s.App.EvmKeeper.NewEVM( + s.Ctx, msg, cfg, nil, s.StateDB, + ) + params := s.App.EvmKeeper.GetParams(s.Ctx) + activePrecompiles := params.GetActivePrecompilesAddrs() + precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) + s.Require().NoError(err, "invalid precompiles", activePrecompiles) + evm.WithPrecompiles(precompileMap, activePrecompiles) + bz, err := s.precompile.Run(evm, contract, tc.readOnly) + if tc.expPass { + s.Require().NoError( + err, "expected no error when running the precompile", + ) + s.Require().Equal( + tc.returnBytes, bz, "the return doesn't match the expected result", + ) + } else { + s.Require().Error( + err, "expected error to be returned when running the precompile", + ) + s.Require().Nil( + bz, "expected returned bytes to be nil", + ) + s.Require().ErrorContains(err, tc.errContains) + } + }) + } +} diff --git a/precompiles/clientchains/setup_test.go b/precompiles/clientchains/setup_test.go new file mode 100644 index 000000000..2ca76b016 --- /dev/null +++ b/precompiles/clientchains/setup_test.go @@ -0,0 +1,32 @@ +package clientchains_test + +import ( + "testing" + + "github.com/ExocoreNetwork/exocore/precompiles/clientchains" + "github.com/ExocoreNetwork/exocore/testutil" + + "github.com/stretchr/testify/suite" +) + +var s *ClientChainsPrecompileSuite + +type ClientChainsPrecompileSuite struct { + testutil.BaseTestSuite + + precompile *clientchains.Precompile +} + +func TestPrecompileTestSuite(t *testing.T) { + s = new(ClientChainsPrecompileSuite) + suite.Run(t, s) +} + +func (s *ClientChainsPrecompileSuite) SetupTest() { + s.DoSetupTest() + precompile, err := clientchains.NewPrecompile( + s.App.AssetsKeeper, s.App.AuthzKeeper, + ) + s.Require().NoError(err) + s.precompile = precompile +} diff --git a/precompiles/clientchains/tx.go b/precompiles/clientchains/tx.go new file mode 100644 index 000000000..316137277 --- /dev/null +++ b/precompiles/clientchains/tx.go @@ -0,0 +1,39 @@ +package clientchains + +import ( + "math" + + errorsmod "cosmossdk.io/errors" + assetstypes "github.com/ExocoreNetwork/exocore/x/assets/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" +) + +func (p Precompile) GetClientChains( + ctx sdk.Context, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) > 0 { + return nil, errorsmod.Wrapf(assetstypes.ErrInvalidInput, "no input is required") + } + infos, err := p.assetsKeeper.GetAllClientChainInfo(ctx) + if err != nil { + return nil, err + } + ids := make([]uint16, 0, len(infos)) + for id := range infos { + // technically LZ supports uint32, but unfortunately all the precompiles + // based it on uint16, so we have to stick with it. + // TODO: change it to uint32 here and in other precompiles. + if id > math.MaxUint16 { + return nil, errorsmod.Wrapf( + assetstypes.ErrInvalidInput, "client chain id is too large", + ) + } + // #nosec G701 // already checked + convId := uint16(id) + ids = append(ids, convId) + } + return method.Outputs.Pack(true, ids) +} diff --git a/x/assets/types/errors.go b/x/assets/types/errors.go index bc3592d69..ae1f7b5b3 100644 --- a/x/assets/types/errors.go +++ b/x/assets/types/errors.go @@ -10,6 +10,7 @@ var ( ModuleName, 0, "there is no stored key for the input chain index", ) + ErrNoClientChainAssetKey = errorsmod.Register( ModuleName, 1, "there is no stored key for the input assetID", @@ -80,4 +81,9 @@ var ( ModuleName, 14, "the genesis data supplied is invalid", ) + + ErrInvalidInput = errorsmod.Register( + ModuleName, 15, + "the input is invalid", + ) ) diff --git a/x/evm/keeper/precompiles.go b/x/evm/keeper/precompiles.go index 7fcdf91ed..8daf2b77c 100644 --- a/x/evm/keeper/precompiles.go +++ b/x/evm/keeper/precompiles.go @@ -6,6 +6,7 @@ import ( avsManagerPrecompile "github.com/ExocoreNetwork/exocore/precompiles/avs" taskPrecompile "github.com/ExocoreNetwork/exocore/precompiles/avsTask" blsPrecompile "github.com/ExocoreNetwork/exocore/precompiles/bls" + clientchainsprecompile "github.com/ExocoreNetwork/exocore/precompiles/clientchains" delegationprecompile "github.com/ExocoreNetwork/exocore/precompiles/delegation" depositprecompile "github.com/ExocoreNetwork/exocore/precompiles/deposit" rewardPrecompile "github.com/ExocoreNetwork/exocore/precompiles/reward" @@ -68,6 +69,14 @@ func AvailablePrecompiles( } // add exoCore chain preCompiles + clientChainsPrecompile, err := clientchainsprecompile.NewPrecompile( + stakingStateKeeper, + authzKeeper, + ) + if err != nil { + panic(fmt.Errorf("failed to load client chains precompile: %w", err)) + } + depositPrecompile, err := depositprecompile.NewPrecompile( stakingStateKeeper, depositKeeper, @@ -123,6 +132,7 @@ func AvailablePrecompiles( precompiles[slashPrecompile.Address()] = slashPrecompile precompiles[rewardPrecompile.Address()] = rewardPrecompile precompiles[withdrawPrecompile.Address()] = withdrawPrecompile + precompiles[clientChainsPrecompile.Address()] = clientChainsPrecompile precompiles[depositPrecompile.Address()] = depositPrecompile precompiles[delegationPrecompile.Address()] = delegationPrecompile precompiles[avsManagerPrecompile.Address()] = avsManagerPrecompile diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 3207d58f7..6021d9a4c 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -11,6 +11,7 @@ var ( DefaultEVMDenom = utils.BaseDenom ExocoreAvailableEVMExtensions = []string{ "0x0000000000000000000000000000000000000800", // Staking precompile + "0x0000000000000000000000000000000000000801", // client chains precompile "0x0000000000000000000000000000000000000802", // ICS20 transfer precompile "0x0000000000000000000000000000000000000804", // deposit precompile "0x0000000000000000000000000000000000000805", // delegation precompile From 98bb1414cb538be6920147042c04303cbdd43559 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:02:12 +0000 Subject: [PATCH 2/5] chore(lint): golang lint --- precompiles/clientchains/tx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/precompiles/clientchains/tx.go b/precompiles/clientchains/tx.go index 316137277..04ed6681a 100644 --- a/precompiles/clientchains/tx.go +++ b/precompiles/clientchains/tx.go @@ -32,8 +32,8 @@ func (p Precompile) GetClientChains( ) } // #nosec G701 // already checked - convId := uint16(id) - ids = append(ids, convId) + convID := uint16(id) + ids = append(ids, convID) } return method.Outputs.Pack(true, ids) } From e3d38051f2fd8df273d06f80543ff006e0a2ea98 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:03:46 +0000 Subject: [PATCH 3/5] build(ci): pin consensus warn version With the upgrade to Go 1.22 on the main branch of orijtech/consensuswarn, the workflow is failing for us (since we are on Go 1.21). See 201f337c5bad0d18ccd62bea5ec211c9c638938e. This change pins the version to one commit before the dependency bump. --- .github/workflows/consensuswarn.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/consensuswarn.yml b/.github/workflows/consensuswarn.yml index 82e8919a6..b7d860760 100644 --- a/.github/workflows/consensuswarn.yml +++ b/.github/workflows/consensuswarn.yml @@ -20,7 +20,7 @@ jobs: steps: # This is used for warning when a PR touches any of the roots, or any function or method directly or indirectly called by a root - uses: actions/checkout@v4 - - uses: orijtech/consensuswarn@main + - uses: orijtech/consensuswarn@956f047a43f56021a28afdfb2a2291a20955f48d with: # example.com/pkg/path.Type.Method roots: 'github.com/ExocoreNetwork/exocore/app.ExocoreApp.DeliverTx,github.com/ExocoreNetwork/exocore/app.ExocoreApp.BeginBlocker,github.com/ExocoreNetwork/exocore/app.ExocoreApp.EndBlocker' From 19c6f928dab90483653e336d6b66fce60ea055bb Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:09:00 +0000 Subject: [PATCH 4/5] chore(lint): gofumpt the file --- precompiles/clientchains/clientchains.go | 1 - 1 file changed, 1 deletion(-) diff --git a/precompiles/clientchains/clientchains.go b/precompiles/clientchains/clientchains.go index 57d67f333..adf8460ec 100644 --- a/precompiles/clientchains/clientchains.go +++ b/precompiles/clientchains/clientchains.go @@ -93,7 +93,6 @@ func (p Precompile) Run( defer cmn.HandleGasError(ctx, contract, initialGas, &err)() bz, err = p.GetClientChains(ctx, method, args) - if err != nil { ctx.Logger().Error( "call client chains precompile error", From 3b40a7302b9cfe41e8ed9377d5dd3be2ec3e8c3f Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:13:43 +0000 Subject: [PATCH 5/5] test(precompile): remove superfluous fields --- precompiles/clientchains/clientchains_test.go | 131 +++++++----------- 1 file changed, 50 insertions(+), 81 deletions(-) diff --git a/precompiles/clientchains/clientchains_test.go b/precompiles/clientchains/clientchains_test.go index 8315f3d72..2bd3b6b1d 100644 --- a/precompiles/clientchains/clientchains_test.go +++ b/precompiles/clientchains/clientchains_test.go @@ -47,85 +47,54 @@ func (s *ClientChainsPrecompileSuite) TestGetClientChains() { s.Require().NoError(err, "failed to pack input") output, err := s.precompile.Methods["getClientChains"].Outputs.Pack(true, []uint16{101}) s.Require().NoError(err, "failed to pack output") - testcases := []struct { - name string - malleate func() []byte - readOnly bool - expPass bool - errContains string - returnBytes []byte - }{ - { - name: "get client chains", - malleate: func() []byte { - return input - }, - readOnly: true, - expPass: true, - returnBytes: output, - }, - } - for _, tc := range testcases { - tc := tc - s.Run(tc.name, func() { - s.SetupTest() - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) - contract := vm.NewPrecompile( - vm.AccountRef(s.Address), - s.precompile, - big.NewInt(0), - uint64(1e6), - ) - contract.Input = tc.malleate() - contractAddr := contract.Address() - txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), - Nonce: 0, - To: &contractAddr, - Amount: nil, - GasLimit: 100000, - GasPrice: app.MainnetMinGasPrices.BigInt(), - GasFeeCap: baseFee, - GasTipCap: big.NewInt(1), - Accesses: ðtypes.AccessList{}, - } - msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig( - s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID(), - ) - s.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, - ) - params := s.App.EvmKeeper.GetParams(s.Ctx) - activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) - err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) - evm.WithPrecompiles(precompileMap, activePrecompiles) - bz, err := s.precompile.Run(evm, contract, tc.readOnly) - if tc.expPass { - s.Require().NoError( - err, "expected no error when running the precompile", - ) - s.Require().Equal( - tc.returnBytes, bz, "the return doesn't match the expected result", - ) - } else { - s.Require().Error( - err, "expected error to be returned when running the precompile", - ) - s.Require().Nil( - bz, "expected returned bytes to be nil", - ) - s.Require().ErrorContains(err, tc.errContains) - } - }) - } + s.Run("get client chains", func() { + s.SetupTest() + baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + contract := vm.NewPrecompile( + vm.AccountRef(s.Address), + s.precompile, + big.NewInt(0), + uint64(1e6), + ) + contract.Input = input + contractAddr := contract.Address() + txArgs := evmtypes.EvmTxArgs{ + ChainID: s.App.EvmKeeper.ChainID(), + Nonce: 0, + To: &contractAddr, + Amount: nil, + GasLimit: 100000, + GasPrice: app.MainnetMinGasPrices.BigInt(), + GasFeeCap: baseFee, + GasTipCap: big.NewInt(1), + Accesses: ðtypes.AccessList{}, + } + msgEthereumTx := evmtypes.NewTx(&txArgs) + msgEthereumTx.From = s.Address.String() + err := msgEthereumTx.Sign(s.EthSigner, s.Signer) + s.Require().NoError(err, "failed to sign Ethereum message") + proposerAddress := s.Ctx.BlockHeader().ProposerAddress + cfg, err := s.App.EvmKeeper.EVMConfig( + s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID(), + ) + s.Require().NoError(err, "failed to instantiate EVM config") + msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) + s.Require().NoError(err, "failed to instantiate Ethereum message") + evm := s.App.EvmKeeper.NewEVM( + s.Ctx, msg, cfg, nil, s.StateDB, + ) + params := s.App.EvmKeeper.GetParams(s.Ctx) + activePrecompiles := params.GetActivePrecompilesAddrs() + precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) + s.Require().NoError(err, "invalid precompiles", activePrecompiles) + evm.WithPrecompiles(precompileMap, activePrecompiles) + bz, err := s.precompile.Run(evm, contract, true) + s.Require().NoError( + err, "expected no error when running the precompile", + ) + s.Require().Equal( + output, bz, "the return doesn't match the expected result", + ) + }) }