From 3df038bc76089426f30b94ed46f1eaa4b60d78a2 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 11 Feb 2025 09:26:06 +0000 Subject: [PATCH] feat(evm): add bech32 precompile - Allow conversion of Solidity `address` to bech32 `string` using the precompile. A prefix must be supplied. - Allow conversion of Solidity `string` from bech32 format to `address`. A prefix must not be supplied and is instead automatically derived. TODO: localnet testing via `cast call` the precompile directly, and via another contract to see how it handles failures --- precompiles/bech32/IBech32.sol | 27 ++++ precompiles/bech32/abi.json | 45 ++++++ precompiles/bech32/bech32.go | 103 ++++++++++++ precompiles/bech32/bech32_test.go | 249 +++++++++++++++++++++++++++++ precompiles/bech32/methods.go | 93 +++++++++++ precompiles/bech32/methods_test.go | 158 ++++++++++++++++++ precompiles/bech32/setup_test.go | 37 +++++ x/evm/keeper/precompiles.go | 39 +++-- x/evm/types/params.go | 4 +- 9 files changed, 736 insertions(+), 19 deletions(-) create mode 100644 precompiles/bech32/IBech32.sol create mode 100644 precompiles/bech32/abi.json create mode 100644 precompiles/bech32/bech32.go create mode 100644 precompiles/bech32/bech32_test.go create mode 100644 precompiles/bech32/methods.go create mode 100644 precompiles/bech32/methods_test.go create mode 100644 precompiles/bech32/setup_test.go diff --git a/precompiles/bech32/IBech32.sol b/precompiles/bech32/IBech32.sol new file mode 100644 index 000000000..a5c82d52e --- /dev/null +++ b/precompiles/bech32/IBech32.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.17; + +/// @dev The IBech32 contract's address. +address constant BECH32_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000400; + +IBech32 constant BECH32_CONTRACT = IBech32(BECH32_PRECOMPILE_ADDRESS); + +/// @author ExocoreNetwork +/// @title Bech32 Precompiled Contract +/// @dev This contract can be used by Solidity devs to convert from `string bech32Addr` to +/// `address 0xAddr` and vice versa. +/// @custom:address 0x0000000000000000000000000000000000000400 +interface IBech32 { + + /// @dev Defines a method for converting a hex formatted address to bech32. + /// @param addr The hex address to be converted. + /// @param prefix The human readable prefix (HRP) of the bech32 address. + /// @return bech32Address The address in bech32 format. + function hexToBech32(address addr, string memory prefix) external returns (string memory bech32Address); + + /// @dev Defines a method for converting a bech32 formatted address to hex. + /// @param bech32Address The bech32 address to be converted. + /// @return addr The address in hex format. + function bech32ToHex(string memory bech32Address) external returns (address addr); + +} diff --git a/precompiles/bech32/abi.json b/precompiles/bech32/abi.json new file mode 100644 index 000000000..c9f7161a3 --- /dev/null +++ b/precompiles/bech32/abi.json @@ -0,0 +1,45 @@ +[ + { + "type": "function", + "name": "bech32ToHex", + "inputs": [ + { + "name": "bech32Address", + "type": "string", + "internalType": "string" + } + ], + "outputs": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hexToBech32", + "inputs": [ + { + "name": "addr", + "type": "address", + "internalType": "address" + }, + { + "name": "prefix", + "type": "string", + "internalType": "string" + } + ], + "outputs": [ + { + "name": "bech32Address", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "nonpayable" + } +] \ No newline at end of file diff --git a/precompiles/bech32/bech32.go b/precompiles/bech32/bech32.go new file mode 100644 index 000000000..35f3b9705 --- /dev/null +++ b/precompiles/bech32/bech32.go @@ -0,0 +1,103 @@ +package bech32 + +import ( + "bytes" + "embed" + "fmt" + + 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/v16/precompiles/common" +) + +const ( + gasPerCall = 6_000 +) + +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 +} + +// NewPrecompile instantiates a new IBech32 precompile. +func NewPrecompile(authzKeeper authzkeeper.Keeper) (*Precompile, error) { + abiBz, err := f.ReadFile("abi.json") + if err != nil { + return nil, fmt.Errorf("error loading the deposit 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, + }, + }, nil +} + +// Address returns the address of the bech32 precompile. +func (p Precompile) Address() common.Address { + return common.HexToAddress("0x0000000000000000000000000000000000000400") +} + +// RequiredGas returns the gas required to execute the bech32 precompile. +func (p Precompile) RequiredGas([]byte) uint64 { + return gasPerCall +} + +// Run performs the bech32 precompile. +func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error) { + ctx, stateDB, method, initialGas, args, err := p.RunSetup(evm, contract, readOnly, p.IsTransaction) + if err != nil { + return nil, err + } + defer cmn.HandleGasError(ctx, contract, initialGas, &err)() + // bug fix to commit dirty objects + if err := stateDB.Commit(); err != nil { + return nil, err + } + + // TODO: check if stateless precompiles can return an error? + switch method.Name { + case MethodHexToBech32: + return p.HexToBech32(method, args) + case MethodBech32ToHex: + return p.Bech32ToHex(method, args) + } + + cost := ctx.GasMeter().GasConsumed() - initialGas + if !contract.UseGas(cost) { + return nil, vm.ErrOutOfGas + } + return nil, nil +} + +// IsTransaction reports whether a precompile is write (true) or read-only (false). +func (Precompile) IsTransaction(methodID string) bool { + switch methodID { + // explicitly mark read-only for these + case MethodBech32ToHex, MethodHexToBech32: + return false + default: + return false + } +} diff --git a/precompiles/bech32/bech32_test.go b/precompiles/bech32/bech32_test.go new file mode 100644 index 000000000..5d647ce05 --- /dev/null +++ b/precompiles/bech32/bech32_test.go @@ -0,0 +1,249 @@ +package bech32_test + +import ( + "math/big" + + "github.com/ExocoreNetwork/exocore/app" + "github.com/ExocoreNetwork/exocore/cmd/config" + "github.com/ExocoreNetwork/exocore/precompiles/bech32" + testutiltx "github.com/ExocoreNetwork/exocore/testutil/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + evmtypes "github.com/evmos/evmos/v16/x/evm/types" +) + +// TestRun tests the precompile's Run method. +func (s *Bech32PrecompileSuite) TestRun() { + inputAddr := testutiltx.GenerateAddress() + contract := vm.NewPrecompile( + vm.AccountRef(inputAddr), s.precompile, big.NewInt(0), uint64(1000000), + ) + + testCases := []struct { + name string + malleate func() *vm.Contract + postCheck func(data []byte) + expPass bool + errContains string + }{ + { + "fail - invalid method", + func() *vm.Contract { + contract.Input = []byte("invalid") + return contract + }, + nil, + false, + "no method with id", + }, + { + "fail - error during unpack", + func() *vm.Contract { + contract.Input = s.precompile.Methods[bech32.MethodHexToBech32].ID + return contract + }, + nil, + false, + "abi: attempting to unmarshall an empty string while arguments are expected", + }, + { + "fail - HexToBech32 method error", + func() *vm.Contract { + input, err := s.precompile.Pack(bech32.MethodHexToBech32, inputAddr, "") + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + nil, + false, + "empty bech32 prefix provided, expected a non-empty string", + }, + { + "pass - hex to bech32 account (exo)", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodHexToBech32, inputAddr, config.Bech32Prefix, + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodHexToBech32, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(string) + s.Require().True(ok) + s.Require().Equal(sdk.AccAddress(inputAddr.Bytes()).String(), addr) + }, + true, + "", + }, + { + "pass - hex to bech32 validator operator (exovaloper)", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodHexToBech32, inputAddr, config.Bech32PrefixValAddr, + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodHexToBech32, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(string) + s.Require().True(ok) + s.Require().Equal(sdk.ValAddress(inputAddr.Bytes()).String(), addr) + }, + true, + "", + }, + { + "pass - hex to bech32 consensus address (exovalcons)", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodHexToBech32, inputAddr, config.Bech32PrefixConsAddr, + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodHexToBech32, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(string) + s.Require().True(ok) + s.Require().Equal(sdk.ConsAddress(inputAddr.Bytes()).String(), addr) + }, + true, + "", + }, + { + "pass - bech32 to hex account address", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodBech32ToHex, sdk.AccAddress(inputAddr.Bytes()).String(), + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodBech32ToHex, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(common.Address) + s.Require().True(ok) + s.Require().Equal(inputAddr, addr) + }, + true, + "", + }, + { + "pass - bech32 to hex validator address", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodBech32ToHex, sdk.ValAddress(inputAddr.Bytes()).String(), + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodBech32ToHex, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(common.Address) + s.Require().True(ok) + s.Require().Equal(inputAddr, addr) + }, + true, + "", + }, + { + "pass - bech32 to hex consensus address", + func() *vm.Contract { + input, err := s.precompile.Pack( + bech32.MethodBech32ToHex, sdk.ValAddress(inputAddr.Bytes()).String(), + ) + s.Require().NoError(err, "failed to pack input") + contract.Input = input + return contract + }, + func(data []byte) { + args, err := s.precompile.Unpack(bech32.MethodBech32ToHex, data) + s.Require().NoError(err, "failed to unpack output") + s.Require().Len(args, 1) + addr, ok := args[0].(common.Address) + s.Require().True(ok) + s.Require().Equal(inputAddr, addr) + }, + true, + "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + contract := tc.malleate() + evm := s.getEvm(contract) + bz, err := s.precompile.Run(evm, contract, true) + // Check results + if tc.expPass { + // check test validity + s.Require().Empty(tc.errContains) + s.Require().NoError(err, "expected no error when running the precompile") + s.Require().NotNil(bz, "expected returned bytes not to be nil") + if tc.postCheck != nil { + tc.postCheck(bz) + } + } 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.Require().Nil(tc.postCheck) + } + }) + } +} + +func (s *Bech32PrecompileSuite) getEvm(contract *vm.Contract) *vm.EVM { + baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + 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") + + // Instantiate config + 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") + + // Instantiate EVM + evm := s.App.EvmKeeper.NewEVM( + s.Ctx, msg, cfg, nil, s.StateDB, + ) + return evm +} diff --git a/precompiles/bech32/methods.go b/precompiles/bech32/methods.go new file mode 100644 index 000000000..2f53612b2 --- /dev/null +++ b/precompiles/bech32/methods.go @@ -0,0 +1,93 @@ +package bech32 + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + cmn "github.com/evmos/evmos/v16/precompiles/common" +) + +const ( + // bech32 separator + separator = "1" +) + +// method IDs (without the type) +const ( + MethodHexToBech32 = "hexToBech32" + MethodBech32ToHex = "bech32ToHex" +) + +// HexToBech32 converts a hex address to a bech32 address with the provided human readable prefix. +func (p Precompile) HexToBech32( + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(method.Inputs), len(args)) + } + + address, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf("invalid hex address") + } + + // validate that the prefix is non-empty + prefix, _ := args[1].(string) + if strings.TrimSpace(prefix) == "" { + return nil, fmt.Errorf( + "empty bech32 prefix provided, expected a non-empty string", + ) + } + + // superfluous check; should never fail given len(common.Address) == 20 + if err := sdk.VerifyAddressFormat(address.Bytes()); err != nil { + return nil, err + } + + bech32Str, err := sdk.Bech32ifyAddressBytes(prefix, address.Bytes()) + if err != nil { + return nil, err + } + + return method.Outputs.Pack(bech32Str) +} + +// Bech32ToHex converts a bech32 address to a hex address. The prefix is automatically detected. +func (p Precompile) Bech32ToHex( + method *abi.Method, + args []interface{}, +) ([]byte, error) { + // validate the number of arguments + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(method.Inputs), len(args)) + } + + address, ok := args[0].(string) + if !ok || address == "" { + return nil, fmt.Errorf("invalid bech32 address") + } + + // find the prefix + prefix := strings.SplitN(address, separator, 2)[0] + if prefix == address { + return nil, fmt.Errorf("invalid bech32 address (no separator): %s", address) + } + + // get the address bytes + addressBz, err := sdk.GetFromBech32(address, prefix) + if err != nil { + return nil, err + } + + // validate the address bytes + if err := sdk.VerifyAddressFormat(addressBz); err != nil { + return nil, err + } + + // pack the address bytes + return method.Outputs.Pack(common.BytesToAddress(addressBz)) +} diff --git a/precompiles/bech32/methods_test.go b/precompiles/bech32/methods_test.go new file mode 100644 index 000000000..f35dc308e --- /dev/null +++ b/precompiles/bech32/methods_test.go @@ -0,0 +1,158 @@ +package bech32_test + +import ( + "fmt" + + "github.com/ExocoreNetwork/exocore/precompiles/bech32" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + cmn "github.com/evmos/evmos/v16/precompiles/common" + + testutiltx "github.com/ExocoreNetwork/exocore/testutil/tx" +) + +func (s *Bech32PrecompileSuite) TestHexToBech32() { + s.SetupTest() + + method := s.precompile.Methods[bech32.MethodHexToBech32] + sampleAddress := testutiltx.GenerateAddress() + samplePrefix := "pre" + sampleAddressConverted, err := sdk.Bech32ifyAddressBytes(samplePrefix, sampleAddress.Bytes()) + s.Require().NoError(err) + + testCases := []struct { + name string + args []interface{} + expErr bool + errContains string + postCheckIfNotErr func([]byte) + }{ + { + name: "invalid number of args", + args: []interface{}{}, + expErr: true, + errContains: fmt.Sprintf(cmn.ErrInvalidNumberOfArgs, len(method.Inputs), 0), + }, + { + name: "invalid hex address", + args: []interface{}{ + "", samplePrefix, + }, + expErr: true, + errContains: "invalid hex address", + }, + { + name: "empty bech32 prefix", + args: []interface{}{ + sampleAddress, "", + }, + expErr: true, + errContains: "empty bech32 prefix provided, expected a non-empty string", + }, + { + name: "valid", + args: []interface{}{ + sampleAddress, samplePrefix, + }, + expErr: false, + postCheckIfNotErr: func(data []byte) { + args, err := s.precompile.Unpack(method.Name, data) + s.Require().NoError(err) + s.Require().Len(args, 1) + addr, ok := args[0].(string) + s.Require().True(ok) + s.Require().Equal(sampleAddressConverted, addr) + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.precompile.HexToBech32(&method, tc.args) + if tc.expErr { + s.Require().Nil(tc.postCheckIfNotErr) + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errContains) + } else { + s.Require().Empty(tc.errContains) + s.Require().NoError(err) + s.Require().NotNil(res) + if tc.postCheckIfNotErr != nil { + tc.postCheckIfNotErr(res) + } + } + }) + } +} + +func (s *Bech32PrecompileSuite) TestBech32ToHex() { + s.SetupTest() + + method := s.precompile.Methods[bech32.MethodBech32ToHex] + sampleAddressHex := testutiltx.GenerateAddress() + sampleAddress := sdk.AccAddress(sampleAddressHex.Bytes()).String() + + testCases := []struct { + name string + args []interface{} + expErr bool + errContains string + postCheckIfNotErr func([]byte) + }{ + { + name: "invalid number of args", + args: []interface{}{}, + expErr: true, + errContains: fmt.Sprintf(cmn.ErrInvalidNumberOfArgs, len(method.Inputs), 0), + }, + { + name: "invalid bech32 address", + args: []interface{}{ + 1, + }, + expErr: true, + errContains: "invalid bech32 address", + }, + { + name: "no separator", + args: []interface{}{ + "hellothisisaninvalidaddress", + }, + expErr: true, + errContains: "invalid bech32 address (no separator)", + }, + { + name: "valid", + args: []interface{}{ + sampleAddress, + }, + expErr: false, + postCheckIfNotErr: func(data []byte) { + args, err := s.precompile.Unpack(method.Name, data) + s.Require().NoError(err) + s.Require().Len(args, 1) + addr, ok := args[0].(common.Address) + s.Require().True(ok) + s.Require().Equal(sampleAddressHex, addr) + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.precompile.Bech32ToHex(&method, tc.args) + if tc.expErr { + s.Require().Nil(tc.postCheckIfNotErr) + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errContains) + } else { + s.Require().Empty(tc.errContains) + s.Require().NoError(err) + s.Require().NotNil(res) + if tc.postCheckIfNotErr != nil { + tc.postCheckIfNotErr(res) + } + } + }) + } +} diff --git a/precompiles/bech32/setup_test.go b/precompiles/bech32/setup_test.go new file mode 100644 index 000000000..2bc93cf65 --- /dev/null +++ b/precompiles/bech32/setup_test.go @@ -0,0 +1,37 @@ +package bech32_test + +import ( + "testing" + + "github.com/ExocoreNetwork/exocore/precompiles/bech32" + + "github.com/ExocoreNetwork/exocore/testutil" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/stretchr/testify/suite" +) + +var s *Bech32PrecompileSuite + +type Bech32PrecompileSuite struct { + testutil.BaseTestSuite + precompile *bech32.Precompile +} + +func TestPrecompileTestSuite(t *testing.T) { + s = new(Bech32PrecompileSuite) + suite.Run(t, s) + + // Run Ginkgo integration tests + RegisterFailHandler(Fail) + RunSpecs(t, "bech32 Precompile Suite") +} + +func (s *Bech32PrecompileSuite) SetupTest() { + s.DoSetupTest() + precompile, err := bech32.NewPrecompile(s.App.AuthzKeeper) + s.Require().NoError(err) + s.precompile = precompile +} diff --git a/x/evm/keeper/precompiles.go b/x/evm/keeper/precompiles.go index 79dc3d2aa..aade1b553 100644 --- a/x/evm/keeper/precompiles.go +++ b/x/evm/keeper/precompiles.go @@ -6,15 +6,16 @@ import ( "sort" assetsprecompile "github.com/ExocoreNetwork/exocore/precompiles/assets" - avsManagerPrecompile "github.com/ExocoreNetwork/exocore/precompiles/avs" - blsPrecompile "github.com/ExocoreNetwork/exocore/precompiles/bls" + avsprecompile "github.com/ExocoreNetwork/exocore/precompiles/avs" + bechprecompile "github.com/ExocoreNetwork/exocore/precompiles/bech32" + blsprecompile "github.com/ExocoreNetwork/exocore/precompiles/bls" delegationprecompile "github.com/ExocoreNetwork/exocore/precompiles/delegation" - rewardPrecompile "github.com/ExocoreNetwork/exocore/precompiles/reward" - stakingStateKeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper" - avsManagerKeeper "github.com/ExocoreNetwork/exocore/x/avs/keeper" - delegationKeeper "github.com/ExocoreNetwork/exocore/x/delegation/keeper" - rewardKeeper "github.com/ExocoreNetwork/exocore/x/reward/keeper" - exoslashKeeper "github.com/ExocoreNetwork/exocore/x/slash/keeper" + rewardprecompile "github.com/ExocoreNetwork/exocore/precompiles/reward" + assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper" + avskeeper "github.com/ExocoreNetwork/exocore/x/avs/keeper" + delegationkeeper "github.com/ExocoreNetwork/exocore/x/delegation/keeper" + rewardkeeper "github.com/ExocoreNetwork/exocore/x/reward/keeper" + exoslashkeeper "github.com/ExocoreNetwork/exocore/x/slash/keeper" sdk "github.com/cosmos/cosmos-sdk/types" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" channelkeeper "github.com/cosmos/ibc-go/v7/modules/core/04-channel/keeper" @@ -34,11 +35,11 @@ func AvailablePrecompiles( authzKeeper authzkeeper.Keeper, _ transferkeeper.Keeper, _ channelkeeper.Keeper, - delegationKeeper delegationKeeper.Keeper, - assetskeeper stakingStateKeeper.Keeper, - _ exoslashKeeper.Keeper, - rewardKeeper rewardKeeper.Keeper, - avsManagerKeeper avsManagerKeeper.Keeper, + delegationKeeper delegationkeeper.Keeper, + assetskeeper assetskeeper.Keeper, + _ exoslashkeeper.Keeper, + rewardKeeper rewardkeeper.Keeper, + avsManagerKeeper avskeeper.Keeper, ) map[common.Address]vm.PrecompiledContract { // Clone the mapping from the latest EVM fork. precompiles := maps.Clone(vm.PrecompiledContractsBerlin) @@ -77,7 +78,7 @@ func AvailablePrecompiles( if err != nil { panic(fmt.Errorf("failed to load slash precompile: %w", err)) } - rewardPrecompile, err := rewardPrecompile.NewPrecompile( + rewardPrecompile, err := rewardprecompile.NewPrecompile( assetskeeper, rewardKeeper, authzKeeper, @@ -85,14 +86,19 @@ func AvailablePrecompiles( if err != nil { panic(fmt.Errorf("failed to load reward precompile: %w", err)) } - avsManagerPrecompile, err := avsManagerPrecompile.NewPrecompile(avsManagerKeeper, authzKeeper) + avsManagerPrecompile, err := avsprecompile.NewPrecompile(avsManagerKeeper, authzKeeper) if err != nil { panic(fmt.Errorf("failed to load avsManager precompile: %w", err)) } - blsPrecompile, err := blsPrecompile.NewPrecompile(BaseGas) + blsPrecompile, err := blsprecompile.NewPrecompile(BaseGas) if err != nil { panic(fmt.Errorf("failed to load bls precompile: %v", err)) } + bech32Precompile, err := bechprecompile.NewPrecompile(authzKeeper) + if err != nil { + panic(fmt.Errorf("failed to load bech32 precompile: %w", err)) + } + // precompiles[slashPrecompile.Address()] = slashPrecompile precompiles[rewardPrecompile.Address()] = rewardPrecompile precompiles[assetsPrecompile.Address()] = assetsPrecompile @@ -100,6 +106,7 @@ func AvailablePrecompiles( precompiles[avsManagerPrecompile.Address()] = avsManagerPrecompile // precompiles[ibcTransferPrecompile.Address()] = ibcTransferPrecompile precompiles[blsPrecompile.Address()] = blsPrecompile + precompiles[bech32Precompile.Address()] = bech32Precompile return precompiles } diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 428a5635d..7c6a9445e 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -10,14 +10,12 @@ var ( // DefaultEVMDenom defines the default EVM denomination on Exocore DefaultEVMDenom = utils.BaseDenom ExocoreAvailableEVMExtensions = []string{ - // 0x0000000000000000000000000000000000000801 client chains precompile has been merged to assets. + "0x0000000000000000000000000000000000000400", // bech32 precompile // "0x0000000000000000000000000000000000000802", // ICS20 transfer precompile "0x0000000000000000000000000000000000000804", // assets precompile "0x0000000000000000000000000000000000000805", // delegation precompile "0x0000000000000000000000000000000000000806", // reward precompile // "0x0000000000000000000000000000000000000807", // slash precompile - // 0x0000000000000000000000000000000000000808 withdraw precompile has been merged to assets. - // the function has been merged to the assets precompile "0x0000000000000000000000000000000000000809", // bls precompile "0x0000000000000000000000000000000000000901", // avs precompile }