Andrea Maria Piana efbf093bc4 Don't fail if one request fails in getTokensBalances
If one request failed, the whole batch would fail.
This caused issue as one of the contract is constantly returning an
error now, and essentially there was not way to fetch balance.
Also extend the timeout to 20s as we throw 165 request to Infura in one
go and it takes its time to reply to those, although it seems like we
should batch them on our side instead of sending them all cuncurrently.
2020-10-05 12:53:51 +02:00

75 lines
2.0 KiB
Go

package wallet
import (
"context"
"sync"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/services/wallet/ierc20"
)
var requestTimeout = 20 * time.Second
// GetTokensBalances takes list of accounts and tokens and returns mapping of token balances for each account.
func GetTokensBalances(parent context.Context, client *ethclient.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
var (
group = NewAtomicGroup(parent)
mu sync.Mutex
response = map[common.Address]map[common.Address]*hexutil.Big{}
)
// requested current head to request balance on the same block number
ctx, cancel := context.WithTimeout(parent, requestTimeout)
header, err := client.HeaderByNumber(ctx, nil)
cancel()
if err != nil {
return nil, err
}
for _, token := range tokens {
caller, err := ierc20.NewIERC20Caller(token, client)
token := token
if err != nil {
return nil, err
}
for _, account := range accounts {
// Why we are doing this?
account := account
group.Add(func(parent context.Context) error {
ctx, cancel := context.WithTimeout(parent, requestTimeout)
balance, err := caller.BalanceOf(&bind.CallOpts{
BlockNumber: header.Number,
Context: ctx,
}, account)
cancel()
// 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
}
mu.Lock()
_, exist := response[account]
if !exist {
response[account] = map[common.Address]*hexutil.Big{}
}
response[account][token] = balance
mu.Unlock()
return nil
})
}
}
select {
case <-group.WaitAsync():
case <-parent.Done():
return nil, parent.Err()
}
return response, group.Error()
}