Scanning of ERC20 tail of transfers history
This commit is contained in:
parent
7d03ae8272
commit
382fcde74e
|
@ -165,29 +165,36 @@ func (c *ContractMaker) NewDirectory(chainID uint64) (*directory.Directory, erro
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContractMaker) NewEthScan(chainID uint64) (*ethscan.BalanceScanner, error) {
|
func (c *ContractMaker) NewEthScan(chainID uint64) (*ethscan.BalanceScanner, uint, error) {
|
||||||
contractAddr, err := ethscan.ContractAddress(chainID)
|
contractAddr, err := ethscan.ContractAddress(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contractCreatedAt, err := ethscan.ContractCreatedAt(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
backend, err := c.RPCClient.EthClient(chainID)
|
backend, err := c.RPCClient.EthClient(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
bytecode, err := backend.CodeAt(context.Background(), contractAddr, nil)
|
bytecode, err := backend.CodeAt(context.Background(), contractAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bytecode) == 0 {
|
if len(bytecode) == 0 {
|
||||||
return nil, errors.New("is not a contract")
|
return nil, 0, errors.New("is not a contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
return ethscan.NewBalanceScanner(
|
scanner, err := ethscan.NewBalanceScanner(
|
||||||
contractAddr,
|
contractAddr,
|
||||||
backend,
|
backend,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return scanner, contractCreatedAt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContractMaker) NewHopL2SaddlSwap(chainID uint64, symbol string) (*hopSwap.HopSwap, error) {
|
func (c *ContractMaker) NewHopL2SaddlSwap(chainID uint64, symbol string) (*hopSwap.HopSwap, error) {
|
||||||
|
|
|
@ -8,20 +8,34 @@ import (
|
||||||
|
|
||||||
var errorNotAvailableOnChainID = errors.New("not available for chainID")
|
var errorNotAvailableOnChainID = errors.New("not available for chainID")
|
||||||
|
|
||||||
var contractAddressByChainID = map[uint64]common.Address{
|
type ContractData struct {
|
||||||
1: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // mainnet
|
Address common.Address
|
||||||
5: common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), // goerli
|
CreatedAtBlock uint
|
||||||
10: common.HexToAddress("0x9e5076df494fc949abc4461f4e57592b81517d81"), // optimism
|
}
|
||||||
420: common.HexToAddress("0xf532c75239fa61b66d31e73f44300c46da41aadd"), // goerli optimism
|
|
||||||
42161: common.HexToAddress("0xbb85398092b83a016935a17fc857507b7851a071"), // arbitrum
|
var contractDataByChainID = map[uint64]ContractData{
|
||||||
421613: common.HexToAddress("0xec21ebe1918e8975fc0cd0c7747d318c00c0acd5"), // goerli arbitrum
|
1: {common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), 12_194_222}, // mainnet
|
||||||
11155111: common.HexToAddress("0xec21ebe1918e8975fc0cd0c7747d318c00c0acd5"), // sepolia
|
5: {common.HexToAddress("0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"), 4_578_854}, // goerli
|
||||||
|
10: {common.HexToAddress("0x9e5076df494fc949abc4461f4e57592b81517d81"), 34_421_097}, // optimism
|
||||||
|
420: {common.HexToAddress("0xf532c75239fa61b66d31e73f44300c46da41aadd"), 2_236_534}, // goerli optimism
|
||||||
|
42161: {common.HexToAddress("0xbb85398092b83a016935a17fc857507b7851a071"), 70_031_945}, // arbitrum
|
||||||
|
421613: {common.HexToAddress("0xec21ebe1918e8975fc0cd0c7747d318c00c0acd5"), 818_155}, // goerli arbitrum
|
||||||
|
777333: {common.HexToAddress("0x0000000000000000000000000000000000777333"), 50}, // unit tests
|
||||||
|
11155111: {common.HexToAddress("0xec21ebe1918e8975fc0cd0c7747d318c00c0acd5"), 4_366_506}, // sepolia
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContractAddress(chainID uint64) (common.Address, error) {
|
func ContractAddress(chainID uint64) (common.Address, error) {
|
||||||
addr, exists := contractAddressByChainID[chainID]
|
contract, exists := contractDataByChainID[chainID]
|
||||||
if !exists {
|
if !exists {
|
||||||
return *new(common.Address), errorNotAvailableOnChainID
|
return *new(common.Address), errorNotAvailableOnChainID
|
||||||
}
|
}
|
||||||
return addr, nil
|
return contract.Address, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContractCreatedAt(chainID uint64) (uint, error) {
|
||||||
|
contract, exists := contractDataByChainID[chainID]
|
||||||
|
if !exists {
|
||||||
|
return 0, errorNotAvailableOnChainID
|
||||||
|
}
|
||||||
|
return contract.CreatedAtBlock, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,19 @@ type ClientInterface interface {
|
||||||
GetBaseFeeFromBlock(blockNumber *big.Int) (string, error)
|
GetBaseFeeFromBlock(blockNumber *big.Int) (string, error)
|
||||||
NetworkID() uint64
|
NetworkID() uint64
|
||||||
ToBigInt() *big.Int
|
ToBigInt() *big.Int
|
||||||
|
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
|
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||||
|
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
|
||||||
|
GetWalletNotifier() func(chainId uint64, message string)
|
||||||
|
SetWalletNotifier(notifier func(chainId uint64, message string))
|
||||||
|
TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error)
|
||||||
|
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
|
||||||
|
BlockNumber(ctx context.Context) (uint64, error)
|
||||||
|
SetIsConnected(value bool)
|
||||||
|
GetIsConnected() bool
|
||||||
|
bind.ContractCaller
|
||||||
|
bind.ContractTransactor
|
||||||
|
bind.ContractFilterer
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientWithFallback struct {
|
type ClientWithFallback struct {
|
||||||
|
@ -169,6 +182,12 @@ func (c *ClientWithFallback) SetIsConnected(value bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientWithFallback) GetIsConnected() bool {
|
||||||
|
c.IsConnectedLock.RLock()
|
||||||
|
defer c.IsConnectedLock.RUnlock()
|
||||||
|
return c.IsConnected
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientWithFallback) makeCallNoReturn(main func() error, fallback func() error) error {
|
func (c *ClientWithFallback) makeCallNoReturn(main func() error, fallback func() error) error {
|
||||||
resultChan := make(chan CommandResult, 1)
|
resultChan := make(chan CommandResult, 1)
|
||||||
c.LastCheckedAt = time.Now().Unix()
|
c.LastCheckedAt = time.Now().Unix()
|
||||||
|
@ -877,3 +896,11 @@ func (c *ClientWithFallback) FullTransactionByBlockNumberAndIndex(ctx context.Co
|
||||||
|
|
||||||
return tx.(*FullTransaction), nil
|
return tx.(*FullTransaction), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientWithFallback) GetWalletNotifier() func(chainId uint64, message string) {
|
||||||
|
return c.WalletNotifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientWithFallback) SetWalletNotifier(notifier func(chainId uint64, message string)) {
|
||||||
|
c.WalletNotifier = notifier
|
||||||
|
}
|
||||||
|
|
|
@ -48,9 +48,9 @@ type Client struct {
|
||||||
UpstreamChainID uint64
|
UpstreamChainID uint64
|
||||||
|
|
||||||
local *gethrpc.Client
|
local *gethrpc.Client
|
||||||
upstream *chain.ClientWithFallback
|
upstream chain.ClientInterface
|
||||||
rpcClientsMutex sync.RWMutex
|
rpcClientsMutex sync.RWMutex
|
||||||
rpcClients map[uint64]*chain.ClientWithFallback
|
rpcClients map[uint64]chain.ClientInterface
|
||||||
|
|
||||||
router *router
|
router *router
|
||||||
NetworkManager *network.Manager
|
NetworkManager *network.Manager
|
||||||
|
@ -84,7 +84,7 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
||||||
local: client,
|
local: client,
|
||||||
NetworkManager: networkManager,
|
NetworkManager: networkManager,
|
||||||
handlers: make(map[string]Handler),
|
handlers: make(map[string]Handler),
|
||||||
rpcClients: make(map[uint64]*chain.ClientWithFallback),
|
rpcClients: make(map[uint64]chain.ClientInterface),
|
||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,12 +112,12 @@ func (c *Client) SetWalletNotifier(notifier func(chainID uint64, message string)
|
||||||
c.walletNotifier = notifier
|
c.walletNotifier = notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getClientUsingCache(chainID uint64) (*chain.ClientWithFallback, error) {
|
func (c *Client) getClientUsingCache(chainID uint64) (chain.ClientInterface, error) {
|
||||||
c.rpcClientsMutex.Lock()
|
c.rpcClientsMutex.Lock()
|
||||||
defer c.rpcClientsMutex.Unlock()
|
defer c.rpcClientsMutex.Unlock()
|
||||||
if rpcClient, ok := c.rpcClients[chainID]; ok {
|
if rpcClient, ok := c.rpcClients[chainID]; ok {
|
||||||
if rpcClient.WalletNotifier == nil {
|
if rpcClient.GetWalletNotifier() == nil {
|
||||||
rpcClient.WalletNotifier = c.walletNotifier
|
rpcClient.SetWalletNotifier(c.walletNotifier)
|
||||||
}
|
}
|
||||||
return rpcClient, nil
|
return rpcClient, nil
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ func (c *Client) getClientUsingCache(chainID uint64) (*chain.ClientWithFallback,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethclient returns ethclient.Client per chain
|
// Ethclient returns ethclient.Client per chain
|
||||||
func (c *Client) EthClient(chainID uint64) (*chain.ClientWithFallback, error) {
|
func (c *Client) EthClient(chainID uint64) (chain.ClientInterface, error) {
|
||||||
client, err := c.getClientUsingCache(chainID)
|
client, err := c.getClientUsingCache(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -169,8 +169,8 @@ func (c *Client) AbstractEthClient(chainID common.ChainID) (chain.BatchCallClien
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) EthClients(chainIDs []uint64) (map[uint64]*chain.ClientWithFallback, error) {
|
func (c *Client) EthClients(chainIDs []uint64) (map[uint64]chain.ClientInterface, error) {
|
||||||
clients := make(map[uint64]*chain.ClientWithFallback, 0)
|
clients := make(map[uint64]chain.ClientInterface, 0)
|
||||||
for _, chainID := range chainIDs {
|
for _, chainID := range chainIDs {
|
||||||
client, err := c.getClientUsingCache(chainID)
|
client, err := c.getClientUsingCache(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -182,6 +182,13 @@ func (c *Client) EthClients(chainIDs []uint64) (map[uint64]*chain.ClientWithFall
|
||||||
return clients, nil
|
return clients, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetClient strictly for testing purposes
|
||||||
|
func (c *Client) SetClient(chainID uint64, client chain.ClientInterface) {
|
||||||
|
c.rpcClientsMutex.Lock()
|
||||||
|
defer c.rpcClientsMutex.Unlock()
|
||||||
|
c.rpcClients[chainID] = client
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateUpstreamURL changes the upstream RPC client URL, if the upstream is enabled.
|
// UpdateUpstreamURL changes the upstream RPC client URL, if the upstream is enabled.
|
||||||
func (c *Client) UpdateUpstreamURL(url string) error {
|
func (c *Client) UpdateUpstreamURL(url string) error {
|
||||||
if c.upstream == nil {
|
if c.upstream == nil {
|
||||||
|
|
|
@ -150,7 +150,7 @@ func (s *Service) isTokenVisible(tokenSymbol string) bool {
|
||||||
|
|
||||||
// Native token implementation of DataSource interface
|
// Native token implementation of DataSource interface
|
||||||
type chainClientSource struct {
|
type chainClientSource struct {
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
currency string
|
currency string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ func (src *chainClientSource) BalanceAt(ctx context.Context, account common.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *chainClientSource) ChainID() uint64 {
|
func (src *chainClientSource) ChainID() uint64 {
|
||||||
return src.chainClient.ChainID
|
return src.chainClient.NetworkID()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *chainClientSource) Currency() string {
|
func (src *chainClientSource) Currency() string {
|
||||||
|
@ -184,7 +184,7 @@ type tokenChainClientSource struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *tokenChainClientSource) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
func (src *tokenChainClientSource) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
network := src.NetworkManager.Find(src.chainClient.ChainID)
|
network := src.NetworkManager.Find(src.chainClient.NetworkID())
|
||||||
if network == nil {
|
if network == nil {
|
||||||
return nil, errors.New("network not found")
|
return nil, errors.New("network not found")
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,7 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||||
}
|
}
|
||||||
hasError := false
|
hasError := false
|
||||||
if client, ok := clients[token.ChainID]; ok {
|
if client, ok := clients[token.ChainID]; ok {
|
||||||
hasError = err != nil || !client.IsConnected
|
hasError = err != nil || !client.GetIsConnected()
|
||||||
}
|
}
|
||||||
if !anyPositiveBalance {
|
if !anyPositiveBalance {
|
||||||
anyPositiveBalance = balance.Cmp(big.NewFloat(0.0)) > 0
|
anyPositiveBalance = balance.Cmp(big.NewFloat(0.0)) > 0
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"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"
|
||||||
|
"github.com/status-im/status-go/contracts/ethscan"
|
||||||
"github.com/status-im/status-go/contracts/ierc20"
|
"github.com/status-im/status-go/contracts/ierc20"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
|
@ -153,7 +154,7 @@ func (tm *Manager) getAddressTokenMap(chainID uint64) (addressTokenMap, bool) {
|
||||||
return tokenMap, chainPresent
|
return tokenMap, chainPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) setTokens(tokens []*Token) {
|
func (tm *Manager) SetTokens(tokens []*Token) {
|
||||||
tm.tokenLock.Lock()
|
tm.tokenLock.Lock()
|
||||||
defer tm.tokenLock.Unlock()
|
defer tm.tokenLock.Unlock()
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ func (tm *Manager) fetchTokens() {
|
||||||
tokenList = mergeTokenLists([][]*Token{tokenList, validTokens})
|
tokenList = mergeTokenLists([][]*Token{tokenList, validTokens})
|
||||||
}
|
}
|
||||||
|
|
||||||
tm.setTokens(tokenList)
|
tm.SetTokens(tokenList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) getFullTokenList(chainID uint64) []*Token {
|
func (tm *Manager) getFullTokenList(chainID uint64) []*Token {
|
||||||
|
@ -577,7 +578,7 @@ func (tm *Manager) DeleteCustom(chainID uint64, address common.Address) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
|
func (tm *Manager) GetTokenBalance(ctx context.Context, client chain.ClientInterface, account common.Address, token common.Address) (*big.Int, error) {
|
||||||
caller, err := ierc20.NewIERC20Caller(token, client)
|
caller, err := ierc20.NewIERC20Caller(token, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -588,23 +589,32 @@ func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.ClientWith
|
||||||
}, account)
|
}, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
|
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client chain.ClientInterface, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
caller, err := ierc20.NewIERC20Caller(token, client)
|
caller, err := ierc20.NewIERC20Caller(token, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return caller.BalanceOf(&bind.CallOpts{
|
balance, err := caller.BalanceOf(&bind.CallOpts{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
BlockNumber: blockNumber,
|
BlockNumber: blockNumber,
|
||||||
}, account)
|
}, account)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err != bind.ErrNoCode {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
balance = big.NewInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return balance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetChainBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address) (*big.Int, error) {
|
func (tm *Manager) GetChainBalance(ctx context.Context, client chain.ClientInterface, account common.Address) (*big.Int, error) {
|
||||||
return client.BalanceAt(ctx, account, nil)
|
return client.BalanceAt(ctx, account, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
|
func (tm *Manager) GetBalance(ctx context.Context, client chain.ClientInterface, account common.Address, token common.Address) (*big.Int, error) {
|
||||||
if token == nativeChainAddress {
|
if token == nativeChainAddress {
|
||||||
return tm.GetChainBalance(ctx, client, account)
|
return tm.GetChainBalance(ctx, client, account)
|
||||||
}
|
}
|
||||||
|
@ -612,7 +622,7 @@ func (tm *Manager) GetBalance(ctx context.Context, client *chain.ClientWithFallb
|
||||||
return tm.GetTokenBalance(ctx, client, account, token)
|
return tm.GetTokenBalance(ctx, client, account, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain.ClientWithFallback, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||||
var (
|
var (
|
||||||
group = async.NewAtomicGroup(parent)
|
group = async.NewAtomicGroup(parent)
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -638,7 +648,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
for clientIdx := range clients {
|
for clientIdx := range clients {
|
||||||
client := clients[clientIdx]
|
client := clients[clientIdx]
|
||||||
|
|
||||||
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
ethScanContract, _, err := tm.contractMaker.NewEthScan(client.NetworkID())
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fetchChainBalance := false
|
fetchChainBalance := false
|
||||||
|
@ -666,7 +676,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}, accounts)
|
}, accounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("can't fetch chain balance", err)
|
log.Error("can't fetch chain balance 2", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for idx, account := range accounts {
|
for idx, account := range accounts {
|
||||||
|
@ -690,7 +700,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}, account, chunk)
|
}, account, chunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("can't fetch erc20 token balance", "account", account, "error", err)
|
log.Error("can't fetch erc20 token balance 3", "account", account, "error", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,7 +723,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
account := accounts[accountIdx]
|
account := accounts[accountIdx]
|
||||||
token := tokens[tokenIdx]
|
token := tokens[tokenIdx]
|
||||||
client := clients[clientIdx]
|
client := clients[clientIdx]
|
||||||
if !tm.inStore(token, client.ChainID) {
|
if !tm.inStore(token, client.NetworkID()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
group.Add(func(parent context.Context) error {
|
group.Add(func(parent context.Context) error {
|
||||||
|
@ -722,7 +732,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
balance, err := tm.GetBalance(ctx, client, account, token)
|
balance, err := tm.GetBalance(ctx, client, account, token)
|
||||||
|
|
||||||
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 erc20 token balance 4", "account", account, "token", token, "error", err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -742,7 +752,11 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
return response, group.Error()
|
return response, group.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64]*chain.ClientWithFallback, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||||
|
return tm.GetBalancesAtByChain(parent, clients, accounts, tokens, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *Manager) GetBalancesAtByChain(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address, atBlocks map[uint64]*big.Int) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||||
var (
|
var (
|
||||||
group = async.NewAtomicGroup(parent)
|
group = async.NewAtomicGroup(parent)
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -769,14 +783,15 @@ func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
for clientIdx := range clients {
|
for _, client := range clients {
|
||||||
client := clients[clientIdx]
|
ethScanContract, availableAtBlock, err := tm.contractMaker.NewEthScan(client.NetworkID())
|
||||||
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error scanning contract", "err", err)
|
log.Error("error scanning contract", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atBlock := atBlocks[client.NetworkID()]
|
||||||
|
|
||||||
fetchChainBalance := false
|
fetchChainBalance := false
|
||||||
var tokenChunks [][]common.Address
|
var tokenChunks [][]common.Address
|
||||||
chunkSize := 500
|
chunkSize := 500
|
||||||
|
@ -799,16 +814,17 @@ func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64
|
||||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
res, err := ethScanContract.EtherBalances(&bind.CallOpts{
|
res, err := ethScanContract.EtherBalances(&bind.CallOpts{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
BlockNumber: atBlock,
|
||||||
}, accounts)
|
}, accounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("can't fetch chain balance", err)
|
log.Error("can't fetch chain balance 5", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for idx, account := range accounts {
|
for idx, account := range accounts {
|
||||||
balance := new(big.Int)
|
balance := new(big.Int)
|
||||||
balance.SetBytes(res[idx].Data)
|
balance.SetBytes(res[idx].Data)
|
||||||
updateBalance(client.ChainID, account, common.HexToAddress("0x"), balance)
|
updateBalance(client.NetworkID(), account, common.HexToAddress("0x"), balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -822,28 +838,46 @@ func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64
|
||||||
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()
|
||||||
res, err := ethScanContract.TokensBalance(&bind.CallOpts{
|
var res []ethscan.BalanceScannerResult
|
||||||
Context: ctx,
|
if atBlock == nil || big.NewInt(int64(availableAtBlock)).Cmp(atBlock) < 0 {
|
||||||
}, account, chunk)
|
res, err = ethScanContract.TokensBalance(&bind.CallOpts{
|
||||||
if err != nil {
|
Context: ctx,
|
||||||
log.Error("can't fetch erc20 token balance", "account", account, "error", err)
|
BlockNumber: atBlock,
|
||||||
return nil
|
}, account, chunk)
|
||||||
}
|
if err != nil {
|
||||||
|
log.Error("can't fetch erc20 token balance 6", "account", account, "error", err)
|
||||||
if len(res) != len(chunk) {
|
return nil
|
||||||
log.Error("can't fetch erc20 token balance", "account", account, "error response not complete")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, token := range chunk {
|
|
||||||
|
|
||||||
if !res[idx].Success {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
balance := new(big.Int)
|
|
||||||
balance.SetBytes(res[idx].Data)
|
if len(res) != len(chunk) {
|
||||||
updateBalance(client.ChainID, account, token, balance)
|
log.Error("can't fetch erc20 token balance 7", "account", account, "error response not complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, token := range chunk {
|
||||||
|
|
||||||
|
if !res[idx].Success {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
balance := new(big.Int)
|
||||||
|
balance.SetBytes(res[idx].Data)
|
||||||
|
updateBalance(client.NetworkID(), account, token, balance)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, token := range chunk {
|
||||||
|
balance, err := tm.GetTokenBalanceAt(ctx, client, account, token, atBlock)
|
||||||
|
if err != nil {
|
||||||
|
if err != bind.ErrNoCode {
|
||||||
|
log.Error("can't fetch erc20 token balance 8", "account", account, "token", token, "error on fetching token balance")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateBalance(client.NetworkID(), account, token, balance)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ func upsertHopBridgeDestinationTx(ctx context.Context, transactionManager *Trans
|
||||||
return multiTx, nil
|
return multiTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHopBridgeMultitransaction(ctx context.Context, client *chain.ClientWithFallback, transactionManager *TransactionManager, tokenManager *token.Manager, subTx *Transfer) (*MultiTransaction, error) {
|
func buildHopBridgeMultitransaction(ctx context.Context, client chain.ClientInterface, transactionManager *TransactionManager, tokenManager *token.Manager, subTx *Transfer) (*MultiTransaction, error) {
|
||||||
// Identify if it's from/to transaction
|
// Identify if it's from/to transaction
|
||||||
switch w_common.GetEventType(subTx.Log) {
|
switch w_common.GetEventType(subTx.Log) {
|
||||||
case w_common.HopBridgeTransferSentToL2EventType:
|
case w_common.HopBridgeTransferSentToL2EventType:
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (c *ethHistoricalCommand) Command() async.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ethHistoricalCommand) Run(ctx context.Context) (err error) {
|
func (c *ethHistoricalCommand) Run(ctx context.Context) (err error) {
|
||||||
log.Info("eth historical downloader start", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
log.Debug("eth historical downloader start", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
||||||
"from", c.from.Number, "to", c.to, "noLimit", c.noLimit)
|
"from", c.from.Number, "to", c.to, "noLimit", c.noLimit)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -101,7 +101,7 @@ func (c *ethHistoricalCommand) Run(ctx context.Context) (err error) {
|
||||||
c.resultingFrom = from
|
c.resultingFrom = from
|
||||||
c.startBlock = startBlock
|
c.startBlock = startBlock
|
||||||
|
|
||||||
log.Info("eth historical downloader finished successfully", "chain", c.chainClient.NetworkID(),
|
log.Debug("eth historical downloader finished successfully", "chain", c.chainClient.NetworkID(),
|
||||||
"address", c.address, "from", from, "to", c.to, "total blocks", len(headers), "time", time.Since(start))
|
"address", c.address, "from", from, "to", c.to, "total blocks", len(headers), "time", time.Since(start))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -147,7 +147,7 @@ func getErc20BatchSize(chainID uint64) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
|
func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
|
||||||
log.Info("wallet historical downloader for erc20 transfers start", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
log.Debug("wallet historical downloader for erc20 transfers start", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
||||||
"from", c.from, "to", c.to)
|
"from", c.from, "to", c.to)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -168,7 +168,7 @@ func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
|
||||||
}
|
}
|
||||||
c.foundHeaders = append(c.foundHeaders, headers...)
|
c.foundHeaders = append(c.foundHeaders, headers...)
|
||||||
}
|
}
|
||||||
log.Info("wallet historical downloader for erc20 transfers finished", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
log.Debug("wallet historical downloader for erc20 transfers finished", "chainID", c.chainClient.NetworkID(), "address", c.address,
|
||||||
"from", c.from, "to", c.to, "time", time.Since(start), "headers", len(c.foundHeaders))
|
"from", c.from, "to", c.to, "time", time.Since(start), "headers", len(c.foundHeaders))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ type controlCommand struct {
|
||||||
blockDAO *BlockDAO
|
blockDAO *BlockDAO
|
||||||
eth *ETHDownloader
|
eth *ETHDownloader
|
||||||
erc20 *ERC20TransfersDownloader
|
erc20 *ERC20TransfersDownloader
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
feed *event.Feed
|
feed *event.Feed
|
||||||
errorsCount int
|
errorsCount int
|
||||||
nonArchivalRPCNode bool
|
nonArchivalRPCNode bool
|
||||||
|
@ -365,7 +365,7 @@ type transfersCommand struct {
|
||||||
eth *ETHDownloader
|
eth *ETHDownloader
|
||||||
blockNums []*big.Int
|
blockNums []*big.Int
|
||||||
address common.Address
|
address common.Address
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
blocksLimit int
|
blocksLimit int
|
||||||
transactionManager *TransactionManager
|
transactionManager *TransactionManager
|
||||||
pendingTxManager *transactions.PendingTxTracker
|
pendingTxManager *transactions.PendingTxTracker
|
||||||
|
@ -625,7 +625,7 @@ type loadTransfersCommand struct {
|
||||||
accounts []common.Address
|
accounts []common.Address
|
||||||
db *Database
|
db *Database
|
||||||
blockDAO *BlockDAO
|
blockDAO *BlockDAO
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
blocksByAddress map[common.Address][]*big.Int
|
blocksByAddress map[common.Address][]*big.Int
|
||||||
transactionManager *TransactionManager
|
transactionManager *TransactionManager
|
||||||
pendingTxManager *transactions.PendingTxTracker
|
pendingTxManager *transactions.PendingTxTracker
|
||||||
|
@ -655,7 +655,7 @@ type findAndCheckBlockRangeCommand struct {
|
||||||
accounts []common.Address
|
accounts []common.Address
|
||||||
db *Database
|
db *Database
|
||||||
blockDAO *BlockDAO
|
blockDAO *BlockDAO
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
balanceCacher balance.Cacher
|
balanceCacher balance.Cacher
|
||||||
feed *event.Feed
|
feed *event.Feed
|
||||||
fromByAddress map[common.Address]*Block
|
fromByAddress map[common.Address]*Block
|
||||||
|
@ -818,11 +818,11 @@ func (c *findAndCheckBlockRangeCommand) fastIndexErc20(ctx context.Context, from
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTransfers(ctx context.Context, accounts []common.Address, blockDAO *BlockDAO, db *Database,
|
func loadTransfers(ctx context.Context, accounts []common.Address, blockDAO *BlockDAO, db *Database,
|
||||||
chainClient *chain.ClientWithFallback, blocksLimitPerAccount int, blocksByAddress map[common.Address][]*big.Int,
|
chainClient chain.ClientInterface, blocksLimitPerAccount int, blocksByAddress map[common.Address][]*big.Int,
|
||||||
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
||||||
tokenManager *token.Manager, feed *event.Feed) error {
|
tokenManager *token.Manager, feed *event.Feed) error {
|
||||||
|
|
||||||
log.Info("loadTransfers start", "accounts", accounts, "chain", chainClient.ChainID, "limit", blocksLimitPerAccount)
|
log.Info("loadTransfers start", "accounts", accounts, "chain", chainClient.NetworkID(), "limit", blocksLimitPerAccount)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
group := async.NewGroup(ctx)
|
group := async.NewGroup(ctx)
|
||||||
|
@ -852,7 +852,7 @@ func loadTransfers(ctx context.Context, accounts []common.Address, blockDAO *Blo
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case <-group.WaitAsync():
|
case <-group.WaitAsync():
|
||||||
log.Info("loadTransfers finished for account", "in", time.Since(start), "chain", chainClient.ChainID)
|
log.Info("loadTransfers finished for account", "in", time.Since(start), "chain", chainClient.NetworkID())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -871,10 +871,10 @@ func getLowestFrom(chainID uint64, to *big.Int) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the latest range up to initialTo where the number of transactions is between 20 and 25
|
// Finds the latest range up to initialTo where the number of transactions is between 20 and 25
|
||||||
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (*big.Int, error) {
|
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client chain.ClientInterface) (*big.Int, error) {
|
||||||
log.Info("findFirstRange", "account", account, "initialTo", initialTo, "client", client)
|
log.Info("findFirstRange", "account", account, "initialTo", initialTo, "client", client)
|
||||||
|
|
||||||
from := getLowestFrom(client.ChainID, initialTo)
|
from := getLowestFrom(client.NetworkID(), initialTo)
|
||||||
to := initialTo
|
to := initialTo
|
||||||
goal := uint64(20)
|
goal := uint64(20)
|
||||||
|
|
||||||
|
@ -930,7 +930,7 @@ func findFirstRange(c context.Context, account common.Address, initialTo *big.In
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the latest ranges up to initialTo where the number of transactions is between 20 and 25
|
// Finds the latest ranges up to initialTo where the number of transactions is between 20 and 25
|
||||||
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (map[common.Address]*big.Int, error) {
|
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client chain.ClientInterface) (map[common.Address]*big.Int, error) {
|
||||||
res := map[common.Address]*big.Int{}
|
res := map[common.Address]*big.Int{}
|
||||||
|
|
||||||
for _, address := range accounts {
|
for _, address := range accounts {
|
||||||
|
|
|
@ -66,22 +66,25 @@ func (c *findNewBlocksCommand) Run(parent context.Context) (err error) {
|
||||||
|
|
||||||
// TODO NewFindBlocksCommand
|
// TODO NewFindBlocksCommand
|
||||||
type findBlocksCommand struct {
|
type findBlocksCommand struct {
|
||||||
account common.Address
|
account common.Address
|
||||||
db *Database
|
db *Database
|
||||||
blockRangeDAO *BlockRangeSequentialDAO
|
blockRangeDAO *BlockRangeSequentialDAO
|
||||||
chainClient chain.ClientInterface
|
chainClient chain.ClientInterface
|
||||||
balanceCacher balance.Cacher
|
balanceCacher balance.Cacher
|
||||||
feed *event.Feed
|
feed *event.Feed
|
||||||
noLimit bool
|
noLimit bool
|
||||||
transactionManager *TransactionManager
|
transactionManager *TransactionManager
|
||||||
fromBlockNumber *big.Int
|
tokenManager *token.Manager
|
||||||
toBlockNumber *big.Int
|
fromBlockNumber *big.Int
|
||||||
blocksLoadedCh chan<- []*DBHeader
|
toBlockNumber *big.Int
|
||||||
|
blocksLoadedCh chan<- []*DBHeader
|
||||||
|
defaultNodeBlockChunkSize int
|
||||||
|
|
||||||
// Not to be set by the caller
|
// Not to be set by the caller
|
||||||
resFromBlock *Block
|
resFromBlock *Block
|
||||||
startBlockNumber *big.Int
|
startBlockNumber *big.Int
|
||||||
error error
|
reachedETHHistoryStart bool
|
||||||
|
error error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *findBlocksCommand) Command() async.Command {
|
func (c *findBlocksCommand) Command() async.Command {
|
||||||
|
@ -91,10 +94,114 @@ func (c *findBlocksCommand) Command() async.Command {
|
||||||
}.Run
|
}.Run
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
func (c *findBlocksCommand) ERC20ScanByBalance(parent context.Context, fromBlock, toBlock *big.Int, token common.Address) ([]*DBHeader, error) {
|
||||||
log.Debug("start findBlocksCommand", "account", c.account, "chain", c.chainClient.NetworkID(), "noLimit", c.noLimit)
|
var err error
|
||||||
|
batchSize := getErc20BatchSize(c.chainClient.NetworkID())
|
||||||
|
ranges := [][]*big.Int{{fromBlock, toBlock}}
|
||||||
|
foundHeaders := []*DBHeader{}
|
||||||
|
cache := map[int64]*big.Int{}
|
||||||
|
for {
|
||||||
|
nextRanges := [][]*big.Int{}
|
||||||
|
for _, blockRange := range ranges {
|
||||||
|
from, to := blockRange[0], blockRange[1]
|
||||||
|
fromBalance, ok := cache[from.Int64()]
|
||||||
|
if !ok {
|
||||||
|
fromBalance, err = c.tokenManager.GetTokenBalanceAt(parent, c.chainClient, c.account, token, from)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
rangeSize := big.NewInt(DefaultNodeBlockChunkSize)
|
if fromBalance == nil {
|
||||||
|
fromBalance = big.NewInt(0)
|
||||||
|
}
|
||||||
|
cache[from.Int64()] = fromBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
toBalance, ok := cache[to.Int64()]
|
||||||
|
if !ok {
|
||||||
|
toBalance, err = c.tokenManager.GetTokenBalanceAt(parent, c.chainClient, c.account, token, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if toBalance == nil {
|
||||||
|
toBalance = big.NewInt(0)
|
||||||
|
}
|
||||||
|
cache[to.Int64()] = toBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromBalance.Cmp(toBalance) != 0 {
|
||||||
|
diff := new(big.Int).Sub(to, from)
|
||||||
|
if diff.Cmp(batchSize) <= 0 {
|
||||||
|
headers, err := c.fastIndexErc20(parent, from, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
foundHeaders = append(foundHeaders, headers...)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
halfOfDiff := new(big.Int).Div(diff, big.NewInt(2))
|
||||||
|
mid := new(big.Int).Add(from, halfOfDiff)
|
||||||
|
|
||||||
|
nextRanges = append(nextRanges, []*big.Int{from, mid})
|
||||||
|
nextRanges = append(nextRanges, []*big.Int{mid, to})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nextRanges) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges = nextRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundHeaders, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *findBlocksCommand) checkERC20Tail(parent context.Context) ([]*DBHeader, error) {
|
||||||
|
log.Debug("checkERC20Tail", "account", c.account, "to block", c.startBlockNumber, "from", c.resFromBlock.Number)
|
||||||
|
tokens, err := c.tokenManager.GetTokens(c.chainClient.NetworkID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addresses := make([]common.Address, len(tokens))
|
||||||
|
for i, token := range tokens {
|
||||||
|
addresses[i] = token.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
from := new(big.Int).Sub(c.resFromBlock.Number, big.NewInt(1))
|
||||||
|
|
||||||
|
clients := make(map[uint64]chain.ClientInterface, 1)
|
||||||
|
clients[c.chainClient.NetworkID()] = c.chainClient
|
||||||
|
atBlocks := make(map[uint64]*big.Int, 1)
|
||||||
|
atBlocks[c.chainClient.NetworkID()] = from
|
||||||
|
balances, err := c.tokenManager.GetBalancesAtByChain(parent, clients, []common.Address{c.account}, addresses, atBlocks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := []*DBHeader{}
|
||||||
|
for token, balance := range balances[c.chainClient.NetworkID()][c.account] {
|
||||||
|
bigintBalance := big.NewInt(balance.ToInt().Int64())
|
||||||
|
if bigintBalance.Cmp(big.NewInt(0)) <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result, err := c.ERC20ScanByBalance(parent, big.NewInt(0), from, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = append(headers, result...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
||||||
|
log.Debug("start findBlocksCommand", "account", c.account, "chain", c.chainClient.NetworkID(), "noLimit", c.noLimit, "from", c.fromBlockNumber, "to", c.toBlockNumber)
|
||||||
|
|
||||||
|
rangeSize := big.NewInt(int64(c.defaultNodeBlockChunkSize))
|
||||||
|
|
||||||
from, to := new(big.Int).Set(c.fromBlockNumber), new(big.Int).Set(c.toBlockNumber)
|
from, to := new(big.Int).Set(c.fromBlockNumber), new(big.Int).Set(c.toBlockNumber)
|
||||||
|
|
||||||
|
@ -104,7 +211,18 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
headers, _ := c.checkRange(parent, from, to)
|
var headers []*DBHeader
|
||||||
|
if c.reachedETHHistoryStart {
|
||||||
|
if c.fromBlockNumber.Cmp(zero) == 0 && c.startBlockNumber != nil && c.startBlockNumber.Cmp(zero) == 1 {
|
||||||
|
headers, err = c.checkERC20Tail(parent)
|
||||||
|
if err != nil {
|
||||||
|
c.error = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
headers, _ = c.checkRange(parent, from, to)
|
||||||
|
}
|
||||||
|
|
||||||
if c.error != nil {
|
if c.error != nil {
|
||||||
log.Error("findBlocksCommand checkRange", "error", c.error, "account", c.account,
|
log.Error("findBlocksCommand checkRange", "error", c.error, "account", c.account,
|
||||||
"chain", c.chainClient.NetworkID(), "from", from, "to", to)
|
"chain", c.chainClient.NetworkID(), "from", from, "to", to)
|
||||||
|
@ -126,17 +244,32 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) {
|
||||||
c.blocksFound(headers)
|
c.blocksFound(headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.reachedETHHistoryStart {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
err = c.upsertBlockRange(&BlockRange{c.startBlockNumber, c.resFromBlock.Number, to})
|
err = c.upsertBlockRange(&BlockRange{c.startBlockNumber, c.resFromBlock.Number, to})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
from, to = nextRange(c.resFromBlock.Number, c.fromBlockNumber)
|
if from.Cmp(to) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFrom, nextTo := nextRange(c.defaultNodeBlockChunkSize, c.resFromBlock.Number, c.fromBlockNumber)
|
||||||
|
|
||||||
|
if nextFrom.Cmp(from) == 0 && nextTo.Cmp(to) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
from = nextFrom
|
||||||
|
to = nextTo
|
||||||
|
|
||||||
if to.Cmp(c.fromBlockNumber) <= 0 || (c.startBlockNumber != nil &&
|
if to.Cmp(c.fromBlockNumber) <= 0 || (c.startBlockNumber != nil &&
|
||||||
c.startBlockNumber.Cmp(big.NewInt(0)) > 0 && to.Cmp(c.startBlockNumber) <= 0) {
|
c.startBlockNumber.Cmp(big.NewInt(0)) > 0 && to.Cmp(c.startBlockNumber) <= 0) {
|
||||||
log.Debug("Checked all ranges, stop execution", "startBlock", c.startBlockNumber, "from", from, "to", to)
|
log.Debug("Checked all ranges, stop execution", "startBlock", c.startBlockNumber, "from", from, "to", to)
|
||||||
break
|
c.reachedETHHistoryStart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,18 +452,18 @@ func (c *findBlocksCommand) fastIndexErc20(ctx context.Context, fromBlockNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTransfersLoop(ctx context.Context, account common.Address, blockDAO *BlockDAO, db *Database,
|
func loadTransfersLoop(ctx context.Context, account common.Address, blockDAO *BlockDAO, db *Database,
|
||||||
chainClient *chain.ClientWithFallback, transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
chainClient chain.ClientInterface, transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
||||||
tokenManager *token.Manager, feed *event.Feed, blocksLoadedCh <-chan []*DBHeader) {
|
tokenManager *token.Manager, feed *event.Feed, blocksLoadedCh <-chan []*DBHeader) {
|
||||||
|
|
||||||
log.Debug("loadTransfersLoop start", "chain", chainClient.ChainID, "account", account)
|
log.Debug("loadTransfersLoop start", "chain", chainClient.NetworkID(), "account", account)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Info("loadTransfersLoop error", "chain", chainClient.ChainID, "account", account, "error", ctx.Err())
|
log.Info("loadTransfersLoop error", "chain", chainClient.NetworkID(), "account", account, "error", ctx.Err())
|
||||||
return
|
return
|
||||||
case dbHeaders := <-blocksLoadedCh:
|
case dbHeaders := <-blocksLoadedCh:
|
||||||
log.Debug("loadTransfersOnDemand transfers received", "chain", chainClient.ChainID, "account", account, "headers", len(dbHeaders))
|
log.Debug("loadTransfersOnDemand transfers received", "chain", chainClient.NetworkID(), "account", account, "headers", len(dbHeaders))
|
||||||
|
|
||||||
blockNums := make([]*big.Int, len(dbHeaders))
|
blockNums := make([]*big.Int, len(dbHeaders))
|
||||||
for i, dbHeader := range dbHeaders {
|
for i, dbHeader := range dbHeaders {
|
||||||
|
@ -347,7 +480,7 @@ func loadTransfersLoop(ctx context.Context, account common.Address, blockDAO *Bl
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLoadBlocksAndTransfersCommand(account common.Address, db *Database,
|
func newLoadBlocksAndTransfersCommand(account common.Address, db *Database,
|
||||||
blockDAO *BlockDAO, chainClient *chain.ClientWithFallback, feed *event.Feed,
|
blockDAO *BlockDAO, chainClient chain.ClientInterface, feed *event.Feed,
|
||||||
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
||||||
tokenManager *token.Manager, balanceCacher balance.Cacher) *loadBlocksAndTransfersCommand {
|
tokenManager *token.Manager, balanceCacher balance.Cacher) *loadBlocksAndTransfersCommand {
|
||||||
|
|
||||||
|
@ -372,7 +505,7 @@ type loadBlocksAndTransfersCommand struct {
|
||||||
db *Database
|
db *Database
|
||||||
blockRangeDAO *BlockRangeSequentialDAO
|
blockRangeDAO *BlockRangeSequentialDAO
|
||||||
blockDAO *BlockDAO
|
blockDAO *BlockDAO
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
feed *event.Feed
|
feed *event.Feed
|
||||||
balanceCacher balance.Cacher
|
balanceCacher balance.Cacher
|
||||||
errorsCount int
|
errorsCount int
|
||||||
|
@ -455,17 +588,19 @@ func (c *loadBlocksAndTransfersCommand) fetchHistoryBlocks(ctx context.Context,
|
||||||
|
|
||||||
if !allHistoryLoaded {
|
if !allHistoryLoaded {
|
||||||
fbc := &findBlocksCommand{
|
fbc := &findBlocksCommand{
|
||||||
account: c.account,
|
account: c.account,
|
||||||
db: c.db,
|
db: c.db,
|
||||||
blockRangeDAO: c.blockRangeDAO,
|
blockRangeDAO: c.blockRangeDAO,
|
||||||
chainClient: c.chainClient,
|
chainClient: c.chainClient,
|
||||||
balanceCacher: c.balanceCacher,
|
balanceCacher: c.balanceCacher,
|
||||||
feed: c.feed,
|
feed: c.feed,
|
||||||
noLimit: false,
|
noLimit: false,
|
||||||
fromBlockNumber: big.NewInt(0),
|
fromBlockNumber: big.NewInt(0),
|
||||||
toBlockNumber: to,
|
toBlockNumber: to,
|
||||||
transactionManager: c.transactionManager,
|
transactionManager: c.transactionManager,
|
||||||
blocksLoadedCh: blocksLoadedCh,
|
tokenManager: c.tokenManager,
|
||||||
|
blocksLoadedCh: blocksLoadedCh,
|
||||||
|
defaultNodeBlockChunkSize: DefaultNodeBlockChunkSize,
|
||||||
}
|
}
|
||||||
group.Add(fbc.Command())
|
group.Add(fbc.Command())
|
||||||
} else {
|
} else {
|
||||||
|
@ -501,6 +636,7 @@ func (c *loadBlocksAndTransfersCommand) startFetchingNewBlocks(group *async.Grou
|
||||||
feed: c.feed,
|
feed: c.feed,
|
||||||
noLimit: false,
|
noLimit: false,
|
||||||
transactionManager: c.transactionManager,
|
transactionManager: c.transactionManager,
|
||||||
|
tokenManager: c.tokenManager,
|
||||||
blocksLoadedCh: blocksLoadedCh,
|
blocksLoadedCh: blocksLoadedCh,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -583,15 +719,14 @@ func getHeadBlockNumber(parent context.Context, chainClient chain.ClientInterfac
|
||||||
return head.Number, err
|
return head.Number, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextRange(from *big.Int, zeroBlockNumber *big.Int) (*big.Int, *big.Int) {
|
func nextRange(maxRangeSize int, prevFrom, zeroBlockNumber *big.Int) (*big.Int, *big.Int) {
|
||||||
log.Debug("next range start", "from", from, "zeroBlockNumber", zeroBlockNumber)
|
log.Debug("next range start", "from", prevFrom, "zeroBlockNumber", zeroBlockNumber)
|
||||||
|
|
||||||
rangeSize := big.NewInt(DefaultNodeBlockChunkSize)
|
rangeSize := big.NewInt(int64(maxRangeSize))
|
||||||
|
|
||||||
to := new(big.Int).Sub(from, big.NewInt(1)) // it won't hit the cache, but we wont load the transfers twice
|
to := big.NewInt(0).Set(prevFrom)
|
||||||
if to.Cmp(rangeSize) > 0 {
|
from := big.NewInt(0).Sub(to, rangeSize)
|
||||||
from.Sub(to, rangeSize)
|
if from.Cmp(zeroBlockNumber) < 0 {
|
||||||
} else {
|
|
||||||
from = new(big.Int).Set(zeroBlockNumber)
|
from = new(big.Int).Set(zeroBlockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,60 +3,243 @@ package transfer
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"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/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/status-im/status-go/contracts/ethscan"
|
||||||
|
"github.com/status-im/status-go/contracts/ierc20"
|
||||||
"github.com/status-im/status-go/rpc/chain"
|
"github.com/status-im/status-go/rpc/chain"
|
||||||
"github.com/status-im/status-go/services/wallet/async"
|
"github.com/status-im/status-go/services/wallet/async"
|
||||||
"github.com/status-im/status-go/services/wallet/balance"
|
"github.com/status-im/status-go/services/wallet/balance"
|
||||||
"github.com/status-im/status-go/t/helpers"
|
"github.com/status-im/status-go/t/helpers"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/params"
|
||||||
|
statusRpc "github.com/status-im/status-go/rpc"
|
||||||
|
"github.com/status-im/status-go/rpc/network"
|
||||||
|
"github.com/status-im/status-go/services/wallet/token"
|
||||||
"github.com/status-im/status-go/walletdatabase"
|
"github.com/status-im/status-go/walletdatabase"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestClient struct {
|
type TestClient struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
// [][block, newBalance, nonceDiff]
|
// [][block, newBalance, nonceDiff]
|
||||||
balances [][]int
|
balances [][]int
|
||||||
balanceHistory map[uint64]*big.Int
|
outgoingERC20Transfers []testERC20Transfer
|
||||||
nonceHistory map[uint64]uint64
|
incomingERC20Transfers []testERC20Transfer
|
||||||
|
balanceHistory map[uint64]*big.Int
|
||||||
|
tokenBalanceHistory map[common.Address]map[uint64]*big.Int
|
||||||
|
nonceHistory map[uint64]uint64
|
||||||
|
traceAPICalls bool
|
||||||
|
printPreparedData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
|
func (tc TestClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
|
||||||
tc.t.Log("BatchCallContext")
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BatchCallContext")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (tc TestClient) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
tc.t.Log("HeaderByHash")
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("HeaderByHash")
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
func (tc TestClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
tc.t.Log("BlockByHash")
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BlockByHash")
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
func (tc TestClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||||
tc.t.Log("BlockByNumber")
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BlockByNumber")
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
func (tc TestClient) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||||
nonce := tc.nonceHistory[blockNumber.Uint64()]
|
nonce := tc.nonceHistory[blockNumber.Uint64()]
|
||||||
|
if tc.traceAPICalls {
|
||||||
tc.t.Log("NonceAt", blockNumber, "result:", nonce)
|
tc.t.Log("NonceAt", blockNumber, "result:", nonce)
|
||||||
|
}
|
||||||
return nonce, nil
|
return nonce, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
func (tc TestClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
||||||
tc.t.Log("FilterLogs")
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("FilterLogs")
|
||||||
|
}
|
||||||
|
//checking only ERC20 for now
|
||||||
|
incomingAddress := q.Topics[len(q.Topics)-1]
|
||||||
|
allTransfers := tc.incomingERC20Transfers
|
||||||
|
if len(incomingAddress) == 0 {
|
||||||
|
allTransfers = tc.outgoingERC20Transfers
|
||||||
|
}
|
||||||
|
|
||||||
|
logs := []types.Log{}
|
||||||
|
for _, transfer := range allTransfers {
|
||||||
|
if transfer.block.Cmp(q.FromBlock) >= 0 && transfer.block.Cmp(q.ToBlock) <= 0 {
|
||||||
|
logs = append(logs, types.Log{
|
||||||
|
BlockNumber: transfer.block.Uint64(),
|
||||||
|
BlockHash: common.BigToHash(transfer.block),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
|
balance := tc.balanceHistory[blockNumber.Uint64()]
|
||||||
|
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BalanceAt", blockNumber, "result:", balance)
|
||||||
|
}
|
||||||
|
return balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) tokenBalanceAt(token common.Address, blockNumber *big.Int) *big.Int {
|
||||||
|
balance := tc.tokenBalanceHistory[token][blockNumber.Uint64()]
|
||||||
|
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("tokenBalanceAt", token, blockNumber, "result:", balance)
|
||||||
|
}
|
||||||
|
return balance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TestClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("HeaderByNumber", number)
|
||||||
|
}
|
||||||
|
header := &types.Header{
|
||||||
|
Number: number,
|
||||||
|
Time: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("FullTransactionByBlockNumberAndIndex")
|
||||||
|
}
|
||||||
|
blockHash := common.BigToHash(blockNumber)
|
||||||
|
tx := &chain.FullTransaction{
|
||||||
|
Tx: &types.Transaction{},
|
||||||
|
TxExtraInfo: chain.TxExtraInfo{
|
||||||
|
BlockNumber: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
BlockHash: &blockHash,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("GetBaseFeeFromBloc")
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) NetworkID() uint64 {
|
||||||
|
return 777333
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) ToBigInt() *big.Int {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("ToBigInt")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ethscanAddress = common.HexToAddress("0x0000000000000000000000000000000000777333")
|
||||||
|
|
||||||
|
func (tc TestClient) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("CodeAt", contract, blockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ethscanAddress == contract {
|
||||||
|
return []byte{1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("CallContract", call, blockNumber, call.To)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *call.To == ethscanAddress {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ethscan.BalanceScannerABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
method := parsed.Methods["tokensBalance"]
|
||||||
|
params := call.Data[len(method.ID):]
|
||||||
|
args, err := method.Inputs.Unpack(params)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Log("ERROR on unpacking", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := args[1].([]common.Address)
|
||||||
|
balances := []*big.Int{}
|
||||||
|
for _, token := range tokens {
|
||||||
|
balances = append(balances, tc.tokenBalanceAt(token, blockNumber))
|
||||||
|
}
|
||||||
|
results := []ethscan.BalanceScannerResult{}
|
||||||
|
for _, balance := range balances {
|
||||||
|
results = append(results, ethscan.BalanceScannerResult{
|
||||||
|
Success: true,
|
||||||
|
Data: balance.Bytes(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := method.Outputs.Pack(results)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Log("ERROR on packing", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if *call.To == tokenTXXAddress {
|
||||||
|
balance := tc.tokenBalanceAt(tokenTXXAddress, blockNumber)
|
||||||
|
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
method := parsed.Methods["balanceOf"]
|
||||||
|
output, err := method.Outputs.Pack(balance)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Log("ERROR on packing ERC20 balance", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,66 +265,233 @@ func (tc *TestClient) prepareBalanceHistory(toBlock int) {
|
||||||
currentNonce += change[2]
|
currentNonce += change[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.t.Log("=========================================")
|
if tc.printPreparedData {
|
||||||
tc.t.Log(tc.balanceHistory)
|
tc.t.Log("========================================= ETH BALANCES")
|
||||||
tc.t.Log(tc.nonceHistory)
|
tc.t.Log(tc.balanceHistory)
|
||||||
tc.t.Log("=========================================")
|
tc.t.Log(tc.nonceHistory)
|
||||||
|
tc.t.Log(tc.tokenBalanceHistory)
|
||||||
|
tc.t.Log("=========================================")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
func (tc *TestClient) prepareTokenBalanceHistory(toBlock int) {
|
||||||
balance := tc.balanceHistory[blockNumber.Uint64()]
|
transfersPerToken := map[common.Address][]testERC20Transfer{}
|
||||||
|
for _, transfer := range tc.outgoingERC20Transfers {
|
||||||
tc.t.Log("BalanceAt", blockNumber, "result:", balance)
|
transfer.amount = new(big.Int).Neg(transfer.amount)
|
||||||
return balance, nil
|
transfersPerToken[transfer.address] = append(transfersPerToken[transfer.address], transfer)
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *TestClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
|
||||||
tc.t.Log("HeaderByNumber", number)
|
|
||||||
header := &types.Header{
|
|
||||||
Number: number,
|
|
||||||
Time: 0,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return header, nil
|
for _, transfer := range tc.incomingERC20Transfers {
|
||||||
}
|
transfersPerToken[transfer.address] = append(transfersPerToken[transfer.address], transfer)
|
||||||
|
|
||||||
func (tc TestClient) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error) {
|
|
||||||
tc.t.Log("FullTransactionByBlockNumberAndIndex")
|
|
||||||
blockHash := common.BigToHash(blockNumber)
|
|
||||||
tx := &chain.FullTransaction{
|
|
||||||
Tx: &types.Transaction{},
|
|
||||||
TxExtraInfo: chain.TxExtraInfo{
|
|
||||||
BlockNumber: (*hexutil.Big)(big.NewInt(0)),
|
|
||||||
BlockHash: &blockHash,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx, nil
|
tc.tokenBalanceHistory = map[common.Address]map[uint64]*big.Int{}
|
||||||
|
|
||||||
|
for token, transfers := range transfersPerToken {
|
||||||
|
sort.Slice(transfers, func(i, j int) bool {
|
||||||
|
return transfers[i].block.Cmp(transfers[j].block) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
currentBlock := uint64(0)
|
||||||
|
currentBalance := big.NewInt(0)
|
||||||
|
|
||||||
|
tc.tokenBalanceHistory[token] = map[uint64]*big.Int{}
|
||||||
|
transfers = append(transfers, testERC20Transfer{big.NewInt(int64(toBlock + 1)), token, big.NewInt(0)})
|
||||||
|
|
||||||
|
for _, transfer := range transfers {
|
||||||
|
for blockN := currentBlock; blockN < transfer.block.Uint64(); blockN++ {
|
||||||
|
tc.tokenBalanceHistory[token][blockN] = new(big.Int).Set(currentBalance)
|
||||||
|
}
|
||||||
|
currentBlock = transfer.block.Uint64()
|
||||||
|
currentBalance = new(big.Int).Add(currentBalance, transfer.amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tc.printPreparedData {
|
||||||
|
tc.t.Log("========================================= ERC20 BALANCES")
|
||||||
|
tc.t.Log(tc.tokenBalanceHistory)
|
||||||
|
tc.t.Log("=========================================")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
|
func (tc TestClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||||
tc.t.Log("GetBaseFeeFromBloc")
|
if tc.traceAPICalls {
|
||||||
return "", nil
|
tc.t.Log("CallContext")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc TestClient) NetworkID() uint64 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc TestClient) ToBigInt() *big.Int {
|
|
||||||
tc.t.Log("ToBigInt")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type findBlockCase struct {
|
func (tc TestClient) GetWalletNotifier() func(chainId uint64, message string) {
|
||||||
balanceChanges [][]int
|
if tc.traceAPICalls {
|
||||||
fromBlock int64
|
tc.t.Log("GetWalletNotifier")
|
||||||
toBlock int64
|
}
|
||||||
expectedBlocksFound int
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var findBlocksCommandCases = []findBlockCase{
|
func (tc TestClient) SetWalletNotifier(notifier func(chainId uint64, message string)) {
|
||||||
{
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SetWalletNotifier")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("EstimateGas")
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("PendingCodeAt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("PendingCallContract")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("PendingNonceAt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SuggestGasPrice")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SendTransaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SuggestGasTipCap")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) BatchCallContextIgnoringLocalHandlers(ctx context.Context, b []rpc.BatchElem) error {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BatchCallContextIgnoringLocalHandlers")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) CallContextIgnoringLocalHandlers(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("CallContextIgnoringLocalHandlers")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) CallRaw(data string) string {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("CallRaw")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) GetChainID() *big.Int {
|
||||||
|
return big.NewInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SubscribeFilterLogs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("TransactionReceipt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("TransactionByHash")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) BlockNumber(ctx context.Context) (uint64, error) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("BlockNumber")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
func (tc TestClient) SetIsConnected(value bool) {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("SetIsConnected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TestClient) GetIsConnected() bool {
|
||||||
|
if tc.traceAPICalls {
|
||||||
|
tc.t.Log("GetIsConnected")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type testERC20Transfer struct {
|
||||||
|
block *big.Int
|
||||||
|
address common.Address
|
||||||
|
amount *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type findBlockCase struct {
|
||||||
|
balanceChanges [][]int
|
||||||
|
ERC20BalanceChanges [][]int
|
||||||
|
fromBlock int64
|
||||||
|
toBlock int64
|
||||||
|
rangeSize int
|
||||||
|
expectedBlocksFound int
|
||||||
|
outgoingERC20Transfers []testERC20Transfer
|
||||||
|
incomingERC20Transfers []testERC20Transfer
|
||||||
|
label string
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferInEachBlock() [][]int {
|
||||||
|
res := [][]int{}
|
||||||
|
|
||||||
|
for i := 1; i < 101; i++ {
|
||||||
|
res = append(res, []int{i, i, i})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCases() []findBlockCase {
|
||||||
|
cases := []findBlockCase{}
|
||||||
|
case1 := findBlockCase{
|
||||||
balanceChanges: [][]int{
|
balanceChanges: [][]int{
|
||||||
{5, 1, 0},
|
{5, 1, 0},
|
||||||
{20, 2, 0},
|
{20, 2, 0},
|
||||||
|
@ -149,19 +499,102 @@ var findBlocksCommandCases = []findBlockCase{
|
||||||
{46, 50, 0},
|
{46, 50, 0},
|
||||||
{75, 0, 1},
|
{75, 0, 1},
|
||||||
},
|
},
|
||||||
|
outgoingERC20Transfers: []testERC20Transfer{
|
||||||
|
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
|
||||||
|
},
|
||||||
|
toBlock: 100,
|
||||||
|
expectedBlocksFound: 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
case100transfers := findBlockCase{
|
||||||
|
balanceChanges: transferInEachBlock(),
|
||||||
|
toBlock: 100,
|
||||||
|
expectedBlocksFound: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
case3 := findBlockCase{
|
||||||
|
balanceChanges: [][]int{
|
||||||
|
{1, 1, 1},
|
||||||
|
{2, 2, 2},
|
||||||
|
{45, 1, 1},
|
||||||
|
{46, 50, 0},
|
||||||
|
{75, 0, 1},
|
||||||
|
},
|
||||||
toBlock: 100,
|
toBlock: 100,
|
||||||
expectedBlocksFound: 5,
|
expectedBlocksFound: 5,
|
||||||
},
|
}
|
||||||
{
|
case4 := findBlockCase{
|
||||||
|
balanceChanges: [][]int{
|
||||||
|
{20, 1, 0},
|
||||||
|
},
|
||||||
|
toBlock: 100,
|
||||||
|
fromBlock: 10,
|
||||||
|
expectedBlocksFound: 1,
|
||||||
|
label: "single block",
|
||||||
|
}
|
||||||
|
|
||||||
|
case5 := findBlockCase{
|
||||||
balanceChanges: [][]int{},
|
balanceChanges: [][]int{},
|
||||||
toBlock: 100,
|
toBlock: 100,
|
||||||
|
fromBlock: 20,
|
||||||
expectedBlocksFound: 0,
|
expectedBlocksFound: 0,
|
||||||
},
|
}
|
||||||
|
|
||||||
|
case6 := findBlockCase{
|
||||||
|
balanceChanges: [][]int{
|
||||||
|
{20, 1, 0},
|
||||||
|
{45, 1, 1},
|
||||||
|
},
|
||||||
|
toBlock: 100,
|
||||||
|
fromBlock: 30,
|
||||||
|
expectedBlocksFound: 1,
|
||||||
|
rangeSize: 20,
|
||||||
|
label: "single block in range",
|
||||||
|
}
|
||||||
|
|
||||||
|
case7emptyHistoryWithOneERC20Transfer := findBlockCase{
|
||||||
|
balanceChanges: [][]int{},
|
||||||
|
toBlock: 100,
|
||||||
|
rangeSize: 20,
|
||||||
|
expectedBlocksFound: 1,
|
||||||
|
incomingERC20Transfers: []testERC20Transfer{
|
||||||
|
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case8emptyHistoryWithERC20Transfers := findBlockCase{
|
||||||
|
balanceChanges: [][]int{},
|
||||||
|
toBlock: 100,
|
||||||
|
rangeSize: 20,
|
||||||
|
expectedBlocksFound: 2,
|
||||||
|
incomingERC20Transfers: []testERC20Transfer{
|
||||||
|
// edge case when a regular scan will find transfer at 80,
|
||||||
|
// but erc20 tail scan should only find transfer at block 6
|
||||||
|
{big.NewInt(80), tokenTXXAddress, big.NewInt(1)},
|
||||||
|
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases = append(cases, case1)
|
||||||
|
cases = append(cases, case100transfers)
|
||||||
|
cases = append(cases, case3)
|
||||||
|
cases = append(cases, case4)
|
||||||
|
cases = append(cases, case5)
|
||||||
|
|
||||||
|
cases = append(cases, case6)
|
||||||
|
cases = append(cases, case7emptyHistoryWithOneERC20Transfer)
|
||||||
|
cases = append(cases, case8emptyHistoryWithERC20Transfers)
|
||||||
|
|
||||||
|
//cases = append([]findBlockCase{}, case8emptyHistoryWithERC20Transfers)
|
||||||
|
|
||||||
|
return cases
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindBlocksCommand(t *testing.T) {
|
var tokenTXXAddress = common.HexToAddress("0x53211")
|
||||||
for _, testCase := range findBlocksCommandCases {
|
|
||||||
|
|
||||||
|
func TestFindBlocksCommand(t *testing.T) {
|
||||||
|
for idx, testCase := range getCases() {
|
||||||
|
t.Log("case #", idx)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
group := async.NewGroup(ctx)
|
group := async.NewGroup(ctx)
|
||||||
|
|
||||||
|
@ -171,32 +604,71 @@ func TestFindBlocksCommand(t *testing.T) {
|
||||||
|
|
||||||
wdb := NewDB(db)
|
wdb := NewDB(db)
|
||||||
tc := &TestClient{
|
tc := &TestClient{
|
||||||
t: t,
|
t: t,
|
||||||
balances: testCase.balanceChanges,
|
balances: testCase.balanceChanges,
|
||||||
|
outgoingERC20Transfers: testCase.outgoingERC20Transfers,
|
||||||
|
incomingERC20Transfers: testCase.incomingERC20Transfers,
|
||||||
}
|
}
|
||||||
|
//tc.traceAPICalls = true
|
||||||
|
//tc.printPreparedData = true
|
||||||
tc.prepareBalanceHistory(100)
|
tc.prepareBalanceHistory(100)
|
||||||
|
tc.prepareTokenBalanceHistory(100)
|
||||||
blockChannel := make(chan []*DBHeader, 100)
|
blockChannel := make(chan []*DBHeader, 100)
|
||||||
|
rangeSize := 20
|
||||||
|
if testCase.rangeSize != 0 {
|
||||||
|
rangeSize = testCase.rangeSize
|
||||||
|
}
|
||||||
|
client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db)
|
||||||
|
client.SetClient(tc.NetworkID(), tc)
|
||||||
|
tokenManager := token.NewTokenManager(db, client, network.NewManager(db))
|
||||||
|
tokenManager.SetTokens([]*token.Token{
|
||||||
|
{
|
||||||
|
Address: tokenTXXAddress,
|
||||||
|
Symbol: "TXX",
|
||||||
|
Decimals: 18,
|
||||||
|
ChainID: tc.NetworkID(),
|
||||||
|
Name: "Test Token 1",
|
||||||
|
Verified: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
fbc := &findBlocksCommand{
|
fbc := &findBlocksCommand{
|
||||||
account: common.HexToAddress("0x1234"),
|
account: common.HexToAddress("0x12345"),
|
||||||
db: wdb,
|
db: wdb,
|
||||||
blockRangeDAO: &BlockRangeSequentialDAO{wdb.client},
|
blockRangeDAO: &BlockRangeSequentialDAO{wdb.client},
|
||||||
chainClient: tc,
|
chainClient: tc,
|
||||||
balanceCacher: balance.NewCache(),
|
balanceCacher: balance.NewCache(),
|
||||||
feed: &event.Feed{},
|
feed: &event.Feed{},
|
||||||
noLimit: false,
|
noLimit: false,
|
||||||
fromBlockNumber: big.NewInt(testCase.fromBlock),
|
fromBlockNumber: big.NewInt(testCase.fromBlock),
|
||||||
toBlockNumber: big.NewInt(testCase.toBlock),
|
toBlockNumber: big.NewInt(testCase.toBlock),
|
||||||
transactionManager: tm,
|
transactionManager: tm,
|
||||||
blocksLoadedCh: blockChannel,
|
blocksLoadedCh: blockChannel,
|
||||||
|
defaultNodeBlockChunkSize: rangeSize,
|
||||||
|
tokenManager: tokenManager,
|
||||||
}
|
}
|
||||||
group.Add(fbc.Command())
|
group.Add(fbc.Command())
|
||||||
|
|
||||||
|
foundBlocks := []*DBHeader{}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
t.Log("ERROR")
|
t.Log("ERROR")
|
||||||
case <-group.WaitAsync():
|
case <-group.WaitAsync():
|
||||||
close(blockChannel)
|
close(blockChannel)
|
||||||
require.Equal(t, testCase.expectedBlocksFound, len(<-blockChannel))
|
for {
|
||||||
|
bloks, ok := <-blockChannel
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
foundBlocks = append(foundBlocks, bloks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers := []int64{}
|
||||||
|
for _, block := range foundBlocks {
|
||||||
|
numbers = append(numbers, block.Number.Int64())
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(numbers, func(i, j int) bool { return numbers[i] < numbers[j] })
|
||||||
|
require.Equal(t, testCase.expectedBlocksFound, len(foundBlocks), testCase.label, "found blocks", numbers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add
|
||||||
// watchAccountsChanges subscribes to a feed and watches for changes in accounts list. If there are new or removed accounts
|
// watchAccountsChanges subscribes to a feed and watches for changes in accounts list. If there are new or removed accounts
|
||||||
// reactor will be restarted.
|
// reactor will be restarted.
|
||||||
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor,
|
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor,
|
||||||
chainClients map[uint64]*chain.ClientWithFallback, initial []common.Address, loadAllTransfers bool) error {
|
chainClients map[uint64]chain.ClientInterface, initial []common.Address, loadAllTransfers bool) error {
|
||||||
|
|
||||||
ch := make(chan accountsevent.Event, 1) // it may block if the rate of updates will be significantly higher
|
ch := make(chan accountsevent.Event, 1) // it may block if the rate of updates will be significantly higher
|
||||||
sub := accountFeed.Subscribe(ch)
|
sub := accountFeed.Subscribe(ch)
|
||||||
|
|
|
@ -74,7 +74,7 @@ type Transfer struct {
|
||||||
|
|
||||||
// ETHDownloader downloads regular eth transfers.
|
// ETHDownloader downloads regular eth transfers.
|
||||||
type ETHDownloader struct {
|
type ETHDownloader struct {
|
||||||
chainClient *chain.ClientWithFallback
|
chainClient chain.ClientInterface
|
||||||
accounts []common.Address
|
accounts []common.Address
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
db *Database
|
db *Database
|
||||||
|
@ -95,7 +95,7 @@ func (d *ETHDownloader) GetTransfersByNumber(ctx context.Context, number *big.In
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used by status-mobile
|
// Only used by status-mobile
|
||||||
func getTransferByHash(ctx context.Context, client *chain.ClientWithFallback, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
|
func getTransferByHash(ctx context.Context, client chain.ClientInterface, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
|
||||||
transaction, _, err := client.TransactionByHash(ctx, hash)
|
transaction, _, err := client.TransactionByHash(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -143,7 +143,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc
|
||||||
for _, address := range accounts {
|
for _, address := range accounts {
|
||||||
// During block discovery, we should have populated the DB with 1 item per Transaction containing
|
// During block discovery, we should have populated the DB with 1 item per Transaction containing
|
||||||
// erc20/erc721 transfers
|
// erc20/erc721 transfers
|
||||||
transactionsToLoad, err := d.db.GetTransactionsToLoad(d.chainClient.ChainID, address, blk.Number())
|
transactionsToLoad, err := d.db.GetTransactionsToLoad(d.chainClient.NetworkID(), address, blk.Number())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc
|
||||||
|
|
||||||
for _, tx := range blk.Transactions() {
|
for _, tx := range blk.Transactions() {
|
||||||
if tx.ChainId().Cmp(big.NewInt(0)) != 0 && tx.ChainId().Cmp(d.chainClient.ToBigInt()) != 0 {
|
if tx.ChainId().Cmp(big.NewInt(0)) != 0 && tx.ChainId().Cmp(d.chainClient.ToBigInt()) != 0 {
|
||||||
log.Info("chain id mismatch", "tx hash", tx.Hash(), "tx chain id", tx.ChainId(), "expected chain id", d.chainClient.ChainID)
|
log.Info("chain id mismatch", "tx hash", tx.Hash(), "tx chain id", tx.ChainId(), "expected chain id", d.chainClient.NetworkID())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
from, err := types.Sender(d.signer, tx)
|
from, err := types.Sender(d.signer, tx)
|
||||||
|
|
|
@ -18,7 +18,7 @@ func SetupIterativeDownloader(
|
||||||
return nil, errors.New("to or from cannot be nil")
|
return nil, errors.New("to or from cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("iterative downloader", "address", address, "from", from, "to", to, "size", size)
|
log.Debug("iterative downloader", "address", address, "from", from, "to", to, "size", size)
|
||||||
d := &IterativeDownloader{
|
d := &IterativeDownloader{
|
||||||
client: client,
|
client: client,
|
||||||
batchSize: size,
|
batchSize: size,
|
||||||
|
@ -65,7 +65,7 @@ func (d *IterativeDownloader) Next(parent context.Context) ([]*DBHeader, *big.In
|
||||||
from = d.from
|
from = d.from
|
||||||
}
|
}
|
||||||
headers, err := d.downloader.GetHeadersInRange(parent, from, to)
|
headers, err := d.downloader.GetHeadersInRange(parent, from, to)
|
||||||
log.Info("load erc20 transfers in range", "from", from, "to", to, "batchSize", d.batchSize)
|
log.Debug("load erc20 transfers in range", "from", from, "to", to, "batchSize", d.batchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to get transfer in between two blocks", "from", from, "to", to, "error", err)
|
log.Error("failed to get transfer in between two blocks", "from", from, "to", to, "error", err)
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
|
|
|
@ -57,7 +57,7 @@ func NewOnDemandFetchStrategy(
|
||||||
transactionManager *TransactionManager,
|
transactionManager *TransactionManager,
|
||||||
pendingTxManager *transactions.PendingTxTracker,
|
pendingTxManager *transactions.PendingTxTracker,
|
||||||
tokenManager *token.Manager,
|
tokenManager *token.Manager,
|
||||||
chainClients map[uint64]*chain.ClientWithFallback,
|
chainClients map[uint64]chain.ClientInterface,
|
||||||
accounts []common.Address,
|
accounts []common.Address,
|
||||||
balanceCacher balance.Cacher,
|
balanceCacher balance.Cacher,
|
||||||
) *OnDemandFetchStrategy {
|
) *OnDemandFetchStrategy {
|
||||||
|
@ -86,11 +86,11 @@ type OnDemandFetchStrategy struct {
|
||||||
transactionManager *TransactionManager
|
transactionManager *TransactionManager
|
||||||
pendingTxManager *transactions.PendingTxTracker
|
pendingTxManager *transactions.PendingTxTracker
|
||||||
tokenManager *token.Manager
|
tokenManager *token.Manager
|
||||||
chainClients map[uint64]*chain.ClientWithFallback
|
chainClients map[uint64]chain.ClientInterface
|
||||||
accounts []common.Address
|
accounts []common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OnDemandFetchStrategy) newControlCommand(chainClient *chain.ClientWithFallback, accounts []common.Address) *controlCommand {
|
func (s *OnDemandFetchStrategy) newControlCommand(chainClient chain.ClientInterface, accounts []common.Address) *controlCommand {
|
||||||
signer := types.LatestSignerForChainID(chainClient.ToBigInt())
|
signer := types.LatestSignerForChainID(chainClient.ToBigInt())
|
||||||
ctl := &controlCommand{
|
ctl := &controlCommand{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
|
@ -277,7 +277,7 @@ func NewReactor(db *Database, blockDAO *BlockDAO, feed *event.Feed, tm *Transact
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start runs reactor loop in background.
|
// Start runs reactor loop in background.
|
||||||
func (r *Reactor) start(chainClients map[uint64]*chain.ClientWithFallback, accounts []common.Address,
|
func (r *Reactor) start(chainClients map[uint64]chain.ClientInterface, accounts []common.Address,
|
||||||
loadAllTransfers bool) error {
|
loadAllTransfers bool) error {
|
||||||
|
|
||||||
r.strategy = r.createFetchStrategy(chainClients, accounts, loadAllTransfers)
|
r.strategy = r.createFetchStrategy(chainClients, accounts, loadAllTransfers)
|
||||||
|
@ -291,14 +291,14 @@ func (r *Reactor) stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reactor) restart(chainClients map[uint64]*chain.ClientWithFallback, accounts []common.Address,
|
func (r *Reactor) restart(chainClients map[uint64]chain.ClientInterface, accounts []common.Address,
|
||||||
loadAllTransfers bool) error {
|
loadAllTransfers bool) error {
|
||||||
|
|
||||||
r.stop()
|
r.stop()
|
||||||
return r.start(chainClients, accounts, loadAllTransfers)
|
return r.start(chainClients, accounts, loadAllTransfers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reactor) createFetchStrategy(chainClients map[uint64]*chain.ClientWithFallback,
|
func (r *Reactor) createFetchStrategy(chainClients map[uint64]chain.ClientInterface,
|
||||||
accounts []common.Address, loadAllTransfers bool) HistoryFetcher {
|
accounts []common.Address, loadAllTransfers bool) HistoryFetcher {
|
||||||
|
|
||||||
if loadAllTransfers {
|
if loadAllTransfers {
|
||||||
|
@ -328,9 +328,9 @@ func (r *Reactor) getTransfersByAddress(ctx context.Context, chainID uint64, add
|
||||||
return nil, errors.New(ReactorNotStarted)
|
return nil, errors.New(ReactorNotStarted)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChainClientByID(clients map[uint64]*chain.ClientWithFallback, id uint64) (*chain.ClientWithFallback, error) {
|
func getChainClientByID(clients map[uint64]chain.ClientInterface, id uint64) (chain.ClientInterface, error) {
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.ChainID == id {
|
if client.NetworkID() == id {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
func NewSequentialFetchStrategy(db *Database, blockDAO *BlockDAO, feed *event.Feed,
|
func NewSequentialFetchStrategy(db *Database, blockDAO *BlockDAO, feed *event.Feed,
|
||||||
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker,
|
||||||
tokenManager *token.Manager,
|
tokenManager *token.Manager,
|
||||||
chainClients map[uint64]*chain.ClientWithFallback,
|
chainClients map[uint64]chain.ClientInterface,
|
||||||
accounts []common.Address,
|
accounts []common.Address,
|
||||||
balanceCacher balance.Cacher,
|
balanceCacher balance.Cacher,
|
||||||
) *SequentialFetchStrategy {
|
) *SequentialFetchStrategy {
|
||||||
|
@ -46,12 +46,12 @@ type SequentialFetchStrategy struct {
|
||||||
transactionManager *TransactionManager
|
transactionManager *TransactionManager
|
||||||
pendingTxManager *transactions.PendingTxTracker
|
pendingTxManager *transactions.PendingTxTracker
|
||||||
tokenManager *token.Manager
|
tokenManager *token.Manager
|
||||||
chainClients map[uint64]*chain.ClientWithFallback
|
chainClients map[uint64]chain.ClientInterface
|
||||||
accounts []common.Address
|
accounts []common.Address
|
||||||
balanceCacher balance.Cacher
|
balanceCacher balance.Cacher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SequentialFetchStrategy) newCommand(chainClient *chain.ClientWithFallback,
|
func (s *SequentialFetchStrategy) newCommand(chainClient chain.ClientInterface,
|
||||||
account common.Address) async.Commander {
|
account common.Address) async.Commander {
|
||||||
|
|
||||||
return newLoadBlocksAndTransfersCommand(account, s.db, s.blockDAO, chainClient, s.feed,
|
return newLoadBlocksAndTransfersCommand(account, s.db, s.blockDAO, chainClient, s.feed,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
const ETHSymbol string = "ETH"
|
const ETHSymbol string = "ETH"
|
||||||
const WETHSymbol string = "WETH"
|
const WETHSymbol string = "WETH"
|
||||||
|
|
||||||
func fetchUniswapV2PairInfo(ctx context.Context, client *chain.ClientWithFallback, pairAddress common.Address) (*common.Address, *common.Address, error) {
|
func fetchUniswapV2PairInfo(ctx context.Context, client chain.ClientInterface, pairAddress common.Address) (*common.Address, *common.Address, error) {
|
||||||
caller, err := uniswapv2.NewUniswapv2Caller(pairAddress, client)
|
caller, err := uniswapv2.NewUniswapv2Caller(pairAddress, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -43,7 +43,7 @@ func fetchUniswapV2PairInfo(ctx context.Context, client *chain.ClientWithFallbac
|
||||||
return &token0Address, &token1Address, nil
|
return &token0Address, &token1Address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUniswapV3PoolInfo(ctx context.Context, client *chain.ClientWithFallback, poolAddress common.Address) (*common.Address, *common.Address, error) {
|
func fetchUniswapV3PoolInfo(ctx context.Context, client chain.ClientInterface, poolAddress common.Address) (*common.Address, *common.Address, error) {
|
||||||
caller, err := uniswapv3.NewUniswapv3Caller(poolAddress, client)
|
caller, err := uniswapv3.NewUniswapv3Caller(poolAddress, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -90,7 +90,7 @@ func identifyUniswapV2Asset(tokenManager *token.Manager, chainID uint64, amount0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUniswapV2Info(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
func fetchUniswapV2Info(ctx context.Context, client chain.ClientInterface, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
||||||
pairAddress, _, _, amount0In, amount1In, amount0Out, amount1Out, err := w_common.ParseUniswapV2Log(log)
|
pairAddress, _, _, amount0In, amount1In, amount0Out, amount1Out, err := w_common.ParseUniswapV2Log(log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -101,7 +101,7 @@ func fetchUniswapV2Info(ctx context.Context, client *chain.ClientWithFallback, t
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fromToken, fromAmountInt, err := identifyUniswapV2Asset(tokenManager, client.ChainID, amount0In, *token0ContractAddress, amount1In, *token1ContractAddress)
|
fromToken, fromAmountInt, err := identifyUniswapV2Asset(tokenManager, client.NetworkID(), amount0In, *token0ContractAddress, amount1In, *token1ContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// "Soft" error, allow to continue with unknown asset
|
// "Soft" error, allow to continue with unknown asset
|
||||||
fromAsset = ""
|
fromAsset = ""
|
||||||
|
@ -111,7 +111,7 @@ func fetchUniswapV2Info(ctx context.Context, client *chain.ClientWithFallback, t
|
||||||
fromAmount = (*hexutil.Big)(fromAmountInt)
|
fromAmount = (*hexutil.Big)(fromAmountInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
toToken, toAmountInt, err := identifyUniswapV2Asset(tokenManager, client.ChainID, amount0Out, *token0ContractAddress, amount1Out, *token1ContractAddress)
|
toToken, toAmountInt, err := identifyUniswapV2Asset(tokenManager, client.NetworkID(), amount0Out, *token0ContractAddress, amount1Out, *token1ContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// "Soft" error, allow to continue with unknown asset
|
// "Soft" error, allow to continue with unknown asset
|
||||||
toAsset = ""
|
toAsset = ""
|
||||||
|
@ -159,7 +159,7 @@ func identifyUniswapV3Assets(tokenManager *token.Manager, chainID uint64, amount
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUniswapV3Info(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
func fetchUniswapV3Info(ctx context.Context, client chain.ClientInterface, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
||||||
poolAddress, _, _, amount0, amount1, err := w_common.ParseUniswapV3Log(log)
|
poolAddress, _, _, amount0, amount1, err := w_common.ParseUniswapV3Log(log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -170,7 +170,7 @@ func fetchUniswapV3Info(ctx context.Context, client *chain.ClientWithFallback, t
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fromToken, fromAmountInt, toToken, toAmountInt, err := identifyUniswapV3Assets(tokenManager, client.ChainID, amount0, *token0ContractAddress, amount1, *token1ContractAddress)
|
fromToken, fromAmountInt, toToken, toAmountInt, err := identifyUniswapV3Assets(tokenManager, client.NetworkID(), amount0, *token0ContractAddress, amount1, *token1ContractAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// "Soft" error, allow to continue with unknown asset
|
// "Soft" error, allow to continue with unknown asset
|
||||||
err = nil
|
err = nil
|
||||||
|
@ -188,7 +188,7 @@ func fetchUniswapV3Info(ctx context.Context, client *chain.ClientWithFallback, t
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUniswapInfo(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log, logType w_common.EventType) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
func fetchUniswapInfo(ctx context.Context, client chain.ClientInterface, tokenManager *token.Manager, log *types.Log, logType w_common.EventType) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) {
|
||||||
switch logType {
|
switch logType {
|
||||||
case w_common.UniswapV2SwapEventType:
|
case w_common.UniswapV2SwapEventType:
|
||||||
return fetchUniswapV2Info(ctx, client, tokenManager, log)
|
return fetchUniswapV2Info(ctx, client, tokenManager, log)
|
||||||
|
@ -201,7 +201,7 @@ func fetchUniswapInfo(ctx context.Context, client *chain.ClientWithFallback, tok
|
||||||
|
|
||||||
// Build a Swap multitransaction from a list containing one or several uniswapV2/uniswapV3 subTxs
|
// Build a Swap multitransaction from a list containing one or several uniswapV2/uniswapV3 subTxs
|
||||||
// We only care about the first and last swap to identify the input/output token and amounts
|
// We only care about the first and last swap to identify the input/output token and amounts
|
||||||
func buildUniswapSwapMultitransaction(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, transfer *Transfer) (*MultiTransaction, error) {
|
func buildUniswapSwapMultitransaction(ctx context.Context, client chain.ClientInterface, tokenManager *token.Manager, transfer *Transfer) (*MultiTransaction, error) {
|
||||||
multiTransaction := MultiTransaction{
|
multiTransaction := MultiTransaction{
|
||||||
Type: MultiTransactionSwap,
|
Type: MultiTransactionSwap,
|
||||||
FromNetworkID: transfer.NetworkID,
|
FromNetworkID: transfer.NetworkID,
|
||||||
|
|
Loading…
Reference in New Issue