mirror of
https://github.com/status-im/status-go.git
synced 2025-02-17 17:28:38 +00:00
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/ethclient"
|
||||
"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/resolver"
|
||||
"github.com/status-im/status-go/contracts/snt"
|
||||
@ -146,3 +147,20 @@ func (c *ContractMaker) NewDirectoryWithBackend(chainID uint64, backend *ethclie
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
24
contracts/ethscan/address.go
Normal file
24
contracts/ethscan/address.go
Normal file
@ -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
|
||||
}
|
3
contracts/ethscan/doc.go
Normal file
3
contracts/ethscan/doc.go
Normal file
@ -0,0 +1,3 @@
|
||||
package ethscan
|
||||
|
||||
//go:generate abigen -sol ethscan.sol -pkg ethscan -out ethscan.go
|
342
contracts/ethscan/ethscan.go
Normal file
342
contracts/ethscan/ethscan.go
Normal file
@ -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)
|
||||
}
|
64
contracts/ethscan/ethscan.sol
Normal file
64
contracts/ethscan/ethscan.sol
Normal file
@ -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/tools v0.1.11 // 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // 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/hexutil"
|
||||
"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/rpc"
|
||||
"github.com/status-im/status-go/rpc/network"
|
||||
@ -20,6 +21,7 @@ import (
|
||||
)
|
||||
|
||||
var requestTimeout = 20 * time.Second
|
||||
var nativeChainAddress = common.HexToAddress("0x")
|
||||
|
||||
type Token struct {
|
||||
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) {
|
||||
if token == common.HexToAddress("0x") {
|
||||
if token == nativeChainAddress {
|
||||
return tm.getChainBalance(ctx, client, account)
|
||||
}
|
||||
|
||||
@ -292,25 +294,8 @@ func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Cli
|
||||
mu sync.Mutex
|
||||
response = map[common.Address]map[common.Address]*hexutil.Big{}
|
||||
)
|
||||
for clientIdx := range clients {
|
||||
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)
|
||||
|
||||
// We don't want to return an error here and prevent
|
||||
// the rest from completing
|
||||
if err != nil {
|
||||
log.Error("can't fetch erc20 token balance", "account", account, "token", token, "error", err)
|
||||
|
||||
return nil
|
||||
}
|
||||
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{}
|
||||
@ -324,10 +309,103 @@ func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Cli
|
||||
sumHex := hexutil.Big(*sum)
|
||||
response[account][token] = &sumHex
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
contractMaker := contracts.ContractMaker{RPCClient: tm.RPCClient}
|
||||
for clientIdx := range clients {
|
||||
client := clients[clientIdx]
|
||||
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
||||
|
||||
if err == nil {
|
||||
fetchChainBalance := false
|
||||
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 {
|
||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||
defer cancel()
|
||||
res, err := ethScanContract.EtherBalances(&bind.CallOpts{
|
||||
Context: ctx,
|
||||
}, accounts)
|
||||
if err != nil {
|
||||
log.Error("can't fetch chain balance", err)
|
||||
return nil
|
||||
}
|
||||
for idx, account := range accounts {
|
||||
balance := new(big.Int)
|
||||
balance.SetBytes(res[idx].Data)
|
||||
updateBalance(account, common.HexToAddress("0x"), balance)
|
||||
}
|
||||
|
||||
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 {
|
||||
case <-group.WaitAsync():
|
||||
|
Loading…
x
Reference in New Issue
Block a user