feat: batch balance (#2833)
This commit is contained in:
parent
0ecbb5e8d7
commit
48f678052c
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/status-im/status-go/contracts/directory"
|
"github.com/status-im/status-go/contracts/directory"
|
||||||
|
"github.com/status-im/status-go/contracts/ethscan"
|
||||||
"github.com/status-im/status-go/contracts/registrar"
|
"github.com/status-im/status-go/contracts/registrar"
|
||||||
"github.com/status-im/status-go/contracts/resolver"
|
"github.com/status-im/status-go/contracts/resolver"
|
||||||
"github.com/status-im/status-go/contracts/snt"
|
"github.com/status-im/status-go/contracts/snt"
|
||||||
|
@ -146,3 +147,20 @@ func (c *ContractMaker) NewDirectoryWithBackend(chainID uint64, backend *ethclie
|
||||||
backend,
|
backend,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ContractMaker) NewEthScan(chainID uint64) (*ethscan.BalanceScanner, error) {
|
||||||
|
contractAddr, err := ethscan.ContractAddress(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backend, err := c.RPCClient.EthClient(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethscan.NewBalanceScanner(
|
||||||
|
contractAddr,
|
||||||
|
backend,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ethscan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorNotAvailableOnChainID = errors.New("not available for chainID")
|
||||||
|
|
||||||
|
var contractAddressByChainID = map[uint64]common.Address{
|
||||||
|
1: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // mainnet
|
||||||
|
3: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // ropsten
|
||||||
|
4: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // rinkeby
|
||||||
|
5: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // goerli
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContractAddress(chainID uint64) (common.Address, error) {
|
||||||
|
addr, exists := contractAddressByChainID[chainID]
|
||||||
|
if !exists {
|
||||||
|
return *new(common.Address), errorNotAvailableOnChainID
|
||||||
|
}
|
||||||
|
return addr, nil
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ethscan
|
||||||
|
|
||||||
|
//go:generate abigen -sol ethscan.sol -pkg ethscan -out ethscan.go
|
|
@ -0,0 +1,342 @@
|
||||||
|
// Code generated - DO NOT EDIT.
|
||||||
|
// This file is a generated binding and any manual changes will be lost.
|
||||||
|
|
||||||
|
package ethscan
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
// BalanceScannerResult is an auto generated low-level Go binding around an user-defined struct.
|
||||||
|
type BalanceScannerResult struct {
|
||||||
|
Success bool
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerABI is the input ABI used to generate the binding from.
|
||||||
|
const BalanceScannerABI = "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"contracts\",\"type\":\"address[]\"},{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256\",\"name\":\"gas\",\"type\":\"uint256\"}],\"name\":\"call\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBalanceScanner.Result[]\",\"name\":\"results\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"contracts\",\"type\":\"address[]\"},{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"}],\"name\":\"call\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBalanceScanner.Result[]\",\"name\":\"results\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"addresses\",\"type\":\"address[]\"}],\"name\":\"etherBalances\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBalanceScanner.Result[]\",\"name\":\"results\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"addresses\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"tokenBalances\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBalanceScanner.Result[]\",\"name\":\"results\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"contracts\",\"type\":\"address[]\"}],\"name\":\"tokensBalance\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBalanceScanner.Result[]\",\"name\":\"results\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]"
|
||||||
|
|
||||||
|
// BalanceScannerFuncSigs maps the 4-byte function signature to its string representation.
|
||||||
|
var BalanceScannerFuncSigs = map[string]string{
|
||||||
|
"458b3a7c": "call(address[],bytes[])",
|
||||||
|
"36738374": "call(address[],bytes[],uint256)",
|
||||||
|
"dbdbb51b": "etherBalances(address[])",
|
||||||
|
"aad33091": "tokenBalances(address[],address)",
|
||||||
|
"e5da1b68": "tokensBalance(address,address[])",
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScanner is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type BalanceScanner struct {
|
||||||
|
BalanceScannerCaller // Read-only binding to the contract
|
||||||
|
BalanceScannerTransactor // Write-only binding to the contract
|
||||||
|
BalanceScannerFilterer // Log filterer for contract events
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type BalanceScannerCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type BalanceScannerTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||||
|
type BalanceScannerFilterer struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type BalanceScannerSession struct {
|
||||||
|
Contract *BalanceScanner // 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type BalanceScannerCallerSession struct {
|
||||||
|
Contract *BalanceScannerCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type BalanceScannerTransactorSession struct {
|
||||||
|
Contract *BalanceScannerTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type BalanceScannerRaw struct {
|
||||||
|
Contract *BalanceScanner // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type BalanceScannerCallerRaw struct {
|
||||||
|
Contract *BalanceScannerCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceScannerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type BalanceScannerTransactorRaw struct {
|
||||||
|
Contract *BalanceScannerTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBalanceScanner creates a new instance of BalanceScanner, bound to a specific deployed contract.
|
||||||
|
func NewBalanceScanner(address common.Address, backend bind.ContractBackend) (*BalanceScanner, error) {
|
||||||
|
contract, err := bindBalanceScanner(address, backend, backend, backend)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BalanceScanner{BalanceScannerCaller: BalanceScannerCaller{contract: contract}, BalanceScannerTransactor: BalanceScannerTransactor{contract: contract}, BalanceScannerFilterer: BalanceScannerFilterer{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBalanceScannerCaller creates a new read-only instance of BalanceScanner, bound to a specific deployed contract.
|
||||||
|
func NewBalanceScannerCaller(address common.Address, caller bind.ContractCaller) (*BalanceScannerCaller, error) {
|
||||||
|
contract, err := bindBalanceScanner(address, caller, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BalanceScannerCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBalanceScannerTransactor creates a new write-only instance of BalanceScanner, bound to a specific deployed contract.
|
||||||
|
func NewBalanceScannerTransactor(address common.Address, transactor bind.ContractTransactor) (*BalanceScannerTransactor, error) {
|
||||||
|
contract, err := bindBalanceScanner(address, nil, transactor, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BalanceScannerTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBalanceScannerFilterer creates a new log filterer instance of BalanceScanner, bound to a specific deployed contract.
|
||||||
|
func NewBalanceScannerFilterer(address common.Address, filterer bind.ContractFilterer) (*BalanceScannerFilterer, error) {
|
||||||
|
contract, err := bindBalanceScanner(address, nil, nil, filterer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &BalanceScannerFilterer{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindBalanceScanner binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindBalanceScanner(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(BalanceScannerABI))
|
||||||
|
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 (_BalanceScanner *BalanceScannerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||||
|
return _BalanceScanner.Contract.BalanceScannerCaller.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 (_BalanceScanner *BalanceScannerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _BalanceScanner.Contract.BalanceScannerTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_BalanceScanner *BalanceScannerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _BalanceScanner.Contract.BalanceScannerTransactor.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 (_BalanceScanner *BalanceScannerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
|
||||||
|
return _BalanceScanner.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 (_BalanceScanner *BalanceScannerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _BalanceScanner.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_BalanceScanner *BalanceScannerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _BalanceScanner.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call is a free data retrieval call binding the contract method 0x36738374.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data, uint256 gas) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCaller) Call(opts *bind.CallOpts, contracts []common.Address, data [][]byte, gas *big.Int) ([]BalanceScannerResult, error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _BalanceScanner.contract.Call(opts, &out, "call", contracts, data, gas)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return *new([]BalanceScannerResult), err
|
||||||
|
}
|
||||||
|
|
||||||
|
out0 := *abi.ConvertType(out[0], new([]BalanceScannerResult)).(*[]BalanceScannerResult)
|
||||||
|
|
||||||
|
return out0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call is a free data retrieval call binding the contract method 0x36738374.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data, uint256 gas) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerSession) Call(contracts []common.Address, data [][]byte, gas *big.Int) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.Call(&_BalanceScanner.CallOpts, contracts, data, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call is a free data retrieval call binding the contract method 0x36738374.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data, uint256 gas) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCallerSession) Call(contracts []common.Address, data [][]byte, gas *big.Int) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.Call(&_BalanceScanner.CallOpts, contracts, data, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call0 is a free data retrieval call binding the contract method 0x458b3a7c.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCaller) Call0(opts *bind.CallOpts, contracts []common.Address, data [][]byte) ([]BalanceScannerResult, error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _BalanceScanner.contract.Call(opts, &out, "call0", contracts, data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return *new([]BalanceScannerResult), err
|
||||||
|
}
|
||||||
|
|
||||||
|
out0 := *abi.ConvertType(out[0], new([]BalanceScannerResult)).(*[]BalanceScannerResult)
|
||||||
|
|
||||||
|
return out0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call0 is a free data retrieval call binding the contract method 0x458b3a7c.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerSession) Call0(contracts []common.Address, data [][]byte) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.Call0(&_BalanceScanner.CallOpts, contracts, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call0 is a free data retrieval call binding the contract method 0x458b3a7c.
|
||||||
|
//
|
||||||
|
// Solidity: function call(address[] contracts, bytes[] data) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCallerSession) Call0(contracts []common.Address, data [][]byte) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.Call0(&_BalanceScanner.CallOpts, contracts, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EtherBalances is a free data retrieval call binding the contract method 0xdbdbb51b.
|
||||||
|
//
|
||||||
|
// Solidity: function etherBalances(address[] addresses) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCaller) EtherBalances(opts *bind.CallOpts, addresses []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _BalanceScanner.contract.Call(opts, &out, "etherBalances", addresses)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return *new([]BalanceScannerResult), err
|
||||||
|
}
|
||||||
|
|
||||||
|
out0 := *abi.ConvertType(out[0], new([]BalanceScannerResult)).(*[]BalanceScannerResult)
|
||||||
|
|
||||||
|
return out0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// EtherBalances is a free data retrieval call binding the contract method 0xdbdbb51b.
|
||||||
|
//
|
||||||
|
// Solidity: function etherBalances(address[] addresses) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerSession) EtherBalances(addresses []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.EtherBalances(&_BalanceScanner.CallOpts, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EtherBalances is a free data retrieval call binding the contract method 0xdbdbb51b.
|
||||||
|
//
|
||||||
|
// Solidity: function etherBalances(address[] addresses) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCallerSession) EtherBalances(addresses []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.EtherBalances(&_BalanceScanner.CallOpts, addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenBalances is a free data retrieval call binding the contract method 0xaad33091.
|
||||||
|
//
|
||||||
|
// Solidity: function tokenBalances(address[] addresses, address token) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCaller) TokenBalances(opts *bind.CallOpts, addresses []common.Address, token common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _BalanceScanner.contract.Call(opts, &out, "tokenBalances", addresses, token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return *new([]BalanceScannerResult), err
|
||||||
|
}
|
||||||
|
|
||||||
|
out0 := *abi.ConvertType(out[0], new([]BalanceScannerResult)).(*[]BalanceScannerResult)
|
||||||
|
|
||||||
|
return out0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenBalances is a free data retrieval call binding the contract method 0xaad33091.
|
||||||
|
//
|
||||||
|
// Solidity: function tokenBalances(address[] addresses, address token) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerSession) TokenBalances(addresses []common.Address, token common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.TokenBalances(&_BalanceScanner.CallOpts, addresses, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenBalances is a free data retrieval call binding the contract method 0xaad33091.
|
||||||
|
//
|
||||||
|
// Solidity: function tokenBalances(address[] addresses, address token) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCallerSession) TokenBalances(addresses []common.Address, token common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.TokenBalances(&_BalanceScanner.CallOpts, addresses, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokensBalance is a free data retrieval call binding the contract method 0xe5da1b68.
|
||||||
|
//
|
||||||
|
// Solidity: function tokensBalance(address owner, address[] contracts) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCaller) TokensBalance(opts *bind.CallOpts, owner common.Address, contracts []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
var out []interface{}
|
||||||
|
err := _BalanceScanner.contract.Call(opts, &out, "tokensBalance", owner, contracts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return *new([]BalanceScannerResult), err
|
||||||
|
}
|
||||||
|
|
||||||
|
out0 := *abi.ConvertType(out[0], new([]BalanceScannerResult)).(*[]BalanceScannerResult)
|
||||||
|
|
||||||
|
return out0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokensBalance is a free data retrieval call binding the contract method 0xe5da1b68.
|
||||||
|
//
|
||||||
|
// Solidity: function tokensBalance(address owner, address[] contracts) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerSession) TokensBalance(owner common.Address, contracts []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.TokensBalance(&_BalanceScanner.CallOpts, owner, contracts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokensBalance is a free data retrieval call binding the contract method 0xe5da1b68.
|
||||||
|
//
|
||||||
|
// Solidity: function tokensBalance(address owner, address[] contracts) view returns((bool,bytes)[] results)
|
||||||
|
func (_BalanceScanner *BalanceScannerCallerSession) TokensBalance(owner common.Address, contracts []common.Address) ([]BalanceScannerResult, error) {
|
||||||
|
return _BalanceScanner.Contract.TokensBalance(&_BalanceScanner.CallOpts, owner, contracts)
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
*Submitted for verification at Etherscan.io on 2021-04-07
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8.3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title An Ether or token balance scanner
|
||||||
|
* @author Maarten Zuidhoorn
|
||||||
|
* @author Luit Hollander
|
||||||
|
*/
|
||||||
|
abstract contract BalanceScanner {
|
||||||
|
struct Result {
|
||||||
|
bool success;
|
||||||
|
bytes data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Get the Ether balance for all addresses specified
|
||||||
|
* @param addresses The addresses to get the Ether balance for
|
||||||
|
* @return results The Ether balance for all addresses in the same order as specified
|
||||||
|
*/
|
||||||
|
function etherBalances(address[] calldata addresses) external virtual view returns (Result[] memory results);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Get the ERC-20 token balance of `token` for all addresses specified
|
||||||
|
* @dev This does not check if the `token` address specified is actually an ERC-20 token
|
||||||
|
* @param addresses The addresses to get the token balance for
|
||||||
|
* @param token The address of the ERC-20 token contract
|
||||||
|
* @return results The token balance for all addresses in the same order as specified
|
||||||
|
*/
|
||||||
|
function tokenBalances(address[] calldata addresses, address token) external virtual view returns (Result[] memory results);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Get the ERC-20 token balance from multiple contracts for a single owner
|
||||||
|
* @param owner The address of the token owner
|
||||||
|
* @param contracts The addresses of the ERC-20 token contracts
|
||||||
|
* @return results The token balances in the same order as the addresses specified
|
||||||
|
*/
|
||||||
|
function tokensBalance(address owner, address[] calldata contracts) external virtual view returns (Result[] memory results);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Call multiple contracts with the provided arbitrary data
|
||||||
|
* @param contracts The contracts to call
|
||||||
|
* @param data The data to call the contracts with
|
||||||
|
* @return results The raw result of the contract calls
|
||||||
|
*/
|
||||||
|
function call(address[] calldata contracts, bytes[] calldata data) external virtual view returns (Result[] memory results);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Call multiple contracts with the provided arbitrary data
|
||||||
|
* @param contracts The contracts to call
|
||||||
|
* @param data The data to call the contracts with
|
||||||
|
* @param gas The amount of gas to call the contracts with
|
||||||
|
* @return results The raw result of the contract calls
|
||||||
|
*/
|
||||||
|
function call(
|
||||||
|
address[] calldata contracts,
|
||||||
|
bytes[] calldata data,
|
||||||
|
uint256 gas
|
||||||
|
) public view virtual returns (Result[] memory results);
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -259,7 +259,6 @@ require (
|
||||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||||
golang.org/x/tools v0.1.11 // indirect
|
golang.org/x/tools v0.1.11 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/status-im/status-go/contracts"
|
||||||
"github.com/status-im/status-go/contracts/ierc20"
|
"github.com/status-im/status-go/contracts/ierc20"
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
"github.com/status-im/status-go/rpc/network"
|
"github.com/status-im/status-go/rpc/network"
|
||||||
|
@ -20,6 +21,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var requestTimeout = 20 * time.Second
|
var requestTimeout = 20 * time.Second
|
||||||
|
var nativeChainAddress = common.HexToAddress("0x")
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Address common.Address `json:"address"`
|
Address common.Address `json:"address"`
|
||||||
|
@ -279,7 +281,7 @@ func (tm *TokenManager) getChainBalance(ctx context.Context, client *chain.Clien
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TokenManager) getBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
func (tm *TokenManager) getBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
|
||||||
if token == common.HexToAddress("0x") {
|
if token == nativeChainAddress {
|
||||||
return tm.getChainBalance(ctx, client, account)
|
return tm.getChainBalance(ctx, client, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,42 +294,118 @@ func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Cli
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
response = map[common.Address]map[common.Address]*hexutil.Big{}
|
response = map[common.Address]map[common.Address]*hexutil.Big{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
updateBalance := func(account common.Address, token common.Address, balance *big.Int) {
|
||||||
|
mu.Lock()
|
||||||
|
if _, ok := response[account]; !ok {
|
||||||
|
response[account] = map[common.Address]*hexutil.Big{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := response[account][token]; !ok {
|
||||||
|
zeroHex := hexutil.Big(*big.NewInt(0))
|
||||||
|
response[account][token] = &zeroHex
|
||||||
|
}
|
||||||
|
sum := big.NewInt(0).Add(response[account][token].ToInt(), balance)
|
||||||
|
sumHex := hexutil.Big(*sum)
|
||||||
|
response[account][token] = &sumHex
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
contractMaker := contracts.ContractMaker{RPCClient: tm.RPCClient}
|
||||||
for clientIdx := range clients {
|
for clientIdx := range clients {
|
||||||
for tokenIdx := range tokens {
|
client := clients[clientIdx]
|
||||||
for accountIdx := range accounts {
|
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
||||||
// Below, we set account, token and client from idx on purpose to avoid override
|
|
||||||
account := accounts[accountIdx]
|
if err == nil {
|
||||||
token := tokens[tokenIdx]
|
fetchChainBalance := false
|
||||||
client := clients[clientIdx]
|
var tokenChunks [][]common.Address
|
||||||
|
chunkSize := 100
|
||||||
|
for i := 0; i < len(tokens); i += chunkSize {
|
||||||
|
end := i + chunkSize
|
||||||
|
if end > len(tokens) {
|
||||||
|
end = len(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenChunks = append(tokenChunks, tokens[i:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, token := range tokens {
|
||||||
|
if token == nativeChainAddress {
|
||||||
|
fetchChainBalance = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fetchChainBalance {
|
||||||
group.Add(func(parent context.Context) error {
|
group.Add(func(parent context.Context) error {
|
||||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
balance, err := tm.getBalance(ctx, client, account, token)
|
res, err := ethScanContract.EtherBalances(&bind.CallOpts{
|
||||||
|
Context: ctx,
|
||||||
// We don't want to return an error here and prevent
|
}, accounts)
|
||||||
// the rest from completing
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("can't fetch erc20 token balance", "account", account, "token", token, "error", err)
|
log.Error("can't fetch chain balance", err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mu.Lock()
|
for idx, account := range accounts {
|
||||||
if _, ok := response[account]; !ok {
|
balance := new(big.Int)
|
||||||
response[account] = map[common.Address]*hexutil.Big{}
|
balance.SetBytes(res[idx].Data)
|
||||||
|
updateBalance(account, common.HexToAddress("0x"), balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := response[account][token]; !ok {
|
|
||||||
zeroHex := hexutil.Big(*big.NewInt(0))
|
|
||||||
response[account][token] = &zeroHex
|
|
||||||
}
|
|
||||||
sum := big.NewInt(0).Add(response[account][token].ToInt(), balance)
|
|
||||||
sumHex := hexutil.Big(*sum)
|
|
||||||
response[account][token] = &sumHex
|
|
||||||
mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for accountIdx := range accounts {
|
||||||
|
account := accounts[accountIdx]
|
||||||
|
for idx := range tokenChunks {
|
||||||
|
chunk := tokenChunks[idx]
|
||||||
|
group.Add(func(parent context.Context) error {
|
||||||
|
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||||
|
defer cancel()
|
||||||
|
res, err := ethScanContract.TokensBalance(&bind.CallOpts{
|
||||||
|
Context: ctx,
|
||||||
|
}, account, chunk)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("can't fetch erc20 token balance", "account", account, "error", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, token := range chunk {
|
||||||
|
if !res[idx].Success {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
balance := new(big.Int)
|
||||||
|
balance.SetBytes(res[idx].Data)
|
||||||
|
updateBalance(account, token, balance)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for tokenIdx := range tokens {
|
||||||
|
for accountIdx := range accounts {
|
||||||
|
// Below, we set account, token and client from idx on purpose to avoid override
|
||||||
|
account := accounts[accountIdx]
|
||||||
|
token := tokens[tokenIdx]
|
||||||
|
client := clients[clientIdx]
|
||||||
|
group.Add(func(parent context.Context) error {
|
||||||
|
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||||
|
defer cancel()
|
||||||
|
balance, err := tm.getBalance(ctx, client, account, token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("can't fetch erc20 token balance", "account", account, "token", token, "error", err)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
updateBalance(account, token, balance)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-group.WaitAsync():
|
case <-group.WaitAsync():
|
||||||
|
|
Loading…
Reference in New Issue