From ce59cc7899f88aee786636a50c9a0826e8e98820 Mon Sep 17 00:00:00 2001 From: Unique-Divine Date: Mon, 1 Jul 2024 02:19:32 -0500 Subject: [PATCH] feat(evm): contract embeds, create fun token from ERC20 --- CHANGELOG.md | 1 + app/server/config/server_config.go | 6 +- app/server/start.go | 4 +- eth/rpc/rpcapi/eth_api_test.go | 6 +- proto/eth/evm/v1/tx.proto | 27 ++ x/evm/const.go | 19 +- x/evm/deps.go | 4 + x/evm/embeds/ERC20Minter.sol | 76 ++++ x/evm/embeds/ERC20MinterCompiled.json | 421 +++++++++++++++++ x/evm/embeds/README.md | 13 + x/evm/embeds/embeds.go | 179 ++++++++ x/evm/embeds/embeds_test.go | 21 + x/evm/evmtest/fixtures.go | 99 ---- x/evm/evmtest/fixtures_test.go | 11 - x/evm/evmtest/tx.go | 81 +++- x/evm/json_tx_args.go | 5 +- x/evm/keeper/erc20.go | 228 ++++++++++ x/evm/keeper/erc20_test.go | 94 ++++ x/evm/keeper/funtoken_state_test.go | 6 +- x/evm/keeper/grpc_query_test.go | 32 +- x/evm/keeper/keeper_test.go | 4 +- x/evm/keeper/msg_ethereum_tx_test.go | 4 +- x/evm/keeper/msg_server.go | 121 ++++- x/evm/msg.go | 36 +- x/evm/tx.pb.go | 621 +++++++++++++++++++++++--- 25 files changed, 1887 insertions(+), 232 deletions(-) create mode 100644 x/evm/embeds/ERC20Minter.sol create mode 100644 x/evm/embeds/ERC20MinterCompiled.json create mode 100644 x/evm/embeds/README.md create mode 100644 x/evm/embeds/embeds.go create mode 100644 x/evm/embeds/embeds_test.go delete mode 100644 x/evm/evmtest/fixtures.go delete mode 100644 x/evm/evmtest/fixtures_test.go create mode 100644 x/evm/keeper/erc20.go create mode 100644 x/evm/keeper/erc20_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1903de5ff..866514360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1922](https://github.com/NibiruChain/nibiru/pull/1922) - feat(evm): tracer option is read from the config. - [#1936](https://github.com/NibiruChain/nibiru/pull/1936) - feat(evm): EVM fungible token protobufs and encoding tests - [#1947](https://github.com/NibiruChain/nibiru/pull/1947) - fix(evm): fix FunToken state marshalling +- [#1950](https://github.com/NibiruChain/nibiru/pull/1950) - feat(evm): Tx to create FunToken mapping from ERC20, contract embeds, and ERC20 queries. #### Dapp modules: perp, spot, oracle, etc diff --git a/app/server/config/server_config.go b/app/server/config/server_config.go index 5b34d7a77..907833d9a 100644 --- a/app/server/config/server_config.go +++ b/app/server/config/server_config.go @@ -56,8 +56,8 @@ const ( // DefaultMaxTxGasWanted is the default gas wanted for each eth tx returned in ante handler in check tx mode DefaultMaxTxGasWanted = 0 - // DefaultGasCap is the default cap on gas that can be used in eth_call/estimateGas - DefaultGasCap uint64 = 25000000 + // DefaultEthCallGasLimit is the default cap on gas that can be used in eth_call/estimateGas + DefaultEthCallGasLimit uint64 = 25_000_000 // DefaultFilterCap is the default cap for total number of filters that can be created DefaultFilterCap int32 = 200 @@ -246,7 +246,7 @@ func DefaultJSONRPCConfig() *JSONRPCConfig { API: GetDefaultAPINamespaces(), Address: DefaultJSONRPCAddress, WsAddress: DefaultJSONRPCWsAddress, - GasCap: DefaultGasCap, + GasCap: DefaultEthCallGasLimit, EVMTimeout: DefaultEVMTimeout, TxFeeCap: DefaultTxFeeCap, FilterCap: DefaultFilterCap, diff --git a/app/server/start.go b/app/server/start.go index 0eb9d689e..f1d56829e 100644 --- a/app/server/start.go +++ b/app/server/start.go @@ -185,8 +185,8 @@ which accepts a path for the resulting pprof file. cmd.Flags().StringSlice(JSONRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled") cmd.Flags().String(JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on") cmd.Flags().String(JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on") - cmd.Flags().Uint64(JSONRPCGasCap, config.DefaultGasCap, "Sets a cap on gas that can be used in eth_call/estimateGas unit is unibi (0=infinite)") //nolint:lll - cmd.Flags().Float64(JSONRPCTxFeeCap, config.DefaultTxFeeCap, "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 nibi)") //nolint:lll + cmd.Flags().Uint64(JSONRPCGasCap, config.DefaultEthCallGasLimit, "Sets a cap on gas that can be used in eth_call/estimateGas unit is unibi (0=infinite)") //nolint:lll + cmd.Flags().Float64(JSONRPCTxFeeCap, config.DefaultTxFeeCap, "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 nibi)") //nolint:lll cmd.Flags().Int32(JSONRPCFilterCap, config.DefaultFilterCap, "Sets the global cap for total number of filters that can be created") cmd.Flags().Duration(JSONRPCEVMTimeout, config.DefaultEVMTimeout, "Sets a timeout used for eth_call (0=infinite)") cmd.Flags().Duration(JSONRPCHTTPTimeout, config.DefaultHTTPTimeout, "Sets a read/write timeout for json-rpc http server (0=infinite)") diff --git a/eth/rpc/rpcapi/eth_api_test.go b/eth/rpc/rpcapi/eth_api_test.go index 8b9527038..35288b4e8 100644 --- a/eth/rpc/rpcapi/eth_api_test.go +++ b/eth/rpc/rpcapi/eth_api_test.go @@ -18,6 +18,7 @@ import ( nibirucommon "github.com/NibiruChain/nibiru/x/common" "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/x/evm/embeds" "github.com/NibiruChain/nibiru/x/evm/evmtest" "github.com/stretchr/testify/suite" @@ -42,7 +43,7 @@ type TestSuite struct { fundedAccEthAddr gethcommon.Address fundedAccNibiAddr sdk.AccAddress - contractData evmtest.CompiledEvmContract + contractData embeds.CompiledEvmContract } func TestSuite_RunAll(t *testing.T) { @@ -63,7 +64,8 @@ func (s *TestSuite) SetupSuite() { s.network = network s.ethClient = network.Validators[0].JSONRPCClient - s.contractData = evmtest.SmartContract_FunToken.Load(s.T()) + s.contractData, err = embeds.SmartContract_FunToken.Load() + s.Require().NoError(err) testAccPrivateKey, _ := crypto.GenerateKey() s.fundedAccPrivateKey = testAccPrivateKey diff --git a/proto/eth/evm/v1/tx.proto b/proto/eth/evm/v1/tx.proto index 0cadc1866..2e393a929 100644 --- a/proto/eth/evm/v1/tx.proto +++ b/proto/eth/evm/v1/tx.proto @@ -20,6 +20,11 @@ service Msg { // UpdateParams defined a governance operation for updating the x/evm module parameters. // The authority is hard-coded to the Cosmos SDK x/gov module account rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); + + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a bank coin, or the + // denomination for a bank coin can be given to create the mapping to an ERC20. + rpc CreateFunToken(MsgCreateFunToken) returns (MsgCreateFunTokenResponse); } // MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. @@ -176,3 +181,25 @@ message MsgUpdateParams { // MsgUpdateParamsResponse defines the response structure for executing a // MsgUpdateParams message. message MsgUpdateParamsResponse {} + +// MsgCreateFunToken: Arguments to create a "FunToken" mapping. Either the ERC20 +// contract address can be given to create the mapping to a bank coin, or the +// denomination for a bank coin can be given to create the mapping to an ERC20. +message MsgCreateFunToken { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + string from_erc20 = 1 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/eth.HexAddr", + (gogoproto.nullable) = false + ]; + + // Coin denomination in the Bank Module. + string from_bank_denom = 2; + + // Sender: Address for the signer of the transaction. + string sender = 3; +} + +message MsgCreateFunTokenResponse { + // Fungible token mapping corresponding to ERC20 tokens. + eth.evm.v1.FunToken funtoken_mapping = 1 [(gogoproto.nullable) = false]; +} diff --git a/x/evm/const.go b/x/evm/const.go index aaf521983..e09ce6e43 100644 --- a/x/evm/const.go +++ b/x/evm/const.go @@ -3,7 +3,8 @@ package evm import ( "github.com/NibiruChain/collections" - "github.com/ethereum/go-ethereum/common" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" ) const ( @@ -49,12 +50,12 @@ const ( var KeyPrefixBzAccState = KeyPrefixAccState.Prefix() // PrefixAccStateEthAddr returns a prefix to iterate over a given account storage. -func PrefixAccStateEthAddr(address common.Address) []byte { +func PrefixAccStateEthAddr(address gethcommon.Address) []byte { return append(KeyPrefixBzAccState, address.Bytes()...) } // StateKey defines the full key under which an account state is stored. -func StateKey(address common.Address, key []byte) []byte { +func StateKey(address gethcommon.Address, key []byte) []byte { return append(PrefixAccStateEthAddr(address), key...) } @@ -71,3 +72,15 @@ const ( // CallTypeSmart call type is used in case of smart contract methods calls CallTypeSmart ) + +// ModuleAddressEVM: Module account address as a `gethcommon.Address`. +func ModuleAddressEVM() gethcommon.Address { + if len(evmModuleAddr) == 0 { + evmModuleAddr = gethcommon.BytesToAddress( + authtypes.NewModuleAddress(ModuleName).Bytes(), + ) + } + return evmModuleAddr +} + +var evmModuleAddr gethcommon.Address diff --git a/x/evm/deps.go b/x/evm/deps.go index 1dab77147..92bc38fb9 100644 --- a/x/evm/deps.go +++ b/x/evm/deps.go @@ -4,6 +4,7 @@ package evm import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" gethcore "github.com/ethereum/go-ethereum/core" gethcoretypes "github.com/ethereum/go-ethereum/core/types" @@ -29,6 +30,9 @@ type BankKeeper interface { SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + + GetDenomMetaData(ctx sdk.Context, denom string) (metadata bank.Metadata, isFound bool) + SetDenomMetaData(ctx sdk.Context, denomMetaData bank.Metadata) } // StakingKeeper returns the historical headers kept in store. diff --git a/x/evm/embeds/ERC20Minter.sol b/x/evm/embeds/ERC20Minter.sol new file mode 100644 index 000000000..90aed1e04 --- /dev/null +++ b/x/evm/embeds/ERC20Minter.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @dev {ERC20} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * + * The contract owner is set automatically in the constructor as the + * deployer due to "Ownable". + * + * The Context contract is inherited indirectly through "ERC20" and "Ownable". + * + * The account that deploys the contract will be able to mint and burn tokens. + */ +contract ERC20Minter is ERC20, ERC20Burnable, Ownable { + uint8 private _decimals; + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` to the + * account that deploys the contract and customizes tokens decimals + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol, uint8 decimals_) + ERC20(name, symbol) { + _setupDecimals(decimals_); + } + + /** + * @dev Sets `_decimals` as `decimals_ once at Deployment' + */ + function _setupDecimals(uint8 decimals_) private { + _decimals = decimals_; + } + + /** + * @dev Overrides the `decimals()` method with custom `_decimals` + */ + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + /** + * @dev Creates `amount` new tokens for `to`. + * + * See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to, uint256 amount) public virtual onlyOwner { + _mint(to, amount); + } + + /** + * @dev Destroys `amount` new tokens for `to`. + * + * See {ERC20-_burn}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function burnCoins(address from, uint256 amount) public virtual onlyOwner { + _burn(from, amount); + } + +} diff --git a/x/evm/embeds/ERC20MinterCompiled.json b/x/evm/embeds/ERC20MinterCompiled.json new file mode 100644 index 000000000..2dff40e72 --- /dev/null +++ b/x/evm/embeds/ERC20MinterCompiled.json @@ -0,0 +1,421 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ERC20Minter", + "sourceName": "contracts/ERC20Minter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnCoins", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b50604051620022fd380380620022fd833981810160405281019062000037919062000356565b828281600390816200004a91906200063b565b5080600490816200005c91906200063b565b5050506200007f620000736200009960201b60201c565b620000a160201b60201c565b62000090816200016760201b60201c565b50505062000722565b600033905090565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b80600560146101000a81548160ff021916908360ff16021790555050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001ee82620001a3565b810181811067ffffffffffffffff8211171562000210576200020f620001b4565b5b80604052505050565b60006200022562000185565b9050620002338282620001e3565b919050565b600067ffffffffffffffff821115620002565762000255620001b4565b5b6200026182620001a3565b9050602081019050919050565b60005b838110156200028e57808201518184015260208101905062000271565b60008484015250505050565b6000620002b1620002ab8462000238565b62000219565b905082815260208101848484011115620002d057620002cf6200019e565b5b620002dd8482856200026e565b509392505050565b600082601f830112620002fd57620002fc62000199565b5b81516200030f8482602086016200029a565b91505092915050565b600060ff82169050919050565b620003308162000318565b81146200033c57600080fd5b50565b600081519050620003508162000325565b92915050565b6000806000606084860312156200037257620003716200018f565b5b600084015167ffffffffffffffff81111562000393576200039262000194565b5b620003a186828701620002e5565b935050602084015167ffffffffffffffff811115620003c557620003c462000194565b5b620003d386828701620002e5565b9250506040620003e6868287016200033f565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200044357607f821691505b602082108103620004595762000458620003fb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620004c37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000484565b620004cf868362000484565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200051c620005166200051084620004e7565b620004f1565b620004e7565b9050919050565b6000819050919050565b6200053883620004fb565b62000550620005478262000523565b84845462000491565b825550505050565b600090565b6200056762000558565b620005748184846200052d565b505050565b5b818110156200059c57620005906000826200055d565b6001810190506200057a565b5050565b601f821115620005eb57620005b5816200045f565b620005c08462000474565b81016020851015620005d0578190505b620005e8620005df8562000474565b83018262000579565b50505b505050565b600082821c905092915050565b60006200061060001984600802620005f0565b1980831691505092915050565b60006200062b8383620005fd565b9150826002028217905092915050565b6200064682620003f0565b67ffffffffffffffff811115620006625762000661620001b4565b5b6200066e82546200042a565b6200067b828285620005a0565b600060209050601f831160018114620006b357600084156200069e578287015190505b620006aa85826200061d565b8655506200071a565b601f198416620006c3866200045f565b60005b82811015620006ed57848901518255600182019150602085019450602081019050620006c6565b868310156200070d578489015162000709601f891682620005fd565b8355505b6001600288020188555050505b505050505050565b611bcb80620007326000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c806370a08231116100a257806395d89b411161007157806395d89b41146102cd578063a457c2d7146102eb578063a9059cbb1461031b578063dd62ed3e1461034b578063f2fde38b1461037b57610116565b806370a0823114610259578063715018a61461028957806379cc6790146102935780638da5cb5b146102af57610116565b806323b872dd116100e957806323b872dd146101a3578063313ce567146101d357806339509351146101f157806340c10f191461022157806342966c681461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd146101695780631cf2c7e214610187575b600080fd5b610123610397565b60405161013091906111c3565b60405180910390f35b610153600480360381019061014e919061127e565b610429565b60405161016091906112d9565b60405180910390f35b61017161044c565b60405161017e9190611303565b60405180910390f35b6101a1600480360381019061019c919061127e565b610456565b005b6101bd60048036038101906101b8919061131e565b61046c565b6040516101ca91906112d9565b60405180910390f35b6101db61049b565b6040516101e8919061138d565b60405180910390f35b61020b6004803603810190610206919061127e565b6104b2565b60405161021891906112d9565b60405180910390f35b61023b6004803603810190610236919061127e565b6104e9565b005b610257600480360381019061025291906113a8565b6104ff565b005b610273600480360381019061026e91906113d5565b610513565b6040516102809190611303565b60405180910390f35b61029161055b565b005b6102ad60048036038101906102a8919061127e565b61056f565b005b6102b761058f565b6040516102c49190611411565b60405180910390f35b6102d56105b9565b6040516102e291906111c3565b60405180910390f35b6103056004803603810190610300919061127e565b61064b565b60405161031291906112d9565b60405180910390f35b6103356004803603810190610330919061127e565b6106c2565b60405161034291906112d9565b60405180910390f35b6103656004803603810190610360919061142c565b6106e5565b6040516103729190611303565b60405180910390f35b610395600480360381019061039091906113d5565b61076c565b005b6060600380546103a69061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d29061149b565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b6000806104346107ef565b90506104418185856107f7565b600191505092915050565b6000600254905090565b61045e6109c0565b6104688282610a3e565b5050565b6000806104776107ef565b9050610484858285610c0b565b61048f858585610c97565b60019150509392505050565b6000600560149054906101000a900460ff16905090565b6000806104bd6107ef565b90506104de8185856104cf85896106e5565b6104d991906114fb565b6107f7565b600191505092915050565b6104f16109c0565b6104fb8282610f0d565b5050565b61051061050a6107ef565b82610a3e565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6105636109c0565b61056d6000611063565b565b6105818261057b6107ef565b83610c0b565b61058b8282610a3e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105c89061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546105f49061149b565b80156106415780601f1061061657610100808354040283529160200191610641565b820191906000526020600020905b81548152906001019060200180831161062457829003601f168201915b5050505050905090565b6000806106566107ef565b9050600061066482866106e5565b9050838110156106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a0906115a1565b60405180910390fd5b6106b682868684036107f7565b60019250505092915050565b6000806106cd6107ef565b90506106da818585610c97565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6107746109c0565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da90611633565b60405180910390fd5b6107ec81611063565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d906116c5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108cc90611757565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516109b39190611303565b60405180910390a3505050565b6109c86107ef565b73ffffffffffffffffffffffffffffffffffffffff166109e661058f565b73ffffffffffffffffffffffffffffffffffffffff1614610a3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a33906117c3565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610aad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa490611855565b60405180910390fd5b610ab982600083611129565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610b3f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b36906118e7565b60405180910390fd5b8181036000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610bf29190611303565b60405180910390a3610c068360008461112e565b505050565b6000610c1784846106e5565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c915781811015610c83576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c7a90611953565b60405180910390fd5b610c9084848484036107f7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610d06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cfd906119e5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6c90611a77565b60405180910390fd5b610d80838383611129565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610e06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dfd90611b09565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ef49190611303565b60405180910390a3610f0784848461112e565b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611b75565b60405180910390fd5b610f8860008383611129565b8060026000828254610f9a91906114fb565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161104b9190611303565b60405180910390a361105f6000838361112e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116d578082015181840152602081019050611152565b60008484015250505050565b6000601f19601f8301169050919050565b600061119582611133565b61119f818561113e565b93506111af81856020860161114f565b6111b881611179565b840191505092915050565b600060208201905081810360008301526111dd818461118a565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611215826111ea565b9050919050565b6112258161120a565b811461123057600080fd5b50565b6000813590506112428161121c565b92915050565b6000819050919050565b61125b81611248565b811461126657600080fd5b50565b60008135905061127881611252565b92915050565b60008060408385031215611295576112946111e5565b5b60006112a385828601611233565b92505060206112b485828601611269565b9150509250929050565b60008115159050919050565b6112d3816112be565b82525050565b60006020820190506112ee60008301846112ca565b92915050565b6112fd81611248565b82525050565b600060208201905061131860008301846112f4565b92915050565b600080600060608486031215611337576113366111e5565b5b600061134586828701611233565b935050602061135686828701611233565b925050604061136786828701611269565b9150509250925092565b600060ff82169050919050565b61138781611371565b82525050565b60006020820190506113a2600083018461137e565b92915050565b6000602082840312156113be576113bd6111e5565b5b60006113cc84828501611269565b91505092915050565b6000602082840312156113eb576113ea6111e5565b5b60006113f984828501611233565b91505092915050565b61140b8161120a565b82525050565b60006020820190506114266000830184611402565b92915050565b60008060408385031215611443576114426111e5565b5b600061145185828601611233565b925050602061146285828601611233565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806114b357607f821691505b6020821081036114c6576114c561146c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061150682611248565b915061151183611248565b9250828201905080821115611529576115286114cc565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061158b60258361113e565b91506115968261152f565b604082019050919050565b600060208201905081810360008301526115ba8161157e565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b600061161d60268361113e565b9150611628826115c1565b604082019050919050565b6000602082019050818103600083015261164c81611610565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006116af60248361113e565b91506116ba82611653565b604082019050919050565b600060208201905081810360008301526116de816116a2565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b600061174160228361113e565b915061174c826116e5565b604082019050919050565b6000602082019050818103600083015261177081611734565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006117ad60208361113e565b91506117b882611777565b602082019050919050565b600060208201905081810360008301526117dc816117a0565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b600061183f60218361113e565b915061184a826117e3565b604082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006118d160228361113e565b91506118dc82611875565b604082019050919050565b60006020820190508181036000830152611900816118c4565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061193d601d8361113e565b915061194882611907565b602082019050919050565b6000602082019050818103600083015261196c81611930565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006119cf60258361113e565b91506119da82611973565b604082019050919050565b600060208201905081810360008301526119fe816119c2565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611a6160238361113e565b9150611a6c82611a05565b604082019050919050565b60006020820190508181036000830152611a9081611a54565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611af360268361113e565b9150611afe82611a97565b604082019050919050565b60006020820190508181036000830152611b2281611ae6565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000611b5f601f8361113e565b9150611b6a82611b29565b602082019050919050565b60006020820190508181036000830152611b8e81611b52565b905091905056fea2646970667358221220ba2a89446f4165c5418487480215c2511caeabfb69a80019473ef5722055fe5364736f6c63430008130033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c806370a08231116100a257806395d89b411161007157806395d89b41146102cd578063a457c2d7146102eb578063a9059cbb1461031b578063dd62ed3e1461034b578063f2fde38b1461037b57610116565b806370a0823114610259578063715018a61461028957806379cc6790146102935780638da5cb5b146102af57610116565b806323b872dd116100e957806323b872dd146101a3578063313ce567146101d357806339509351146101f157806340c10f191461022157806342966c681461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd146101695780631cf2c7e214610187575b600080fd5b610123610397565b60405161013091906111c3565b60405180910390f35b610153600480360381019061014e919061127e565b610429565b60405161016091906112d9565b60405180910390f35b61017161044c565b60405161017e9190611303565b60405180910390f35b6101a1600480360381019061019c919061127e565b610456565b005b6101bd60048036038101906101b8919061131e565b61046c565b6040516101ca91906112d9565b60405180910390f35b6101db61049b565b6040516101e8919061138d565b60405180910390f35b61020b6004803603810190610206919061127e565b6104b2565b60405161021891906112d9565b60405180910390f35b61023b6004803603810190610236919061127e565b6104e9565b005b610257600480360381019061025291906113a8565b6104ff565b005b610273600480360381019061026e91906113d5565b610513565b6040516102809190611303565b60405180910390f35b61029161055b565b005b6102ad60048036038101906102a8919061127e565b61056f565b005b6102b761058f565b6040516102c49190611411565b60405180910390f35b6102d56105b9565b6040516102e291906111c3565b60405180910390f35b6103056004803603810190610300919061127e565b61064b565b60405161031291906112d9565b60405180910390f35b6103356004803603810190610330919061127e565b6106c2565b60405161034291906112d9565b60405180910390f35b6103656004803603810190610360919061142c565b6106e5565b6040516103729190611303565b60405180910390f35b610395600480360381019061039091906113d5565b61076c565b005b6060600380546103a69061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d29061149b565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b6000806104346107ef565b90506104418185856107f7565b600191505092915050565b6000600254905090565b61045e6109c0565b6104688282610a3e565b5050565b6000806104776107ef565b9050610484858285610c0b565b61048f858585610c97565b60019150509392505050565b6000600560149054906101000a900460ff16905090565b6000806104bd6107ef565b90506104de8185856104cf85896106e5565b6104d991906114fb565b6107f7565b600191505092915050565b6104f16109c0565b6104fb8282610f0d565b5050565b61051061050a6107ef565b82610a3e565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6105636109c0565b61056d6000611063565b565b6105818261057b6107ef565b83610c0b565b61058b8282610a3e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105c89061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546105f49061149b565b80156106415780601f1061061657610100808354040283529160200191610641565b820191906000526020600020905b81548152906001019060200180831161062457829003601f168201915b5050505050905090565b6000806106566107ef565b9050600061066482866106e5565b9050838110156106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a0906115a1565b60405180910390fd5b6106b682868684036107f7565b60019250505092915050565b6000806106cd6107ef565b90506106da818585610c97565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6107746109c0565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da90611633565b60405180910390fd5b6107ec81611063565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d906116c5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108cc90611757565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516109b39190611303565b60405180910390a3505050565b6109c86107ef565b73ffffffffffffffffffffffffffffffffffffffff166109e661058f565b73ffffffffffffffffffffffffffffffffffffffff1614610a3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a33906117c3565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610aad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa490611855565b60405180910390fd5b610ab982600083611129565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610b3f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b36906118e7565b60405180910390fd5b8181036000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610bf29190611303565b60405180910390a3610c068360008461112e565b505050565b6000610c1784846106e5565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c915781811015610c83576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c7a90611953565b60405180910390fd5b610c9084848484036107f7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610d06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cfd906119e5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610d75576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6c90611a77565b60405180910390fd5b610d80838383611129565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610e06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dfd90611b09565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ef49190611303565b60405180910390a3610f0784848461112e565b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611b75565b60405180910390fd5b610f8860008383611129565b8060026000828254610f9a91906114fb565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161104b9190611303565b60405180910390a361105f6000838361112e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116d578082015181840152602081019050611152565b60008484015250505050565b6000601f19601f8301169050919050565b600061119582611133565b61119f818561113e565b93506111af81856020860161114f565b6111b881611179565b840191505092915050565b600060208201905081810360008301526111dd818461118a565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611215826111ea565b9050919050565b6112258161120a565b811461123057600080fd5b50565b6000813590506112428161121c565b92915050565b6000819050919050565b61125b81611248565b811461126657600080fd5b50565b60008135905061127881611252565b92915050565b60008060408385031215611295576112946111e5565b5b60006112a385828601611233565b92505060206112b485828601611269565b9150509250929050565b60008115159050919050565b6112d3816112be565b82525050565b60006020820190506112ee60008301846112ca565b92915050565b6112fd81611248565b82525050565b600060208201905061131860008301846112f4565b92915050565b600080600060608486031215611337576113366111e5565b5b600061134586828701611233565b935050602061135686828701611233565b925050604061136786828701611269565b9150509250925092565b600060ff82169050919050565b61138781611371565b82525050565b60006020820190506113a2600083018461137e565b92915050565b6000602082840312156113be576113bd6111e5565b5b60006113cc84828501611269565b91505092915050565b6000602082840312156113eb576113ea6111e5565b5b60006113f984828501611233565b91505092915050565b61140b8161120a565b82525050565b60006020820190506114266000830184611402565b92915050565b60008060408385031215611443576114426111e5565b5b600061145185828601611233565b925050602061146285828601611233565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806114b357607f821691505b6020821081036114c6576114c561146c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061150682611248565b915061151183611248565b9250828201905080821115611529576115286114cc565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061158b60258361113e565b91506115968261152f565b604082019050919050565b600060208201905081810360008301526115ba8161157e565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b600061161d60268361113e565b9150611628826115c1565b604082019050919050565b6000602082019050818103600083015261164c81611610565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006116af60248361113e565b91506116ba82611653565b604082019050919050565b600060208201905081810360008301526116de816116a2565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b600061174160228361113e565b915061174c826116e5565b604082019050919050565b6000602082019050818103600083015261177081611734565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006117ad60208361113e565b91506117b882611777565b602082019050919050565b600060208201905081810360008301526117dc816117a0565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b600061183f60218361113e565b915061184a826117e3565b604082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006118d160228361113e565b91506118dc82611875565b604082019050919050565b60006020820190508181036000830152611900816118c4565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061193d601d8361113e565b915061194882611907565b602082019050919050565b6000602082019050818103600083015261196c81611930565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006119cf60258361113e565b91506119da82611973565b604082019050919050565b600060208201905081810360008301526119fe816119c2565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611a6160238361113e565b9150611a6c82611a05565b604082019050919050565b60006020820190508181036000830152611a9081611a54565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611af360268361113e565b9150611afe82611a97565b604082019050919050565b60006020820190508181036000830152611b2281611ae6565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000611b5f601f8361113e565b9150611b6a82611b29565b602082019050919050565b60006020820190508181036000830152611b8e81611b52565b905091905056fea2646970667358221220ba2a89446f4165c5418487480215c2511caeabfb69a80019473ef5722055fe5364736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/README.md b/x/evm/embeds/README.md new file mode 100644 index 000000000..d04248dab --- /dev/null +++ b/x/evm/embeds/README.md @@ -0,0 +1,13 @@ +# Sample Hardhat Project + +This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract. + +Try running some of the following tasks: + +```shell +npx hardhat help +npx hardhat test +REPORT_GAS=true npx hardhat test +npx hardhat node +npx hardhat ignition deploy ./ignition/modules/Lock.js +``` diff --git a/x/evm/embeds/embeds.go b/x/evm/embeds/embeds.go new file mode 100644 index 000000000..0c0318849 --- /dev/null +++ b/x/evm/embeds/embeds.go @@ -0,0 +1,179 @@ +// Package "embeds" adds access to files (smart contracts) embedded in the Go +// runtime. Go source files that import "embed" can use the //go:embed directive +// to initialize a variable of type string, \[]byte, or \[FS] with the contents +// of files read from the package directory or subdirectories at compile time. +package embeds + +import ( + _ "embed" + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "runtime" + "strings" + + // Adds access to files (smart contracts, in this case) embedded in the Go + + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +var ( + //go:embed ERC20MinterCompiled.json + erc20MinterContractJSON []byte + + EmbeddedContractERC20Minter CompiledEvmContract +) + +func init() { + out, err := SmartContract_ERC20Minter.Load() + if err != nil { + panic(err) + } + EmbeddedContractERC20Minter = out +} + +var ( + SmartContract_FunToken = SmartContractFixture{ + Name: "FunToken.sol", + FixtureType: FixtueType_Test, + } + + SmartContract_ERC20Minter = SmartContractFixture{ + Name: "ERC20Minter.sol", + FixtureType: FixtueType_Embed, + EmbedJSON: &erc20MinterContractJSON, + } +) + +// CompiledEvmContract: EVM contract that can be deployed into the EVM state and +// used as a valid precompile. +type CompiledEvmContract struct { + ABI gethabi.ABI `json:"abi"` + Bytecode []byte `json:"bytecode"` +} + +type SmartContractFixture struct { + Name string + FixtureType ContractFixtureType + EmbedJSON *[]byte +} + +type ContractFixtureType string + +const ( + FixtueType_Embed = "embed" + FixtueType_Test = "test" +) + +// HardhatOutput: Expected format for smart contract test fixtures. +type HardhatOutput struct { + ABI json.RawMessage `json:"abi"` + Bytecode HexString `json:"bytecode"` +} + +// HexString: Hexadecimal-encoded string +type HexString string + +func (h HexString) Bytes() []byte { + return gethcommon.Hex2Bytes( + strings.TrimPrefix(string(h), "0x"), + ) +} +func (h HexString) String() string { return string(h) } +func (h HexString) FromBytes(bz []byte) HexString { + return HexString(gethcommon.Bytes2Hex(bz)) +} + +func NewHardhatOutputFromJson( + jsonBz []byte, +) (out HardhatOutput, err error) { + rawJsonBz := make(map[string]json.RawMessage) + err = json.Unmarshal(jsonBz, &rawJsonBz) + if err != nil { + return + } + var rawBytecodeBz HexString + err = json.Unmarshal(rawJsonBz["bytecode"], &rawBytecodeBz) + if err != nil { + return + } + + return HardhatOutput{ + ABI: rawJsonBz["abi"], + Bytecode: rawBytecodeBz, + }, err +} + +func (jsonObj HardhatOutput) EvmContract() (out CompiledEvmContract, err error) { + newAbi := new(gethabi.ABI) + err = newAbi.UnmarshalJSON(jsonObj.ABI) + if err != nil { + return + } + + return CompiledEvmContract{ + ABI: *newAbi, + Bytecode: jsonObj.Bytecode.Bytes(), + }, err +} + +func (sc SmartContractFixture) Load() (out CompiledEvmContract, err error) { + var jsonBz []byte + + // Locate the contracts directory. + switch sc.FixtureType { + case FixtueType_Embed: + if sc.EmbedJSON == nil { + return out, fmt.Errorf("missing compiled contract embed") + } + jsonBz = *sc.EmbedJSON + case FixtueType_Test: + contractsDirPath, err := pathToE2EContracts() + if err != nil { + return out, err + } + baseName := strings.TrimSuffix(sc.Name, ".sol") + compiledPath := fmt.Sprintf("%s/%sCompiled.json", contractsDirPath, baseName) + + jsonBz, err = os.ReadFile(compiledPath) + if err != nil { + return out, err + } + default: + panic(fmt.Errorf("unexpected case type \"%s\"", sc.FixtureType)) + } + + compiledJson, err := NewHardhatOutputFromJson(jsonBz) + if err != nil { + return + } + + return compiledJson.EvmContract() +} + +// pathToE2EContracts: Returns the absolute path to the E2E test contract +// directory located at path, "NibiruChain/nibiru/e2e/evm/contracts". +func pathToE2EContracts() (thePath string, err error) { + dirEvmTest, _ := GetPackageDir() + dirOfRepo := path.Dir(path.Dir(path.Dir(dirEvmTest))) + dirEvmE2e := path.Join(dirOfRepo, "e2e/evm") + if path.Base(dirEvmE2e) != "evm" { + return thePath, fmt.Errorf("failed to locate the e2e/evm directory") + } + return dirEvmE2e + "/contracts", nil +} + +// GetPackageDir: Returns the absolute path of the Golang package that +// calls this function. +func GetPackageDir() (string, error) { + // Get the import path of the current package + _, filename, _, _ := runtime.Caller(0) + pkgDir := path.Dir(filename) + pkgPath := path.Join(path.Base(pkgDir), "..") + + // Get the directory path of the package + return filepath.Abs(pkgPath) +} diff --git a/x/evm/embeds/embeds_test.go b/x/evm/embeds/embeds_test.go new file mode 100644 index 000000000..21a6051a4 --- /dev/null +++ b/x/evm/embeds/embeds_test.go @@ -0,0 +1,21 @@ +package embeds_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/NibiruChain/nibiru/x/evm/embeds" +) + +func TestLoadContracts(t *testing.T) { + for _, tc := range []embeds.SmartContractFixture{ + embeds.SmartContract_FunToken, + embeds.SmartContract_ERC20Minter, + } { + t.Run(tc.Name, func(t *testing.T) { + _, err := tc.Load() + assert.NoError(t, err) + }) + } +} diff --git a/x/evm/evmtest/fixtures.go b/x/evm/evmtest/fixtures.go deleted file mode 100644 index 1e901dcda..000000000 --- a/x/evm/evmtest/fixtures.go +++ /dev/null @@ -1,99 +0,0 @@ -package evmtest - -import ( - "encoding/json" - "fmt" - "os" - "path" - "strings" - "testing" - - gethabi "github.com/ethereum/go-ethereum/accounts/abi" - gethcommon "github.com/ethereum/go-ethereum/common" - - "github.com/stretchr/testify/require" - - "github.com/NibiruChain/nibiru/x/common/testutil" -) - -type SmartContractFixture string - -const ( - SmartContract_FunToken SmartContractFixture = "FunToken.sol" -) - -type CompiledEvmContract struct { - ABI gethabi.ABI `json:"abi"` - Bytecode []byte `json:"bytecode"` -} - -// HardhatOutput: Expected format for smart contract test fixtures. -type HardhatOutput struct { - ABI json.RawMessage `json:"abi"` - Bytecode HexString `json:"bytecode"` -} - -// HexString: Hexadecimal-encoded string -type HexString string - -func (h HexString) Bytes() []byte { - return gethcommon.Hex2Bytes( - strings.TrimPrefix(string(h), "0x"), - ) -} -func (h HexString) String() string { return string(h) } -func (h HexString) FromBytes(bz []byte) HexString { - return HexString(gethcommon.Bytes2Hex(bz)) -} - -func NewHardhatOutputFromJson( - t *testing.T, - jsonBz []byte, -) HardhatOutput { - rawJsonBz := make(map[string]json.RawMessage) - err := json.Unmarshal(jsonBz, &rawJsonBz) - require.NoError(t, err) - var rawBytecodeBz HexString - err = json.Unmarshal(rawJsonBz["bytecode"], &rawBytecodeBz) - require.NoError(t, err) - - return HardhatOutput{ - ABI: rawJsonBz["abi"], - Bytecode: rawBytecodeBz, - } -} - -func (jsonObj HardhatOutput) EvmContract(t *testing.T) CompiledEvmContract { - newAbi := new(gethabi.ABI) - err := newAbi.UnmarshalJSON(jsonObj.ABI) - require.NoError(t, err) - - return CompiledEvmContract{ - ABI: *newAbi, - Bytecode: jsonObj.Bytecode.Bytes(), - } -} - -func (sc SmartContractFixture) Load(t *testing.T) CompiledEvmContract { - contractsDirPath := pathToContractsDir(t) - baseName := strings.TrimSuffix(string(sc), ".sol") - compiledPath := fmt.Sprintf("%s/%sCompiled.json", contractsDirPath, baseName) - - jsonBz, err := os.ReadFile(compiledPath) - require.NoError(t, err) - - compiledJson := NewHardhatOutputFromJson(t, jsonBz) - require.NoError(t, err) - return compiledJson.EvmContract(t) -} - -// pathToContractsDir: Returns the absolute path to the E2E test contract -// directory located at path, "NibiruChain/nibiru/e2e/evm/contracts". -func pathToContractsDir(t *testing.T) string { - dirEvmTest, err := testutil.GetPackageDir() - require.NoError(t, err) - dirOfRepo := path.Dir(path.Dir(path.Dir(dirEvmTest))) - dirEvmE2e := path.Join(dirOfRepo, "e2e/evm") - require.Equal(t, "evm", path.Base(dirEvmE2e)) - return dirEvmE2e + "/contracts" -} diff --git a/x/evm/evmtest/fixtures_test.go b/x/evm/evmtest/fixtures_test.go deleted file mode 100644 index f698c9141..000000000 --- a/x/evm/evmtest/fixtures_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package evmtest_test - -import ( - "testing" - - "github.com/NibiruChain/nibiru/x/evm/evmtest" -) - -func TestLoadContracts(t *testing.T) { - evmtest.SmartContract_FunToken.Load(t) -} diff --git a/x/evm/evmtest/tx.go b/x/evm/evmtest/tx.go index 0181b2bbe..3814898ca 100644 --- a/x/evm/evmtest/tx.go +++ b/x/evm/evmtest/tx.go @@ -7,6 +7,7 @@ import ( "math/big" "testing" + "cosmossdk.io/errors" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethcore "github.com/ethereum/go-ethereum/core/types" @@ -17,6 +18,7 @@ import ( srvconfig "github.com/NibiruChain/nibiru/app/server/config" "github.com/NibiruChain/nibiru/x/evm" + "github.com/NibiruChain/nibiru/x/evm/embeds" ) type GethTxType = uint8 @@ -152,28 +154,77 @@ func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) *evm.MsgEthereumTx { return ethTxMsg } -// ExecuteERC20Transfer deploys contract, executes transfer and returns tx hash -func ExecuteERC20Transfer(deps *TestDeps, t *testing.T) (*evm.MsgEthereumTx, []*evm.MsgEthereumTx) { - // TX 1: Deploy ERC-20 contract - contractData := SmartContract_FunToken.Load(t) +func GasLimitCreateContract() *big.Int { + return new(big.Int).SetUint64( + gethparams.TxGasContractCreation + 700, + ) +} + +type DeployContractResult struct { + TxResp *evm.MsgEthereumTxResponse + EthTxMsg *evm.MsgEthereumTx + ContractData embeds.CompiledEvmContract + Nonce uint64 + ContractAddr gethcommon.Address +} + +func DeployContract( + deps *TestDeps, + contract embeds.SmartContractFixture, + t *testing.T, + args ...any, +) (result DeployContractResult, err error) { + contractData, err := contract.Load() + require.NoError(t, err) + + // Use contract args + packedArgs, err := contractData.ABI.Pack("", args...) + if err != nil { + err = errors.Wrap(err, "failed to pack ABI args") + return + } + bytecodeForCall := append(contractData.Bytecode, packedArgs...) + nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) - txArgs := evm.JsonTxArgs{ - From: &deps.Sender.EthAddr, + jsonTxArgs := evm.JsonTxArgs{ Nonce: (*hexutil.Uint64)(&nonce), - Data: (*hexutil.Bytes)(&contractData.Bytecode), + Input: (*hexutil.Bytes)(&bytecodeForCall), + From: &deps.Sender.EthAddr, + // ChainID: deps.Chain.EvmKeeper.EthChainID(deps.Ctx), } - ethTxMsg, err := GenerateAndSignEthTxMsg(txArgs, deps) + ethTxMsg, err := GenerateAndSignEthTxMsg(jsonTxArgs, deps) require.NoError(t, err) resp, err := deps.Chain.EvmKeeper.EthereumTx(deps.GoCtx(), ethTxMsg) require.NoError(t, err) require.Empty(t, resp.VmError) + contractAddress := crypto.CreateAddress(deps.Sender.EthAddr, nonce) + + return DeployContractResult{ + TxResp: resp, + EthTxMsg: ethTxMsg, + ContractData: contractData, + Nonce: nonce, + ContractAddr: contractAddress, + }, nil +} + +// DeployAndExecuteERC20Transfer deploys contract, executes transfer and returns tx hash +func DeployAndExecuteERC20Transfer( + deps *TestDeps, t *testing.T, +) (*evm.MsgEthereumTx, []*evm.MsgEthereumTx) { + // TX 1: Deploy ERC-20 contract + deployResp, err := DeployContract(deps, embeds.SmartContract_FunToken, t) + require.NoError(t, err) + contractData := deployResp.ContractData + nonce := deployResp.Nonce + // Contract address is deterministic contractAddress := crypto.CreateAddress(deps.Sender.EthAddr, nonce) deps.Chain.Commit() predecessors := []*evm.MsgEthereumTx{ - ethTxMsg, + deployResp.EthTxMsg, } // TX 2: execute ERC-20 contract transfer @@ -182,16 +233,16 @@ func ExecuteERC20Transfer(deps *TestDeps, t *testing.T) (*evm.MsgEthereumTx, []* ) require.NoError(t, err) nonce = deps.StateDB().GetNonce(deps.Sender.EthAddr) - txArgs = evm.JsonTxArgs{ + txArgs := evm.JsonTxArgs{ From: &deps.Sender.EthAddr, To: &contractAddress, Nonce: (*hexutil.Uint64)(&nonce), Data: (*hexutil.Bytes)(&input), } - ethTxMsg, err = GenerateAndSignEthTxMsg(txArgs, deps) + ethTxMsg, err := GenerateAndSignEthTxMsg(txArgs, deps) require.NoError(t, err) - resp, err = deps.Chain.EvmKeeper.EthereumTx(deps.GoCtx(), ethTxMsg) + resp, err := deps.Chain.EvmKeeper.EthereumTx(deps.GoCtx(), ethTxMsg) require.NoError(t, err) require.Empty(t, resp.VmError) @@ -199,14 +250,16 @@ func ExecuteERC20Transfer(deps *TestDeps, t *testing.T) (*evm.MsgEthereumTx, []* } // GenerateAndSignEthTxMsg estimates gas, sets gas limit and sings the tx -func GenerateAndSignEthTxMsg(txArgs evm.JsonTxArgs, deps *TestDeps) (*evm.MsgEthereumTx, error) { +func GenerateAndSignEthTxMsg( + txArgs evm.JsonTxArgs, deps *TestDeps, +) (*evm.MsgEthereumTx, error) { estimateArgs, err := json.Marshal(&txArgs) if err != nil { return nil, err } res, err := deps.Chain.EvmKeeper.EstimateGas(deps.GoCtx(), &evm.EthCallRequest{ Args: estimateArgs, - GasCap: srvconfig.DefaultGasCap, + GasCap: srvconfig.DefaultEthCallGasLimit, ProposerAddress: []byte{}, ChainId: deps.Chain.EvmKeeper.EthChainID(deps.Ctx).Int64(), }) diff --git a/x/evm/json_tx_args.go b/x/evm/json_tx_args.go index 14fde2da3..38e3a1a4b 100644 --- a/x/evm/json_tx_args.go +++ b/x/evm/json_tx_args.go @@ -31,7 +31,10 @@ type JsonTxArgs struct { // We accept "data" and "input" for backwards-compatibility reasons. // "input" is the newer name and should be preferred by clients. // Issue detail: https://github.com/ethereum/go-ethereum/issues/15628 - Data *hexutil.Bytes `json:"data"` + Data *hexutil.Bytes `json:"data"` + // Both "data" and "input" are accepted for backwards-compatibility reasons. + // "input" is the newer name and should be preferred by clients. + // Issue detail: https://github.com/ethereum/go-ethereum/issues/15628 Input *hexutil.Bytes `json:"input"` // Introduced by AccessListTxType transaction. diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go new file mode 100644 index 000000000..b222ded13 --- /dev/null +++ b/x/evm/keeper/erc20.go @@ -0,0 +1,228 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "encoding/json" + "math/big" + + "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + + serverconfig "github.com/NibiruChain/nibiru/app/server/config" + "github.com/NibiruChain/nibiru/x/common" + "github.com/NibiruChain/nibiru/x/evm" + "github.com/NibiruChain/nibiru/x/evm/embeds" + "github.com/NibiruChain/nibiru/x/evm/statedb" +) + +func (k Keeper) FindERC20Metadata( + ctx sdk.Context, + contract gethcommon.Address, +) (info ERC20Metadata, err error) { + var abi gethabi.ABI = embeds.EmbeddedContractERC20Minter.ABI + + errs := []error{} + + // Load name, symbol, decimals + name, err := k.LoadERC20Name(ctx, abi, contract) + errs = append(errs, err) + symbol, err := k.LoadERC20Symbol(ctx, abi, contract) + errs = append(errs, err) + decimals, err := k.LoadERC20Decimals(ctx, abi, contract) + errs = append(errs, err) + + err = common.CombineErrors(errs...) + if err != nil { + return info, errors.Wrap(err, "failed to \"FindERC20Metadata\"") + } + + return ERC20Metadata{ + Name: name, + Symbol: symbol, + Decimals: decimals, + }, nil +} + +type ERC20Metadata struct { + Name string + Symbol string + Decimals uint8 +} + +type ( + ERC20String struct{ Value string } + ERC20Uint8 struct{ Value uint8 } + ERC20Bool struct{ Value bool } +) + +// CallContract invokes a smart contract on the method specified by [methodName] +// using the given [args]. +// +// Parameters: +// - ctx: The SDK context for the transaction. +// - abi: The ABI (Application Binary Interface) of the smart contract. +// - fromAcc: The Ethereum address of the account initiating the contract call. +// - contract: Pointer to the Ethereum address of the contract to be called. +// - commit: Boolean flag indicating whether to commit the transaction (true) or simulate it (false). +// - methodName: The name of the contract method to be called. +// - args: Variadic parameter for the arguments to be passed to the contract method. +// +// Note: This function handles both contract method calls and simulations, +// depending on the 'commit' parameter. It uses a default gas limit for +// simulations and estimates gas for actual transactions. +func (k Keeper) CallContract( + ctx sdk.Context, + abi gethabi.ABI, + fromAcc gethcommon.Address, + contract *gethcommon.Address, + commit bool, + methodName string, + args ...any, +) (evmResp *evm.MsgEthereumTxResponse, err error) { + contractData, err := abi.Pack(methodName, args...) + if err != nil { + return evmResp, err + } + + nonce := k.GetAccNonce(ctx, fromAcc) + + // FIXME: Is this gas limit convention useful? + gasLimit := serverconfig.DefaultEthCallGasLimit + if commit { + jsonArgs, err := json.Marshal(evm.JsonTxArgs{ + From: &fromAcc, + To: contract, + Data: (*hexutil.Bytes)(&contractData), + }) + if err != nil { + return evmResp, err // TODO: UD-DEBUG: ... + } + + gasRes, err := k.EstimateGasForEvmCallType( + sdk.WrapSDKContext(ctx), + &evm.EthCallRequest{ + Args: jsonArgs, + GasCap: gasLimit, + }, + evm.CallTypeSmart, + ) + if err != nil { + return evmResp, err + } + + gasLimit = gasRes.Gas + } + + unusedBitInt := big.NewInt(0) + evmMsg := gethcore.NewMessage( + fromAcc, + contract, + nonce, + unusedBitInt, // amount + gasLimit, + unusedBitInt, // gasFeeCap + unusedBitInt, // gasTipCap + unusedBitInt, // gasPrice + contractData, + gethcore.AccessList{}, + !commit, // isFake + ) + + // Apply EVM message + cfg, err := k.GetEVMConfig( + ctx, + sdk.ConsAddress(ctx.BlockHeader().ProposerAddress), + k.EthChainID(ctx), + ) + if err != nil { + return evmResp, errors.Wrap(err, "failed to load evm config") + } + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash())) + evmResp, err = k.ApplyEvmMsg( + ctx, evmMsg, evm.NewNoOpTracer(), commit, cfg, txConfig, + ) + if err != nil { + return evmResp, err + } + + if evmResp.Failed() { + return evmResp, errors.Wrap(err, evmResp.VmError) + } + + return evmResp, err +} + +func (k Keeper) LoadERC20Name( + ctx sdk.Context, abi gethabi.ABI, erc20 gethcommon.Address, +) (out string, err error) { + return k.loadERC20String(ctx, abi, erc20, "name") +} + +func (k Keeper) LoadERC20Symbol( + ctx sdk.Context, abi gethabi.ABI, erc20 gethcommon.Address, +) (out string, err error) { + return k.loadERC20String(ctx, abi, erc20, "symbol") +} + +func (k Keeper) LoadERC20Decimals( + ctx sdk.Context, abi gethabi.ABI, erc20 gethcommon.Address, +) (out uint8, err error) { + return k.loadERC20Uint8(ctx, abi, erc20, "decimals") +} + +func (k Keeper) loadERC20String( + ctx sdk.Context, + erc20Abi gethabi.ABI, + erc20Contract gethcommon.Address, + methodName string, +) (out string, err error) { + res, err := k.CallContract( + ctx, erc20Abi, + evm.ModuleAddressEVM(), + &erc20Contract, + false, methodName, + ) + if err != nil { + return out, err + } + + erc20string := new(ERC20String) + err = erc20Abi.UnpackIntoInterface( + erc20string, methodName, res.Ret, + ) + if err != nil { + return out, err + } + return erc20string.Value, err +} + +func (k Keeper) loadERC20Uint8( + ctx sdk.Context, + erc20Abi gethabi.ABI, + erc20Contract gethcommon.Address, + methodName string, +) (out uint8, err error) { + res, err := k.CallContract( + ctx, erc20Abi, + evm.ModuleAddressEVM(), + &erc20Contract, + false, methodName, + ) + if err != nil { + return out, err + } + + erc20uint8 := new(ERC20Uint8) + err = erc20Abi.UnpackIntoInterface( + erc20uint8, methodName, res.Ret, + ) + if err != nil { + return out, err + } + return erc20uint8.Value, err +} diff --git a/x/evm/keeper/erc20_test.go b/x/evm/keeper/erc20_test.go new file mode 100644 index 000000000..b769441c0 --- /dev/null +++ b/x/evm/keeper/erc20_test.go @@ -0,0 +1,94 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/eth" + "github.com/NibiruChain/nibiru/x/evm" + "github.com/NibiruChain/nibiru/x/evm/embeds" + "github.com/NibiruChain/nibiru/x/evm/evmtest" + "github.com/NibiruChain/nibiru/x/evm/keeper" +) + +func (s *Suite) TestCreateFunTokenFromERC20() { + deps := evmtest.NewTestDeps() + + // Compute contract address. FindERC20 should fail + nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) + contractAddress := crypto.CreateAddress(deps.Sender.EthAddr, nonce) + _, err := deps.K.FindERC20Metadata(deps.Ctx, contractAddress) + s.Error(err) + + s.T().Log("Case 1: Deploy and invoke ERC20 for info") + + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_ERC20Minter, s.T(), + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.NoError(err) + s.Equal(contractAddress, deployResp.ContractAddr) + + info, err := deps.K.FindERC20Metadata(deps.Ctx, deployResp.ContractAddr) + s.NoError(err, info) + s.Equal(metadata, info) + + s.T().Log("Case 2: Deploy and invoke ERC20 for info") + + metadata = keeper.ERC20Metadata{ + Name: "gwei", + Symbol: "GWEI", + Decimals: 9, + } + deployResp, err = evmtest.DeployContract( + &deps, embeds.SmartContract_ERC20Minter, s.T(), + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.NoError(err) + s.NotEqual(contractAddress, deployResp.ContractAddr) + + info, err = deps.K.FindERC20Metadata(deps.Ctx, deployResp.ContractAddr) + s.NoError(err, info) + s.Equal(metadata, info) + + s.T().Log("happy: CreateFunToken for the ERC20") + + erc20Addr := eth.NewHexAddr(contractAddress) + queryCodeReq := &evm.QueryCodeRequest{ + Address: erc20Addr.String(), + } + _, err = deps.K.Code(deps.Ctx, queryCodeReq) + s.NoError(err) + + createFuntokenResp, err := deps.K.CreateFunToken( + deps.GoCtx(), + &evm.MsgCreateFunToken{ + FromErc20: erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.NoError(err, "erc20 %s", erc20Addr) + s.Equal( + createFuntokenResp.FuntokenMapping, + evm.FunToken{ + Erc20Addr: erc20Addr, + BankDenom: fmt.Sprintf("erc20/%s", erc20Addr.String()), + IsMadeFromCoin: false, + }) + + // s.T().Log("sad: CreateFunToken for the ERC20: already registered") + // _, err = deps.K.CreateFunToken( + // deps.GoCtx(), + // &evm.MsgCreateFunToken{ + // FromErc20: erc20Addr, + // }, + // ) + // s.Error(err) +} diff --git a/x/evm/keeper/funtoken_state_test.go b/x/evm/keeper/funtoken_state_test.go index 122e3f409..cce331bb6 100644 --- a/x/evm/keeper/funtoken_state_test.go +++ b/x/evm/keeper/funtoken_state_test.go @@ -8,7 +8,7 @@ import ( "github.com/NibiruChain/nibiru/x/evm/evmtest" ) -func (s *KeeperSuite) TestInsertAndGet() { +func (s *Suite) TestInsertAndGet() { deps := evmtest.NewTestDeps() erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") @@ -31,7 +31,7 @@ func (s *KeeperSuite) TestInsertAndGet() { // deps.K.FunTokens.Collect(ctx) } -func (s *KeeperSuite) TestCollect() { +func (s *Suite) TestCollect() { deps := evmtest.NewTestDeps() erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") @@ -60,7 +60,7 @@ func (s *KeeperSuite) TestCollect() { s.Require().True(funTokens[0].IsMadeFromCoin) } -func (s *KeeperSuite) TestDelete() { +func (s *Suite) TestDelete() { deps := evmtest.NewTestDeps() erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 49c13a34a..b38184ed4 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -16,6 +16,7 @@ import ( "github.com/NibiruChain/nibiru/eth" "github.com/NibiruChain/nibiru/x/common/testutil/testapp" "github.com/NibiruChain/nibiru/x/evm" + "github.com/NibiruChain/nibiru/x/evm/embeds" "github.com/NibiruChain/nibiru/x/evm/evmtest" ) @@ -65,7 +66,7 @@ func TraceERC20Transfer() string { }` } -func (s *KeeperSuite) TestQueryNibiruAccount() { +func (s *Suite) TestQueryNibiruAccount() { type In = *evm.QueryNibiruAccountRequest type Out = *evm.QueryNibiruAccountResponse testCases := []TestCase[In, Out]{ @@ -136,7 +137,7 @@ func (s *KeeperSuite) TestQueryNibiruAccount() { } } -func (s *KeeperSuite) TestQueryEthAccount() { +func (s *Suite) TestQueryEthAccount() { type In = *evm.QueryEthAccountRequest type Out = *evm.QueryEthAccountResponse testCases := []TestCase[In, Out]{ @@ -203,7 +204,7 @@ func (s *KeeperSuite) TestQueryEthAccount() { } } -func (s *KeeperSuite) TestQueryValidatorAccount() { +func (s *Suite) TestQueryValidatorAccount() { type In = *evm.QueryValidatorAccountRequest type Out = *evm.QueryValidatorAccountResponse testCases := []TestCase[In, Out]{ @@ -313,7 +314,7 @@ func (s *KeeperSuite) TestQueryValidatorAccount() { } } -func (s *KeeperSuite) TestQueryStorage() { +func (s *Suite) TestQueryStorage() { type In = *evm.QueryStorageRequest type Out = *evm.QueryStorageResponse testCases := []TestCase[In, Out]{ @@ -389,7 +390,7 @@ func (s *KeeperSuite) TestQueryStorage() { } } -func (s *KeeperSuite) TestQueryCode() { +func (s *Suite) TestQueryCode() { type In = *evm.QueryCodeRequest type Out = *evm.QueryCodeResponse testCases := []TestCase[In, Out]{ @@ -451,7 +452,7 @@ func (s *KeeperSuite) TestQueryCode() { } } -func (s *KeeperSuite) TestQueryParams() { +func (s *Suite) TestQueryParams() { deps := evmtest.NewTestDeps() want := evm.DefaultParams() deps.K.SetParams(deps.Ctx, want) @@ -474,7 +475,7 @@ func (s *KeeperSuite) TestQueryParams() { s.Require().True(want.Equal(got), "want %s, got %s", want, got) } -func (s *KeeperSuite) TestQueryEthCall() { +func (s *Suite) TestQueryEthCall() { type In = *evm.EthCallRequest type Out = *evm.MsgEthereumTxResponse testCases := []TestCase[In, Out]{ @@ -495,7 +496,8 @@ func (s *KeeperSuite) TestQueryEthCall() { { name: "happy: eth call for erc20 token transfer", scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { - fungibleTokenContract := evmtest.SmartContract_FunToken.Load(s.T()) + fungibleTokenContract, err := embeds.SmartContract_FunToken.Load() + s.Require().NoError(err) jsonTxArgs, err := json.Marshal(&evm.JsonTxArgs{ From: &deps.Sender.EthAddr, @@ -529,7 +531,7 @@ func (s *KeeperSuite) TestQueryEthCall() { } } -func (s *KeeperSuite) TestQueryBalance() { +func (s *Suite) TestQueryBalance() { type In = *evm.QueryBalanceRequest type Out = *evm.QueryBalanceResponse testCases := []TestCase[In, Out]{ @@ -605,7 +607,7 @@ func (s *KeeperSuite) TestQueryBalance() { } } -func (s *KeeperSuite) TestQueryBaseFee() { +func (s *Suite) TestQueryBaseFee() { type In = *evm.QueryBaseFeeRequest type Out = *evm.QueryBaseFeeResponse testCases := []TestCase[In, Out]{ @@ -642,7 +644,7 @@ func (s *KeeperSuite) TestQueryBaseFee() { } } -func (s *KeeperSuite) TestEstimateGasForEvmCallType() { +func (s *Suite) TestEstimateGasForEvmCallType() { type In = *evm.EthCallRequest type Out = *evm.EstimateGasResponse testCases := []TestCase[In, Out]{ @@ -753,7 +755,7 @@ func (s *KeeperSuite) TestEstimateGasForEvmCallType() { } } -func (s *KeeperSuite) TestTestTraceTx() { +func (s *Suite) TestTestTraceTx() { type In = *evm.QueryTraceTxRequest type Out = string @@ -781,7 +783,7 @@ func (s *KeeperSuite) TestTestTraceTx() { "happy: trace erc-20 transfer tx", nil, func(deps *evmtest.TestDeps) (req In, wantResp Out) { - txMsg, predecessors := evmtest.ExecuteERC20Transfer(deps, s.T()) + txMsg, predecessors := evmtest.DeployAndExecuteERC20Transfer(deps, s.T()) req = &evm.QueryTraceTxRequest{ Msg: txMsg, @@ -823,7 +825,7 @@ func (s *KeeperSuite) TestTestTraceTx() { } } -func (s *KeeperSuite) TestTestTraceBlock() { +func (s *Suite) TestTestTraceBlock() { type In = *evm.QueryTraceBlockRequest type Out = string testCases := []TestCase[In, Out]{ @@ -853,7 +855,7 @@ func (s *KeeperSuite) TestTestTraceBlock() { name: "happy: trace erc-20 transfer tx", setup: nil, scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { - txMsg, _ := evmtest.ExecuteERC20Transfer(deps, s.T()) + txMsg, _ := evmtest.DeployAndExecuteERC20Transfer(deps, s.T()) req = &evm.QueryTraceBlockRequest{ Txs: []*evm.MsgEthereumTx{ txMsg, diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index 164ca6db6..1c81893b3 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/suite" ) -type KeeperSuite struct { +type Suite struct { suite.Suite } // TestKeeperSuite: Runs all the tests in the suite. func TestKeeperSuite(t *testing.T) { - s := new(KeeperSuite) + s := new(Suite) suite.Run(t, s) } diff --git a/x/evm/keeper/msg_ethereum_tx_test.go b/x/evm/keeper/msg_ethereum_tx_test.go index ee8682f38..9646665b6 100644 --- a/x/evm/keeper/msg_ethereum_tx_test.go +++ b/x/evm/keeper/msg_ethereum_tx_test.go @@ -11,7 +11,7 @@ import ( "github.com/NibiruChain/nibiru/x/evm/evmtest" ) -func (s *KeeperSuite) TestMsgEthereumTx_CreateContract() { +func (s *Suite) TestMsgEthereumTx_CreateContract() { testCases := []struct { name string scenario func() @@ -72,7 +72,7 @@ func (s *KeeperSuite) TestMsgEthereumTx_CreateContract() { } } -func (s *KeeperSuite) TestMsgEthereumTx_SimpleTransfer() { +func (s *Suite) TestMsgEthereumTx_SimpleTransfer() { testCases := []struct { name string scenario func() diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 93259f963..e79532b50 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -14,7 +14,8 @@ import ( tmbytes "github.com/cometbft/cometbft/libs/bytes" tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -147,7 +148,7 @@ func (k *Keeper) ApplyEvmTx( } } - var contractAddr common.Address + var contractAddr gethcommon.Address if msg.To() == nil { contractAddr = crypto.CreateAddress(msg.From(), msg.Nonce()) } @@ -222,7 +223,7 @@ func (k *Keeper) ApplyEvmMsgWithEmptyTxConfig( return nil, errors.Wrap(err, "failed to load evm config") } - txConfig := statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash())) + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash())) return k.ApplyEvmMsg(ctx, msg, tracer, commit, cfg, txConfig) } @@ -272,11 +273,11 @@ func (k *Keeper) NewEVM( // 2. The requested height is from an previous height from the same chain epoch // 3. The requested height is from a height greater than the latest one func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { - return func(height uint64) common.Hash { + return func(height uint64) gethcommon.Hash { h, err := eth.SafeInt64(height) if err != nil { k.Logger(ctx).Error("failed to cast height to int64", "error", err) - return common.Hash{} + return gethcommon.Hash{} } switch { @@ -286,7 +287,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { // Note: The headerHash is only set at begin block, it will be nil in case of a query context headerHash := ctx.HeaderHash() if len(headerHash) != 0 { - return common.BytesToHash(headerHash) + return gethcommon.BytesToHash(headerHash) } // only recompute the hash if not set (eg: checkTxState) @@ -294,11 +295,11 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { header, err := tmtypes.HeaderFromProto(&contextBlockHeader) if err != nil { k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) - return common.Hash{} + return gethcommon.Hash{} } headerHash = header.Hash() - return common.BytesToHash(headerHash) + return gethcommon.BytesToHash(headerHash) case ctx.BlockHeight() > h: // Case 2: if the chain is not the current height we need to retrieve the hash from the store for the @@ -306,19 +307,19 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, h) if !found { k.Logger(ctx).Debug("historical info not found", "height", h) - return common.Hash{} + return gethcommon.Hash{} } header, err := tmtypes.HeaderFromProto(&histInfo.Header) if err != nil { k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) - return common.Hash{} + return gethcommon.Hash{} } - return common.BytesToHash(header.Hash()) + return gethcommon.BytesToHash(header.Hash()) default: // Case 3: heights greater than the current one returns an empty hash. - return common.Hash{} + return gethcommon.Hash{} } } } @@ -387,7 +388,7 @@ func (k *Keeper) ApplyEvmMsg(ctx sdk.Context, if cfg.Params.HasCustomPrecompiles() { customPrecompiles := cfg.Params.GetActivePrecompilesAddrs() - activePrecompiles := make([]common.Address, len(vm.PrecompiledAddressesBerlin)+len(customPrecompiles)) + activePrecompiles := make([]gethcommon.Address, len(vm.PrecompiledAddressesBerlin)+len(customPrecompiles)) copy(activePrecompiles[:len(vm.PrecompiledAddressesBerlin)], vm.PrecompiledAddressesBerlin) copy(activePrecompiles[len(vm.PrecompiledAddressesBerlin):], customPrecompiles) @@ -504,3 +505,97 @@ func (k *Keeper) ApplyEvmMsg(ctx sdk.Context, Hash: txConfig.TxHash.Hex(), }, nil } + +func (k *Keeper) CreateFunToken( + goCtx context.Context, msg *evm.MsgCreateFunToken, +) (resp *evm.MsgCreateFunTokenResponse, err error) { + var funtoken evm.FunToken + err = msg.ValidateBasic() + if err != nil { + return + } + + // TODO: UD-DEBUG: feat: Add fee upon registration. + + ctx := sdk.UnwrapSDKContext(goCtx) + switch { + case msg.FromErc20 != "" && msg.FromBankDenom == "": + funtoken, err = k.CreateFunTokenFromERC20(ctx, msg.FromErc20) + case msg.FromErc20 == "" && msg.FromBankDenom != "": + panic("TODO: UD-DEBUG: not yet implemented") + default: + panic("TODO: UD-DEBUG: cannot set both") // impossible because of ValidateBasic + } + if err != nil { + return + } + + return &evm.MsgCreateFunTokenResponse{ + FuntokenMapping: funtoken, + }, err +} + +func (k *Keeper) CreateFunTokenFromERC20( + ctx sdk.Context, erc20 eth.HexAddr, +) (funtoken evm.FunToken, err error) { + erc20Addr := erc20.ToAddr() + + // ERC20 already registered? + if funtokens := k.FunTokens.Collect( + ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr), + ); len(funtokens) > 0 { + return funtoken, fmt.Errorf("Funtoken mapping already created for ERC20 \"%s\"", erc20Addr.Hex()) + } + + // Find the existing ERC20 + info, err := k.FindERC20Metadata(ctx, erc20Addr) + if err != nil { + return + } + bankDenom := fmt.Sprintf("erc20/%s", erc20.String()) + + // Coin already registered? + _, isAlreadyCoin := k.bankKeeper.GetDenomMetaData(ctx, bankDenom) + if isAlreadyCoin { + return funtoken, fmt.Errorf("Bank coin denom already registered with denom \"%s\"", bankDenom) + } + if funtokens := k.FunTokens.Collect( + ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom), + ); len(funtokens) > 0 { + return funtoken, fmt.Errorf("Funtoken mapping already created for bank denom \"%s\"", bankDenom) + } + + // Set bank coin denom metadata in state + bankMetadata := bank.Metadata{ + Description: fmt.Sprintf("Bank coin representation of ERC20 token \"%s\"", erc20.String()), + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: info.Symbol, + } + + err = bankMetadata.Validate() + if err != nil { + return + } + k.bankKeeper.SetDenomMetaData(ctx, bankMetadata) + + // Officially create the funtoken mapping + funtoken = evm.FunToken{ + Erc20Addr: erc20, + BankDenom: bankDenom, + IsMadeFromCoin: false, + } + + return funtoken, k.FunTokens.SafeInsert( + ctx, funtoken.Erc20Addr.ToAddr(), + funtoken.BankDenom, + funtoken.IsMadeFromCoin, + ) +} diff --git a/x/evm/msg.go b/x/evm/msg.go index d67ba6a97..5d80786b9 100644 --- a/x/evm/msg.go +++ b/x/evm/msg.go @@ -33,6 +33,7 @@ var ( _ sdk.Tx = &MsgEthereumTx{} _ ante.GasTx = &MsgEthereumTx{} _ sdk.Msg = &MsgUpdateParams{} + _ sdk.Msg = &MsgCreateFunToken{} _ codectypes.UnpackInterfacesMessage = MsgEthereumTx{} ) @@ -181,7 +182,7 @@ func (msg MsgEthereumTx) ValidateBasic() error { } if err := txData.Validate(); err != nil { - return err + return errorsmod.Wrap(err, "failed \"TxData.Validate\"") } // Validate Hash field after validated txData to avoid panic @@ -472,3 +473,36 @@ func BinSearch( } return hi, nil } + +// GetSigners returns the expected signers for a MsgCreateFunToken message. +func (m MsgCreateFunToken) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{addr} +} + +func errMsgCreateFunTokenValidate(errMsg string) error { + return fmt.Errorf("MsgCreateFunToken ValidateBasic error: %s", errMsg) +} + +// ValidateBasic does a sanity check of the provided data +func (m *MsgCreateFunToken) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return errMsgCreateFunTokenValidate("invalid sender addr") + } + + erc20 := m.FromErc20 + bankDenom := m.FromBankDenom + if (erc20 == "" && bankDenom == "") || (erc20 != "" && bankDenom != "") { + return errMsgCreateFunTokenValidate(fmt.Sprintf( + "Either the \"from_erc20\" or \"from_bank_denom\" must be set (but not both)."+ + "got values (from_erc20=\"%s\", from_bank_denom=\"%s\")", erc20, bankDenom, + )) + } + + return nil +} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgCreateFunToken) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) +} diff --git a/x/evm/tx.pb.go b/x/evm/tx.pb.go index b86d83cfa..5677fcde6 100644 --- a/x/evm/tx.pb.go +++ b/x/evm/tx.pb.go @@ -8,6 +8,7 @@ import ( cosmossdk_io_math "cosmossdk.io/math" encoding_binary "encoding/binary" fmt "fmt" + github_com_NibiruChain_nibiru_eth "github.com/NibiruChain/nibiru/eth" _ "github.com/cosmos/cosmos-proto" types "github.com/cosmos/cosmos-sdk/codec/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" @@ -440,6 +441,110 @@ func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo +// MsgCreateFunToken: Arguments to create a "FunToken" mapping. Either the ERC20 +// contract address can be given to create the mapping to a bank coin, or the +// denomination for a bank coin can be given to create the mapping to an ERC20. +type MsgCreateFunToken struct { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + FromErc20 github_com_NibiruChain_nibiru_eth.HexAddr `protobuf:"bytes,1,opt,name=from_erc20,json=fromErc20,proto3,customtype=github.com/NibiruChain/nibiru/eth.HexAddr" json:"from_erc20"` + // Coin denomination in the Bank Module. + FromBankDenom string `protobuf:"bytes,2,opt,name=from_bank_denom,json=fromBankDenom,proto3" json:"from_bank_denom,omitempty"` + // Sender: Address for the signer of the transaction. + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` +} + +func (m *MsgCreateFunToken) Reset() { *m = MsgCreateFunToken{} } +func (m *MsgCreateFunToken) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFunToken) ProtoMessage() {} +func (*MsgCreateFunToken) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{8} +} +func (m *MsgCreateFunToken) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFunToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFunToken.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateFunToken) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFunToken.Merge(m, src) +} +func (m *MsgCreateFunToken) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFunToken) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFunToken.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFunToken proto.InternalMessageInfo + +func (m *MsgCreateFunToken) GetFromBankDenom() string { + if m != nil { + return m.FromBankDenom + } + return "" +} + +func (m *MsgCreateFunToken) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +type MsgCreateFunTokenResponse struct { + // Fungible token mapping corresponding to ERC20 tokens. + FuntokenMapping FunToken `protobuf:"bytes,1,opt,name=funtoken_mapping,json=funtokenMapping,proto3" json:"funtoken_mapping"` +} + +func (m *MsgCreateFunTokenResponse) Reset() { *m = MsgCreateFunTokenResponse{} } +func (m *MsgCreateFunTokenResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFunTokenResponse) ProtoMessage() {} +func (*MsgCreateFunTokenResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{9} +} +func (m *MsgCreateFunTokenResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFunTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFunTokenResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateFunTokenResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFunTokenResponse.Merge(m, src) +} +func (m *MsgCreateFunTokenResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFunTokenResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFunTokenResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFunTokenResponse proto.InternalMessageInfo + +func (m *MsgCreateFunTokenResponse) GetFuntokenMapping() FunToken { + if m != nil { + return m.FuntokenMapping + } + return FunToken{} +} + func init() { proto.RegisterType((*MsgEthereumTx)(nil), "eth.evm.v1.MsgEthereumTx") proto.RegisterType((*LegacyTx)(nil), "eth.evm.v1.LegacyTx") @@ -449,72 +554,83 @@ func init() { proto.RegisterType((*MsgEthereumTxResponse)(nil), "eth.evm.v1.MsgEthereumTxResponse") proto.RegisterType((*MsgUpdateParams)(nil), "eth.evm.v1.MsgUpdateParams") proto.RegisterType((*MsgUpdateParamsResponse)(nil), "eth.evm.v1.MsgUpdateParamsResponse") + proto.RegisterType((*MsgCreateFunToken)(nil), "eth.evm.v1.MsgCreateFunToken") + proto.RegisterType((*MsgCreateFunTokenResponse)(nil), "eth.evm.v1.MsgCreateFunTokenResponse") } func init() { proto.RegisterFile("eth/evm/v1/tx.proto", fileDescriptor_82a0bfe4f0bab953) } var fileDescriptor_82a0bfe4f0bab953 = []byte{ - // 956 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0xbf, 0x6f, 0x23, 0x45, - 0x14, 0xc7, 0xbd, 0xf6, 0xfa, 0xd7, 0xd8, 0xe4, 0xd0, 0x90, 0x53, 0xd6, 0xe6, 0xe4, 0x35, 0x9b, - 0xc6, 0x42, 0xca, 0x2e, 0x17, 0x24, 0xa4, 0x8b, 0x44, 0x11, 0x5f, 0x72, 0xe8, 0x50, 0x02, 0xd1, - 0xe2, 0x34, 0x34, 0xd6, 0x64, 0x3d, 0x19, 0x8f, 0xf0, 0xee, 0xac, 0x76, 0xc6, 0x96, 0x4d, 0x79, - 0x15, 0x12, 0x05, 0x20, 0x7a, 0x44, 0x4d, 0x45, 0x71, 0x05, 0xff, 0x01, 0x27, 0xaa, 0x13, 0x34, - 0x88, 0xc2, 0x20, 0x07, 0x09, 0x29, 0x25, 0x05, 0x35, 0x9a, 0x99, 0x75, 0x6c, 0xdf, 0xc9, 0x01, - 0x22, 0x74, 0xdd, 0xbc, 0x79, 0x3f, 0xe6, 0xcd, 0xf7, 0xb3, 0xfb, 0x06, 0xbc, 0x82, 0x45, 0xdf, - 0xc3, 0xa3, 0xd0, 0x1b, 0xdd, 0xf5, 0xc4, 0xd8, 0x8d, 0x13, 0x26, 0x18, 0x04, 0x58, 0xf4, 0x5d, - 0x3c, 0x0a, 0xdd, 0xd1, 0xdd, 0xfa, 0x56, 0xc0, 0x78, 0xc8, 0xb8, 0x17, 0x72, 0x22, 0x63, 0x42, - 0x4e, 0x74, 0x50, 0xbd, 0xa6, 0x1d, 0x5d, 0x65, 0x79, 0xda, 0x48, 0x5d, 0x9b, 0x4b, 0x45, 0x65, - 0x99, 0x74, 0x97, 0x30, 0xc2, 0x74, 0xb4, 0x5c, 0xa5, 0xbb, 0x77, 0x08, 0x63, 0x64, 0x80, 0x3d, - 0x14, 0x53, 0x0f, 0x45, 0x11, 0x13, 0x48, 0x50, 0x16, 0xcd, 0x2b, 0xd5, 0x52, 0xaf, 0xb2, 0xce, - 0x86, 0xe7, 0x1e, 0x8a, 0x26, 0xda, 0xe5, 0x7c, 0x66, 0x80, 0x97, 0x8e, 0x39, 0x39, 0x14, 0x7d, - 0x9c, 0xe0, 0x61, 0xd8, 0x19, 0xc3, 0x16, 0x30, 0x7b, 0x48, 0x20, 0xcb, 0x68, 0x1a, 0xad, 0xca, - 0xee, 0xa6, 0xab, 0x73, 0xdd, 0x79, 0xae, 0xbb, 0x1f, 0x4d, 0x7c, 0x15, 0x01, 0x6b, 0xc0, 0xe4, - 0xf4, 0x63, 0x6c, 0x65, 0x9b, 0x46, 0xcb, 0x68, 0xe7, 0x2f, 0xa7, 0xb6, 0xb1, 0xe3, 0xab, 0x2d, - 0x68, 0x03, 0xb3, 0x8f, 0x78, 0xdf, 0xca, 0x35, 0x8d, 0x56, 0xb9, 0x5d, 0xf9, 0x73, 0x6a, 0x17, - 0x93, 0x41, 0xbc, 0xe7, 0xec, 0x38, 0xbe, 0x72, 0x40, 0x08, 0xcc, 0xf3, 0x84, 0x85, 0x96, 0x29, - 0x03, 0x7c, 0xb5, 0xde, 0x33, 0x3f, 0xf9, 0xda, 0xce, 0x38, 0x5f, 0x64, 0x41, 0xe9, 0x08, 0x13, - 0x14, 0x4c, 0x3a, 0x63, 0xb8, 0x09, 0xf2, 0x11, 0x8b, 0x02, 0xac, 0xba, 0x31, 0x7d, 0x6d, 0xc0, - 0xb7, 0x40, 0x99, 0x20, 0xa9, 0x19, 0x0d, 0xf4, 0xe9, 0xe5, 0x76, 0xed, 0x97, 0xa9, 0x7d, 0x5b, - 0xcb, 0xc7, 0x7b, 0x1f, 0xb9, 0x94, 0x79, 0x21, 0x12, 0x7d, 0xf7, 0x61, 0x24, 0xfc, 0x12, 0x41, - 0xfc, 0x44, 0x86, 0xc2, 0x06, 0xc8, 0x11, 0xc4, 0x55, 0x53, 0x66, 0xbb, 0x3a, 0x9b, 0xda, 0xa5, - 0x77, 0x10, 0x3f, 0xa2, 0x21, 0x15, 0xbe, 0x74, 0xc0, 0x0d, 0x90, 0x15, 0x2c, 0x6d, 0x29, 0x2b, - 0x18, 0xbc, 0x07, 0xf2, 0x23, 0x34, 0x18, 0x62, 0x2b, 0xaf, 0xce, 0xd8, 0x5e, 0x7b, 0xc6, 0x6c, - 0x6a, 0x17, 0xf6, 0x43, 0x36, 0x8c, 0x84, 0xaf, 0x33, 0xe4, 0xfd, 0x94, 0x8a, 0x85, 0xa6, 0xd1, - 0xaa, 0xa6, 0x7a, 0x55, 0x81, 0x31, 0xb2, 0x8a, 0x6a, 0xc3, 0x18, 0x49, 0x2b, 0xb1, 0x4a, 0xda, - 0x4a, 0xa4, 0xc5, 0xad, 0xb2, 0xb6, 0xf8, 0xde, 0x86, 0x54, 0xe2, 0x87, 0xc7, 0x3b, 0x85, 0xce, - 0xf8, 0x00, 0x09, 0xe4, 0x7c, 0x97, 0x03, 0xd5, 0xfd, 0x20, 0xc0, 0x9c, 0x1f, 0x51, 0x2e, 0x3a, - 0x63, 0xf8, 0x2e, 0x28, 0x05, 0x7d, 0x44, 0xa3, 0x2e, 0xed, 0x29, 0x69, 0xca, 0x6d, 0xef, 0xba, - 0xe6, 0x8a, 0xf7, 0x65, 0xf0, 0xc3, 0x83, 0xcb, 0xa9, 0x5d, 0x0c, 0xf4, 0xd2, 0x4f, 0x17, 0xbd, - 0x85, 0xc6, 0xd9, 0xb5, 0x1a, 0xe7, 0xfe, 0xb3, 0xc6, 0xe6, 0xf5, 0x1a, 0xe7, 0x9f, 0xd7, 0xb8, - 0x70, 0x63, 0x8d, 0x8b, 0x4b, 0x1a, 0x9f, 0x82, 0x12, 0x52, 0x42, 0x61, 0x6e, 0x95, 0x9a, 0xb9, - 0x56, 0x65, 0x77, 0xcb, 0x5d, 0xfc, 0x87, 0xae, 0x16, 0xb1, 0x33, 0x8c, 0x07, 0xb8, 0xdd, 0x7c, - 0x32, 0xb5, 0x33, 0x97, 0x53, 0x1b, 0xa0, 0x2b, 0x65, 0xbf, 0xf9, 0xd5, 0x06, 0x0b, 0x9d, 0xfd, - 0xab, 0x52, 0x1a, 0x5d, 0x79, 0x05, 0x1d, 0x58, 0x41, 0x57, 0x59, 0x87, 0xee, 0xaf, 0x1c, 0xa8, - 0x1e, 0x4c, 0x22, 0x14, 0xd2, 0xe0, 0x01, 0xc6, 0x2f, 0x04, 0xdd, 0x3d, 0x50, 0x91, 0xe8, 0x04, - 0x8d, 0xbb, 0x01, 0x8a, 0xff, 0x19, 0x9e, 0x04, 0xdd, 0xa1, 0xf1, 0x7d, 0x14, 0xcf, 0x53, 0xcf, - 0x31, 0x56, 0xa9, 0xe6, 0xbf, 0x49, 0x7d, 0x80, 0xb1, 0x4c, 0x4d, 0xc1, 0xe7, 0xaf, 0x07, 0x5f, - 0x78, 0x1e, 0x7c, 0xf1, 0xc6, 0xe0, 0x4b, 0x6b, 0xc0, 0x97, 0xff, 0x67, 0xf0, 0x60, 0x05, 0x7c, - 0x65, 0x05, 0x7c, 0x75, 0x1d, 0x78, 0x07, 0xd4, 0x0f, 0xc7, 0x02, 0x47, 0x9c, 0xb2, 0xe8, 0xfd, - 0x58, 0x8d, 0xe3, 0xc5, 0x94, 0x4d, 0x67, 0xdd, 0x57, 0x06, 0xb8, 0xbd, 0x32, 0x7d, 0x7d, 0xcc, - 0x63, 0x16, 0x71, 0x75, 0x45, 0x35, 0x40, 0x0d, 0x3d, 0x1f, 0xd5, 0xcc, 0xdc, 0x06, 0xe6, 0x80, - 0x11, 0x6e, 0x65, 0xd5, 0xf5, 0x6e, 0x2d, 0x5f, 0xef, 0x88, 0x11, 0x5f, 0x39, 0xe1, 0xcb, 0x20, - 0x97, 0x60, 0xa1, 0xa0, 0x57, 0x7d, 0xb9, 0x84, 0x35, 0x50, 0x1a, 0x85, 0x5d, 0x9c, 0x24, 0x2c, - 0x49, 0x67, 0x5b, 0x71, 0x14, 0x1e, 0x4a, 0x53, 0xba, 0x24, 0xee, 0x21, 0xc7, 0x3d, 0x0d, 0xce, - 0x2f, 0x12, 0xc4, 0x4f, 0x39, 0xee, 0xa5, 0x0d, 0x7e, 0x6a, 0x80, 0x5b, 0xc7, 0x9c, 0x9c, 0xc6, - 0x3d, 0x24, 0xf0, 0x09, 0x4a, 0x50, 0xc8, 0xe5, 0x64, 0x40, 0x43, 0xd1, 0x67, 0x09, 0x15, 0x93, - 0xf4, 0x0b, 0xb6, 0x7e, 0x7c, 0xbc, 0xb3, 0x99, 0x3e, 0x5e, 0xfb, 0xbd, 0x5e, 0x82, 0x39, 0xff, - 0x40, 0x24, 0x34, 0x22, 0xfe, 0x22, 0x14, 0xbe, 0x01, 0x0a, 0xb1, 0xaa, 0xa0, 0xbe, 0xd6, 0xca, - 0x2e, 0x5c, 0xbe, 0x80, 0xae, 0xdd, 0x36, 0x25, 0x1a, 0x3f, 0x8d, 0xdb, 0xdb, 0x78, 0xf4, 0xc7, - 0xb7, 0xaf, 0x2f, 0x2a, 0x38, 0x35, 0xb0, 0xf5, 0x4c, 0x33, 0x73, 0xbd, 0x76, 0xbf, 0x37, 0x40, - 0xee, 0x98, 0x13, 0x18, 0x01, 0xb0, 0xf4, 0x96, 0xd5, 0x96, 0x8f, 0x58, 0x11, 0xba, 0xfe, 0xda, - 0x5a, 0xd7, 0xbc, 0xa6, 0xe3, 0x3c, 0xfa, 0xe9, 0xf7, 0x2f, 0xb3, 0x77, 0x9c, 0xba, 0x17, 0xd1, - 0x33, 0x9a, 0x0c, 0xaf, 0x1e, 0xe3, 0x34, 0xb4, 0x2b, 0xc6, 0xf0, 0x04, 0x54, 0x57, 0xc4, 0x79, - 0xf5, 0x99, 0xb2, 0xcb, 0xce, 0xfa, 0xf6, 0x35, 0xce, 0xf9, 0xa9, 0xed, 0xb7, 0x9f, 0xcc, 0x1a, - 0xc6, 0xd3, 0x59, 0xc3, 0xf8, 0x6d, 0xd6, 0x30, 0x3e, 0xbf, 0x68, 0x64, 0x9e, 0x5e, 0x34, 0x32, - 0x3f, 0x5f, 0x34, 0x32, 0x1f, 0x6e, 0x13, 0x2a, 0xfa, 0xc3, 0x33, 0x37, 0x60, 0xa1, 0xf7, 0x9e, - 0xea, 0x48, 0xcd, 0x86, 0x79, 0x77, 0x63, 0xd9, 0xdf, 0x59, 0x41, 0x3d, 0xd4, 0x6f, 0xfe, 0x1d, - 0x00, 0x00, 0xff, 0xff, 0x10, 0x4e, 0x86, 0x00, 0x93, 0x08, 0x00, 0x00, + // 1104 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xbf, 0x6f, 0x23, 0xc5, + 0x17, 0xcf, 0xda, 0xeb, 0x5f, 0xcf, 0xbe, 0xe4, 0xbe, 0xfb, 0xcd, 0x71, 0xb6, 0x39, 0xbc, 0xc6, + 0x11, 0xc8, 0x20, 0x65, 0xf7, 0x2e, 0x48, 0x48, 0x17, 0x89, 0x22, 0xce, 0x0f, 0x38, 0x94, 0x40, + 0xb4, 0x38, 0x14, 0x34, 0xd6, 0x78, 0x77, 0xb2, 0x5e, 0x25, 0x3b, 0xb3, 0xda, 0x99, 0xb5, 0x1c, + 0xca, 0xab, 0x90, 0x28, 0x00, 0xd1, 0x03, 0x0d, 0x0d, 0x15, 0xc5, 0x15, 0xfc, 0x09, 0x27, 0xaa, + 0x13, 0x34, 0xe8, 0x0a, 0x83, 0x12, 0x24, 0xa4, 0x94, 0x14, 0xd4, 0x68, 0x66, 0xd7, 0xbf, 0x12, + 0xd9, 0xc0, 0x09, 0xd1, 0xcd, 0x9b, 0xf7, 0xde, 0x67, 0xde, 0xfb, 0x7c, 0x66, 0xdf, 0x2c, 0xfc, + 0x1f, 0xf3, 0x9e, 0x89, 0xfb, 0xbe, 0xd9, 0xbf, 0x67, 0xf2, 0x81, 0x11, 0x84, 0x94, 0x53, 0x0d, + 0x30, 0xef, 0x19, 0xb8, 0xef, 0x1b, 0xfd, 0x7b, 0xd5, 0xdb, 0x36, 0x65, 0x3e, 0x65, 0xa6, 0xcf, + 0x5c, 0x11, 0xe3, 0x33, 0x37, 0x0e, 0xaa, 0x56, 0x62, 0x47, 0x47, 0x5a, 0x66, 0x6c, 0x24, 0xae, + 0xd5, 0x29, 0x50, 0x01, 0x93, 0xec, 0xba, 0xd4, 0xa5, 0x71, 0xb4, 0x58, 0x25, 0xbb, 0x77, 0x5c, + 0x4a, 0xdd, 0x53, 0x6c, 0xa2, 0xc0, 0x33, 0x11, 0x21, 0x94, 0x23, 0xee, 0x51, 0x32, 0x42, 0xaa, + 0x24, 0x5e, 0x69, 0x75, 0xa3, 0x63, 0x13, 0x91, 0xb3, 0xd8, 0xd5, 0xf8, 0x44, 0x81, 0x1b, 0x07, + 0xcc, 0xdd, 0xe5, 0x3d, 0x1c, 0xe2, 0xc8, 0x6f, 0x0f, 0xb4, 0x26, 0xa8, 0x0e, 0xe2, 0xa8, 0xac, + 0xd4, 0x95, 0x66, 0x71, 0x63, 0xd5, 0x88, 0x73, 0x8d, 0x51, 0xae, 0xb1, 0x45, 0xce, 0x2c, 0x19, + 0xa1, 0x55, 0x40, 0x65, 0xde, 0x87, 0xb8, 0x9c, 0xaa, 0x2b, 0x4d, 0xa5, 0x95, 0xb9, 0x1c, 0xea, + 0xca, 0xba, 0x25, 0xb7, 0x34, 0x1d, 0xd4, 0x1e, 0x62, 0xbd, 0x72, 0xba, 0xae, 0x34, 0x0b, 0xad, + 0xe2, 0xef, 0x43, 0x3d, 0x17, 0x9e, 0x06, 0x9b, 0x8d, 0xf5, 0x86, 0x25, 0x1d, 0x9a, 0x06, 0xea, + 0x71, 0x48, 0xfd, 0xb2, 0x2a, 0x02, 0x2c, 0xb9, 0xde, 0x54, 0x3f, 0xfa, 0x4a, 0x5f, 0x6a, 0x7c, + 0x96, 0x82, 0xfc, 0x3e, 0x76, 0x91, 0x7d, 0xd6, 0x1e, 0x68, 0xab, 0x90, 0x21, 0x94, 0xd8, 0x58, + 0x56, 0xa3, 0x5a, 0xb1, 0xa1, 0xbd, 0x0e, 0x05, 0x17, 0x09, 0xce, 0x3c, 0x3b, 0x3e, 0xbd, 0xd0, + 0xaa, 0x3c, 0x1d, 0xea, 0xb7, 0x62, 0xfa, 0x98, 0x73, 0x62, 0x78, 0xd4, 0xf4, 0x11, 0xef, 0x19, + 0x0f, 0x08, 0xb7, 0xf2, 0x2e, 0x62, 0x87, 0x22, 0x54, 0xab, 0x41, 0xda, 0x45, 0x4c, 0x16, 0xa5, + 0xb6, 0x4a, 0xe7, 0x43, 0x3d, 0xff, 0x26, 0x62, 0xfb, 0x9e, 0xef, 0x71, 0x4b, 0x38, 0xb4, 0x65, + 0x48, 0x71, 0x9a, 0x94, 0x94, 0xe2, 0x54, 0xbb, 0x0f, 0x99, 0x3e, 0x3a, 0x8d, 0x70, 0x39, 0x23, + 0xcf, 0x58, 0x9b, 0x7b, 0xc6, 0xf9, 0x50, 0xcf, 0x6e, 0xf9, 0x34, 0x22, 0xdc, 0x8a, 0x33, 0x44, + 0x7f, 0x92, 0xc5, 0x6c, 0x5d, 0x69, 0x96, 0x12, 0xbe, 0x4a, 0xa0, 0xf4, 0xcb, 0x39, 0xb9, 0xa1, + 0xf4, 0x85, 0x15, 0x96, 0xf3, 0xb1, 0x15, 0x0a, 0x8b, 0x95, 0x0b, 0xb1, 0xc5, 0x36, 0x97, 0x05, + 0x13, 0xdf, 0x3f, 0x5a, 0xcf, 0xb6, 0x07, 0x3b, 0x88, 0xa3, 0xc6, 0x77, 0x69, 0x28, 0x6d, 0xd9, + 0x36, 0x66, 0x6c, 0xdf, 0x63, 0xbc, 0x3d, 0xd0, 0xde, 0x86, 0xbc, 0xdd, 0x43, 0x1e, 0xe9, 0x78, + 0x8e, 0xa4, 0xa6, 0xd0, 0x32, 0x17, 0x15, 0x97, 0xdb, 0x16, 0xc1, 0x0f, 0x76, 0x2e, 0x87, 0x7a, + 0xce, 0x8e, 0x97, 0x56, 0xb2, 0x70, 0x26, 0x1c, 0xa7, 0xe6, 0x72, 0x9c, 0xfe, 0xc7, 0x1c, 0xab, + 0x8b, 0x39, 0xce, 0x5c, 0xe7, 0x38, 0xfb, 0xcc, 0x1c, 0xe7, 0xa6, 0x38, 0x3e, 0x82, 0x3c, 0x92, + 0x44, 0x61, 0x56, 0xce, 0xd7, 0xd3, 0xcd, 0xe2, 0xc6, 0x6d, 0x63, 0xf2, 0x1d, 0x1a, 0x31, 0x89, + 0xed, 0x28, 0x38, 0xc5, 0xad, 0xfa, 0xe3, 0xa1, 0xbe, 0x74, 0x39, 0xd4, 0x01, 0x8d, 0x99, 0xfd, + 0xe6, 0x67, 0x1d, 0x26, 0x3c, 0x5b, 0x63, 0xa8, 0x58, 0xba, 0xc2, 0x8c, 0x74, 0x30, 0x23, 0x5d, + 0x71, 0x9e, 0x74, 0x7f, 0xa4, 0xa1, 0xb4, 0x73, 0x46, 0x90, 0xef, 0xd9, 0x7b, 0x18, 0xff, 0x27, + 0xd2, 0xdd, 0x87, 0xa2, 0x90, 0x8e, 0x7b, 0x41, 0xc7, 0x46, 0xc1, 0x5f, 0x8b, 0x27, 0x84, 0x6e, + 0x7b, 0xc1, 0x36, 0x0a, 0x46, 0xa9, 0xc7, 0x18, 0xcb, 0x54, 0xf5, 0xef, 0xa4, 0xee, 0x61, 0x2c, + 0x52, 0x13, 0xe1, 0x33, 0x8b, 0x85, 0xcf, 0x5e, 0x17, 0x3e, 0xf7, 0xcc, 0xc2, 0xe7, 0xe7, 0x08, + 0x5f, 0xf8, 0x97, 0x85, 0x87, 0x19, 0xe1, 0x8b, 0x33, 0xc2, 0x97, 0xe6, 0x09, 0xdf, 0x80, 0xea, + 0xee, 0x80, 0x63, 0xc2, 0x3c, 0x4a, 0xde, 0x0d, 0xe4, 0x38, 0x9e, 0x4c, 0xd9, 0x64, 0xd6, 0x7d, + 0xa1, 0xc0, 0xad, 0x99, 0xe9, 0x6b, 0x61, 0x16, 0x50, 0xc2, 0x64, 0x8b, 0x72, 0x80, 0x2a, 0xf1, + 0x7c, 0x94, 0x33, 0x73, 0x0d, 0xd4, 0x53, 0xea, 0xb2, 0x72, 0x4a, 0xb6, 0xb7, 0x32, 0xdd, 0xde, + 0x3e, 0x75, 0x2d, 0xe9, 0xd4, 0x6e, 0x42, 0x3a, 0xc4, 0x5c, 0x8a, 0x5e, 0xb2, 0xc4, 0x52, 0xab, + 0x40, 0xbe, 0xef, 0x77, 0x70, 0x18, 0xd2, 0x30, 0x99, 0x6d, 0xb9, 0xbe, 0xbf, 0x2b, 0x4c, 0xe1, + 0x12, 0x72, 0x47, 0x0c, 0x3b, 0xb1, 0x70, 0x56, 0xce, 0x45, 0xec, 0x88, 0x61, 0x27, 0x29, 0xf0, + 0x63, 0x05, 0x56, 0x0e, 0x98, 0x7b, 0x14, 0x38, 0x88, 0xe3, 0x43, 0x14, 0x22, 0x9f, 0x89, 0xc9, + 0x80, 0x22, 0xde, 0xa3, 0xa1, 0xc7, 0xcf, 0x92, 0x1b, 0x5c, 0xfe, 0xe1, 0xd1, 0xfa, 0x6a, 0xf2, + 0x78, 0x6d, 0x39, 0x4e, 0x88, 0x19, 0x7b, 0x8f, 0x87, 0x1e, 0x71, 0xad, 0x49, 0xa8, 0x76, 0x17, + 0xb2, 0x81, 0x44, 0x90, 0xb7, 0xb5, 0xb8, 0xa1, 0x4d, 0x37, 0x10, 0x63, 0xb7, 0x54, 0x21, 0x8d, + 0x95, 0xc4, 0x6d, 0x2e, 0x3f, 0xfc, 0xed, 0xdb, 0x57, 0x27, 0x08, 0x8d, 0x0a, 0xdc, 0xbe, 0x52, + 0xcc, 0x88, 0xaf, 0xc6, 0xd7, 0x0a, 0xfc, 0xef, 0x80, 0xb9, 0xdb, 0x21, 0x46, 0x1c, 0xef, 0x45, + 0xa4, 0x4d, 0x4f, 0x30, 0xd1, 0x0e, 0x01, 0xc4, 0xcb, 0xd2, 0xc1, 0xa1, 0xbd, 0x71, 0x37, 0xa9, + 0xf5, 0x9e, 0x38, 0xe2, 0xe9, 0x50, 0x7f, 0xc5, 0xf5, 0x78, 0x2f, 0xea, 0x1a, 0x36, 0xf5, 0xcd, + 0x77, 0xbc, 0xae, 0x17, 0x46, 0xf2, 0x4b, 0x33, 0x89, 0x5c, 0x9b, 0xa2, 0xb6, 0xb7, 0xf0, 0x40, + 0x74, 0x63, 0x15, 0x04, 0xc8, 0xae, 0xc0, 0xd0, 0x5e, 0x86, 0x15, 0x89, 0xd8, 0x45, 0xe4, 0xa4, + 0xe3, 0x60, 0x42, 0xfd, 0xf8, 0x01, 0xb2, 0x6e, 0x88, 0xed, 0x16, 0x22, 0x27, 0x3b, 0x62, 0x53, + 0x7b, 0x0e, 0xb2, 0x0c, 0x13, 0x07, 0x87, 0xf1, 0xe7, 0x67, 0x25, 0x56, 0xa3, 0x0b, 0x95, 0x6b, + 0x65, 0x8e, 0x45, 0xdf, 0x85, 0x9b, 0xc7, 0x11, 0xe1, 0x62, 0xaf, 0xe3, 0xa3, 0x20, 0xf0, 0x88, + 0x3b, 0x7e, 0x86, 0xa7, 0xb8, 0x1a, 0xe5, 0x25, 0x6c, 0xad, 0x8c, 0x72, 0x0e, 0xe2, 0x94, 0x8d, + 0x2f, 0x53, 0x90, 0x3e, 0x60, 0xae, 0x46, 0x00, 0xa6, 0xde, 0xf5, 0xca, 0x34, 0xc4, 0xcc, 0xa5, + 0xab, 0xbe, 0x38, 0xd7, 0x35, 0xe6, 0xb7, 0xf1, 0xf0, 0xc7, 0x5f, 0x3f, 0x4f, 0xdd, 0x69, 0x54, + 0xc7, 0xfc, 0x24, 0x3f, 0x26, 0x49, 0x68, 0x87, 0x0f, 0xb4, 0x43, 0x28, 0xcd, 0x5c, 0x94, 0xe7, + 0xaf, 0xc0, 0x4e, 0x3b, 0xab, 0x6b, 0x0b, 0x9c, 0x63, 0x42, 0xde, 0x87, 0xe5, 0x2b, 0x8a, 0xbe, + 0x70, 0x25, 0x6d, 0xd6, 0x5d, 0x7d, 0x69, 0xa1, 0x7b, 0x84, 0xdb, 0x7a, 0xe3, 0xf1, 0x79, 0x4d, + 0x79, 0x72, 0x5e, 0x53, 0x7e, 0x39, 0xaf, 0x29, 0x9f, 0x5e, 0xd4, 0x96, 0x9e, 0x5c, 0xd4, 0x96, + 0x7e, 0xba, 0xa8, 0x2d, 0x7d, 0xb0, 0xb6, 0xf8, 0x56, 0x0c, 0x44, 0xdf, 0xdd, 0xac, 0xfc, 0x19, + 0x7a, 0xed, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa7, 0xf8, 0xd4, 0x86, 0xf7, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -534,6 +650,10 @@ type MsgClient interface { // UpdateParams defined a governance operation for updating the x/evm module parameters. // The authority is hard-coded to the Cosmos SDK x/gov module account UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a bank coin, or the + // denomination for a bank coin can be given to create the mapping to an ERC20. + CreateFunToken(ctx context.Context, in *MsgCreateFunToken, opts ...grpc.CallOption) (*MsgCreateFunTokenResponse, error) } type msgClient struct { @@ -562,6 +682,15 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts return out, nil } +func (c *msgClient) CreateFunToken(ctx context.Context, in *MsgCreateFunToken, opts ...grpc.CallOption) (*MsgCreateFunTokenResponse, error) { + out := new(MsgCreateFunTokenResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/CreateFunToken", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // EthereumTx defines a method submitting Ethereum transactions. @@ -569,6 +698,10 @@ type MsgServer interface { // UpdateParams defined a governance operation for updating the x/evm module parameters. // The authority is hard-coded to the Cosmos SDK x/gov module account UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a bank coin, or the + // denomination for a bank coin can be given to create the mapping to an ERC20. + CreateFunToken(context.Context, *MsgCreateFunToken) (*MsgCreateFunTokenResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -581,6 +714,9 @@ func (*UnimplementedMsgServer) EthereumTx(ctx context.Context, req *MsgEthereumT func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") } +func (*UnimplementedMsgServer) CreateFunToken(ctx context.Context, req *MsgCreateFunToken) (*MsgCreateFunTokenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateFunToken not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -622,6 +758,24 @@ func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Msg_CreateFunToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateFunToken) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateFunToken(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/CreateFunToken", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateFunToken(ctx, req.(*MsgCreateFunToken)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "eth.evm.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -634,6 +788,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UpdateParams", Handler: _Msg_UpdateParams_Handler, }, + { + MethodName: "CreateFunToken", + Handler: _Msg_CreateFunToken_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "eth/evm/v1/tx.proto", @@ -1183,6 +1341,86 @@ func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *MsgCreateFunToken) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateFunToken) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFunToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x1a + } + if len(m.FromBankDenom) > 0 { + i -= len(m.FromBankDenom) + copy(dAtA[i:], m.FromBankDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromBankDenom))) + i-- + dAtA[i] = 0x12 + } + { + size := m.FromErc20.Size() + i -= size + if _, err := m.FromErc20.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgCreateFunTokenResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateFunTokenResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFunTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.FuntokenMapping.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -1434,6 +1672,36 @@ func (m *MsgUpdateParamsResponse) Size() (n int) { return n } +func (m *MsgCreateFunToken) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.FromErc20.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.FromBankDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateFunTokenResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.FuntokenMapping.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -3177,6 +3445,237 @@ func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgCreateFunToken) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateFunToken: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFunToken: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromErc20", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FromErc20.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromBankDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromBankDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateFunTokenResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateFunTokenResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFunTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuntokenMapping", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FuntokenMapping.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0