feat: add wallet token endpoint
This commit is contained in:
parent
f3b1448efa
commit
eff02a79a9
|
@ -35,8 +35,8 @@ func (api *API) StartWallet(ctx context.Context, chainIDs []uint64) error {
|
|||
return api.reader.Start(ctx, chainIDs)
|
||||
}
|
||||
|
||||
func (api *API) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, error) {
|
||||
return api.reader.GetWallet(ctx, chainIDs)
|
||||
func (api *API) GetWalletToken(ctx context.Context) (map[common.Address][]Token, error) {
|
||||
return api.reader.GetWalletToken(ctx)
|
||||
}
|
||||
|
||||
type DerivedAddress struct {
|
||||
|
|
|
@ -78,10 +78,28 @@ func getRealSymbol(symbol string) string {
|
|||
return strings.ToUpper(symbol)
|
||||
}
|
||||
|
||||
func fetchCryptoComparePrices(symbols []string, currency string) (map[string]float64, error) {
|
||||
realSymbols := renameSymbols(symbols)
|
||||
httpClient := http.Client{Timeout: time.Minute}
|
||||
func chunkSymbols(symbols []string) [][]string {
|
||||
var chunks [][]string
|
||||
chunkSize := 20
|
||||
for i := 0; i < len(symbols); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
|
||||
if end > len(symbols) {
|
||||
end = len(symbols)
|
||||
}
|
||||
|
||||
chunks = append(chunks, symbols[i:end])
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
func fetchCryptoComparePrices(symbols []string, currency string) (map[string]float64, error) {
|
||||
chunks := chunkSymbols(symbols)
|
||||
result := make(map[string]float64)
|
||||
for _, smbls := range chunks {
|
||||
realSymbols := renameSymbols(smbls)
|
||||
httpClient := http.Client{Timeout: time.Minute}
|
||||
url := fmt.Sprintf("%s/data/pricemulti?fsyms=%s&tsyms=%s&extraParams=Status.im", cryptocompareURL, strings.Join(realSymbols, ","), currency)
|
||||
resp, err := httpClient.Get(url)
|
||||
if err != nil {
|
||||
|
@ -100,10 +118,10 @@ func fetchCryptoComparePrices(symbols []string, currency string) (map[string]flo
|
|||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]float64)
|
||||
for _, symbol := range symbols {
|
||||
for _, symbol := range smbls {
|
||||
result[symbol] = prices[getRealSymbol(symbol)][strings.ToUpper(currency)]
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@ package wallet
|
|||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/transfer"
|
||||
)
|
||||
|
||||
func NewReader(s *Service) *Reader {
|
||||
|
@ -20,32 +21,29 @@ type Reader struct {
|
|||
s *Service
|
||||
}
|
||||
|
||||
type ReaderToken struct {
|
||||
Token *token.Token `json:"token"`
|
||||
OraclePrice float64 `json:"oraclePrice"`
|
||||
CryptoBalance *hexutil.Big `json:"cryptoBalance"`
|
||||
FiatBalance *big.Float `json:"fiatBalance"`
|
||||
type ChainBalance struct {
|
||||
Balance *big.Float `json:"balance"`
|
||||
Address common.Address `json:"address"`
|
||||
ChainID uint64 `json:"chainId"`
|
||||
}
|
||||
|
||||
type ReaderAccount struct {
|
||||
Account *accounts.Account `json:"account"`
|
||||
Collections map[uint64][]OpenseaCollection `json:"collections"`
|
||||
Tokens map[uint64][]ReaderToken `json:"tokens"`
|
||||
Transactions map[uint64][]transfer.View `json:"transactions"`
|
||||
|
||||
FiatBalance *big.Float `json:"fiatBalance"`
|
||||
}
|
||||
|
||||
type Wallet struct {
|
||||
Accounts []ReaderAccount `json:"accounts"`
|
||||
OnRamp []CryptoOnRamp `json:"onRamp"`
|
||||
SavedAddresses map[uint64][]SavedAddress `json:"savedAddresses"`
|
||||
Tokens map[uint64][]*token.Token `json:"tokens"`
|
||||
CustomTokens []*token.Token `json:"customTokens"`
|
||||
PendingTransactions map[uint64][]*PendingTransaction `json:"pendingTransactions"`
|
||||
|
||||
FiatBalance *big.Float `json:"fiatBalance"`
|
||||
Currency string `json:"currency"`
|
||||
type Token struct {
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Color string `json:"color"`
|
||||
Decimals uint `json:"decimals"`
|
||||
BalancesPerChain map[uint64]ChainBalance `json:"balancesPerChain"`
|
||||
Description string `json:"description"`
|
||||
AssetWebsiteURL string `json:"assetWebsiteUrl"`
|
||||
BuiltOn string `json:"builtOn"`
|
||||
MarketCap string `json:"marketCap"`
|
||||
HighDay string `json:"highDay"`
|
||||
LowDay string `json:"lowDay"`
|
||||
ChangePctHour string `json:"changePctHour"`
|
||||
ChangePctDay string `json:"changePctDay"`
|
||||
ChangePct24hour string `json:"changePct24hour"`
|
||||
Change24hour string `json:"change24hour"`
|
||||
CurrencyPrice float64 `json:"currencyPrice"`
|
||||
}
|
||||
|
||||
func getAddresses(accounts []*accounts.Account) []common.Address {
|
||||
|
@ -56,52 +54,39 @@ func getAddresses(accounts []*accounts.Account) []common.Address {
|
|||
return addresses
|
||||
}
|
||||
|
||||
func (r *Reader) buildReaderAccount(
|
||||
ctx context.Context,
|
||||
chainIDs []uint64,
|
||||
account *accounts.Account,
|
||||
visibleTokens map[uint64][]*token.Token,
|
||||
prices map[string]float64,
|
||||
balances map[common.Address]*hexutil.Big,
|
||||
) (ReaderAccount, error) {
|
||||
limit := int64(20)
|
||||
toBlock := big.NewInt(0)
|
||||
func getTokenBySymbols(tokens []*token.Token) map[string][]*token.Token {
|
||||
res := make(map[string][]*token.Token)
|
||||
|
||||
collections := make(map[uint64][]OpenseaCollection)
|
||||
tokens := make(map[uint64][]ReaderToken)
|
||||
transactions := make(map[uint64][]transfer.View)
|
||||
accountFiatBalance := big.NewFloat(0)
|
||||
for _, chainID := range chainIDs {
|
||||
client, err := newOpenseaClient(chainID, r.s.openseaAPIKey)
|
||||
if err == nil {
|
||||
c, _ := client.fetchAllCollectionsByOwner(common.Address(account.Address))
|
||||
collections[chainID] = c
|
||||
for _, t := range tokens {
|
||||
if _, ok := res[t.Symbol]; !ok {
|
||||
res[t.Symbol] = make([]*token.Token, 0)
|
||||
}
|
||||
|
||||
for _, token := range visibleTokens[chainID] {
|
||||
oraclePrice := prices[token.Symbol]
|
||||
cryptoBalance := balances[token.Address].ToInt()
|
||||
fiatBalance := big.NewFloat(0).Mul(big.NewFloat(oraclePrice), new(big.Float).SetInt(cryptoBalance))
|
||||
tokens[chainID] = append(tokens[chainID], ReaderToken{
|
||||
Token: token,
|
||||
OraclePrice: oraclePrice,
|
||||
CryptoBalance: balances[token.Address],
|
||||
FiatBalance: fiatBalance,
|
||||
})
|
||||
accountFiatBalance = accountFiatBalance.Add(accountFiatBalance, fiatBalance)
|
||||
res[t.Symbol] = append(res[t.Symbol], t)
|
||||
}
|
||||
t, err := r.s.transferController.GetTransfersByAddress(ctx, chainID, common.Address(account.Address), toBlock, limit, false)
|
||||
if err == nil {
|
||||
transactions[chainID] = t
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func getTokenSymbols(tokens []*token.Token) []string {
|
||||
tokensBySymbols := getTokenBySymbols(tokens)
|
||||
res := make([]string, 0)
|
||||
for symbol := range tokensBySymbols {
|
||||
res = append(res, symbol)
|
||||
}
|
||||
return ReaderAccount{
|
||||
Account: account,
|
||||
Collections: collections,
|
||||
Tokens: tokens,
|
||||
Transactions: transactions,
|
||||
FiatBalance: accountFiatBalance,
|
||||
}, nil
|
||||
return res
|
||||
}
|
||||
|
||||
func getTokenAddresses(tokens []*token.Token) []common.Address {
|
||||
set := make(map[common.Address]bool)
|
||||
for _, token := range tokens {
|
||||
set[token.Address] = true
|
||||
}
|
||||
res := make([]common.Address, 0)
|
||||
for address := range set {
|
||||
res = append(res, address)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *Reader) Start(ctx context.Context, chainIDs []uint64) error {
|
||||
|
@ -113,108 +98,135 @@ func (r *Reader) Start(ctx context.Context, chainIDs []uint64) error {
|
|||
return r.s.transferController.CheckRecentHistory(chainIDs, getAddresses(accounts))
|
||||
}
|
||||
|
||||
func (r *Reader) GetWallet(ctx context.Context, chainIDs []uint64) (*Wallet, error) {
|
||||
func (r *Reader) GetWalletToken(ctx context.Context) (map[common.Address][]Token, error) {
|
||||
networks, err := r.s.rpcClient.NetworkManager.Get(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chainIDs := make([]uint64, 0)
|
||||
for _, network := range networks {
|
||||
chainIDs = append(chainIDs, network.ChainID)
|
||||
}
|
||||
|
||||
currency, err := r.s.accountsDB.GetCurrency()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokensMap := make(map[uint64][]*token.Token)
|
||||
for _, chainID := range chainIDs {
|
||||
tokens, err := r.s.tokenManager.GetTokens(chainID)
|
||||
allTokens, err := r.s.tokenManager.GetAllTokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokensMap[chainID] = tokens
|
||||
for _, network := range networks {
|
||||
allTokens = append(allTokens, r.s.tokenManager.ToToken(network))
|
||||
}
|
||||
|
||||
customTokens, err := r.s.tokenManager.GetCustoms()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
visibleTokens, err := r.s.tokenManager.GetVisible(chainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenAddresses := make([]common.Address, 0)
|
||||
tokenSymbols := make([]string, 0)
|
||||
for _, tokens := range visibleTokens {
|
||||
for _, token := range tokens {
|
||||
tokenAddresses = append(tokenAddresses, token.Address)
|
||||
tokenSymbols = append(tokenSymbols, token.Symbol)
|
||||
}
|
||||
}
|
||||
tokenSymbols := getTokenSymbols(allTokens)
|
||||
tokenAddresses := getTokenAddresses(allTokens)
|
||||
|
||||
accounts, err := r.s.accountsDB.GetAccounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prices, err := fetchCryptoComparePrices(tokenSymbols, currency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
prices = map[string]float64{}
|
||||
tokenDetails = map[string]Coin{}
|
||||
tokenMarketValues = map[string]MarketCoinValues{}
|
||||
balances = map[uint64]map[common.Address]map[common.Address]*hexutil.Big{}
|
||||
)
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
prices, err = fetchCryptoComparePrices(tokenSymbols, currency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
tokenDetails, err = fetchCryptoCompareTokenDetails(tokenSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
tokenMarketValues, err = fetchTokenMarketValues(tokenSymbols, currency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
clients, err := chain.NewClients(r.s.rpcClient, chainIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
balances, err := r.s.tokenManager.GetBalances(ctx, clients, getAddresses(accounts), tokenAddresses)
|
||||
balances, err = r.s.tokenManager.GetBalancesByChain(ctx, clients, getAddresses(accounts), tokenAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
select {
|
||||
case <-group.WaitAsync():
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
err = group.Error()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
readerAccounts := make([]ReaderAccount, len(accounts))
|
||||
walletFiatBalance := big.NewFloat(0)
|
||||
for i, account := range accounts {
|
||||
readerAccount, err := r.buildReaderAccount(
|
||||
ctx,
|
||||
chainIDs,
|
||||
account,
|
||||
visibleTokens,
|
||||
prices,
|
||||
balances[common.Address(account.Address)],
|
||||
result := make(map[common.Address][]Token)
|
||||
for _, account := range accounts {
|
||||
commonAddress := common.Address(account.Address)
|
||||
for symbol, tokens := range getTokenBySymbols(allTokens) {
|
||||
balancesPerChain := make(map[uint64]ChainBalance)
|
||||
decimals := tokens[0].Decimals
|
||||
for _, token := range tokens {
|
||||
hexBalance := balances[token.ChainID][commonAddress][token.Address]
|
||||
balance := big.NewFloat(0.0)
|
||||
if hexBalance != nil {
|
||||
balance = new(big.Float).Quo(
|
||||
new(big.Float).SetInt(hexBalance.ToInt()),
|
||||
big.NewFloat(math.Pow(10, float64(decimals))),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
walletFiatBalance = walletFiatBalance.Add(walletFiatBalance, readerAccount.FiatBalance)
|
||||
readerAccounts[i] = readerAccount
|
||||
balancesPerChain[token.ChainID] = ChainBalance{
|
||||
Balance: balance,
|
||||
Address: token.Address,
|
||||
ChainID: token.ChainID,
|
||||
}
|
||||
}
|
||||
|
||||
savedAddressesMap := make(map[uint64][]SavedAddress)
|
||||
for _, chainID := range chainIDs {
|
||||
savedAddresses, err := r.s.savedAddressesManager.GetSavedAddressesForChainID(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
savedAddressesMap[chainID] = savedAddresses
|
||||
walletToken := Token{
|
||||
Name: tokens[0].Name,
|
||||
Color: tokens[0].Color,
|
||||
Symbol: symbol,
|
||||
BalancesPerChain: balancesPerChain,
|
||||
Decimals: decimals,
|
||||
Description: tokenDetails[symbol].Description,
|
||||
AssetWebsiteURL: tokenDetails[symbol].AssetWebsiteURL,
|
||||
BuiltOn: tokenDetails[symbol].BuiltOn,
|
||||
MarketCap: tokenMarketValues[symbol].MKTCAP,
|
||||
HighDay: tokenMarketValues[symbol].HIGHDAY,
|
||||
LowDay: tokenMarketValues[symbol].LOWDAY,
|
||||
ChangePctHour: tokenMarketValues[symbol].CHANGEPCTHOUR,
|
||||
ChangePctDay: tokenMarketValues[symbol].CHANGEPCTDAY,
|
||||
ChangePct24hour: tokenMarketValues[symbol].CHANGEPCT24HOUR,
|
||||
Change24hour: tokenMarketValues[symbol].CHANGE24HOUR,
|
||||
CurrencyPrice: prices[symbol],
|
||||
}
|
||||
|
||||
onRamp, err := r.s.cryptoOnRampManager.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
result[commonAddress] = append(result[commonAddress], walletToken)
|
||||
}
|
||||
|
||||
pendingTransactions := make(map[uint64][]*PendingTransaction)
|
||||
for _, chainID := range chainIDs {
|
||||
pendingTx, err := r.s.transactionManager.getAllPendings([]uint64{chainID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pendingTransactions[chainID] = pendingTx
|
||||
}
|
||||
return &Wallet{
|
||||
Accounts: readerAccounts,
|
||||
OnRamp: onRamp,
|
||||
SavedAddresses: savedAddressesMap,
|
||||
Tokens: tokensMap,
|
||||
CustomTokens: customTokens,
|
||||
PendingTransactions: pendingTransactions,
|
||||
Currency: currency,
|
||||
FiatBalance: walletFiatBalance,
|
||||
}, nil
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -77,12 +77,7 @@ func NewTokenManager(
|
|||
|
||||
func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token {
|
||||
if tokenSymbol == network.NativeCurrencySymbol {
|
||||
return &Token{
|
||||
Address: common.HexToAddress("0x"),
|
||||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
Name: network.NativeCurrencyName,
|
||||
}
|
||||
return tm.ToToken(network)
|
||||
}
|
||||
|
||||
tokens, err := tm.GetTokens(network.ChainID)
|
||||
|
@ -117,6 +112,25 @@ func (tm *Manager) FindSNT(chainID uint64) *Token {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tm *Manager) GetAllTokens() ([]*Token, error) {
|
||||
result := make([]*Token, 0)
|
||||
for _, tokens := range tokenStore {
|
||||
for _, token := range tokens {
|
||||
result = append(result, token)
|
||||
}
|
||||
}
|
||||
|
||||
tokens, err := tm.GetCustoms()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, token := range tokens {
|
||||
result = append(result, token)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (tm *Manager) GetTokens(chainID uint64) ([]*Token, error) {
|
||||
tokensMap, ok := tokenStore[chainID]
|
||||
if !ok {
|
||||
|
@ -244,6 +258,16 @@ func (tm *Manager) Toggle(chainID uint64, address common.Address) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (tm *Manager) ToToken(network *params.Network) *Token {
|
||||
return &Token{
|
||||
Address: common.HexToAddress("0x"),
|
||||
Name: network.NativeCurrencyName,
|
||||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
ChainID: network.ChainID,
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *Manager) GetVisible(chainIDs []uint64) (map[uint64][]*Token, error) {
|
||||
customTokens, err := tm.GetCustoms()
|
||||
if err != nil {
|
||||
|
@ -258,13 +282,7 @@ func (tm *Manager) GetVisible(chainIDs []uint64) (map[uint64][]*Token, error) {
|
|||
}
|
||||
|
||||
rst[chainID] = make([]*Token, 0)
|
||||
rst[chainID] = append(rst[chainID], &Token{
|
||||
Address: common.HexToAddress("0x"),
|
||||
Name: network.NativeCurrencyName,
|
||||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
ChainID: chainID,
|
||||
})
|
||||
rst[chainID] = append(rst[chainID], tm.ToToken(network))
|
||||
}
|
||||
rows, err := tm.db.Query("SELECT chain_id, address FROM visible_tokens")
|
||||
if err != nil {
|
||||
|
@ -475,3 +493,133 @@ func (tm *Manager) GetBalances(parent context.Context, clients []*chain.Client,
|
|||
}
|
||||
return response, group.Error()
|
||||
}
|
||||
|
||||
func (tm *Manager) GetBalancesByChain(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||
var (
|
||||
group = async.NewAtomicGroup(parent)
|
||||
mu sync.Mutex
|
||||
response = map[uint64]map[common.Address]map[common.Address]*hexutil.Big{}
|
||||
)
|
||||
|
||||
updateBalance := func(chainID uint64, account common.Address, token common.Address, balance *big.Int) {
|
||||
mu.Lock()
|
||||
if _, ok := response[chainID]; !ok {
|
||||
response[chainID] = map[common.Address]map[common.Address]*hexutil.Big{}
|
||||
}
|
||||
|
||||
if _, ok := response[chainID][account]; !ok {
|
||||
response[chainID][account] = map[common.Address]*hexutil.Big{}
|
||||
}
|
||||
|
||||
if _, ok := response[chainID][account][token]; !ok {
|
||||
zeroHex := hexutil.Big(*big.NewInt(0))
|
||||
response[chainID][account][token] = &zeroHex
|
||||
}
|
||||
sum := big.NewInt(0).Add(response[chainID][account][token].ToInt(), balance)
|
||||
sumHex := hexutil.Big(*sum)
|
||||
response[chainID][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(client.ChainID, 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(client.ChainID, 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(client.ChainID, account, token, balance)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
select {
|
||||
case <-group.WaitAsync():
|
||||
case <-parent.Done():
|
||||
return nil, parent.Err()
|
||||
}
|
||||
return response, group.Error()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue