From 033b4ddf5bd6542a2ab14d5071df6d6d21727a1d Mon Sep 17 00:00:00 2001 From: beer-1 <147697694+beer-1@users.noreply.github.com> Date: Wed, 8 May 2024 16:58:22 +0900 Subject: [PATCH] optional custom erc20 support (#13) --- app/ibc-hooks/common_test.go | 1 + proto/minievm/evm/v1/types.proto | 10 + x/bank/keeper/common_test.go | 1 + x/evm/contracts/custom_erc20/CustomERC20.go | 1007 +++++++++++++++++ x/evm/contracts/custom_erc20/CustomERC20.sol | 76 ++ x/evm/contracts/erc20/ERC20.go | 2 +- x/evm/contracts/erc20/ERC20.sol | 3 +- x/evm/contracts/erc20_factory/ERC20Factory.go | 377 ++++++ .../contracts/erc20_factory/ERC20Factory.sol | 26 + .../contracts/erc20_registry/ERC20Registry.go | 2 +- .../i_erc20_registry/IERC20Registry.go | 23 +- .../i_erc20_registry/IERC20Registry.sol | 1 + x/evm/keeper/common_test.go | 16 +- x/evm/keeper/erc20.go | 50 +- x/evm/keeper/erc20_stores.go | 39 + x/evm/keeper/erc20_stores_test.go | 71 ++ x/evm/keeper/erc20_test.go | 13 +- x/evm/keeper/erc721_test.go | 2 +- x/evm/keeper/genesis.go | 28 +- x/evm/keeper/genesis_test.go | 2 +- x/evm/precompiles/erc20_registry/contract.go | 22 +- .../erc20_registry/contract_test.go | 27 +- x/evm/precompiles/erc20_registry/types.go | 13 +- x/evm/types/address.go | 28 + x/evm/types/collection.go | 2 +- x/evm/types/errors.go | 4 +- x/evm/types/expected_keeper.go | 1 + x/evm/types/params.go | 11 +- x/evm/types/params_test.go | 48 + x/evm/types/types.pb.go | 146 ++- 30 files changed, 1975 insertions(+), 77 deletions(-) create mode 100644 x/evm/contracts/custom_erc20/CustomERC20.go create mode 100644 x/evm/contracts/custom_erc20/CustomERC20.sol create mode 100644 x/evm/contracts/erc20_factory/ERC20Factory.go create mode 100644 x/evm/contracts/erc20_factory/ERC20Factory.sol create mode 100644 x/evm/keeper/erc20_stores_test.go create mode 100644 x/evm/types/params_test.go diff --git a/app/ibc-hooks/common_test.go b/app/ibc-hooks/common_test.go index 0645504..758bbe4 100644 --- a/app/ibc-hooks/common_test.go +++ b/app/ibc-hooks/common_test.go @@ -311,6 +311,7 @@ func _createTestInput( ) evmParams := evmtypes.DefaultParams() require.NoError(t, evmKeeper.Params.Set(ctx, evmParams)) + require.NoError(t, evmKeeper.Initialize(ctx)) // set erc20 keeper *erc20Keeper = *evmKeeper.ERC20Keeper().(*evmkeeper.ERC20Keeper) diff --git a/proto/minievm/evm/v1/types.proto b/proto/minievm/evm/v1/types.proto index 2cdd077..cdcd2cb 100644 --- a/proto/minievm/evm/v1/types.proto +++ b/proto/minievm/evm/v1/types.proto @@ -21,6 +21,16 @@ message Params { // and an empty list is interpreted as allowing anyone to distribute. repeated string allowed_publishers = 2 [(gogoproto.moretags) = "yaml:\"allowed_publishers\"", (amino.dont_omitempty) = true]; + + // allow_custom_erc20 defines whether the chain allows custom erc20 tokens + // to be registered on cosmos bank interface. + bool allow_custom_erc20 = 3 [(gogoproto.customname) = "AllowCustomERC20"]; + repeated string allowed_custom_erc20s = 4 [ + (gogoproto.customname) = "AllowedCustomERC20s", + (gogoproto.moretags) = "yaml:\"allowed_custom_erc20s\"", + (amino.dont_omitempty) = true + ]; + ; } // Log represents a contract log event. These events are generated by diff --git a/x/bank/keeper/common_test.go b/x/bank/keeper/common_test.go index ea52e51..aa19db5 100644 --- a/x/bank/keeper/common_test.go +++ b/x/bank/keeper/common_test.go @@ -303,6 +303,7 @@ func _createTestInput( ) evmParams := evmtypes.DefaultParams() require.NoError(t, evmKeeper.Params.Set(ctx, evmParams)) + require.NoError(t, evmKeeper.Initialize(ctx)) // set erc20 keeper *erc20Keeper = *evmKeeper.ERC20Keeper().(*evmkeeper.ERC20Keeper) diff --git a/x/evm/contracts/custom_erc20/CustomERC20.go b/x/evm/contracts/custom_erc20/CustomERC20.go new file mode 100644 index 0000000..ffcb030 --- /dev/null +++ b/x/evm/contracts/custom_erc20/CustomERC20.go @@ -0,0 +1,1007 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package custom_erc20 + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// CustomErc20MetaData contains all meta data concerning the CustomErc20 contract. +var CustomErc20MetaData = &bind.MetaData{ + 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\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"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\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\":\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]", + Bin: "0x608060405234801562000010575f80fd5b5060405162001946380380620019468339818101604052810190620000369190620002da565b335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060f273ffffffffffffffffffffffffffffffffffffffff16635e6c57596040518163ffffffff1660e01b81526004015f604051808303815f87803b158015620000bc575f80fd5b505af1158015620000cf573d5f803e3d5ffd5b505050508260039081620000e49190620005a8565b508160049081620000f69190620005a8565b508060055f6101000a81548160ff021916908360ff1602179055505050506200068c565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6200017b8262000133565b810181811067ffffffffffffffff821117156200019d576200019c62000143565b5b80604052505050565b5f620001b16200011a565b9050620001bf828262000170565b919050565b5f67ffffffffffffffff821115620001e157620001e062000143565b5b620001ec8262000133565b9050602081019050919050565b5f5b8381101562000218578082015181840152602081019050620001fb565b5f8484015250505050565b5f620002396200023384620001c4565b620001a6565b9050828152602081018484840111156200025857620002576200012f565b5b62000265848285620001f9565b509392505050565b5f82601f8301126200028457620002836200012b565b5b81516200029684826020860162000223565b91505092915050565b5f60ff82169050919050565b620002b6816200029f565b8114620002c1575f80fd5b50565b5f81519050620002d481620002ab565b92915050565b5f805f60608486031215620002f457620002f362000123565b5b5f84015167ffffffffffffffff81111562000314576200031362000127565b5b62000322868287016200026d565b935050602084015167ffffffffffffffff81111562000346576200034562000127565b5b62000354868287016200026d565b92505060406200036786828701620002c4565b9150509250925092565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680620003c057607f821691505b602082108103620003d657620003d56200037b565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026200043a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620003fd565b620004468683620003fd565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620004906200048a62000484846200045e565b62000467565b6200045e565b9050919050565b5f819050919050565b620004ab8362000470565b620004c3620004ba8262000497565b84845462000409565b825550505050565b5f90565b620004d9620004cb565b620004e6818484620004a0565b505050565b5b818110156200050d57620005015f82620004cf565b600181019050620004ec565b5050565b601f8211156200055c576200052681620003dc565b6200053184620003ee565b8101602085101562000541578190505b620005596200055085620003ee565b830182620004eb565b50505b505050565b5f82821c905092915050565b5f6200057e5f198460080262000561565b1980831691505092915050565b5f6200059883836200056d565b9150826002028217905092915050565b620005b38262000371565b67ffffffffffffffff811115620005cf57620005ce62000143565b5b620005db8254620003a8565b620005e882828562000511565b5f60209050601f8311600181146200061e575f841562000609578287015190505b6200061585826200058b565b86555062000684565b601f1984166200062e86620003dc565b5f5b82811015620006575784890151825560018201915060208501945060208101905062000630565b8683101562000677578489015162000673601f8916826200056d565b8355505b6001600288020188555050505b505050505050565b6112ac806200069a5f395ff3fe608060405234801561000f575f80fd5b50600436106100cd575f3560e01c806370a082311161008a5780639dc29fac116100645780639dc29fac14610213578063a9059cbb1461022f578063dd62ed3e1461025f578063f2fde38b1461028f576100cd565b806370a08231146101a75780638da5cb5b146101d757806395d89b41146101f5576100cd565b806306fdde03146100d1578063095ea7b3146100ef57806318160ddd1461011f57806323b872dd1461013d578063313ce5671461016d57806340c10f191461018b575b5f80fd5b6100d96102ab565b6040516100e69190610ed2565b60405180910390f35b61010960048036038101906101049190610f83565b610337565b6040516101169190610fdb565b60405180910390f35b610127610424565b6040516101349190611003565b60405180910390f35b6101576004803603810190610152919061101c565b61042a565b6040516101649190610fdb565b60405180910390f35b6101756106b6565b6040516101829190611087565b60405180910390f35b6101a560048036038101906101a09190610f83565b6106c8565b005b6101c160048036038101906101bc91906110a0565b61072c565b6040516101ce9190611003565b60405180910390f35b6101df610741565b6040516101ec91906110da565b60405180910390f35b6101fd610764565b60405161020a9190610ed2565b60405180910390f35b61022d60048036038101906102289190610f83565b6107f0565b005b61024960048036038101906102449190610f83565b610854565b6040516102569190610fdb565b60405180910390f35b610279600480360381019061027491906110f3565b610a51565b6040516102869190611003565b60405180910390f35b6102a960048036038101906102a491906110a0565b610a71565b005b600380546102b89061115e565b80601f01602080910402602001604051908101604052809291908181526020018280546102e49061115e565b801561032f5780601f106103065761010080835404028352916020019161032f565b820191905f5260205f20905b81548152906001019060200180831161031257829003601f168201915b505050505081565b5f8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516104129190611003565b60405180910390a36001905092915050565b60065481565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161046691906110da565b602060405180830381865afa158015610481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a591906111b8565b6105115760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b81526004016104e391906110da565b5f604051808303815f87803b1580156104fa575f80fd5b505af115801561050c573d5f803e3d5ffd5b505050505b8260025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105989190611210565b925050819055508260015f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105eb9190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825461063e9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040516106a29190611003565b60405180910390a360019150509392505050565b60055f9054906101000a900460ff1681565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461071e575f80fd5b6107288282610bb9565b5050565b6001602052805f5260405f205f915090505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600480546107719061115e565b80601f016020809104026020016040519081016040528092919081815260200182805461079d9061115e565b80156107e85780601f106107bf576101008083540402835291602001916107e8565b820191905f5260205f20905b8154815290600101906020018083116107cb57829003601f168201915b505050505081565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610846575f80fd5b6108508282610d74565b5050565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161089091906110da565b602060405180830381865afa1580156108ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108cf91906111b8565b61093b5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b815260040161090d91906110da565b5f604051808303815f87803b158015610924575f80fd5b505af1158015610936573d5f803e3d5ffd5b505050505b8260015f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109879190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109da9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef85604051610a3e9190611003565b60405180910390a3600191505092915050565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ac7575f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610afe575f80fd5b8073ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8160f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b8152600401610bf491906110da565b602060405180830381865afa158015610c0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3391906111b8565b610c9f5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b8152600401610c7191906110da565b5f604051808303815f87803b158015610c88575f80fd5b505af1158015610c9a573d5f803e3d5ffd5b505050505b8160015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ceb9190611243565b925050819055508160065f828254610d039190611243565b925050819055508273ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610d679190611003565b60405180910390a3505050565b8060015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610dc09190611210565b925050819055508060065f828254610dd89190611210565b925050819055505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610e3c9190611003565b60405180910390a35050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610e7f578082015181840152602081019050610e64565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ea482610e48565b610eae8185610e52565b9350610ebe818560208601610e62565b610ec781610e8a565b840191505092915050565b5f6020820190508181035f830152610eea8184610e9a565b905092915050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610f1f82610ef6565b9050919050565b610f2f81610f15565b8114610f39575f80fd5b50565b5f81359050610f4a81610f26565b92915050565b5f819050919050565b610f6281610f50565b8114610f6c575f80fd5b50565b5f81359050610f7d81610f59565b92915050565b5f8060408385031215610f9957610f98610ef2565b5b5f610fa685828601610f3c565b9250506020610fb785828601610f6f565b9150509250929050565b5f8115159050919050565b610fd581610fc1565b82525050565b5f602082019050610fee5f830184610fcc565b92915050565b610ffd81610f50565b82525050565b5f6020820190506110165f830184610ff4565b92915050565b5f805f6060848603121561103357611032610ef2565b5b5f61104086828701610f3c565b935050602061105186828701610f3c565b925050604061106286828701610f6f565b9150509250925092565b5f60ff82169050919050565b6110818161106c565b82525050565b5f60208201905061109a5f830184611078565b92915050565b5f602082840312156110b5576110b4610ef2565b5b5f6110c284828501610f3c565b91505092915050565b6110d481610f15565b82525050565b5f6020820190506110ed5f8301846110cb565b92915050565b5f806040838503121561110957611108610ef2565b5b5f61111685828601610f3c565b925050602061112785828601610f3c565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061117557607f821691505b60208210810361118857611187611131565b5b50919050565b61119781610fc1565b81146111a1575f80fd5b50565b5f815190506111b28161118e565b92915050565b5f602082840312156111cd576111cc610ef2565b5b5f6111da848285016111a4565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61121a82610f50565b915061122583610f50565b925082820390508181111561123d5761123c6111e3565b5b92915050565b5f61124d82610f50565b915061125883610f50565b92508282019050808211156112705761126f6111e3565b5b9291505056fea26469706673582212200a27c26244aa71c5e398725dcc35daa636754ef6cd7aaa924611c155b684230364736f6c63430008180033", +} + +// CustomErc20ABI is the input ABI used to generate the binding from. +// Deprecated: Use CustomErc20MetaData.ABI instead. +var CustomErc20ABI = CustomErc20MetaData.ABI + +// CustomErc20Bin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use CustomErc20MetaData.Bin instead. +var CustomErc20Bin = CustomErc20MetaData.Bin + +// DeployCustomErc20 deploys a new Ethereum contract, binding an instance of CustomErc20 to it. +func DeployCustomErc20(auth *bind.TransactOpts, backend bind.ContractBackend, _name string, _symbol string, _decimals uint8) (common.Address, *types.Transaction, *CustomErc20, error) { + parsed, err := CustomErc20MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(CustomErc20Bin), backend, _name, _symbol, _decimals) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &CustomErc20{CustomErc20Caller: CustomErc20Caller{contract: contract}, CustomErc20Transactor: CustomErc20Transactor{contract: contract}, CustomErc20Filterer: CustomErc20Filterer{contract: contract}}, nil +} + +// CustomErc20 is an auto generated Go binding around an Ethereum contract. +type CustomErc20 struct { + CustomErc20Caller // Read-only binding to the contract + CustomErc20Transactor // Write-only binding to the contract + CustomErc20Filterer // Log filterer for contract events +} + +// CustomErc20Caller is an auto generated read-only Go binding around an Ethereum contract. +type CustomErc20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CustomErc20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type CustomErc20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CustomErc20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type CustomErc20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CustomErc20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type CustomErc20Session struct { + Contract *CustomErc20 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// CustomErc20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type CustomErc20CallerSession struct { + Contract *CustomErc20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// CustomErc20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type CustomErc20TransactorSession struct { + Contract *CustomErc20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// CustomErc20Raw is an auto generated low-level Go binding around an Ethereum contract. +type CustomErc20Raw struct { + Contract *CustomErc20 // Generic contract binding to access the raw methods on +} + +// CustomErc20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type CustomErc20CallerRaw struct { + Contract *CustomErc20Caller // Generic read-only contract binding to access the raw methods on +} + +// CustomErc20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type CustomErc20TransactorRaw struct { + Contract *CustomErc20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewCustomErc20 creates a new instance of CustomErc20, bound to a specific deployed contract. +func NewCustomErc20(address common.Address, backend bind.ContractBackend) (*CustomErc20, error) { + contract, err := bindCustomErc20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &CustomErc20{CustomErc20Caller: CustomErc20Caller{contract: contract}, CustomErc20Transactor: CustomErc20Transactor{contract: contract}, CustomErc20Filterer: CustomErc20Filterer{contract: contract}}, nil +} + +// NewCustomErc20Caller creates a new read-only instance of CustomErc20, bound to a specific deployed contract. +func NewCustomErc20Caller(address common.Address, caller bind.ContractCaller) (*CustomErc20Caller, error) { + contract, err := bindCustomErc20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &CustomErc20Caller{contract: contract}, nil +} + +// NewCustomErc20Transactor creates a new write-only instance of CustomErc20, bound to a specific deployed contract. +func NewCustomErc20Transactor(address common.Address, transactor bind.ContractTransactor) (*CustomErc20Transactor, error) { + contract, err := bindCustomErc20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &CustomErc20Transactor{contract: contract}, nil +} + +// NewCustomErc20Filterer creates a new log filterer instance of CustomErc20, bound to a specific deployed contract. +func NewCustomErc20Filterer(address common.Address, filterer bind.ContractFilterer) (*CustomErc20Filterer, error) { + contract, err := bindCustomErc20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &CustomErc20Filterer{contract: contract}, nil +} + +// bindCustomErc20 binds a generic wrapper to an already deployed contract. +func bindCustomErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := CustomErc20MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_CustomErc20 *CustomErc20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _CustomErc20.Contract.CustomErc20Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_CustomErc20 *CustomErc20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _CustomErc20.Contract.CustomErc20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_CustomErc20 *CustomErc20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _CustomErc20.Contract.CustomErc20Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_CustomErc20 *CustomErc20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _CustomErc20.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_CustomErc20 *CustomErc20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _CustomErc20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_CustomErc20 *CustomErc20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _CustomErc20.Contract.contract.Transact(opts, method, params...) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_CustomErc20 *CustomErc20Caller) Allowance(opts *bind.CallOpts, arg0 common.Address, arg1 common.Address) (*big.Int, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "allowance", arg0, arg1) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_CustomErc20 *CustomErc20Session) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _CustomErc20.Contract.Allowance(&_CustomErc20.CallOpts, arg0, arg1) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_CustomErc20 *CustomErc20CallerSession) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _CustomErc20.Contract.Allowance(&_CustomErc20.CallOpts, arg0, arg1) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_CustomErc20 *CustomErc20Caller) BalanceOf(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "balanceOf", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_CustomErc20 *CustomErc20Session) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _CustomErc20.Contract.BalanceOf(&_CustomErc20.CallOpts, arg0) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_CustomErc20 *CustomErc20CallerSession) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _CustomErc20.Contract.BalanceOf(&_CustomErc20.CallOpts, arg0) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_CustomErc20 *CustomErc20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_CustomErc20 *CustomErc20Session) Decimals() (uint8, error) { + return _CustomErc20.Contract.Decimals(&_CustomErc20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_CustomErc20 *CustomErc20CallerSession) Decimals() (uint8, error) { + return _CustomErc20.Contract.Decimals(&_CustomErc20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_CustomErc20 *CustomErc20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_CustomErc20 *CustomErc20Session) Name() (string, error) { + return _CustomErc20.Contract.Name(&_CustomErc20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_CustomErc20 *CustomErc20CallerSession) Name() (string, error) { + return _CustomErc20.Contract.Name(&_CustomErc20.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_CustomErc20 *CustomErc20Caller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_CustomErc20 *CustomErc20Session) Owner() (common.Address, error) { + return _CustomErc20.Contract.Owner(&_CustomErc20.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_CustomErc20 *CustomErc20CallerSession) Owner() (common.Address, error) { + return _CustomErc20.Contract.Owner(&_CustomErc20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_CustomErc20 *CustomErc20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_CustomErc20 *CustomErc20Session) Symbol() (string, error) { + return _CustomErc20.Contract.Symbol(&_CustomErc20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_CustomErc20 *CustomErc20CallerSession) Symbol() (string, error) { + return _CustomErc20.Contract.Symbol(&_CustomErc20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_CustomErc20 *CustomErc20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _CustomErc20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_CustomErc20 *CustomErc20Session) TotalSupply() (*big.Int, error) { + return _CustomErc20.Contract.TotalSupply(&_CustomErc20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_CustomErc20 *CustomErc20CallerSession) TotalSupply() (*big.Int, error) { + return _CustomErc20.Contract.TotalSupply(&_CustomErc20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "approve", spender, amount) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Session) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Approve(&_CustomErc20.TransactOpts, spender, amount) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20TransactorSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Approve(&_CustomErc20.TransactOpts, spender, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_CustomErc20 *CustomErc20Transactor) Burn(opts *bind.TransactOpts, from common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "burn", from, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_CustomErc20 *CustomErc20Session) Burn(from common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Burn(&_CustomErc20.TransactOpts, from, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_CustomErc20 *CustomErc20TransactorSession) Burn(from common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Burn(&_CustomErc20.TransactOpts, from, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_CustomErc20 *CustomErc20Transactor) Mint(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "mint", to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_CustomErc20 *CustomErc20Session) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Mint(&_CustomErc20.TransactOpts, to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_CustomErc20 *CustomErc20TransactorSession) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Mint(&_CustomErc20.TransactOpts, to, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Transactor) Transfer(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "transfer", recipient, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Session) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Transfer(&_CustomErc20.TransactOpts, recipient, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20TransactorSession) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.Transfer(&_CustomErc20.TransactOpts, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Transactor) TransferFrom(opts *bind.TransactOpts, sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "transferFrom", sender, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20Session) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.TransferFrom(&_CustomErc20.TransactOpts, sender, recipient, amount) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) +func (_CustomErc20 *CustomErc20TransactorSession) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _CustomErc20.Contract.TransferFrom(&_CustomErc20.TransactOpts, sender, recipient, amount) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_CustomErc20 *CustomErc20Transactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _CustomErc20.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_CustomErc20 *CustomErc20Session) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _CustomErc20.Contract.TransferOwnership(&_CustomErc20.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_CustomErc20 *CustomErc20TransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _CustomErc20.Contract.TransferOwnership(&_CustomErc20.TransactOpts, newOwner) +} + +// CustomErc20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the CustomErc20 contract. +type CustomErc20ApprovalIterator struct { + Event *CustomErc20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *CustomErc20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(CustomErc20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(CustomErc20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *CustomErc20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *CustomErc20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// CustomErc20Approval represents a Approval event raised by the CustomErc20 contract. +type CustomErc20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*CustomErc20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _CustomErc20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &CustomErc20ApprovalIterator{contract: _CustomErc20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *CustomErc20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _CustomErc20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(CustomErc20Approval) + if err := _CustomErc20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) ParseApproval(log types.Log) (*CustomErc20Approval, error) { + event := new(CustomErc20Approval) + if err := _CustomErc20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// CustomErc20OwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the CustomErc20 contract. +type CustomErc20OwnershipTransferredIterator struct { + Event *CustomErc20OwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *CustomErc20OwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(CustomErc20OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(CustomErc20OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *CustomErc20OwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *CustomErc20OwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// CustomErc20OwnershipTransferred represents a OwnershipTransferred event raised by the CustomErc20 contract. +type CustomErc20OwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_CustomErc20 *CustomErc20Filterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*CustomErc20OwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _CustomErc20.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &CustomErc20OwnershipTransferredIterator{contract: _CustomErc20.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_CustomErc20 *CustomErc20Filterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *CustomErc20OwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _CustomErc20.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(CustomErc20OwnershipTransferred) + if err := _CustomErc20.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_CustomErc20 *CustomErc20Filterer) ParseOwnershipTransferred(log types.Log) (*CustomErc20OwnershipTransferred, error) { + event := new(CustomErc20OwnershipTransferred) + if err := _CustomErc20.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// CustomErc20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the CustomErc20 contract. +type CustomErc20TransferIterator struct { + Event *CustomErc20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *CustomErc20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(CustomErc20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(CustomErc20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *CustomErc20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *CustomErc20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// CustomErc20Transfer represents a Transfer event raised by the CustomErc20 contract. +type CustomErc20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*CustomErc20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _CustomErc20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &CustomErc20TransferIterator{contract: _CustomErc20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *CustomErc20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _CustomErc20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(CustomErc20Transfer) + if err := _CustomErc20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_CustomErc20 *CustomErc20Filterer) ParseTransfer(log types.Log) (*CustomErc20Transfer, error) { + event := new(CustomErc20Transfer) + if err := _CustomErc20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/x/evm/contracts/custom_erc20/CustomERC20.sol b/x/evm/contracts/custom_erc20/CustomERC20.sol new file mode 100644 index 0000000..69f79e4 --- /dev/null +++ b/x/evm/contracts/custom_erc20/CustomERC20.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "../i_erc20/IERC20.sol"; +import "../ownable/Ownable.sol"; +import "../erc20_registry/ERC20Registry.sol"; + +contract CustomERC20 is IERC20, Ownable, ERC20Registry { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + + constructor(string memory _name, string memory _symbol, uint8 _decimals) register_erc20 { + name = _name; + symbol = _symbol; + decimals = _decimals; + } + + function transfer( + address recipient, + uint256 amount + ) external register_erc20_store(recipient) returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(msg.sender, recipient, amount); + return true; + } + + function approve(address spender, uint256 amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external register_erc20_store(recipient) returns (bool) { + allowance[sender][msg.sender] -= amount; + balanceOf[sender] -= amount; + balanceOf[recipient] += amount; + emit Transfer(sender, recipient, amount); + return true; + } + + function _mint(address to, uint256 amount) internal register_erc20_store(to) { + balanceOf[to] += amount; + totalSupply += amount; + emit Transfer(address(0), to, amount); + } + + function _burn(address from, uint256 amount) internal { + balanceOf[from] -= amount; + totalSupply -= amount; + emit Transfer(from, address(0), amount); + } + + function mint(address to, uint256 amount) external onlyOwner{ + _mint(to, amount); + } + + function burn(address from, uint256 amount) external onlyOwner { + _burn(from, amount); + } +} diff --git a/x/evm/contracts/erc20/ERC20.go b/x/evm/contracts/erc20/ERC20.go index 08f8c70..4b3f6e0 100644 --- a/x/evm/contracts/erc20/ERC20.go +++ b/x/evm/contracts/erc20/ERC20.go @@ -32,7 +32,7 @@ var ( // Erc20MetaData contains all meta data concerning the Erc20 contract. var Erc20MetaData = &bind.MetaData{ 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\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"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\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\":\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]", - Bin: "0x608060405234801562000010575f80fd5b5060405162001946380380620019468339818101604052810190620000369190620002da565b335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060f273ffffffffffffffffffffffffffffffffffffffff16635e6c57596040518163ffffffff1660e01b81526004015f604051808303815f87803b158015620000bc575f80fd5b505af1158015620000cf573d5f803e3d5ffd5b505050508260039081620000e49190620005a8565b508160049081620000f69190620005a8565b508060055f6101000a81548160ff021916908360ff1602179055505050506200068c565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6200017b8262000133565b810181811067ffffffffffffffff821117156200019d576200019c62000143565b5b80604052505050565b5f620001b16200011a565b9050620001bf828262000170565b919050565b5f67ffffffffffffffff821115620001e157620001e062000143565b5b620001ec8262000133565b9050602081019050919050565b5f5b8381101562000218578082015181840152602081019050620001fb565b5f8484015250505050565b5f620002396200023384620001c4565b620001a6565b9050828152602081018484840111156200025857620002576200012f565b5b62000265848285620001f9565b509392505050565b5f82601f8301126200028457620002836200012b565b5b81516200029684826020860162000223565b91505092915050565b5f60ff82169050919050565b620002b6816200029f565b8114620002c1575f80fd5b50565b5f81519050620002d481620002ab565b92915050565b5f805f60608486031215620002f457620002f362000123565b5b5f84015167ffffffffffffffff81111562000314576200031362000127565b5b62000322868287016200026d565b935050602084015167ffffffffffffffff81111562000346576200034562000127565b5b62000354868287016200026d565b92505060406200036786828701620002c4565b9150509250925092565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680620003c057607f821691505b602082108103620003d657620003d56200037b565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026200043a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620003fd565b620004468683620003fd565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620004906200048a62000484846200045e565b62000467565b6200045e565b9050919050565b5f819050919050565b620004ab8362000470565b620004c3620004ba8262000497565b84845462000409565b825550505050565b5f90565b620004d9620004cb565b620004e6818484620004a0565b505050565b5b818110156200050d57620005015f82620004cf565b600181019050620004ec565b5050565b601f8211156200055c576200052681620003dc565b6200053184620003ee565b8101602085101562000541578190505b620005596200055085620003ee565b830182620004eb565b50505b505050565b5f82821c905092915050565b5f6200057e5f198460080262000561565b1980831691505092915050565b5f6200059883836200056d565b9150826002028217905092915050565b620005b38262000371565b67ffffffffffffffff811115620005cf57620005ce62000143565b5b620005db8254620003a8565b620005e882828562000511565b5f60209050601f8311600181146200061e575f841562000609578287015190505b6200061585826200058b565b86555062000684565b601f1984166200062e86620003dc565b5f5b82811015620006575784890151825560018201915060208501945060208101905062000630565b8683101562000677578489015162000673601f8916826200056d565b8355505b6001600288020188555050505b505050505050565b6112ac806200069a5f395ff3fe608060405234801561000f575f80fd5b50600436106100cd575f3560e01c806370a082311161008a5780639dc29fac116100645780639dc29fac14610213578063a9059cbb1461022f578063dd62ed3e1461025f578063f2fde38b1461028f576100cd565b806370a08231146101a75780638da5cb5b146101d757806395d89b41146101f5576100cd565b806306fdde03146100d1578063095ea7b3146100ef57806318160ddd1461011f57806323b872dd1461013d578063313ce5671461016d57806340c10f191461018b575b5f80fd5b6100d96102ab565b6040516100e69190610ed2565b60405180910390f35b61010960048036038101906101049190610f83565b610337565b6040516101169190610fdb565b60405180910390f35b610127610424565b6040516101349190611003565b60405180910390f35b6101576004803603810190610152919061101c565b61042a565b6040516101649190610fdb565b60405180910390f35b6101756106b6565b6040516101829190611087565b60405180910390f35b6101a560048036038101906101a09190610f83565b6106c8565b005b6101c160048036038101906101bc91906110a0565b61072c565b6040516101ce9190611003565b60405180910390f35b6101df610741565b6040516101ec91906110da565b60405180910390f35b6101fd610764565b60405161020a9190610ed2565b60405180910390f35b61022d60048036038101906102289190610f83565b6107f0565b005b61024960048036038101906102449190610f83565b610854565b6040516102569190610fdb565b60405180910390f35b610279600480360381019061027491906110f3565b610a51565b6040516102869190611003565b60405180910390f35b6102a960048036038101906102a491906110a0565b610a71565b005b600380546102b89061115e565b80601f01602080910402602001604051908101604052809291908181526020018280546102e49061115e565b801561032f5780601f106103065761010080835404028352916020019161032f565b820191905f5260205f20905b81548152906001019060200180831161031257829003601f168201915b505050505081565b5f8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516104129190611003565b60405180910390a36001905092915050565b60065481565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161046691906110da565b602060405180830381865afa158015610481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a591906111b8565b6105115760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b81526004016104e391906110da565b5f604051808303815f87803b1580156104fa575f80fd5b505af115801561050c573d5f803e3d5ffd5b505050505b8260025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105989190611210565b925050819055508260015f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105eb9190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825461063e9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040516106a29190611003565b60405180910390a360019150509392505050565b60055f9054906101000a900460ff1681565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461071e575f80fd5b6107288282610bb9565b5050565b6001602052805f5260405f205f915090505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600480546107719061115e565b80601f016020809104026020016040519081016040528092919081815260200182805461079d9061115e565b80156107e85780601f106107bf576101008083540402835291602001916107e8565b820191905f5260205f20905b8154815290600101906020018083116107cb57829003601f168201915b505050505081565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610846575f80fd5b6108508282610d74565b5050565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161089091906110da565b602060405180830381865afa1580156108ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108cf91906111b8565b61093b5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b815260040161090d91906110da565b5f604051808303815f87803b158015610924575f80fd5b505af1158015610936573d5f803e3d5ffd5b505050505b8260015f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109879190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109da9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef85604051610a3e9190611003565b60405180910390a3600191505092915050565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ac7575f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610afe575f80fd5b8073ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8160f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b8152600401610bf491906110da565b602060405180830381865afa158015610c0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3391906111b8565b610c9f5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b8152600401610c7191906110da565b5f604051808303815f87803b158015610c88575f80fd5b505af1158015610c9a573d5f803e3d5ffd5b505050505b8160015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ceb9190611243565b925050819055508160065f828254610d039190611243565b925050819055508273ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610d679190611003565b60405180910390a3505050565b8060015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610dc09190611210565b925050819055508060065f828254610dd89190611210565b925050819055505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610e3c9190611003565b60405180910390a35050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610e7f578082015181840152602081019050610e64565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ea482610e48565b610eae8185610e52565b9350610ebe818560208601610e62565b610ec781610e8a565b840191505092915050565b5f6020820190508181035f830152610eea8184610e9a565b905092915050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610f1f82610ef6565b9050919050565b610f2f81610f15565b8114610f39575f80fd5b50565b5f81359050610f4a81610f26565b92915050565b5f819050919050565b610f6281610f50565b8114610f6c575f80fd5b50565b5f81359050610f7d81610f59565b92915050565b5f8060408385031215610f9957610f98610ef2565b5b5f610fa685828601610f3c565b9250506020610fb785828601610f6f565b9150509250929050565b5f8115159050919050565b610fd581610fc1565b82525050565b5f602082019050610fee5f830184610fcc565b92915050565b610ffd81610f50565b82525050565b5f6020820190506110165f830184610ff4565b92915050565b5f805f6060848603121561103357611032610ef2565b5b5f61104086828701610f3c565b935050602061105186828701610f3c565b925050604061106286828701610f6f565b9150509250925092565b5f60ff82169050919050565b6110818161106c565b82525050565b5f60208201905061109a5f830184611078565b92915050565b5f602082840312156110b5576110b4610ef2565b5b5f6110c284828501610f3c565b91505092915050565b6110d481610f15565b82525050565b5f6020820190506110ed5f8301846110cb565b92915050565b5f806040838503121561110957611108610ef2565b5b5f61111685828601610f3c565b925050602061112785828601610f3c565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061117557607f821691505b60208210810361118857611187611131565b5b50919050565b61119781610fc1565b81146111a1575f80fd5b50565b5f815190506111b28161118e565b92915050565b5f602082840312156111cd576111cc610ef2565b5b5f6111da848285016111a4565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61121a82610f50565b915061122583610f50565b925082820390508181111561123d5761123c6111e3565b5b92915050565b5f61124d82610f50565b915061125883610f50565b92508282019050808211156112705761126f6111e3565b5b9291505056fea2646970667358221220631f7ea7be7dc1618c68e102ecd6aba2b7f8b26c880ff4c10b3a55dc5ef79c4064736f6c63430008180033", + Bin: "0x608060405234801562000010575f80fd5b50604051620018e8380380620018e883398181016040528101906200003691906200027c565b335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600390816200008691906200054a565b5081600490816200009891906200054a565b508060055f6101000a81548160ff021916908360ff1602179055505050506200062e565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6200011d82620000d5565b810181811067ffffffffffffffff821117156200013f576200013e620000e5565b5b80604052505050565b5f62000153620000bc565b905062000161828262000112565b919050565b5f67ffffffffffffffff821115620001835762000182620000e5565b5b6200018e82620000d5565b9050602081019050919050565b5f5b83811015620001ba5780820151818401526020810190506200019d565b5f8484015250505050565b5f620001db620001d58462000166565b62000148565b905082815260208101848484011115620001fa57620001f9620000d1565b5b620002078482856200019b565b509392505050565b5f82601f830112620002265762000225620000cd565b5b815162000238848260208601620001c5565b91505092915050565b5f60ff82169050919050565b620002588162000241565b811462000263575f80fd5b50565b5f8151905062000276816200024d565b92915050565b5f805f60608486031215620002965762000295620000c5565b5b5f84015167ffffffffffffffff811115620002b657620002b5620000c9565b5b620002c4868287016200020f565b935050602084015167ffffffffffffffff811115620002e857620002e7620000c9565b5b620002f6868287016200020f565b9250506040620003098682870162000266565b9150509250925092565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200036257607f821691505b6020821081036200037857620003776200031d565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620003dc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200039f565b620003e886836200039f565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620004326200042c620004268462000400565b62000409565b62000400565b9050919050565b5f819050919050565b6200044d8362000412565b620004656200045c8262000439565b848454620003ab565b825550505050565b5f90565b6200047b6200046d565b6200048881848462000442565b505050565b5b81811015620004af57620004a35f8262000471565b6001810190506200048e565b5050565b601f821115620004fe57620004c8816200037e565b620004d38462000390565b81016020851015620004e3578190505b620004fb620004f28562000390565b8301826200048d565b50505b505050565b5f82821c905092915050565b5f620005205f198460080262000503565b1980831691505092915050565b5f6200053a83836200050f565b9150826002028217905092915050565b620005558262000313565b67ffffffffffffffff811115620005715762000570620000e5565b5b6200057d82546200034a565b6200058a828285620004b3565b5f60209050601f831160018114620005c0575f8415620005ab578287015190505b620005b785826200052d565b86555062000626565b601f198416620005d0866200037e565b5f5b82811015620005f957848901518255600182019150602085019450602081019050620005d2565b8683101562000619578489015162000615601f8916826200050f565b8355505b6001600288020188555050505b505050505050565b6112ac806200063c5f395ff3fe608060405234801561000f575f80fd5b50600436106100cd575f3560e01c806370a082311161008a5780639dc29fac116100645780639dc29fac14610213578063a9059cbb1461022f578063dd62ed3e1461025f578063f2fde38b1461028f576100cd565b806370a08231146101a75780638da5cb5b146101d757806395d89b41146101f5576100cd565b806306fdde03146100d1578063095ea7b3146100ef57806318160ddd1461011f57806323b872dd1461013d578063313ce5671461016d57806340c10f191461018b575b5f80fd5b6100d96102ab565b6040516100e69190610ed2565b60405180910390f35b61010960048036038101906101049190610f83565b610337565b6040516101169190610fdb565b60405180910390f35b610127610424565b6040516101349190611003565b60405180910390f35b6101576004803603810190610152919061101c565b61042a565b6040516101649190610fdb565b60405180910390f35b6101756106b6565b6040516101829190611087565b60405180910390f35b6101a560048036038101906101a09190610f83565b6106c8565b005b6101c160048036038101906101bc91906110a0565b61072c565b6040516101ce9190611003565b60405180910390f35b6101df610741565b6040516101ec91906110da565b60405180910390f35b6101fd610764565b60405161020a9190610ed2565b60405180910390f35b61022d60048036038101906102289190610f83565b6107f0565b005b61024960048036038101906102449190610f83565b610854565b6040516102569190610fdb565b60405180910390f35b610279600480360381019061027491906110f3565b610a51565b6040516102869190611003565b60405180910390f35b6102a960048036038101906102a491906110a0565b610a71565b005b600380546102b89061115e565b80601f01602080910402602001604051908101604052809291908181526020018280546102e49061115e565b801561032f5780601f106103065761010080835404028352916020019161032f565b820191905f5260205f20905b81548152906001019060200180831161031257829003601f168201915b505050505081565b5f8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516104129190611003565b60405180910390a36001905092915050565b60065481565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161046691906110da565b602060405180830381865afa158015610481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a591906111b8565b6105115760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b81526004016104e391906110da565b5f604051808303815f87803b1580156104fa575f80fd5b505af115801561050c573d5f803e3d5ffd5b505050505b8260025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105989190611210565b925050819055508260015f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105eb9190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825461063e9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040516106a29190611003565b60405180910390a360019150509392505050565b60055f9054906101000a900460ff1681565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461071e575f80fd5b6107288282610bb9565b5050565b6001602052805f5260405f205f915090505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600480546107719061115e565b80601f016020809104026020016040519081016040528092919081815260200182805461079d9061115e565b80156107e85780601f106107bf576101008083540402835291602001916107e8565b820191905f5260205f20905b8154815290600101906020018083116107cb57829003601f168201915b505050505081565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610846575f80fd5b6108508282610d74565b5050565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161089091906110da565b602060405180830381865afa1580156108ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108cf91906111b8565b61093b5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b815260040161090d91906110da565b5f604051808303815f87803b158015610924575f80fd5b505af1158015610936573d5f803e3d5ffd5b505050505b8260015f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109879190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109da9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef85604051610a3e9190611003565b60405180910390a3600191505092915050565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ac7575f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610afe575f80fd5b8073ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8160f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b8152600401610bf491906110da565b602060405180830381865afa158015610c0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3391906111b8565b610c9f5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b8152600401610c7191906110da565b5f604051808303815f87803b158015610c88575f80fd5b505af1158015610c9a573d5f803e3d5ffd5b505050505b8160015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ceb9190611243565b925050819055508160065f828254610d039190611243565b925050819055508273ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610d679190611003565b60405180910390a3505050565b8060015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610dc09190611210565b925050819055508060065f828254610dd89190611210565b925050819055505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610e3c9190611003565b60405180910390a35050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610e7f578082015181840152602081019050610e64565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ea482610e48565b610eae8185610e52565b9350610ebe818560208601610e62565b610ec781610e8a565b840191505092915050565b5f6020820190508181035f830152610eea8184610e9a565b905092915050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610f1f82610ef6565b9050919050565b610f2f81610f15565b8114610f39575f80fd5b50565b5f81359050610f4a81610f26565b92915050565b5f819050919050565b610f6281610f50565b8114610f6c575f80fd5b50565b5f81359050610f7d81610f59565b92915050565b5f8060408385031215610f9957610f98610ef2565b5b5f610fa685828601610f3c565b9250506020610fb785828601610f6f565b9150509250929050565b5f8115159050919050565b610fd581610fc1565b82525050565b5f602082019050610fee5f830184610fcc565b92915050565b610ffd81610f50565b82525050565b5f6020820190506110165f830184610ff4565b92915050565b5f805f6060848603121561103357611032610ef2565b5b5f61104086828701610f3c565b935050602061105186828701610f3c565b925050604061106286828701610f6f565b9150509250925092565b5f60ff82169050919050565b6110818161106c565b82525050565b5f60208201905061109a5f830184611078565b92915050565b5f602082840312156110b5576110b4610ef2565b5b5f6110c284828501610f3c565b91505092915050565b6110d481610f15565b82525050565b5f6020820190506110ed5f8301846110cb565b92915050565b5f806040838503121561110957611108610ef2565b5b5f61111685828601610f3c565b925050602061112785828601610f3c565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061117557607f821691505b60208210810361118857611187611131565b5b50919050565b61119781610fc1565b81146111a1575f80fd5b50565b5f815190506111b28161118e565b92915050565b5f602082840312156111cd576111cc610ef2565b5b5f6111da848285016111a4565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61121a82610f50565b915061122583610f50565b925082820390508181111561123d5761123c6111e3565b5b92915050565b5f61124d82610f50565b915061125883610f50565b92508282019050808211156112705761126f6111e3565b5b9291505056fea264697066735822122075dfce4979d3f63ac4a6d3d47c5370ed955d4050130854ee823177a6231faf1264736f6c63430008180033", } // Erc20ABI is the input ABI used to generate the binding from. diff --git a/x/evm/contracts/erc20/ERC20.sol b/x/evm/contracts/erc20/ERC20.sol index 543648c..7e6037a 100644 --- a/x/evm/contracts/erc20/ERC20.sol +++ b/x/evm/contracts/erc20/ERC20.sol @@ -20,7 +20,8 @@ contract ERC20 is IERC20, Ownable, ERC20Registry { uint8 public decimals; uint256 public totalSupply; - constructor(string memory _name, string memory _symbol, uint8 _decimals) register_erc20 { + // for custom erc20s, you should add `register_erc20` modifier to the constructor + constructor(string memory _name, string memory _symbol, uint8 _decimals) { name = _name; symbol = _symbol; decimals = _decimals; diff --git a/x/evm/contracts/erc20_factory/ERC20Factory.go b/x/evm/contracts/erc20_factory/ERC20Factory.go new file mode 100644 index 0000000..8b47abc --- /dev/null +++ b/x/evm/contracts/erc20_factory/ERC20Factory.go @@ -0,0 +1,377 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc20_factory + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// Erc20FactoryMetaData contains all meta data concerning the Erc20Factory contract. +var Erc20FactoryMetaData = &bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"erc20\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC20Created\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"}],\"name\":\"createERC20\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561000f575f80fd5b50611e818061001d5f395ff3fe608060405234801562000010575f80fd5b50600436106200002c575f3560e01c806306ef1a861462000030575b5f80fd5b6200004e600480360381019062000048919062000390565b62000066565b6040516200005d91906200046a565b60405180910390f35b5f808484846040516200007990620001de565b62000087939291906200051a565b604051809103905ff080158015620000a1573d5f803e3d5ffd5b50905060f273ffffffffffffffffffffffffffffffffffffffff1663d126274a826040518263ffffffff1660e01b8152600401620000e091906200046a565b5f604051808303815f87803b158015620000f8575f80fd5b505af11580156200010b573d5f803e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff1663f2fde38b336040518263ffffffff1660e01b81526004016200014a91906200046a565b5f604051808303815f87803b15801562000162575f80fd5b505af115801562000175573d5f803e3d5ffd5b505050503373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f85e892981b234101136bc30081e0a5c44345bebc0940193230c20a43b279e2d160405160405180910390a3809150509392505050565b6118e8806200056483390190565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6200024d8262000205565b810181811067ffffffffffffffff821117156200026f576200026e62000215565b5b80604052505050565b5f62000283620001ec565b905062000291828262000242565b919050565b5f67ffffffffffffffff821115620002b357620002b262000215565b5b620002be8262000205565b9050602081019050919050565b828183375f83830152505050565b5f620002ef620002e98462000296565b62000278565b9050828152602081018484840111156200030e576200030d62000201565b5b6200031b848285620002cb565b509392505050565b5f82601f8301126200033a5762000339620001fd565b5b81356200034c848260208601620002d9565b91505092915050565b5f60ff82169050919050565b6200036c8162000355565b811462000377575f80fd5b50565b5f813590506200038a8162000361565b92915050565b5f805f60608486031215620003aa57620003a9620001f5565b5b5f84013567ffffffffffffffff811115620003ca57620003c9620001f9565b5b620003d88682870162000323565b935050602084013567ffffffffffffffff811115620003fc57620003fb620001f9565b5b6200040a8682870162000323565b92505060406200041d868287016200037a565b9150509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620004528262000427565b9050919050565b620004648162000446565b82525050565b5f6020820190506200047f5f83018462000459565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015620004be578082015181840152602081019050620004a1565b5f8484015250505050565b5f620004d58262000485565b620004e181856200048f565b9350620004f38185602086016200049f565b620004fe8162000205565b840191505092915050565b620005148162000355565b82525050565b5f6060820190508181035f830152620005348186620004c9565b905081810360208301526200054a8185620004c9565b90506200055b604083018462000509565b94935050505056fe608060405234801562000010575f80fd5b50604051620018e8380380620018e883398181016040528101906200003691906200027c565b335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600390816200008691906200054a565b5081600490816200009891906200054a565b508060055f6101000a81548160ff021916908360ff1602179055505050506200062e565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6200011d82620000d5565b810181811067ffffffffffffffff821117156200013f576200013e620000e5565b5b80604052505050565b5f62000153620000bc565b905062000161828262000112565b919050565b5f67ffffffffffffffff821115620001835762000182620000e5565b5b6200018e82620000d5565b9050602081019050919050565b5f5b83811015620001ba5780820151818401526020810190506200019d565b5f8484015250505050565b5f620001db620001d58462000166565b62000148565b905082815260208101848484011115620001fa57620001f9620000d1565b5b620002078482856200019b565b509392505050565b5f82601f830112620002265762000225620000cd565b5b815162000238848260208601620001c5565b91505092915050565b5f60ff82169050919050565b620002588162000241565b811462000263575f80fd5b50565b5f8151905062000276816200024d565b92915050565b5f805f60608486031215620002965762000295620000c5565b5b5f84015167ffffffffffffffff811115620002b657620002b5620000c9565b5b620002c4868287016200020f565b935050602084015167ffffffffffffffff811115620002e857620002e7620000c9565b5b620002f6868287016200020f565b9250506040620003098682870162000266565b9150509250925092565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200036257607f821691505b6020821081036200037857620003776200031d565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620003dc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200039f565b620003e886836200039f565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620004326200042c620004268462000400565b62000409565b62000400565b9050919050565b5f819050919050565b6200044d8362000412565b620004656200045c8262000439565b848454620003ab565b825550505050565b5f90565b6200047b6200046d565b6200048881848462000442565b505050565b5b81811015620004af57620004a35f8262000471565b6001810190506200048e565b5050565b601f821115620004fe57620004c8816200037e565b620004d38462000390565b81016020851015620004e3578190505b620004fb620004f28562000390565b8301826200048d565b50505b505050565b5f82821c905092915050565b5f620005205f198460080262000503565b1980831691505092915050565b5f6200053a83836200050f565b9150826002028217905092915050565b620005558262000313565b67ffffffffffffffff811115620005715762000570620000e5565b5b6200057d82546200034a565b6200058a828285620004b3565b5f60209050601f831160018114620005c0575f8415620005ab578287015190505b620005b785826200052d565b86555062000626565b601f198416620005d0866200037e565b5f5b82811015620005f957848901518255600182019150602085019450602081019050620005d2565b8683101562000619578489015162000615601f8916826200050f565b8355505b6001600288020188555050505b505050505050565b6112ac806200063c5f395ff3fe608060405234801561000f575f80fd5b50600436106100cd575f3560e01c806370a082311161008a5780639dc29fac116100645780639dc29fac14610213578063a9059cbb1461022f578063dd62ed3e1461025f578063f2fde38b1461028f576100cd565b806370a08231146101a75780638da5cb5b146101d757806395d89b41146101f5576100cd565b806306fdde03146100d1578063095ea7b3146100ef57806318160ddd1461011f57806323b872dd1461013d578063313ce5671461016d57806340c10f191461018b575b5f80fd5b6100d96102ab565b6040516100e69190610ed2565b60405180910390f35b61010960048036038101906101049190610f83565b610337565b6040516101169190610fdb565b60405180910390f35b610127610424565b6040516101349190611003565b60405180910390f35b6101576004803603810190610152919061101c565b61042a565b6040516101649190610fdb565b60405180910390f35b6101756106b6565b6040516101829190611087565b60405180910390f35b6101a560048036038101906101a09190610f83565b6106c8565b005b6101c160048036038101906101bc91906110a0565b61072c565b6040516101ce9190611003565b60405180910390f35b6101df610741565b6040516101ec91906110da565b60405180910390f35b6101fd610764565b60405161020a9190610ed2565b60405180910390f35b61022d60048036038101906102289190610f83565b6107f0565b005b61024960048036038101906102449190610f83565b610854565b6040516102569190610fdb565b60405180910390f35b610279600480360381019061027491906110f3565b610a51565b6040516102869190611003565b60405180910390f35b6102a960048036038101906102a491906110a0565b610a71565b005b600380546102b89061115e565b80601f01602080910402602001604051908101604052809291908181526020018280546102e49061115e565b801561032f5780601f106103065761010080835404028352916020019161032f565b820191905f5260205f20905b81548152906001019060200180831161031257829003601f168201915b505050505081565b5f8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516104129190611003565b60405180910390a36001905092915050565b60065481565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161046691906110da565b602060405180830381865afa158015610481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a591906111b8565b6105115760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b81526004016104e391906110da565b5f604051808303815f87803b1580156104fa575f80fd5b505af115801561050c573d5f803e3d5ffd5b505050505b8260025f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105989190611210565b925050819055508260015f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546105eb9190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825461063e9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040516106a29190611003565b60405180910390a360019150509392505050565b60055f9054906101000a900460ff1681565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461071e575f80fd5b6107288282610bb9565b5050565b6001602052805f5260405f205f915090505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600480546107719061115e565b80601f016020809104026020016040519081016040528092919081815260200182805461079d9061115e565b80156107e85780601f106107bf576101008083540402835291602001916107e8565b820191905f5260205f20905b8154815290600101906020018083116107cb57829003601f168201915b505050505081565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610846575f80fd5b6108508282610d74565b5050565b5f8260f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b815260040161089091906110da565b602060405180830381865afa1580156108ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108cf91906111b8565b61093b5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b815260040161090d91906110da565b5f604051808303815f87803b158015610924575f80fd5b505af1158015610936573d5f803e3d5ffd5b505050505b8260015f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109879190611210565b925050819055508260015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546109da9190611243565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef85604051610a3e9190611003565b60405180910390a3600191505092915050565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ac7575f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610afe575f80fd5b8073ffffffffffffffffffffffffffffffffffffffff165f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b8160f273ffffffffffffffffffffffffffffffffffffffff16634e25ab64826040518263ffffffff1660e01b8152600401610bf491906110da565b602060405180830381865afa158015610c0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3391906111b8565b610c9f5760f273ffffffffffffffffffffffffffffffffffffffff1663ceeae52a826040518263ffffffff1660e01b8152600401610c7191906110da565b5f604051808303815f87803b158015610c88575f80fd5b505af1158015610c9a573d5f803e3d5ffd5b505050505b8160015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610ceb9190611243565b925050819055508160065f828254610d039190611243565b925050819055508273ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610d679190611003565b60405180910390a3505050565b8060015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254610dc09190611210565b925050819055508060065f828254610dd89190611210565b925050819055505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610e3c9190611003565b60405180910390a35050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610e7f578082015181840152602081019050610e64565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ea482610e48565b610eae8185610e52565b9350610ebe818560208601610e62565b610ec781610e8a565b840191505092915050565b5f6020820190508181035f830152610eea8184610e9a565b905092915050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610f1f82610ef6565b9050919050565b610f2f81610f15565b8114610f39575f80fd5b50565b5f81359050610f4a81610f26565b92915050565b5f819050919050565b610f6281610f50565b8114610f6c575f80fd5b50565b5f81359050610f7d81610f59565b92915050565b5f8060408385031215610f9957610f98610ef2565b5b5f610fa685828601610f3c565b9250506020610fb785828601610f6f565b9150509250929050565b5f8115159050919050565b610fd581610fc1565b82525050565b5f602082019050610fee5f830184610fcc565b92915050565b610ffd81610f50565b82525050565b5f6020820190506110165f830184610ff4565b92915050565b5f805f6060848603121561103357611032610ef2565b5b5f61104086828701610f3c565b935050602061105186828701610f3c565b925050604061106286828701610f6f565b9150509250925092565b5f60ff82169050919050565b6110818161106c565b82525050565b5f60208201905061109a5f830184611078565b92915050565b5f602082840312156110b5576110b4610ef2565b5b5f6110c284828501610f3c565b91505092915050565b6110d481610f15565b82525050565b5f6020820190506110ed5f8301846110cb565b92915050565b5f806040838503121561110957611108610ef2565b5b5f61111685828601610f3c565b925050602061112785828601610f3c565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061117557607f821691505b60208210810361118857611187611131565b5b50919050565b61119781610fc1565b81146111a1575f80fd5b50565b5f815190506111b28161118e565b92915050565b5f602082840312156111cd576111cc610ef2565b5b5f6111da848285016111a4565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61121a82610f50565b915061122583610f50565b925082820390508181111561123d5761123c6111e3565b5b92915050565b5f61124d82610f50565b915061125883610f50565b92508282019050808211156112705761126f6111e3565b5b9291505056fea264697066735822122075dfce4979d3f63ac4a6d3d47c5370ed955d4050130854ee823177a6231faf1264736f6c63430008180033a2646970667358221220d0ec928f08268f995edf07fea115f98565a542f202405a2c74f10e1d2450155064736f6c63430008180033", +} + +// Erc20FactoryABI is the input ABI used to generate the binding from. +// Deprecated: Use Erc20FactoryMetaData.ABI instead. +var Erc20FactoryABI = Erc20FactoryMetaData.ABI + +// Erc20FactoryBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use Erc20FactoryMetaData.Bin instead. +var Erc20FactoryBin = Erc20FactoryMetaData.Bin + +// DeployErc20Factory deploys a new Ethereum contract, binding an instance of Erc20Factory to it. +func DeployErc20Factory(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Erc20Factory, error) { + parsed, err := Erc20FactoryMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(Erc20FactoryBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Erc20Factory{Erc20FactoryCaller: Erc20FactoryCaller{contract: contract}, Erc20FactoryTransactor: Erc20FactoryTransactor{contract: contract}, Erc20FactoryFilterer: Erc20FactoryFilterer{contract: contract}}, nil +} + +// Erc20Factory is an auto generated Go binding around an Ethereum contract. +type Erc20Factory struct { + Erc20FactoryCaller // Read-only binding to the contract + Erc20FactoryTransactor // Write-only binding to the contract + Erc20FactoryFilterer // Log filterer for contract events +} + +// Erc20FactoryCaller is an auto generated read-only Go binding around an Ethereum contract. +type Erc20FactoryCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// Erc20FactoryTransactor is an auto generated write-only Go binding around an Ethereum contract. +type Erc20FactoryTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// Erc20FactoryFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type Erc20FactoryFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// Erc20FactorySession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type Erc20FactorySession struct { + Contract *Erc20Factory // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// Erc20FactoryCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type Erc20FactoryCallerSession struct { + Contract *Erc20FactoryCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// Erc20FactoryTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type Erc20FactoryTransactorSession struct { + Contract *Erc20FactoryTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// Erc20FactoryRaw is an auto generated low-level Go binding around an Ethereum contract. +type Erc20FactoryRaw struct { + Contract *Erc20Factory // Generic contract binding to access the raw methods on +} + +// Erc20FactoryCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type Erc20FactoryCallerRaw struct { + Contract *Erc20FactoryCaller // Generic read-only contract binding to access the raw methods on +} + +// Erc20FactoryTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type Erc20FactoryTransactorRaw struct { + Contract *Erc20FactoryTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewErc20Factory creates a new instance of Erc20Factory, bound to a specific deployed contract. +func NewErc20Factory(address common.Address, backend bind.ContractBackend) (*Erc20Factory, error) { + contract, err := bindErc20Factory(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Erc20Factory{Erc20FactoryCaller: Erc20FactoryCaller{contract: contract}, Erc20FactoryTransactor: Erc20FactoryTransactor{contract: contract}, Erc20FactoryFilterer: Erc20FactoryFilterer{contract: contract}}, nil +} + +// NewErc20FactoryCaller creates a new read-only instance of Erc20Factory, bound to a specific deployed contract. +func NewErc20FactoryCaller(address common.Address, caller bind.ContractCaller) (*Erc20FactoryCaller, error) { + contract, err := bindErc20Factory(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &Erc20FactoryCaller{contract: contract}, nil +} + +// NewErc20FactoryTransactor creates a new write-only instance of Erc20Factory, bound to a specific deployed contract. +func NewErc20FactoryTransactor(address common.Address, transactor bind.ContractTransactor) (*Erc20FactoryTransactor, error) { + contract, err := bindErc20Factory(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &Erc20FactoryTransactor{contract: contract}, nil +} + +// NewErc20FactoryFilterer creates a new log filterer instance of Erc20Factory, bound to a specific deployed contract. +func NewErc20FactoryFilterer(address common.Address, filterer bind.ContractFilterer) (*Erc20FactoryFilterer, error) { + contract, err := bindErc20Factory(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &Erc20FactoryFilterer{contract: contract}, nil +} + +// bindErc20Factory binds a generic wrapper to an already deployed contract. +func bindErc20Factory(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := Erc20FactoryMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Erc20Factory *Erc20FactoryRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Erc20Factory.Contract.Erc20FactoryCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Erc20Factory *Erc20FactoryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Erc20Factory.Contract.Erc20FactoryTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Erc20Factory *Erc20FactoryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Erc20Factory.Contract.Erc20FactoryTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Erc20Factory *Erc20FactoryCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Erc20Factory.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Erc20Factory *Erc20FactoryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Erc20Factory.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Erc20Factory *Erc20FactoryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Erc20Factory.Contract.contract.Transact(opts, method, params...) +} + +// CreateERC20 is a paid mutator transaction binding the contract method 0x06ef1a86. +// +// Solidity: function createERC20(string name, string symbol, uint8 decimals) returns(address) +func (_Erc20Factory *Erc20FactoryTransactor) CreateERC20(opts *bind.TransactOpts, name string, symbol string, decimals uint8) (*types.Transaction, error) { + return _Erc20Factory.contract.Transact(opts, "createERC20", name, symbol, decimals) +} + +// CreateERC20 is a paid mutator transaction binding the contract method 0x06ef1a86. +// +// Solidity: function createERC20(string name, string symbol, uint8 decimals) returns(address) +func (_Erc20Factory *Erc20FactorySession) CreateERC20(name string, symbol string, decimals uint8) (*types.Transaction, error) { + return _Erc20Factory.Contract.CreateERC20(&_Erc20Factory.TransactOpts, name, symbol, decimals) +} + +// CreateERC20 is a paid mutator transaction binding the contract method 0x06ef1a86. +// +// Solidity: function createERC20(string name, string symbol, uint8 decimals) returns(address) +func (_Erc20Factory *Erc20FactoryTransactorSession) CreateERC20(name string, symbol string, decimals uint8) (*types.Transaction, error) { + return _Erc20Factory.Contract.CreateERC20(&_Erc20Factory.TransactOpts, name, symbol, decimals) +} + +// Erc20FactoryERC20CreatedIterator is returned from FilterERC20Created and is used to iterate over the raw logs and unpacked data for ERC20Created events raised by the Erc20Factory contract. +type Erc20FactoryERC20CreatedIterator struct { + Event *Erc20FactoryERC20Created // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *Erc20FactoryERC20CreatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(Erc20FactoryERC20Created) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(Erc20FactoryERC20Created) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *Erc20FactoryERC20CreatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *Erc20FactoryERC20CreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// Erc20FactoryERC20Created represents a ERC20Created event raised by the Erc20Factory contract. +type Erc20FactoryERC20Created struct { + Erc20 common.Address + Owner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20Created is a free log retrieval operation binding the contract event 0x85e892981b234101136bc30081e0a5c44345bebc0940193230c20a43b279e2d1. +// +// Solidity: event ERC20Created(address indexed erc20, address indexed owner) +func (_Erc20Factory *Erc20FactoryFilterer) FilterERC20Created(opts *bind.FilterOpts, erc20 []common.Address, owner []common.Address) (*Erc20FactoryERC20CreatedIterator, error) { + + var erc20Rule []interface{} + for _, erc20Item := range erc20 { + erc20Rule = append(erc20Rule, erc20Item) + } + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + + logs, sub, err := _Erc20Factory.contract.FilterLogs(opts, "ERC20Created", erc20Rule, ownerRule) + if err != nil { + return nil, err + } + return &Erc20FactoryERC20CreatedIterator{contract: _Erc20Factory.contract, event: "ERC20Created", logs: logs, sub: sub}, nil +} + +// WatchERC20Created is a free log subscription operation binding the contract event 0x85e892981b234101136bc30081e0a5c44345bebc0940193230c20a43b279e2d1. +// +// Solidity: event ERC20Created(address indexed erc20, address indexed owner) +func (_Erc20Factory *Erc20FactoryFilterer) WatchERC20Created(opts *bind.WatchOpts, sink chan<- *Erc20FactoryERC20Created, erc20 []common.Address, owner []common.Address) (event.Subscription, error) { + + var erc20Rule []interface{} + for _, erc20Item := range erc20 { + erc20Rule = append(erc20Rule, erc20Item) + } + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + + logs, sub, err := _Erc20Factory.contract.WatchLogs(opts, "ERC20Created", erc20Rule, ownerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(Erc20FactoryERC20Created) + if err := _Erc20Factory.contract.UnpackLog(event, "ERC20Created", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20Created is a log parse operation binding the contract event 0x85e892981b234101136bc30081e0a5c44345bebc0940193230c20a43b279e2d1. +// +// Solidity: event ERC20Created(address indexed erc20, address indexed owner) +func (_Erc20Factory *Erc20FactoryFilterer) ParseERC20Created(log types.Log) (*Erc20FactoryERC20Created, error) { + event := new(Erc20FactoryERC20Created) + if err := _Erc20Factory.contract.UnpackLog(event, "ERC20Created", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/x/evm/contracts/erc20_factory/ERC20Factory.sol b/x/evm/contracts/erc20_factory/ERC20Factory.sol new file mode 100644 index 0000000..fd6bcf1 --- /dev/null +++ b/x/evm/contracts/erc20_factory/ERC20Factory.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "../erc20/ERC20.sol"; +import "../i_erc20_registry/IERC20Registry.sol"; + +contract ERC20Factory is ERC20Registry { + event ERC20Created(address indexed erc20, address indexed owner); + + function createERC20( + string memory name, + string memory symbol, + uint8 decimals + ) external returns (address) { + ERC20 erc20 = new ERC20(name, symbol, decimals); + + // register the ERC20 contract with the ERC20 registry + ERC20_REGISTRY_CONTRACT.register_erc20_from_factory(address(erc20)); + + // transfer ownership of the ERC20 contract to the sender + erc20.transferOwnership(msg.sender); + + emit ERC20Created(address(erc20), msg.sender); + return address(erc20); + } +} \ No newline at end of file diff --git a/x/evm/contracts/erc20_registry/ERC20Registry.go b/x/evm/contracts/erc20_registry/ERC20Registry.go index b2b05e0..857bf26 100644 --- a/x/evm/contracts/erc20_registry/ERC20Registry.go +++ b/x/evm/contracts/erc20_registry/ERC20Registry.go @@ -32,7 +32,7 @@ var ( // Erc20RegistryMetaData contains all meta data concerning the Erc20Registry contract. var Erc20RegistryMetaData = &bind.MetaData{ ABI: "[]", - Bin: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea26469706673582212209ab1a12abb95d9e5b29308b186f8f6580a12f1b1cf49beefd9334f5cf9b0f7b064736f6c63430008180033", + Bin: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea26469706673582212204c91fb1fe8736e2d08198eabf24561ced7824f04c14b279eb20699250dd43ee964736f6c63430008180033", } // Erc20RegistryABI is the input ABI used to generate the binding from. diff --git a/x/evm/contracts/i_erc20_registry/IERC20Registry.go b/x/evm/contracts/i_erc20_registry/IERC20Registry.go index c1f22be..b3bee8b 100644 --- a/x/evm/contracts/i_erc20_registry/IERC20Registry.go +++ b/x/evm/contracts/i_erc20_registry/IERC20Registry.go @@ -31,7 +31,7 @@ var ( // IErc20RegistryMetaData contains all meta data concerning the IErc20Registry contract. var IErc20RegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"is_erc20_store_registered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"registered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"register_erc20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"register_erc20_store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"is_erc20_store_registered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"registered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"register_erc20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"erc20\",\"type\":\"address\"}],\"name\":\"register_erc20_from_factory\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"register_erc20_store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IErc20RegistryABI is the input ABI used to generate the binding from. @@ -232,6 +232,27 @@ func (_IErc20Registry *IErc20RegistryTransactorSession) RegisterErc20() (*types. return _IErc20Registry.Contract.RegisterErc20(&_IErc20Registry.TransactOpts) } +// RegisterErc20FromFactory is a paid mutator transaction binding the contract method 0xd126274a. +// +// Solidity: function register_erc20_from_factory(address erc20) returns() +func (_IErc20Registry *IErc20RegistryTransactor) RegisterErc20FromFactory(opts *bind.TransactOpts, erc20 common.Address) (*types.Transaction, error) { + return _IErc20Registry.contract.Transact(opts, "register_erc20_from_factory", erc20) +} + +// RegisterErc20FromFactory is a paid mutator transaction binding the contract method 0xd126274a. +// +// Solidity: function register_erc20_from_factory(address erc20) returns() +func (_IErc20Registry *IErc20RegistrySession) RegisterErc20FromFactory(erc20 common.Address) (*types.Transaction, error) { + return _IErc20Registry.Contract.RegisterErc20FromFactory(&_IErc20Registry.TransactOpts, erc20) +} + +// RegisterErc20FromFactory is a paid mutator transaction binding the contract method 0xd126274a. +// +// Solidity: function register_erc20_from_factory(address erc20) returns() +func (_IErc20Registry *IErc20RegistryTransactorSession) RegisterErc20FromFactory(erc20 common.Address) (*types.Transaction, error) { + return _IErc20Registry.Contract.RegisterErc20FromFactory(&_IErc20Registry.TransactOpts, erc20) +} + // RegisterErc20Store is a paid mutator transaction binding the contract method 0xceeae52a. // // Solidity: function register_erc20_store(address account) returns() diff --git a/x/evm/contracts/i_erc20_registry/IERC20Registry.sol b/x/evm/contracts/i_erc20_registry/IERC20Registry.sol index 90a9498..5985244 100644 --- a/x/evm/contracts/i_erc20_registry/IERC20Registry.sol +++ b/x/evm/contracts/i_erc20_registry/IERC20Registry.sol @@ -11,6 +11,7 @@ IERC20Registry constant ERC20_REGISTRY_CONTRACT = IERC20Registry( interface IERC20Registry { function register_erc20() external; + function register_erc20_from_factory(address erc20) external; function register_erc20_store(address account) external; function is_erc20_store_registered( address account diff --git a/x/evm/keeper/common_test.go b/x/evm/keeper/common_test.go index 17f001b..e3f7755 100644 --- a/x/evm/keeper/common_test.go +++ b/x/evm/keeper/common_test.go @@ -152,13 +152,13 @@ type TestKeepers struct { // createDefaultTestInput common settings for createTestInput func createDefaultTestInput(t testing.TB) (sdk.Context, TestKeepers) { - return createTestInput(t, false) + return createTestInput(t, false, true) } // createTestInput encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default) -func createTestInput(t testing.TB, isCheckTx bool) (sdk.Context, TestKeepers) { +func createTestInput(t testing.TB, isCheckTx, withInitialize bool) (sdk.Context, TestKeepers) { // Load default move config - return _createTestInput(t, isCheckTx, dbm.NewMemDB()) + return _createTestInput(t, isCheckTx, withInitialize, dbm.NewMemDB()) } var keyCounter uint64 @@ -180,6 +180,7 @@ func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { func _createTestInput( t testing.TB, isCheckTx bool, + withInitialize bool, db dbm.DB, ) (sdk.Context, TestKeepers) { keys := storetypes.NewKVStoreKeys( @@ -269,8 +270,13 @@ func _createTestInput( }, }, ) - evmParams := evmtypes.DefaultParams() - require.NoError(t, evmKeeper.Params.Set(ctx, evmParams)) + + if withInitialize { + evmParams := evmtypes.DefaultParams() + evmParams.AllowCustomERC20 = false + require.NoError(t, evmKeeper.Params.Set(ctx, evmParams)) + require.NoError(t, evmKeeper.Initialize(ctx)) + } // set erc20 keeper *erc20Keeper = *evmKeeper.ERC20Keeper().(*evmkeeper.ERC20Keeper) diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index f9ae3d7..936f451 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -17,17 +17,24 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/initia-labs/minievm/x/evm/contracts/erc20" + "github.com/initia-labs/minievm/x/evm/contracts/erc20_factory" "github.com/initia-labs/minievm/x/evm/types" ) type ERC20Keeper struct { *Keeper - *abi.ABI - ERC20Bin []byte + ERC20Bin []byte + ERC20ABI *abi.ABI + ERC20FactoryABI *abi.ABI } func NewERC20Keeper(k *Keeper) (types.IERC20Keeper, error) { - abi, err := erc20.Erc20MetaData.GetAbi() + erc20ABI, err := erc20.Erc20MetaData.GetAbi() + if err != nil { + return ERC20Keeper{}, err + } + + factoryABI, err := erc20_factory.Erc20FactoryMetaData.GetAbi() if err != nil { return ERC20Keeper{}, err } @@ -37,7 +44,7 @@ func NewERC20Keeper(k *Keeper) (types.IERC20Keeper, error) { return ERC20Keeper{}, err } - return &ERC20Keeper{k, abi, erc20Bin}, nil + return &ERC20Keeper{k, erc20Bin, erc20ABI, factoryABI}, nil } // BurnCoins implements IERC20Keeper. @@ -64,7 +71,7 @@ func (k ERC20Keeper) BurnCoins(ctx context.Context, addr sdk.AccAddress, amount return err } - inputBz, err := k.ABI.Pack("burn", evmAddr, coin.Amount.BigInt()) + inputBz, err := k.ERC20ABI.Pack("burn", evmAddr, coin.Amount.BigInt()) if err != nil { return types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -282,7 +289,7 @@ func (k ERC20Keeper) MintCoins(ctx context.Context, addr sdk.AccAddress, amount if found, err := k.ERC20ContractAddrsByDenom.Has(ctx, denom); err != nil { return err } else if !found { - contractAddr, err := k.nextContractAddress(ctx, types.StdAddress) + contractAddr, err := k.nextContractAddress(ctx, types.ERC20FactoryAddress()) if err != nil { return err } @@ -295,12 +302,12 @@ func (k ERC20Keeper) MintCoins(ctx context.Context, addr sdk.AccAddress, amount return err } - inputBz, err := k.ABI.Pack("", denom, denom, uint8(0)) + inputBz, err := k.ERC20FactoryABI.Pack("createERC20", denom, denom, uint8(0)) if err != nil { return types.ErrFailedToPackABI.Wrap(err.Error()) } - ret, _, err := k.EVMCreate(ctx, types.StdAddress, append(k.ERC20Bin, inputBz...)) + ret, _, err := k.EVMCall(ctx, types.StdAddress, types.ERC20FactoryAddress(), inputBz) if err != nil { return err } @@ -310,7 +317,7 @@ func (k ERC20Keeper) MintCoins(ctx context.Context, addr sdk.AccAddress, amount sdk.NewEvent( types.EventTypeERC20Created, sdk.NewAttribute(types.AttributeKeyDenom, denom), - sdk.NewAttribute(types.AttributeKeyContract, hexutil.Encode(ret)), + sdk.NewAttribute(types.AttributeKeyContract, hexutil.Encode(ret[12:])), ), ) } @@ -320,8 +327,7 @@ func (k ERC20Keeper) MintCoins(ctx context.Context, addr sdk.AccAddress, amount if err != nil { return err } - - inputBz, err := k.ABI.Pack("mint", evmAddr, coin.Amount.BigInt()) + inputBz, err := k.ERC20ABI.Pack("mint", evmAddr, coin.Amount.BigInt()) if err != nil { return types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -353,7 +359,7 @@ func (k ERC20Keeper) SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toA return err } - inputBz, err := k.ABI.Pack("transfer", evmToAddr, coin.Amount.BigInt()) + inputBz, err := k.ERC20ABI.Pack("transfer", evmToAddr, coin.Amount.BigInt()) if err != nil { return types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -369,7 +375,7 @@ func (k ERC20Keeper) SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toA } func (k ERC20Keeper) balanceOf(ctx context.Context, addr, contractAddr common.Address) (math.Int, error) { - inputBz, err := k.ABI.Pack("balanceOf", addr) + inputBz, err := k.ERC20ABI.Pack("balanceOf", addr) if err != nil { return math.ZeroInt(), types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -379,7 +385,7 @@ func (k ERC20Keeper) balanceOf(ctx context.Context, addr, contractAddr common.Ad return math.ZeroInt(), err } - res, err := k.ABI.Unpack("balanceOf", retBz) + res, err := k.ERC20ABI.Unpack("balanceOf", retBz) if err != nil { return math.ZeroInt(), types.ErrFailedToUnpackABI.Wrap(err.Error()) } @@ -393,7 +399,7 @@ func (k ERC20Keeper) balanceOf(ctx context.Context, addr, contractAddr common.Ad } func (k ERC20Keeper) totalSupply(ctx context.Context, contractAddr common.Address) (math.Int, error) { - inputBz, err := k.ABI.Pack("totalSupply") + inputBz, err := k.ERC20ABI.Pack("totalSupply") if err != nil { return math.ZeroInt(), types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -403,7 +409,7 @@ func (k ERC20Keeper) totalSupply(ctx context.Context, contractAddr common.Addres return math.ZeroInt(), err } - res, err := k.ABI.Unpack("totalSupply", retBz) + res, err := k.ERC20ABI.Unpack("totalSupply", retBz) if err != nil { return math.ZeroInt(), types.ErrFailedToUnpackABI.Wrap(err.Error()) } @@ -417,7 +423,7 @@ func (k ERC20Keeper) totalSupply(ctx context.Context, contractAddr common.Addres } func (k ERC20Keeper) name(ctx context.Context, contractAddr common.Address) (string, error) { - inputBz, err := k.ABI.Pack("name") + inputBz, err := k.ERC20ABI.Pack("name") if err != nil { return "", types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -427,7 +433,7 @@ func (k ERC20Keeper) name(ctx context.Context, contractAddr common.Address) (str return "", err } - res, err := k.ABI.Unpack("name", retBz) + res, err := k.ERC20ABI.Unpack("name", retBz) if err != nil { return "", types.ErrFailedToUnpackABI.Wrap(err.Error()) } @@ -441,7 +447,7 @@ func (k ERC20Keeper) name(ctx context.Context, contractAddr common.Address) (str } func (k ERC20Keeper) symbol(ctx context.Context, contractAddr common.Address) (string, error) { - inputBz, err := k.ABI.Pack("symbol") + inputBz, err := k.ERC20ABI.Pack("symbol") if err != nil { return "", types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -451,7 +457,7 @@ func (k ERC20Keeper) symbol(ctx context.Context, contractAddr common.Address) (s return "", err } - res, err := k.ABI.Unpack("symbol", retBz) + res, err := k.ERC20ABI.Unpack("symbol", retBz) if err != nil { return "", types.ErrFailedToUnpackABI.Wrap(err.Error()) } @@ -465,7 +471,7 @@ func (k ERC20Keeper) symbol(ctx context.Context, contractAddr common.Address) (s } func (k ERC20Keeper) decimals(ctx context.Context, contractAddr common.Address) (uint8, error) { - inputBz, err := k.ABI.Pack("decimals") + inputBz, err := k.ERC20ABI.Pack("decimals") if err != nil { return 0, types.ErrFailedToPackABI.Wrap(err.Error()) } @@ -475,7 +481,7 @@ func (k ERC20Keeper) decimals(ctx context.Context, contractAddr common.Address) return 0, err } - res, err := k.ABI.Unpack("decimals", retBz) + res, err := k.ERC20ABI.Unpack("decimals", retBz) if err != nil { return 0, types.ErrFailedToUnpackABI.Wrap(err.Error()) } diff --git a/x/evm/keeper/erc20_stores.go b/x/evm/keeper/erc20_stores.go index 9ee7e1c..d553428 100644 --- a/x/evm/keeper/erc20_stores.go +++ b/x/evm/keeper/erc20_stores.go @@ -2,10 +2,13 @@ package keeper import ( "context" + "slices" "cosmossdk.io/collections" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/initia-labs/minievm/x/evm/types" ) @@ -28,6 +31,12 @@ func (k ERC20StoresKeeper) IsStoreRegistered(ctx context.Context, addr sdk.AccAd // RegisterStore registers the erc20 contract address to user's store. func (k ERC20StoresKeeper) RegisterStore(ctx context.Context, addr sdk.AccAddress, contractAddr common.Address) error { + if found, err := k.ERC20s.Has(ctx, contractAddr.Bytes()); err != nil { + return err + } else if !found { + return nil + } + // create account if not exists if !k.accountKeeper.HasAccount(ctx, addr) { k.accountKeeper.SetAccount(ctx, k.accountKeeper.NewAccountWithAddress(ctx, addr)) @@ -38,6 +47,27 @@ func (k ERC20StoresKeeper) RegisterStore(ctx context.Context, addr sdk.AccAddres // Register registers the erc20 contract address to the store. func (k ERC20StoresKeeper) Register(ctx context.Context, contractAddr common.Address) error { + params, err := k.Params.Get(ctx) + if err != nil { + return err + } + + if !params.AllowCustomERC20 { + return types.ErrCustomERC20NotAllowed + } + + // default action is to allow all custom erc20s + // but if allowedCustomERC20s is set, only allow those + if len(params.AllowedCustomERC20s) > 0 { + if idx := slices.IndexFunc(params.AllowedCustomERC20s, func(s string) bool { + // skip error checking here because it's already checked in the params.Validate + allowedERC20Addr, _ := types.ContractAddressFromString(k.ac, s) + return contractAddr == allowedERC20Addr + }); idx == -1 { + return types.ErrCustomERC20NotAllowed + } + } + if found, err := k.ERC20s.Has(ctx, contractAddr.Bytes()); err != nil { return err } else if found { @@ -46,3 +76,12 @@ func (k ERC20StoresKeeper) Register(ctx context.Context, contractAddr common.Add return k.ERC20s.Set(ctx, contractAddr.Bytes()) } + +// RegisterFromFactory registers the erc20 contract address to the store. +func (k ERC20StoresKeeper) RegisterFromFactory(ctx context.Context, caller, contractAddr common.Address) error { + if caller != types.ERC20FactoryAddress() { + return types.ErrInvalidERC20FactoryAddr + } + + return k.ERC20s.Set(ctx, contractAddr.Bytes()) +} diff --git a/x/evm/keeper/erc20_stores_test.go b/x/evm/keeper/erc20_stores_test.go new file mode 100644 index 0000000..74055bd --- /dev/null +++ b/x/evm/keeper/erc20_stores_test.go @@ -0,0 +1,71 @@ +package keeper_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/initia-labs/minievm/x/evm/contracts/custom_erc20" + "github.com/initia-labs/minievm/x/evm/types" +) + +func deployCustomERC20(t *testing.T, ctx sdk.Context, input TestKeepers, caller common.Address, denom string, success bool) common.Address { + abi, err := custom_erc20.CustomErc20MetaData.GetAbi() + require.NoError(t, err) + + bin, err := hexutil.Decode(custom_erc20.CustomErc20MetaData.Bin) + require.NoError(t, err) + + inputBz, err := abi.Pack("", denom, denom, uint8(6)) + require.NoError(t, err) + + _, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, caller, append(bin, inputBz...)) + if success { + require.NoError(t, err) + } else { + require.Error(t, err) + } + + return contractAddr +} + +func Test_CanDeployCustomERC20(t *testing.T) { + ctx, input := createDefaultTestInput(t) + + _, _, addr := keyPubAddr() + evmAddr := common.BytesToAddress(addr.Bytes()) + + _, _, addr2 := keyPubAddr() + evmAddr2 := common.BytesToAddress(addr2.Bytes()) + + params, err := input.EVMKeeper.Params.Get(ctx) + require.NoError(t, err) + + // allow custom erc20 + params.AllowCustomERC20 = true + err = input.EVMKeeper.Params.Set(ctx, params) + require.NoError(t, err) + + // deploy custom erc20 contract + fooContractAddr := deployCustomERC20(t, ctx, input, evmAddr, "foo", true) + fooDenom, err := types.ContractAddrToDenom(ctx, &input.EVMKeeper, fooContractAddr) + require.NoError(t, err) + require.Equal(t, "evm/"+fooContractAddr.Hex()[2:], fooDenom) + + // limit allowed custom erc20s + estimatedContractAddr := crypto.CreateAddress(evmAddr2, 0) + params.AllowedCustomERC20s = []string{"foo", estimatedContractAddr.Hex()} + err = input.EVMKeeper.Params.Set(ctx, params) + require.NoError(t, err) + + // should failed to deploy new custom contract from addr + deployCustomERC20(t, ctx, input, evmAddr, "foo2", false) + + // should success to deploy new custom contract from addr2 + deployCustomERC20(t, ctx, input, evmAddr2, "foo2", true) +} diff --git a/x/evm/keeper/erc20_test.go b/x/evm/keeper/erc20_test.go index e1f5f22..265c6ec 100644 --- a/x/evm/keeper/erc20_test.go +++ b/x/evm/keeper/erc20_test.go @@ -5,31 +5,28 @@ import ( "cosmossdk.io/math" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/initia-labs/minievm/x/evm/contracts/erc20" + "github.com/initia-labs/minievm/x/evm/contracts/erc20_factory" "github.com/initia-labs/minievm/x/evm/keeper" "github.com/initia-labs/minievm/x/evm/types" ) func deployERC20(t *testing.T, ctx sdk.Context, input TestKeepers, caller common.Address, denom string) common.Address { - abi, err := erc20.Erc20MetaData.GetAbi() - require.NoError(t, err) - - inputBz, err := abi.Pack("", denom, denom, uint8(6)) + abi, err := erc20_factory.Erc20FactoryMetaData.GetAbi() require.NoError(t, err) - erc20Bin, err := hexutil.Decode(erc20.Erc20Bin) + inputBz, err := abi.Pack("createERC20", denom, denom, uint8(6)) require.NoError(t, err) - _, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, caller, append(erc20Bin, inputBz...)) + ret, _, err := input.EVMKeeper.EVMCall(ctx, caller, types.ERC20FactoryAddress(), inputBz) require.NoError(t, err) - return contractAddr + return common.BytesToAddress(ret[12:]) } func mintERC20(t *testing.T, ctx sdk.Context, input TestKeepers, caller, recipient common.Address, amount sdk.Coin) { diff --git a/x/evm/keeper/erc721_test.go b/x/evm/keeper/erc721_test.go index f9de9c5..b99e219 100644 --- a/x/evm/keeper/erc721_test.go +++ b/x/evm/keeper/erc721_test.go @@ -20,7 +20,7 @@ func Test_CreateCollection(t *testing.T) { classId := "test-class-id" classUri := "test-class-uri" - contractAddr := crypto.CreateAddress(types.StdAddress, 0) + contractAddr := crypto.CreateAddress(types.StdAddress, 1) err = erc721Keeper.CreateOrUpdateClass(ctx, classId, classUri, "") require.NoError(t, err) diff --git a/x/evm/keeper/genesis.go b/x/evm/keeper/genesis.go index 62155c9..9c94eb0 100644 --- a/x/evm/keeper/genesis.go +++ b/x/evm/keeper/genesis.go @@ -5,16 +5,40 @@ import ( "context" "cosmossdk.io/collections" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + coretypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/initia-labs/minievm/x/evm/contracts/erc20_factory" "github.com/initia-labs/minievm/x/evm/types" ) +func (k Keeper) Initialize(ctx context.Context) error { + code, err := hexutil.Decode(erc20_factory.Erc20FactoryBin) + if err != nil { + return err + } + + _, _, err = k.EVMCreate2(ctx, types.StdAddress, code, types.ERC20FactorySalt) + if err != nil { + return err + } + + return nil +} + func (k Keeper) InitGenesis(ctx context.Context, genState types.GenesisState) error { if err := k.Params.Set(ctx, genState.Params); err != nil { return err } - // else set the state from the genesis - if err := k.VMRoot.Set(ctx, genState.StateRoot); err != nil { + // if the state root is empty, initialize the state + if common.BytesToHash(genState.StateRoot) == coretypes.EmptyRootHash { + if err := k.Initialize(ctx); err != nil { + return err + } + } else if err := k.VMRoot.Set(ctx, genState.StateRoot); err != nil { return err } diff --git a/x/evm/keeper/genesis_test.go b/x/evm/keeper/genesis_test.go index af91acb..84eae07 100644 --- a/x/evm/keeper/genesis_test.go +++ b/x/evm/keeper/genesis_test.go @@ -8,7 +8,7 @@ import ( ) func Test_Genesis(t *testing.T) { - ctx, input := createTestInput(t, false) + ctx, input := createTestInput(t, false, false) genState := types.DefaultGenesis() genState.StateRoot = []byte{1, 2, 3, 4} diff --git a/x/evm/precompiles/erc20_registry/contract.go b/x/evm/precompiles/erc20_registry/contract.go index f04d2be..c08cbce 100644 --- a/x/evm/precompiles/erc20_registry/contract.go +++ b/x/evm/precompiles/erc20_registry/contract.go @@ -39,9 +39,10 @@ func (e ERC20RegistryPrecompile) WithContext(ctx context.Context) vm.Precompiled } const ( - METHOD_REGISTER = "register_erc20" - METHOD_REGISTER_STORE = "register_erc20_store" - METHOD_IS_STORE_REGISTERED = "is_erc20_store_registered" + METHOD_REGISTER = "register_erc20" + METHOD_REGISTER_FROM_FACTORY = "register_erc20_from_factory" + METHOD_REGISTER_STORE = "register_erc20_store" + METHOD_IS_STORE_REGISTERED = "is_erc20_store_registered" ) // ExtendedRun implements vm.ExtendedPrecompiledContract. @@ -70,6 +71,21 @@ func (e ERC20RegistryPrecompile) ExtendedRun(caller vm.ContractRef, input []byte if err := e.k.Register(ctx, caller.Address()); err != nil { return nil, ctx.GasMeter().GasConsumedToLimit(), types.ErrPrecompileFailed.Wrap(err.Error()) } + case METHOD_REGISTER_FROM_FACTORY: + ctx.GasMeter().ConsumeGas(REGISTER_FROM_FACTORY_GAS, "register_erc20_from_factory") + + if readOnly { + return nil, ctx.GasMeter().GasConsumedToLimit(), types.ErrNonReadOnlyMethod.Wrap(method.Name) + } + + var registerArgs RegisterERC20FromFactoryArguments + if err := method.Inputs.Copy(®isterArgs, args); err != nil { + return nil, ctx.GasMeter().GasConsumedToLimit(), types.ErrPrecompileFailed.Wrap(err.Error()) + } + + if err := e.k.RegisterFromFactory(ctx, caller.Address(), registerArgs.ERC20); err != nil { + return nil, ctx.GasMeter().GasConsumedToLimit(), types.ErrPrecompileFailed.Wrap(err.Error()) + } case METHOD_REGISTER_STORE: ctx.GasMeter().ConsumeGas(REGISTER_STORE_GAS, "register_erc20_store") diff --git a/x/evm/precompiles/erc20_registry/contract_test.go b/x/evm/precompiles/erc20_registry/contract_test.go index ae1d194..90c6fd7 100644 --- a/x/evm/precompiles/erc20_registry/contract_test.go +++ b/x/evm/precompiles/erc20_registry/contract_test.go @@ -41,6 +41,11 @@ func (e ERC20StoresKeeper) Register(ctx context.Context, contractAddr common.Add return nil } +func (e ERC20StoresKeeper) RegisterFromFactory(ctx context.Context, caller, contractAddr common.Address) error { + e.erc20s[contractAddr.Hex()] = true + return nil +} + // IsRegistered implements types.IERC20StoresKeeper. func (e ERC20StoresKeeper) IsStoreRegistered(ctx context.Context, addr sdk.AccAddress, contractAddr common.Address) (bool, error) { store, ok := e.stores[addr.String()] @@ -74,6 +79,8 @@ func Test_ERC20RegistryPrecompile(t *testing.T) { erc20Addr := common.HexToAddress("0x1") accountAddr := common.HexToAddress("0x2") + erc20Addr2 := common.HexToAddress("0x3") + erc20FactoryAddr := common.HexToAddress("0x4") abi, err := contracts.IErc20RegistryMetaData.GetAbi() require.NoError(t, err) @@ -95,8 +102,26 @@ func Test_ERC20RegistryPrecompile(t *testing.T) { require.NoError(t, err) require.Equal(t, usedGas, uint64(precompiles.REGISTER_GAS)+uint64(len(bz))) + // register erc20 from factory + bz, err = abi.Pack(precompiles.METHOD_REGISTER_FROM_FACTORY, erc20Addr2) + require.NoError(t, err) + + // out of gas panic + require.Panics(t, func() { + _, _, _ = registry.ExtendedRun(vm.AccountRef(erc20FactoryAddr), bz, precompiles.REGISTER_FROM_FACTORY_GAS-1, false) + }) + + // non read only method fail + _, _, err = registry.ExtendedRun(vm.AccountRef(erc20FactoryAddr), bz, precompiles.REGISTER_FROM_FACTORY_GAS+uint64(len(bz)), true) + require.Error(t, err) + + // success + _, usedGas, err = registry.ExtendedRun(vm.AccountRef(erc20FactoryAddr), bz, precompiles.REGISTER_FROM_FACTORY_GAS+uint64(len(bz)), false) + require.NoError(t, err) + require.Equal(t, usedGas, uint64(precompiles.REGISTER_GAS)+uint64(len(bz))) + // check erc20 registered - require.True(t, k.(ERC20StoresKeeper).erc20s[erc20Addr.Hex()]) + require.True(t, k.(ERC20StoresKeeper).erc20s[erc20Addr2.Hex()]) // check unregistered bz, err = abi.Pack(precompiles.METHOD_IS_STORE_REGISTERED, accountAddr) diff --git a/x/evm/precompiles/erc20_registry/types.go b/x/evm/precompiles/erc20_registry/types.go index f76db65..f8fb219 100644 --- a/x/evm/precompiles/erc20_registry/types.go +++ b/x/evm/precompiles/erc20_registry/types.go @@ -6,6 +6,10 @@ import ( "github.com/ethereum/go-ethereum/common" ) +type RegisterERC20FromFactoryArguments struct { + ERC20 common.Address `abi:"erc20"` +} + type RegisterStoreArguments struct { Account common.Address `abi:"account"` } @@ -15,8 +19,9 @@ type IsStoreRegisteredArguments struct { } const ( - REGISTER_GAS storetypes.Gas = 200 - REGISTER_STORE_GAS storetypes.Gas = 200 - IS_STORE_REGISTERED_GAS storetypes.Gas = 200 - GAS_PER_BYTE storetypes.Gas = 1 + REGISTER_GAS storetypes.Gas = 200 + REGISTER_FROM_FACTORY_GAS storetypes.Gas = 200 + REGISTER_STORE_GAS storetypes.Gas = 200 + IS_STORE_REGISTERED_GAS storetypes.Gas = 200 + GAS_PER_BYTE storetypes.Gas = 1 ) diff --git a/x/evm/types/address.go b/x/evm/types/address.go index 44ce15f..078c065 100644 --- a/x/evm/types/address.go +++ b/x/evm/types/address.go @@ -4,6 +4,11 @@ import ( "cosmossdk.io/core/address" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" + + "github.com/initia-labs/minievm/x/evm/contracts/erc20_factory" ) // 0x0 null address @@ -12,6 +17,9 @@ var NullAddress common.Address = common.HexToAddress("0x0") // 0x1 std address var StdAddress common.Address = common.HexToAddress("0x1") +// ERC20FactorySalt is the salt used to create the ERC20 factory address +var ERC20FactorySalt = uint64(1) + // 0xf1 Cosmos precompile address var CosmosPrecompileAddress common.Address = common.HexToAddress("0xf1") @@ -38,3 +46,23 @@ func ContractAddressFromString(ac address.Codec, contractAddrInString string) (c return contractAddr, nil } + +// factoryCodeHash is the hash of the factory code +var factoryCodeHash common.Hash +var factoryAddr common.Address + +func init() { + // to avoid repeated hashing, we hash the factory code once at init + bz, err := hexutil.Decode(erc20_factory.Erc20FactoryBin) + if err != nil { + panic(err) + } + + factoryCodeHash = crypto.Keccak256Hash(bz) + factoryAddr = crypto.CreateAddress2(StdAddress, uint256.NewInt(ERC20FactorySalt).Bytes32(), factoryCodeHash.Bytes()) +} + +// ERC20FactoryAddress returns the address of the ERC20 factory +func ERC20FactoryAddress() common.Address { + return factoryAddr +} diff --git a/x/evm/types/collection.go b/x/evm/types/collection.go index 808020b..99a6a61 100644 --- a/x/evm/types/collection.go +++ b/x/evm/types/collection.go @@ -34,7 +34,7 @@ func ContractAddressFromClassId(ctx context.Context, k ERC721ClassIdKeeper, clas if strings.HasPrefix(classId, EVMPrefix) { contractAddrInString := strings.TrimPrefix(classId, EVMPrefix) if !common.IsHexAddress(contractAddrInString) { - return NullAddress, ErrInvalidClasssId + return NullAddress, ErrInvalidClassId } return common.HexToAddress(contractAddrInString), nil diff --git a/x/evm/types/errors.go b/x/evm/types/errors.go index 0a9b945..7ca914c 100644 --- a/x/evm/types/errors.go +++ b/x/evm/types/errors.go @@ -25,5 +25,7 @@ var ( ErrNotSupportedCosmosMessage = errorsmod.Register(ModuleName, 17, "Not supported cosmos message") ErrNotSupportedCosmosQuery = errorsmod.Register(ModuleName, 18, "Not supported cosmos query") ErrInvalidTokenId = errorsmod.Register(ModuleName, 19, "Invalid token id") - ErrInvalidClasssId = errorsmod.Register(ModuleName, 20, "Invalid class id") + ErrInvalidClassId = errorsmod.Register(ModuleName, 20, "Invalid class id") + ErrCustomERC20NotAllowed = errorsmod.Register(ModuleName, 21, "Custom ERC20 is not allowed") + ErrInvalidERC20FactoryAddr = errorsmod.Register(ModuleName, 22, "Invalid ERC20 factory address") ) diff --git a/x/evm/types/expected_keeper.go b/x/evm/types/expected_keeper.go index daefdfe..bab7c4c 100644 --- a/x/evm/types/expected_keeper.go +++ b/x/evm/types/expected_keeper.go @@ -31,6 +31,7 @@ type CommunityPoolKeeper interface { type IERC20StoresKeeper interface { Register(ctx context.Context, contractAddr common.Address) error + RegisterFromFactory(ctx context.Context, caller, contractAddr common.Address) error RegisterStore(ctx context.Context, addr sdk.AccAddress, contractAddr common.Address) error IsStoreRegistered(ctx context.Context, addr sdk.AccAddress, contractAddr common.Address) (bool, error) } diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 080ee6a..8c65d4a 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -6,7 +6,9 @@ import ( ) func DefaultParams() Params { - return Params{} + return Params{ + AllowCustomERC20: true, + } } func (p Params) String() string { @@ -25,5 +27,12 @@ func (p Params) Validate(ac address.Codec) error { } } + for _, addr := range p.AllowedCustomERC20s { + _, err := ContractAddressFromString(ac, addr) + if err != nil { + return err + } + } + return nil } diff --git a/x/evm/types/params_test.go b/x/evm/types/params_test.go new file mode 100644 index 0000000..fe014d0 --- /dev/null +++ b/x/evm/types/params_test.go @@ -0,0 +1,48 @@ +package types_test + +import ( + "encoding/binary" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/crypto/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + + "github.com/initia-labs/minievm/x/evm/types" +) + +func Test_ParamsValidate(t *testing.T) { + params := types.DefaultParams() + + ac := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) + err := params.Validate(ac) + require.NoError(t, err) + + seed := make([]byte, 8) + binary.BigEndian.PutUint64(seed, rand.Uint64()) + + key := ed25519.GenPrivKeyFromSecret(seed) + pub := key.PubKey() + addr := sdk.AccAddress(pub.Address()) + + params.AllowedPublishers = append(params.AllowedPublishers, addr.String()) + params.AllowedCustomERC20s = append(params.AllowedCustomERC20s, addr.String()) + + err = params.Validate(ac) + require.NoError(t, err) + + // invalid address + params.AllowedPublishers = append(params.AllowedPublishers, addr.String()+"abc") + + err = params.Validate(ac) + require.Error(t, err) + + // invalid erc20 address + params.AllowedPublishers = params.AllowedPublishers[:1] + params.AllowedCustomERC20s = append(params.AllowedCustomERC20s, addr.String()+"abc") + err = params.Validate(ac) + require.Error(t, err) +} diff --git a/x/evm/types/types.pb.go b/x/evm/types/types.pb.go index bafd948..e1b0e6d 100644 --- a/x/evm/types/types.pb.go +++ b/x/evm/types/types.pb.go @@ -31,6 +31,10 @@ type Params struct { // It is a list of addresses with permission to distribute contracts, // and an empty list is interpreted as allowing anyone to distribute. AllowedPublishers []string `protobuf:"bytes,2,rep,name=allowed_publishers,json=allowedPublishers,proto3" json:"allowed_publishers,omitempty" yaml:"allowed_publishers"` + // allow_custom_erc20 defines whether the chain allows custom erc20 tokens + // to be registered on cosmos bank interface. + AllowCustomERC20 bool `protobuf:"varint,3,opt,name=allow_custom_erc20,json=allowCustomErc20,proto3" json:"allow_custom_erc20,omitempty"` + AllowedCustomERC20s []string `protobuf:"bytes,4,rep,name=allowed_custom_erc20s,json=allowedCustomErc20s,proto3" json:"allowed_custom_erc20s,omitempty" yaml:"allowed_custom_erc20s"` } func (m *Params) Reset() { *m = Params{} } @@ -117,29 +121,34 @@ func init() { func init() { proto.RegisterFile("minievm/evm/v1/types.proto", fileDescriptor_98c9eab1c4bf0154) } var fileDescriptor_98c9eab1c4bf0154 = []byte{ - // 345 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x51, 0x31, 0x6f, 0xea, 0x30, - 0x18, 0x8c, 0x5f, 0x9e, 0x78, 0x8a, 0x87, 0x27, 0x61, 0x55, 0x6d, 0xca, 0xe0, 0x20, 0x4f, 0x08, - 0xa9, 0x44, 0xa8, 0x1b, 0x5b, 0x91, 0x18, 0xaa, 0x32, 0xd0, 0x8c, 0x5d, 0x90, 0x43, 0xac, 0x60, - 0x29, 0xc1, 0x51, 0x6c, 0x28, 0xfc, 0x85, 0x4e, 0x1d, 0x3b, 0x32, 0x32, 0xb2, 0xf5, 0x2f, 0x30, - 0x32, 0x76, 0x42, 0x6d, 0x18, 0xe8, 0xcc, 0x2f, 0xa8, 0xe2, 0x84, 0x76, 0xe8, 0xf0, 0x59, 0x77, - 0xdf, 0x9d, 0xa5, 0xb3, 0x0f, 0xd6, 0x62, 0x3e, 0xe1, 0x6c, 0x16, 0xbb, 0xf9, 0xcc, 0xda, 0xae, - 0x5a, 0x24, 0x4c, 0xb6, 0x92, 0x54, 0x28, 0x81, 0xfe, 0x97, 0x5a, 0x2b, 0x9f, 0x59, 0xbb, 0x56, - 0xa5, 0x31, 0x9f, 0x08, 0x57, 0x9f, 0x85, 0xa5, 0x76, 0x16, 0x8a, 0x50, 0x68, 0xe8, 0xe6, 0xa8, - 0xd8, 0x92, 0x57, 0x00, 0x2b, 0x03, 0x9a, 0xd2, 0x58, 0xa2, 0x1b, 0x08, 0xd9, 0x5c, 0xa5, 0x74, - 0xc8, 0x78, 0x22, 0x6d, 0x50, 0x37, 0x1b, 0x66, 0x97, 0x64, 0x3b, 0xc7, 0xea, 0xe5, 0xdb, 0xde, - 0xed, 0x40, 0x1e, 0x77, 0x4e, 0x75, 0x41, 0xe3, 0xa8, 0x43, 0x7e, 0x8c, 0xc4, 0xb3, 0x34, 0xe9, - 0xf1, 0x44, 0xa2, 0x7b, 0x88, 0x68, 0x14, 0x89, 0x47, 0x16, 0x0c, 0x93, 0xa9, 0x1f, 0x71, 0x39, - 0x66, 0xa9, 0xb4, 0xff, 0xd4, 0xcd, 0x86, 0xd5, 0x25, 0xc7, 0x9d, 0x73, 0x59, 0xdc, 0xfe, 0xed, - 0x21, 0xab, 0xc3, 0xba, 0x09, 0xbc, 0x6a, 0xa9, 0x0c, 0xbe, 0x85, 0xce, 0xc5, 0xcb, 0xd2, 0x31, - 0x3e, 0x97, 0x0e, 0x78, 0x3a, 0xac, 0x9b, 0x30, 0x7f, 0x7a, 0x11, 0x97, 0xdc, 0x41, 0xb3, 0x2f, - 0x42, 0x64, 0xc3, 0x7f, 0x34, 0x08, 0x52, 0x26, 0xf3, 0xc8, 0xa0, 0x61, 0x79, 0x27, 0x8a, 0xce, - 0x61, 0x45, 0x89, 0x84, 0x8f, 0xca, 0x00, 0x5e, 0xc9, 0x10, 0x82, 0x7f, 0x03, 0xaa, 0xa8, 0x6d, - 0x6a, 0xbb, 0xc6, 0xdd, 0xfe, 0xe6, 0x03, 0x1b, 0xab, 0x0c, 0x83, 0x4d, 0x86, 0xc1, 0x36, 0xc3, - 0xe0, 0x3d, 0xc3, 0xe0, 0x79, 0x8f, 0x8d, 0xed, 0x1e, 0x1b, 0x6f, 0x7b, 0x6c, 0x3c, 0x34, 0x43, - 0xae, 0xc6, 0x53, 0xbf, 0x35, 0x12, 0xb1, 0xcb, 0x27, 0x5c, 0x71, 0x7a, 0x15, 0x51, 0x5f, 0xba, - 0xa7, 0x52, 0xe6, 0xba, 0x16, 0xdd, 0x89, 0x5f, 0xd1, 0x7f, 0x7b, 0xfd, 0x15, 0x00, 0x00, 0xff, - 0xff, 0x21, 0x97, 0xce, 0xbe, 0xb2, 0x01, 0x00, 0x00, + // 430 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x52, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xf6, 0xd5, 0x55, 0xc0, 0x37, 0xa0, 0xe6, 0x5a, 0xc0, 0x44, 0xe8, 0x1c, 0x79, 0x8a, 0x22, + 0x11, 0xb7, 0x65, 0xab, 0x58, 0xe2, 0xca, 0x03, 0xa2, 0x43, 0xf0, 0xc8, 0x62, 0x9d, 0xed, 0x93, + 0x7b, 0x92, 0x9d, 0xb3, 0x7c, 0x97, 0xd0, 0xfe, 0x05, 0x26, 0x46, 0xc6, 0x8e, 0x1d, 0xfb, 0x33, + 0x3a, 0x76, 0x64, 0xb2, 0xc0, 0x19, 0xca, 0xc2, 0x92, 0x5f, 0x80, 0xee, 0xec, 0x40, 0x22, 0x86, + 0x67, 0xbd, 0xf7, 0xbe, 0xef, 0x3d, 0xbf, 0xf7, 0xee, 0x83, 0x83, 0x82, 0xcd, 0x19, 0x5d, 0x16, + 0x9e, 0xb2, 0xe5, 0x89, 0x27, 0xaf, 0x4b, 0x2a, 0x26, 0x65, 0xc5, 0x25, 0x47, 0xcf, 0x3a, 0x6c, + 0xa2, 0x6c, 0x79, 0x32, 0xe8, 0x93, 0x82, 0xcd, 0xb9, 0xa7, 0xbf, 0x2d, 0x65, 0x70, 0x94, 0xf1, + 0x8c, 0x6b, 0xd7, 0x53, 0x5e, 0x9b, 0x75, 0x7f, 0xef, 0xc1, 0xde, 0x8c, 0x54, 0xa4, 0x10, 0x68, + 0x0a, 0x21, 0xbd, 0x92, 0x15, 0x89, 0x28, 0x2b, 0x85, 0x0d, 0x86, 0xe6, 0xc8, 0xf4, 0xdd, 0xa6, + 0x76, 0xac, 0x40, 0x65, 0x83, 0xf7, 0x33, 0xb1, 0xae, 0x9d, 0xfe, 0x35, 0x29, 0xf2, 0x33, 0xf7, + 0x1f, 0xd1, 0x0d, 0x2d, 0x1d, 0x04, 0xac, 0x14, 0xe8, 0x23, 0x44, 0x24, 0xcf, 0xf9, 0x67, 0x9a, + 0x46, 0xe5, 0x22, 0xce, 0x99, 0xb8, 0xa4, 0x95, 0xb0, 0xf7, 0x86, 0xe6, 0xc8, 0xf2, 0xdd, 0x75, + 0xed, 0xbc, 0x6a, 0xab, 0xff, 0xe7, 0xb8, 0xb7, 0x8f, 0x77, 0x63, 0x10, 0xf6, 0x3b, 0x64, 0xf6, + 0x17, 0x40, 0x7e, 0xd7, 0x32, 0x4a, 0x16, 0x42, 0xf2, 0x22, 0xa2, 0x55, 0x72, 0x7a, 0x6c, 0x9b, + 0x43, 0x30, 0x7a, 0xea, 0x1f, 0x35, 0xb5, 0x73, 0x30, 0x55, 0xe8, 0xb9, 0x06, 0x83, 0xf0, 0xfc, + 0xf4, 0x38, 0x3c, 0x20, 0x5b, 0x19, 0xc5, 0x46, 0x25, 0x7c, 0xbe, 0xf9, 0xe5, 0x76, 0x17, 0x61, + 0xef, 0xeb, 0xc9, 0xde, 0x35, 0xb5, 0x73, 0x38, 0x6d, 0x09, 0x5b, 0x8d, 0xd4, 0xba, 0xaf, 0x77, + 0x07, 0xde, 0xa9, 0xee, 0x66, 0x3e, 0x24, 0x3b, 0x95, 0x1a, 0x3a, 0x7b, 0xf9, 0xed, 0xc6, 0x31, + 0x7e, 0xdd, 0x38, 0xe0, 0xcb, 0xe3, 0xdd, 0x18, 0xaa, 0x07, 0x6b, 0x8f, 0xec, 0x7e, 0x80, 0xe6, + 0x05, 0xcf, 0x90, 0x0d, 0x9f, 0x90, 0x34, 0xad, 0xa8, 0x50, 0x87, 0x06, 0x23, 0x2b, 0xdc, 0x84, + 0xe8, 0x05, 0xec, 0x49, 0x5e, 0xb2, 0xa4, 0x3b, 0x5b, 0xd8, 0x45, 0x08, 0xc1, 0xfd, 0x94, 0x48, + 0xa2, 0x37, 0xb7, 0x42, 0xed, 0xfb, 0x17, 0xf7, 0x3f, 0xb1, 0x71, 0xdb, 0x60, 0x70, 0xdf, 0x60, + 0xf0, 0xd0, 0x60, 0xf0, 0xa3, 0xc1, 0xe0, 0xeb, 0x0a, 0x1b, 0x0f, 0x2b, 0x6c, 0x7c, 0x5f, 0x61, + 0xe3, 0xd3, 0x38, 0x63, 0xf2, 0x72, 0x11, 0x4f, 0x12, 0x5e, 0x78, 0x6c, 0xce, 0x24, 0x23, 0x6f, + 0x72, 0x12, 0x0b, 0x6f, 0x23, 0xa5, 0x2b, 0x2d, 0x26, 0xad, 0xa4, 0xb8, 0xa7, 0x15, 0xf1, 0xf6, + 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x41, 0xfc, 0x84, 0x5b, 0x68, 0x02, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -177,6 +186,17 @@ func (this *Params) Equal(that interface{}) bool { return false } } + if this.AllowCustomERC20 != that1.AllowCustomERC20 { + return false + } + if len(this.AllowedCustomERC20s) != len(that1.AllowedCustomERC20s) { + return false + } + for i := range this.AllowedCustomERC20s { + if this.AllowedCustomERC20s[i] != that1.AllowedCustomERC20s[i] { + return false + } + } return true } func (this *Log) Equal(that interface{}) bool { @@ -234,6 +254,25 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.AllowedCustomERC20s) > 0 { + for iNdEx := len(m.AllowedCustomERC20s) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedCustomERC20s[iNdEx]) + copy(dAtA[i:], m.AllowedCustomERC20s[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.AllowedCustomERC20s[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if m.AllowCustomERC20 { + i-- + if m.AllowCustomERC20 { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } if len(m.AllowedPublishers) > 0 { for iNdEx := len(m.AllowedPublishers) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AllowedPublishers[iNdEx]) @@ -341,6 +380,15 @@ func (m *Params) Size() (n int) { n += 1 + l + sovTypes(uint64(l)) } } + if m.AllowCustomERC20 { + n += 2 + } + if len(m.AllowedCustomERC20s) > 0 { + for _, s := range m.AllowedCustomERC20s { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } return n } @@ -510,6 +558,58 @@ func (m *Params) Unmarshal(dAtA []byte) error { } m.AllowedPublishers = append(m.AllowedPublishers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowCustomERC20", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowCustomERC20 = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedCustomERC20s", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + 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 ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedCustomERC20s = append(m.AllowedCustomERC20s, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:])