From 4d39547b81e01e6da3913a1cbc8f2e394262ad0e Mon Sep 17 00:00:00 2001 From: Anthony Laibe <491074+alaibe@users.noreply.github.com> Date: Mon, 4 Apr 2022 18:54:44 +0200 Subject: [PATCH] Feat/expose registrar address (#2600) * feat: expose registrar address * fix code gen --- contracts/ierc20/ierc20.go | 98 ++++++++++++++++++++++++++++++++++- contracts/ierc20/ierc20.sol | 8 ++- services/ens/api.go | 4 ++ services/wallet/api.go | 6 +++ services/wallet/service.go | 2 +- services/wallet/token.go | 43 ++++++++++++++- services/wallet/token_test.go | 2 +- 7 files changed, 158 insertions(+), 5 deletions(-) diff --git a/contracts/ierc20/ierc20.go b/contracts/ierc20/ierc20.go index 86e9f653b..3c64530be 100644 --- a/contracts/ierc20/ierc20.go +++ b/contracts/ierc20/ierc20.go @@ -27,13 +27,16 @@ var ( ) // IERC20ABI is the input ABI used to generate the binding from. -const IERC20ABI = "[{\"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\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"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\"}]" +const IERC20ABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"spender\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"sender\",\"type\":\"address\"},{\"name\":\"recipient\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"recipient\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"},{\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]" // IERC20FuncSigs maps the 4-byte function signature to its string representation. var IERC20FuncSigs = map[string]string{ "dd62ed3e": "allowance(address,address)", "095ea7b3": "approve(address,uint256)", "70a08231": "balanceOf(address)", + "313ce567": "decimals()", + "06fdde03": "name()", + "95d89b41": "symbol()", "18160ddd": "totalSupply()", "a9059cbb": "transfer(address,uint256)", "23b872dd": "transferFrom(address,address,uint256)", @@ -243,6 +246,99 @@ func (_IERC20 *IERC20CallerSession) BalanceOf(account common.Address) (*big.Int, return _IERC20.Contract.BalanceOf(&_IERC20.CallOpts, account) } +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_IERC20 *IERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _IERC20.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 (_IERC20 *IERC20Session) Decimals() (uint8, error) { + return _IERC20.Contract.Decimals(&_IERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_IERC20 *IERC20CallerSession) Decimals() (uint8, error) { + return _IERC20.Contract.Decimals(&_IERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_IERC20 *IERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _IERC20.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 (_IERC20 *IERC20Session) Name() (string, error) { + return _IERC20.Contract.Name(&_IERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_IERC20 *IERC20CallerSession) Name() (string, error) { + return _IERC20.Contract.Name(&_IERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_IERC20 *IERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _IERC20.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 (_IERC20 *IERC20Session) Symbol() (string, error) { + return _IERC20.Contract.Symbol(&_IERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_IERC20 *IERC20CallerSession) Symbol() (string, error) { + return _IERC20.Contract.Symbol(&_IERC20.CallOpts) +} + // TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. // // Solidity: function totalSupply() view returns(uint256) diff --git a/contracts/ierc20/ierc20.sol b/contracts/ierc20/ierc20.sol index b8876dc0e..335838690 100644 --- a/contracts/ierc20/ierc20.sol +++ b/contracts/ierc20/ierc20.sol @@ -1,11 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.4.24; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); + /** * @dev Returns the amount of tokens in existence. */ diff --git a/services/ens/api.go b/services/ens/api.go index 316cf1f8f..03c423db3 100644 --- a/services/ens/api.go +++ b/services/ens/api.go @@ -56,6 +56,10 @@ type API struct { config *params.NodeConfig } +func (api *API) GetRegistrarAddress(ctx context.Context, chainID uint64) (common.Address, error) { + return registrar.ContractAddress(chainID) +} + func (api *API) Resolver(ctx context.Context, chainID uint64, username string) (*common.Address, error) { err := validateENSUsername(username) if err != nil { diff --git a/services/wallet/api.go b/services/wallet/api.go index 642382980..7544226d6 100644 --- a/services/wallet/api.go +++ b/services/wallet/api.go @@ -93,6 +93,12 @@ func (api *API) GetCustomTokens(ctx context.Context) ([]*Token, error) { return rst, err } +func (api *API) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) { + log.Debug("call to get discover token") + token, err := api.s.tokenManager.discoverToken(ctx, chainID, address) + return token, err +} + func (api *API) AddCustomToken(ctx context.Context, token Token) error { log.Debug("call to create or edit custom token") if token.ChainID == 0 { diff --git a/services/wallet/service.go b/services/wallet/service.go index dac9412b7..606bc4afe 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -17,7 +17,7 @@ func NewService(db *sql.DB, rpcClient *rpc.Client, accountFeed *event.Feed, open cryptoOnRampManager := NewCryptoOnRampManager(&CryptoOnRampOptions{ dataSourceType: DataSourceStatic, }) - tokenManager := &TokenManager{db: db} + tokenManager := &TokenManager{db: db, RPCClient: rpcClient} savedAddressesManager := &SavedAddressesManager{db: db} transactionManager := &TransactionManager{db: db} favouriteManager := &FavouriteManager{db: db} diff --git a/services/wallet/token.go b/services/wallet/token.go index 40c2af884..ef5c485ac 100644 --- a/services/wallet/token.go +++ b/services/wallet/token.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/contracts/ierc20" + "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/services/wallet/async" "github.com/status-im/status-go/services/wallet/chain" ) @@ -32,7 +33,8 @@ type Token struct { } type TokenManager struct { - db *sql.DB + db *sql.DB + RPCClient *rpc.Client } func (tm *TokenManager) getTokens(chainID uint64) ([]*Token, error) { @@ -50,6 +52,45 @@ func (tm *TokenManager) getTokens(chainID uint64) ([]*Token, error) { return res, nil } +func (tm *TokenManager) discoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) { + backend, err := tm.RPCClient.EthClient(chainID) + if err != nil { + return nil, err + } + caller, err := ierc20.NewIERC20Caller(address, backend) + if err != nil { + return nil, err + } + + name, err := caller.Name(&bind.CallOpts{ + Context: ctx, + }) + if err != nil { + return nil, err + } + + symbol, err := caller.Symbol(&bind.CallOpts{ + Context: ctx, + }) + if err != nil { + return nil, err + } + + decimal, err := caller.Decimals(&bind.CallOpts{ + Context: ctx, + }) + if err != nil { + return nil, err + } + + return &Token{ + Address: address, + Name: name, + Symbol: symbol, + Decimals: uint(decimal), + }, nil +} + func (tm *TokenManager) getCustoms() ([]*Token, error) { rows, err := tm.db.Query("SELECT address, name, symbol, decimals, color, network_id FROM tokens") if err != nil { diff --git a/services/wallet/token_test.go b/services/wallet/token_test.go index b2914b4cc..9e26acf3e 100644 --- a/services/wallet/token_test.go +++ b/services/wallet/token_test.go @@ -17,7 +17,7 @@ func setupTestTokenDB(t *testing.T) (*TokenManager, func()) { require.NoError(t, err) db, err := appdatabase.InitializeDB(tmpfile.Name(), "wallet-token-tests") require.NoError(t, err) - return &TokenManager{db}, func() { + return &TokenManager{db, nil}, func() { require.NoError(t, db.Close()) require.NoError(t, os.Remove(tmpfile.Name())) }