Merge pull request #181 from hermeznetwork/feature/erc20 Add methods to get ERC20 constants
laisolizq 37299818+laisolizq@users.noreply.github.com
Tue, 06 Oct 2020 17:16:22 +0200
7 files changed,
408 insertions(+),
5 deletions(-)
M
eth/contracts/README.md
→
eth/contracts/README.md
@@ -11,4 +11,20 @@ You must compile the contracts to get the `.bin` and `.abi` files. The contracts used are in the repo: https://github.com/hermeznetwork/contracts-circuits
Specifically they have been processed in the commit with hash: `745e8d588496d7762d4084a54bafd4435061ae35` -> abigen version 1.9.21+> abigen version 1.9.21 + +--- + +ERC20 go code was generated with the following command: +``` +abigen --sol erc20.sol --pkg erc20 --out erc20/erc20.go +``` + +Versions: +``` + $ abigen --version +abigen version 1.9.21-stable-0287d548 + $ solc --version +solc, the solidity compiler commandline interface +Version: 0.7.1+commit.f4a555be.Linux.g++ +```
A
eth/contracts/erc20.sol
@@ -0,0 +1,19 @@
+pragma solidity ^0.7.0; + +contract ERC20 { + string public constant name = ""; + string public constant symbol = ""; + uint8 public constant decimals = 0; + + /* + function totalSupply() public constant returns (uint); + function balanceOf(address tokenOwner) public constant returns (uint balance); + function allowance(address tokenOwner, address spender) public constant returns (uint remaining); + function transfer(address to, uint tokens) public returns (bool success); + function approve(address spender, uint tokens) public returns (bool success); + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); + */ +}
A
eth/contracts/erc20/erc20.go
@@ -0,0 +1,274 @@
+// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc20 + +import ( + "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 ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// ERC20ABI is the input ABI used to generate the binding from. +const ERC20ABI = "[{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" + +// ERC20FuncSigs maps the 4-byte function signature to its string representation. +var ERC20FuncSigs = map[string]string{ + "313ce567": "decimals()", + "06fdde03": "name()", + "95d89b41": "symbol()", +} + +// ERC20Bin is the compiled bytecode used for deploying new contracts. +var ERC20Bin = "0x608060405234801561001057600080fd5b50610123806100206000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c806306fdde03146041578063313ce5671460b957806395d89b41146041575b600080fd5b604760d5565b6040805160208082528351818301528351919283929083019185019080838360005b83811015607f5781810151838201526020016069565b50505050905090810190601f16801560ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60bf60e8565b6040805160ff9092168252519081900360200190f35b6040518060200160405280600081525081565b60008156fea26469706673582212209717f9f3c7b4f090e7741b44c5cb9425a26b593410462c0f4c2c0c0879db648d64736f6c63430007010033" + +// DeployERC20 deploys a new Ethereum contract, binding an instance of ERC20 to it. +func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ERC20, error) { + parsed, err := abi.JSON(strings.NewReader(ERC20ABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ERC20Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil +} + +// ERC20 is an auto generated Go binding around an Ethereum contract. +type ERC20 struct { + ERC20Caller // Read-only binding to the contract + ERC20Transactor // Write-only binding to the contract + ERC20Filterer // Log filterer for contract events +} + +// ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type ERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC20Session struct { + Contract *ERC20 // 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 +} + +// ERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC20CallerSession struct { + Contract *ERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC20TransactorSession struct { + Contract *ERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type ERC20Raw struct { + Contract *ERC20 // Generic contract binding to access the raw methods on +} + +// ERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC20CallerRaw struct { + Contract *ERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// ERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC20TransactorRaw struct { + Contract *ERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC20 creates a new instance of ERC20, bound to a specific deployed contract. +func NewERC20(address common.Address, backend bind.ContractBackend) (*ERC20, error) { + contract, err := bindERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil +} + +// NewERC20Caller creates a new read-only instance of ERC20, bound to a specific deployed contract. +func NewERC20Caller(address common.Address, caller bind.ContractCaller) (*ERC20Caller, error) { + contract, err := bindERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC20Caller{contract: contract}, nil +} + +// NewERC20Transactor creates a new write-only instance of ERC20, bound to a specific deployed contract. +func NewERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC20Transactor, error) { + contract, err := bindERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC20Transactor{contract: contract}, nil +} + +// NewERC20Filterer creates a new log filterer instance of ERC20, bound to a specific deployed contract. +func NewERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC20Filterer, error) { + contract, err := bindERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC20Filterer{contract: contract}, nil +} + +// bindERC20 binds a generic wrapper to an already deployed contract. +func bindERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(ERC20ABI)) + 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 (_ERC20 *ERC20Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ERC20.Contract.ERC20Caller.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 (_ERC20 *ERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20.Contract.ERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20 *ERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20.Contract.ERC20Transactor.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 (_ERC20 *ERC20CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _ERC20.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 (_ERC20 *ERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20 *ERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20.Contract.contract.Transact(opts, method, params...) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var ( + ret0 = new(uint8) + ) + out := ret0 + err := _ERC20.contract.Call(opts, out, "decimals") + return *ret0, err +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20Session) Decimals() (uint8, error) { + return _ERC20.Contract.Decimals(&_ERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_ERC20 *ERC20CallerSession) Decimals() (uint8, error) { + return _ERC20.Contract.Decimals(&_ERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var ( + ret0 = new(string) + ) + out := ret0 + err := _ERC20.contract.Call(opts, out, "name") + return *ret0, err +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20Session) Name() (string, error) { + return _ERC20.Contract.Name(&_ERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_ERC20 *ERC20CallerSession) Name() (string, error) { + return _ERC20.Contract.Name(&_ERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var ( + ret0 = new(string) + ) + out := ret0 + err := _ERC20.contract.Call(opts, out, "symbol") + return *ret0, err +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20Session) Symbol() (string, error) { + return _ERC20.Contract.Symbol(&_ERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_ERC20 *ERC20CallerSession) Symbol() (string, error) { + return _ERC20.Contract.Symbol(&_ERC20.CallOpts) +}
M
eth/ethereum.go
→
eth/ethereum.go
@@ -13,9 +13,17 @@ ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/hermeznetwork/hermez-node/common" + "github.com/hermeznetwork/hermez-node/eth/contracts/erc20" "github.com/hermeznetwork/hermez-node/log" ) +// ERC20Consts are the constants defined in a particular ERC20 Token instance +type ERC20Consts struct { + Name string + Symbol string + Decimals uint64 +} + // EthereumInterface is the interface to Ethereum type EthereumInterface interface { EthCurrentBlock() (int64, error)@@ -23,6 +31,8 @@ // EthHeaderByNumber(context.Context, *big.Int) (*types.Header, error)
EthBlockByNumber(context.Context, int64) (*common.Block, error) EthAddress() (*ethCommon.Address, error) EthTransactionReceipt(context.Context, ethCommon.Hash) (*types.Receipt, error) + + EthERC20Consts(ethCommon.Address) (*ERC20Consts, error) } var (@@ -264,3 +274,30 @@ Hash: block.Hash(),
} return b, nil } + +// EthERC20Consts returns the constants defined for a particular ERC20 Token instance. +func (c *EthereumClient) EthERC20Consts(tokenAddress ethCommon.Address) (*ERC20Consts, error) { + instance, err := erc20.NewERC20(tokenAddress, c.client) + if err != nil { + return nil, err + } + name, err := instance.Name(nil) + if err != nil { + return nil, err + } + + symbol, err := instance.Symbol(nil) + if err != nil { + return nil, err + } + + decimals, err := instance.Decimals(nil) + if err != nil { + return nil, err + } + return &ERC20Consts{ + Name: name, + Symbol: symbol, + Decimals: uint64(decimals), + }, nil +}
A
eth/ethereum_test.go
@@ -0,0 +1,24 @@
+package eth + +import ( + "os" + "testing" + + ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEthERC20(t *testing.T) { + address := ethCommon.HexToAddress("0x44021007485550008e0f9f1f7b506c7d970ad8ce") + ethClient, err := ethclient.Dial(os.Getenv("ETHCLIENT_DIAL_URL")) + require.Nil(t, err) + client := NewEthereumClient(ethClient, accountAux, ks, nil) + + consts, err := client.EthERC20Consts(address) + require.Nil(t, err) + assert.Equal(t, "Golem Network Token", consts.Name) + assert.Equal(t, "GNT", consts.Symbol) + assert.Equal(t, uint64(18), consts.Decimals) +}
M
test/ethclient.go
→
test/ethclient.go
@@ -176,6 +176,7 @@ BlockNum int64
Time int64 Hash ethCommon.Hash ParentHash ethCommon.Hash + Tokens map[ethCommon.Address]eth.ERC20Consts // state ethState }@@ -200,14 +201,15 @@ func (b *Block) Next() *Block {
blockNext := b.copy() blockNext.Rollup.Events = eth.NewRollupEvents() blockNext.Auction.Events = eth.NewAuctionEvents() - blockNext.Eth = &EthereumBlock{ - BlockNum: b.Eth.BlockNum + 1, - ParentHash: b.Eth.Hash, - } + + blockNext.Eth.BlockNum = b.Eth.BlockNum + 1 + blockNext.Eth.ParentHash = b.Eth.Hash + blockNext.Rollup.Constants = b.Rollup.Constants blockNext.Auction.Constants = b.Auction.Constants blockNext.Rollup.Eth = blockNext.Eth blockNext.Auction.Eth = blockNext.Eth + return blockNext }@@ -360,6 +362,7 @@ BlockNum: blockNum,
Time: timer.Time(), Hash: hasher.Next(), ParentHash: ethCommon.Hash{}, + Tokens: make(map[ethCommon.Address]eth.ERC20Consts), }, } blockCurrent.Rollup.Eth = blockCurrent.Eth@@ -506,6 +509,23 @@ }
} return nil, nil +} + +// CtlAddERC20 adds an ERC20 token to the blockchain. +func (c *Client) CtlAddERC20(tokenAddr ethCommon.Address, constants eth.ERC20Consts) { + nextBlock := c.nextBlock() + e := nextBlock.Eth + e.Tokens[tokenAddr] = constants +} + +// EthERC20Consts returns the constants defined for a particular ERC20 Token instance. +func (c *Client) EthERC20Consts(tokenAddr ethCommon.Address) (*eth.ERC20Consts, error) { + currentBlock := c.currentBlock() + e := currentBlock.Eth + if constants, ok := e.Tokens[tokenAddr]; ok { + return &constants, nil + } + return nil, fmt.Errorf("tokenAddr not found") } // func newHeader(number *big.Int) *types.Header {
M
test/ethclient_test.go
→
test/ethclient_test.go
@@ -62,6 +62,19 @@ block, err = c.EthBlockByNumber(context.TODO(), 2)
require.Nil(t, err) assert.Equal(t, int64(2), block.EthBlockNum) assert.Equal(t, time.Unix(2, 0), block.Timestamp) + + // Add a token + tokenAddr := ethCommon.HexToAddress("0x44021007485550008e0f9f1f7b506c7d970ad8ce") + constants := eth.ERC20Consts{ + Name: "FooBar", + Symbol: "FOO", + Decimals: 4, + } + c.CtlAddERC20(tokenAddr, constants) + c.CtlMineBlock() + tokenConstants, err := c.EthERC20Consts(tokenAddr) + require.Nil(t, err) + assert.Equal(t, constants, *tokenConstants) } func TestClientAuction(t *testing.T) {