feat(wallet): Added detection of ERC1155 SingleTransfer events

This commit is contained in:
Ivan Belyakov 2023-09-22 18:09:14 +02:00 committed by IvanBelyakoff
parent a584ab086a
commit 57e370e7b9
5 changed files with 342 additions and 71 deletions

View File

@ -20,20 +20,24 @@ type EventType string
const (
// Transaction types
EthTransfer Type = "eth"
Erc20Transfer Type = "erc20"
Erc721Transfer Type = "erc721"
UniswapV2Swap Type = "uniswapV2Swap"
UniswapV3Swap Type = "uniswapV3Swap"
HopBridgeFrom Type = "HopBridgeFrom"
HopBridgeTo Type = "HopBridgeTo"
unknownTransaction Type = "unknown"
EthTransfer Type = "eth"
Erc20Transfer Type = "erc20"
Erc721Transfer Type = "erc721"
Erc1155SingleTransfer Type = "erc1155"
Erc1155BatchTransfer Type = "erc1155"
UniswapV2Swap Type = "uniswapV2Swap"
UniswapV3Swap Type = "uniswapV3Swap"
HopBridgeFrom Type = "HopBridgeFrom"
HopBridgeTo Type = "HopBridgeTo"
unknownTransaction Type = "unknown"
// Event types
WETHDepositEventType EventType = "wethDepositEvent"
WETHWithdrawalEventType EventType = "wethWithdrawalEvent"
Erc20TransferEventType EventType = "erc20Event"
Erc721TransferEventType EventType = "erc721Event"
Erc1155TransferSingleEventType EventType = "erc1155SingleEvent"
Erc1155TransferBatchEventType EventType = "erc1155BatchEvent"
UniswapV2SwapEventType EventType = "uniswapV2SwapEvent"
UniswapV3SwapEventType EventType = "uniswapV3SwapEvent"
HopBridgeTransferSentToL2EventType EventType = "hopBridgeTransferSentToL2Event"
@ -49,10 +53,13 @@ const (
// Transfer (index_topic_1 address from, index_topic_2 address to, uint256 value)
// Transfer (index_topic_1 address from, index_topic_2 address to, index_topic_3 uint256 tokenId)
Erc20_721TransferEventSignature = "Transfer(address,address,uint256)"
Erc20_721TransferEventSignature = "Transfer(address,address,uint256)"
Erc1155TransferSingleEventSignature = "TransferSingle(address,address,address,uint256,uint256)" // operator, from, to, id, value
Erc1155TransferBatchEventSignature = "TransferBatch(address,address,address,uint256[],uint256[])" // operator, from, to, ids, values
erc20TransferEventIndexedParameters = 3 // signature, from, to
erc721TransferEventIndexedParameters = 4 // signature, from, to, tokenId
erc20TransferEventIndexedParameters = 3 // signature, from, to
erc721TransferEventIndexedParameters = 4 // signature, from, to, tokenId
erc1155TransferEventIndexedParameters = 4 // signature, operator, from, to (id, value are not indexed)
// Swap (index_topic_1 address sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, index_topic_2 address to)
uniswapV2SwapEventSignature = "Swap(address,uint256,uint256,uint256,uint256,address)" // also used by SushiSwap
@ -79,6 +86,8 @@ func GetEventType(log *types.Log) EventType {
wethDepositEventSignatureHash := GetEventSignatureHash(wethDepositEventSignature)
wethWithdrawalEventSignatureHash := GetEventSignatureHash(wethWithdrawalEventSignature)
erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature)
erc1155TransferSingleEventSignatureHash := GetEventSignatureHash(Erc1155TransferSingleEventSignature)
erc1155TransferBatchEventSignatureHash := GetEventSignatureHash(Erc1155TransferBatchEventSignature)
uniswapV2SwapEventSignatureHash := GetEventSignatureHash(uniswapV2SwapEventSignature)
uniswapV3SwapEventSignatureHash := GetEventSignatureHash(uniswapV3SwapEventSignature)
hopBridgeTransferSentToL2EventSignatureHash := GetEventSignatureHash(hopBridgeTransferSentToL2EventSignature)
@ -99,6 +108,10 @@ func GetEventType(log *types.Log) EventType {
case erc721TransferEventIndexedParameters:
return Erc721TransferEventType
}
case erc1155TransferSingleEventSignatureHash:
return Erc1155TransferSingleEventType
case erc1155TransferBatchEventSignatureHash:
return Erc1155TransferBatchEventType
case uniswapV2SwapEventSignatureHash:
return UniswapV2SwapEventType
case uniswapV3SwapEventSignatureHash:
@ -123,6 +136,10 @@ func EventTypeToSubtransactionType(eventType EventType) Type {
return Erc20Transfer
case Erc721TransferEventType:
return Erc721Transfer
case Erc1155TransferSingleEventType:
return Erc1155SingleTransfer
case Erc1155TransferBatchEventType:
return Erc1155BatchTransfer
case UniswapV2SwapEventType:
return UniswapV2Swap
case UniswapV3SwapEventType:
@ -149,7 +166,12 @@ func GetFirstEvent(logs []*types.Log) (EventType, *types.Log) {
func IsTokenTransfer(logs []*types.Log) bool {
eventType, _ := GetFirstEvent(logs)
return eventType == Erc20TransferEventType
switch eventType {
case Erc20TransferEventType, Erc721TransferEventType, Erc1155TransferSingleEventType, Erc1155TransferBatchEventType:
return true
}
return false
}
func ParseWETHDepositLog(ethlog *types.Log) (src common.Address, amount *big.Int) {
@ -200,7 +222,7 @@ func ParseWETHWithdrawLog(ethlog *types.Log) (dst common.Address, amount *big.In
func ParseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) {
amount = new(big.Int)
if len(ethlog.Topics) < 3 {
if len(ethlog.Topics) < erc20TransferEventIndexedParameters {
log.Warn("not enough topics for erc20 transfer", "topics", ethlog.Topics)
return
}
@ -225,7 +247,7 @@ func ParseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount *
func ParseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID *big.Int) {
tokenID = new(big.Int)
if len(ethlog.Topics) < 4 {
if len(ethlog.Topics) < erc721TransferEventIndexedParameters {
log.Warn("not enough topics for erc721 transfer", "topics", ethlog.Topics)
return
}
@ -248,6 +270,37 @@ func ParseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID
return
}
func ParseErc1155TransferSingleLog(ethlog *types.Log) (operator, from, to common.Address, id, amount *big.Int) {
if len(ethlog.Topics) < erc1155TransferEventIndexedParameters {
log.Warn("not enough topics for erc1155 transfer single", "topics", ethlog.Topics)
return
}
amount = new(big.Int)
id = new(big.Int)
for i := 1; i < erc1155TransferEventIndexedParameters; i++ {
if len(ethlog.Topics[i]) != common.HashLength {
log.Warn(fmt.Sprintf("topic %d is not padded to %d byte address, topic=%s", i, common.HashLength, ethlog.Topics[1]))
return
}
}
addressIdx := common.HashLength - common.AddressLength
copy(operator[:], ethlog.Topics[1][addressIdx:])
copy(from[:], ethlog.Topics[2][addressIdx:])
copy(to[:], ethlog.Topics[3][addressIdx:])
if len(ethlog.Data) != common.HashLength*2 {
log.Warn("data is not padded to 64 bytes", "data", ethlog.Data)
return
}
id.SetBytes(ethlog.Data[:common.HashLength])
amount.SetBytes(ethlog.Data[common.HashLength:])
return
}
func ParseUniswapV2Log(ethlog *types.Log) (pairAddress common.Address, from common.Address, to common.Address, amount0In *big.Int, amount1In *big.Int, amount0Out *big.Int, amount1Out *big.Int, err error) {
amount0In = new(big.Int)
amount1In = new(big.Int)
@ -502,6 +555,14 @@ func ExtractTokenIdentity(dbEntryType Type, log *types.Log, tx *types.Transactio
txTokenID = tokenID
txFrom = &from
txTo = &to
case Erc1155SingleTransfer:
tokenAddress = new(common.Address)
*tokenAddress = log.Address
_, from, to, tokenID, value := ParseErc1155TransferSingleLog(log)
txTokenID = tokenID
txFrom = &from
txTo = &to
txValue = value
}
return

View File

@ -116,9 +116,10 @@ type erc20HistoricalCommand struct {
chainClient chain.ClientInterface
feed *event.Feed
iterator *IterativeDownloader
to *big.Int
from *big.Int
iterator *IterativeDownloader
to *big.Int
from *big.Int
foundHeaders []*DBHeader
}
@ -166,7 +167,7 @@ func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
for !c.iterator.Finished() {
headers, _, _, err := c.iterator.Next(ctx)
if err != nil {
log.Error("failed to get next batch", "error", err)
log.Error("failed to get next batch", "error", err, "chainID", c.chainClient.NetworkID()) // TODO: stop inifinite command in case of an error that we can't fix like missing trie node
return err
}
c.foundHeaders = append(c.foundHeaders, headers...)

View File

@ -281,6 +281,7 @@ func (c *findBlocksCommand) Run(parent context.Context) (err error) {
break
}
// if we have found first ETH block and we have not reached the start of ETH history yet
if c.startBlockNumber != nil && c.fromBlockNumber.Cmp(from) == -1 {
log.Debug("ERC20 tail should be checked", "initial from", c.fromBlockNumber, "actual from", from, "first ETH block", c.startBlockNumber)
c.reachedETHHistoryStart = true

View File

@ -11,6 +11,8 @@ import (
"time"
"github.com/stretchr/testify/mock"
"golang.org/x/exp/slices" // since 1.21, this is in the standard library
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum"
@ -25,6 +27,7 @@ import (
"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/balance"
w_common "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/t/helpers"
"github.com/status-im/status-go/params"
@ -39,17 +42,19 @@ import (
type TestClient struct {
t *testing.T
// [][block, newBalance, nonceDiff]
balances [][]int
outgoingERC20Transfers []testERC20Transfer
incomingERC20Transfers []testERC20Transfer
balanceHistory map[uint64]*big.Int
tokenBalanceHistory map[common.Address]map[uint64]*big.Int
nonceHistory map[uint64]uint64
traceAPICalls bool
printPreparedData bool
rw sync.RWMutex
callsCounter map[string]int
currentBlock uint64
balances [][]int
outgoingERC20Transfers []testERC20Transfer
incomingERC20Transfers []testERC20Transfer
outgoingERC1155SingleTransfers []testERC20Transfer
incomingERC1155SingleTransfers []testERC20Transfer
balanceHistory map[uint64]*big.Int
tokenBalanceHistory map[common.Address]map[uint64]*big.Int
nonceHistory map[uint64]uint64
traceAPICalls bool
printPreparedData bool
rw sync.RWMutex
callsCounter map[string]int
currentBlock uint64
}
func (tc *TestClient) incCounter(method string) {
@ -126,11 +131,36 @@ func (tc *TestClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([
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
// We do not verify addresses for now
allTransfers := []testERC20Transfer{}
signatures := q.Topics[0]
erc20TransferSignature := w_common.GetEventSignatureHash(w_common.Erc20_721TransferEventSignature)
erc1155TransferSingleSignature := w_common.GetEventSignatureHash(w_common.Erc1155TransferSingleEventSignature)
if slices.Contains(signatures, erc1155TransferSingleSignature) {
from := q.Topics[2]
var to []common.Hash
if len(q.Topics) > 3 {
to = q.Topics[3]
}
if len(to) > 0 {
allTransfers = append(allTransfers, tc.incomingERC1155SingleTransfers...)
}
if len(from) > 0 {
allTransfers = append(allTransfers, tc.outgoingERC1155SingleTransfers...)
}
}
if slices.Contains(signatures, erc20TransferSignature) {
from := q.Topics[1]
to := q.Topics[2]
if len(to) > 0 {
allTransfers = append(allTransfers, tc.incomingERC20Transfers...)
}
if len(from) > 0 {
allTransfers = append(allTransfers, tc.outgoingERC20Transfers...)
}
}
logs := []types.Log{}
@ -341,6 +371,15 @@ func (tc *TestClient) prepareTokenBalanceHistory(toBlock int) {
transfersPerToken[transfer.address] = append(transfersPerToken[transfer.address], transfer)
}
for _, transfer := range tc.outgoingERC1155SingleTransfers {
transfer.amount = new(big.Int).Neg(transfer.amount)
transfersPerToken[transfer.address] = append(transfersPerToken[transfer.address], transfer)
}
for _, transfer := range tc.incomingERC1155SingleTransfers {
transfersPerToken[transfer.address] = append(transfersPerToken[transfer.address], transfer)
}
tc.tokenBalanceHistory = map[common.Address]map[uint64]*big.Int{}
for token, transfers := range transfersPerToken {
@ -539,16 +578,18 @@ type testERC20Transfer struct {
}
type findBlockCase struct {
balanceChanges [][]int
ERC20BalanceChanges [][]int
fromBlock int64
toBlock int64
rangeSize int
expectedBlocksFound int
outgoingERC20Transfers []testERC20Transfer
incomingERC20Transfers []testERC20Transfer
label string
expectedCalls map[string]int
balanceChanges [][]int
ERC20BalanceChanges [][]int
fromBlock int64
toBlock int64
rangeSize int
expectedBlocksFound int
outgoingERC20Transfers []testERC20Transfer
incomingERC20Transfers []testERC20Transfer
outgoingERC1155SingleTransfers []testERC20Transfer
incomingERC1155SingleTransfers []testERC20Transfer
label string
expectedCalls map[string]int
}
func transferInEachBlock() [][]int {
@ -577,11 +618,7 @@ func getCases() []findBlockCase {
toBlock: 100,
expectedBlocksFound: 6,
expectedCalls: map[string]int{
"BalanceAt": 27,
//TODO(rasom) NonceAt is flaky, sometimes it's called 18 times, sometimes 17
//to be investigated
//"NonceAt": 18,
"FilterLogs": 10,
"FilterLogs": 15,
"HeaderByNumber": 5,
},
}
@ -593,7 +630,7 @@ func getCases() []findBlockCase {
expectedCalls: map[string]int{
"BalanceAt": 101,
"NonceAt": 0,
"FilterLogs": 10,
"FilterLogs": 15,
"HeaderByNumber": 100,
},
}
@ -660,7 +697,7 @@ func getCases() []findBlockCase {
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 3,
"FilterLogs": 5,
"CallContract": 3,
},
}
@ -677,8 +714,7 @@ func getCases() []findBlockCase {
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 3,
"CallContract": 5,
"FilterLogs": 5,
},
}
@ -690,12 +726,115 @@ func getCases() []findBlockCase {
label: "single block range, no transactions",
expectedCalls: map[string]int{
// only two requests to check the range for incoming ERC20
"FilterLogs": 2,
"FilterLogs": 3,
// no contract calls as ERC20 is not checked
"CallContract": 0,
},
}
case11IncomingERC1155SingleTransfers := findBlockCase{
balanceChanges: [][]int{},
toBlock: 100,
rangeSize: 20,
// we expect only a single eth_getLogs to be executed here for both erc20 transfers,
// thus only 2 blocks found
expectedBlocksFound: 2,
incomingERC1155SingleTransfers: []testERC20Transfer{
{big.NewInt(7), tokenTXYAddress, big.NewInt(1)},
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 5,
"CallContract": 5,
},
}
case12OutgoingERC1155SingleTransfers := findBlockCase{
balanceChanges: [][]int{
{6, 1, 0},
},
toBlock: 100,
rangeSize: 20,
expectedBlocksFound: 3,
outgoingERC1155SingleTransfers: []testERC20Transfer{
{big.NewInt(80), tokenTXYAddress, big.NewInt(1)},
{big.NewInt(6), tokenTXXAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 15, // 3 for each range
},
}
case13outgoingERC20ERC1155SingleTransfers := findBlockCase{
balanceChanges: [][]int{
{63, 1, 0},
},
toBlock: 100,
rangeSize: 20,
expectedBlocksFound: 3,
outgoingERC1155SingleTransfers: []testERC20Transfer{
{big.NewInt(80), tokenTXYAddress, big.NewInt(1)},
},
outgoingERC20Transfers: []testERC20Transfer{
{big.NewInt(63), tokenTXYAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 6, // 3 for each range, 0 for tail check becauseERC20ScanByBalance returns no ranges
},
}
case14outgoingERC20ERC1155SingleTransfersMoreFilterLogs := findBlockCase{
balanceChanges: [][]int{
{61, 1, 0},
},
toBlock: 100,
rangeSize: 20,
expectedBlocksFound: 3,
outgoingERC1155SingleTransfers: []testERC20Transfer{
{big.NewInt(80), tokenTXYAddress, big.NewInt(1)},
},
outgoingERC20Transfers: []testERC20Transfer{
{big.NewInt(61), tokenTXYAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 9, // 3 for each range of [40-100], 0 for tail check because ERC20ScanByBalance returns no ranges
},
label: "outgoing ERC20 and ERC1155 transfers but more FilterLogs calls because startFromBlock is not detected at range [60-80] as it is in the first subrange",
}
case15incomingERC20outgoingERC1155SingleTransfers := findBlockCase{
balanceChanges: [][]int{
{85, 1, 0},
},
toBlock: 100,
rangeSize: 20,
expectedBlocksFound: 2,
outgoingERC1155SingleTransfers: []testERC20Transfer{
{big.NewInt(85), tokenTXYAddress, big.NewInt(1)},
},
incomingERC20Transfers: []testERC20Transfer{
{big.NewInt(88), tokenTXYAddress, big.NewInt(1)},
},
expectedCalls: map[string]int{
"FilterLogs": 3, // 3 for each range of [40-100], 0 for tail check because ERC20ScanByBalance returns no ranges
},
label: "incoming ERC20 and outgoing ERC1155 transfers are fetched with same topic",
}
case16 := findBlockCase{
balanceChanges: [][]int{
{75, 0, 1},
},
outgoingERC20Transfers: []testERC20Transfer{
{big.NewInt(80), tokenTXXAddress, big.NewInt(4)},
},
toBlock: 100,
rangeSize: 20,
expectedBlocksFound: 3, // ideally we should find 2 blocks, but we will find 3 and this test shows that we are ok with that
label: `duplicate blocks detected but we wont fix it because we want to save requests on the edges of the ranges,
taking balance and nonce from cache while ETH and tokens ranges searching are tightly coupled`,
}
cases = append(cases, case1)
cases = append(cases, case100transfers)
cases = append(cases, case3)
@ -707,6 +846,12 @@ func getCases() []findBlockCase {
cases = append(cases, case8emptyHistoryWithERC20Transfers)
cases = append(cases, case9emptyHistoryWithERC20Transfers)
cases = append(cases, case10)
cases = append(cases, case11IncomingERC1155SingleTransfers)
cases = append(cases, case12OutgoingERC1155SingleTransfers)
cases = append(cases, case13outgoingERC20ERC1155SingleTransfers)
cases = append(cases, case14outgoingERC20ERC1155SingleTransfersMoreFilterLogs)
cases = append(cases, case15incomingERC20outgoingERC1155SingleTransfers)
cases = append(cases, case16)
//cases = append([]findBlockCase{}, case10)
@ -718,7 +863,7 @@ var tokenTXYAddress = common.HexToAddress("0x73211")
func TestFindBlocksCommand(t *testing.T) {
for idx, testCase := range getCases() {
t.Log("case #", idx)
t.Log("case #", idx+1)
ctx := context.Background()
group := async.NewGroup(ctx)
@ -728,11 +873,13 @@ func TestFindBlocksCommand(t *testing.T) {
wdb := NewDB(db)
tc := &TestClient{
t: t,
balances: testCase.balanceChanges,
outgoingERC20Transfers: testCase.outgoingERC20Transfers,
incomingERC20Transfers: testCase.incomingERC20Transfers,
callsCounter: map[string]int{},
t: t,
balances: testCase.balanceChanges,
outgoingERC20Transfers: testCase.outgoingERC20Transfers,
incomingERC20Transfers: testCase.incomingERC20Transfers,
outgoingERC1155SingleTransfers: testCase.outgoingERC1155SingleTransfers,
incomingERC1155SingleTransfers: testCase.incomingERC1155SingleTransfers,
callsCounter: map[string]int{},
}
//tc.traceAPICalls = true
//tc.printPreparedData = true

View File

@ -240,11 +240,13 @@ func NewERC20TransfersDownloader(client chain.ClientInterface, accounts []common
signature := w_common.GetEventSignatureHash(w_common.Erc20_721TransferEventSignature)
return &ERC20TransfersDownloader{
client: client,
accounts: accounts,
incomingOnly: incomingOnly,
signature: signature,
signer: signer,
client: client,
accounts: accounts,
signature: signature,
incomingOnly: incomingOnly,
signatureErc1155Single: w_common.GetEventSignatureHash(w_common.Erc1155TransferSingleEventSignature),
signatureErc1155Batch: w_common.GetEventSignatureHash(w_common.Erc1155TransferBatchEventSignature),
signer: signer,
}
}
@ -259,7 +261,9 @@ type ERC20TransfersDownloader struct {
incomingOnly bool
// hash of the Transfer event signature
signature common.Hash
signature common.Hash
signatureErc1155Single common.Hash
signatureErc1155Batch common.Hash
// signer is used to derive tx sender from tx signature
signer types.Signer
@ -279,6 +283,14 @@ func (d *ERC20TransfersDownloader) outboundTopics(address common.Address) [][]co
return [][]common.Hash{{d.signature}, {d.paddedAddress(address)}, {}}
}
func (d *ERC20TransfersDownloader) inboundERC20OutboundERC1155Topics(address common.Address) [][]common.Hash {
return [][]common.Hash{{d.signature, d.signatureErc1155Single}, {}, {d.paddedAddress(address)}}
}
func (d *ERC20TransfersDownloader) inboundTopicsERC1155Single(address common.Address) [][]common.Hash {
return [][]common.Hash{{d.signatureErc1155Single}, {}, {}, {d.paddedAddress(address)}}
}
func (d *ETHDownloader) subTransactionsFromTransactionHash(parent context.Context, txHash common.Hash, address common.Address) ([]Transfer, error) {
ctx, cancel := context.WithTimeout(parent, 3*time.Second)
tx, _, err := d.chainClient.TransactionByHash(ctx, txHash)
@ -321,7 +333,7 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(tx *types.Transaction
rst := make([]Transfer, 0, len(receipt.Logs))
for _, log := range receipt.Logs {
eventType := w_common.GetEventType(log)
// Only add ERC20/ERC721 transfers from/to the given account
// Only add ERC20/ERC721/ERC1155 transfers from/to the given account
// Other types of events get always added
mustAppend := false
switch eventType {
@ -335,6 +347,11 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(tx *types.Transaction
if trFrom == address || trTo == address {
mustAppend = true
}
case w_common.Erc1155TransferSingleEventType:
_, trFrom, trTo, _, _ := w_common.ParseErc1155TransferSingleLog(log)
if trFrom == address || trTo == address {
mustAppend = true
}
case w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType:
mustAppend = true
case w_common.HopBridgeTransferSentToL2EventType, w_common.HopBridgeTransferFromL1CompletedEventType:
@ -382,6 +399,13 @@ func (d *ERC20TransfersDownloader) blocksFromLogs(parent context.Context, logs [
return nil, err
}
logType := w_common.EventTypeToSubtransactionType(w_common.GetEventType(&l))
log.Debug("block from logs", "block", l.BlockNumber, "log", l, "logType", logType, "address", address, "id", id)
if logType != w_common.Erc1155SingleTransfer && logType != w_common.Erc1155BatchTransfer {
logType = w_common.Erc20Transfer
}
header := &DBHeader{
Number: big.NewInt(int64(l.BlockNumber)),
Hash: l.BlockHash,
@ -392,7 +416,7 @@ func (d *ERC20TransfersDownloader) blocksFromLogs(parent context.Context, logs [
ID: id,
From: address,
Loaded: false,
Type: w_common.Erc20Transfer,
Type: logType,
Log: &l,
BaseGasFees: baseGasFee,
}},
@ -422,6 +446,7 @@ func (d *ERC20TransfersDownloader) GetHeadersInRange(parent context.Context, fro
var err error
for _, address := range d.accounts {
outbound := []types.Log{}
var inboundOrMixed []types.Log // inbound ERC20 or outbound ERC1155 share the same signature for our purposes
if !d.incomingOnly {
outbound, err = d.client.FilterLogs(ctx, ethereum.FilterQuery{
FromBlock: from,
@ -431,17 +456,38 @@ func (d *ERC20TransfersDownloader) GetHeadersInRange(parent context.Context, fro
if err != nil {
return nil, err
}
inboundOrMixed, err = d.client.FilterLogs(ctx, ethereum.FilterQuery{
FromBlock: from,
ToBlock: to,
Topics: d.inboundERC20OutboundERC1155Topics(address),
})
if err != nil {
return nil, err
}
} else {
inboundOrMixed, err = d.client.FilterLogs(ctx, ethereum.FilterQuery{
FromBlock: from,
ToBlock: to,
Topics: d.inboundTopics(address),
})
if err != nil {
return nil, err
}
}
inbound, err := d.client.FilterLogs(ctx, ethereum.FilterQuery{
inbound1155, err := d.client.FilterLogs(ctx, ethereum.FilterQuery{
FromBlock: from,
ToBlock: to,
Topics: d.inboundTopics(address),
Topics: d.inboundTopicsERC1155Single(address),
})
if err != nil {
return nil, err
}
logs := append(outbound, inbound...)
logs := concatLogs(outbound, inboundOrMixed, inbound1155)
if len(logs) == 0 {
log.Debug("no logs found for account")
continue
}
@ -458,7 +504,22 @@ func (d *ERC20TransfersDownloader) GetHeadersInRange(parent context.Context, fro
"from", from, "to", to, "headers", len(headers))
}
}
log.Debug("get erc20 transfers in range end", "chainID", d.client.NetworkID(),
"from", from, "to", to, "headers", len(headers), "took", time.Since(start))
return headers, nil
}
func concatLogs(slices ...[]types.Log) []types.Log {
var totalLen int
for _, s := range slices {
totalLen += len(s)
}
tmp := make([]types.Log, totalLen)
var i int
for _, s := range slices {
i += copy(tmp[i:], s)
}
return tmp
}