From 7d1927396a12ed5c63022fe331dceccf9e054340 Mon Sep 17 00:00:00 2001 From: Ivan Belyakov Date: Wed, 24 Jan 2024 14:00:08 +0100 Subject: [PATCH] fix(wallet): do not return gas-only ETH entries of transfers table for activities request Closes #4618 --- services/wallet/activity/activity_test.go | 71 +++++++++++++++++++ services/wallet/activity/filter.sql | 13 ++-- .../transfer/block_ranges_sequential_dao.go | 10 +-- services/wallet/transfer/commands.go | 23 +++--- services/wallet/transfer/database.go | 22 ++++++ 5 files changed, 119 insertions(+), 20 deletions(-) diff --git a/services/wallet/activity/activity_test.go b/services/wallet/activity/activity_test.go index 0043ec7ad..591d08658 100644 --- a/services/wallet/activity/activity_test.go +++ b/services/wallet/activity/activity_test.go @@ -1378,3 +1378,74 @@ func TestGetMultiTxDetails(t *testing.T) { require.Equal(t, td.multiTx1Tr2.Hash, details.ChainDetails[1].Hash) require.Equal(t, td.multiTx1Tr2.Contract, *details.ChainDetails[1].Contract) } + +func TestGetActivityEntriesSkipEthGasFeeOnlyTransfers(t *testing.T) { + deps, close := setupTestActivityDB(t) + defer close() + + to := eth.Address{0x1} + from := eth.Address{0x2} + hash := eth.Hash{0x3} + blkNum := int64(1) + chainID := common.ChainID(1) + nonce := uint64(1) + + // Insert 0-value gas-only ETH transfer as a result of token transfer's gas fee + transfer.InsertTestTransfer(t, deps.db, to, &transfer.TestTransfer{ + TestTransaction: transfer.TestTransaction{ + ChainID: chainID, + From: from, + Hash: hash, + BlkNumber: blkNum, + Nonce: nonce, + }, + To: to, + Value: 0, + }) + + entries, err := getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(entries)) + require.Equal(t, hash, entries[0].transaction.Hash) + + // Insert token transfer + transfer.InsertTestTransferWithOptions(t, deps.db, to, + &transfer.TestTransfer{ + TestTransaction: transfer.TestTransaction{ + ChainID: chainID, + From: from, + Hash: hash, + BlkNumber: blkNum, + Nonce: nonce, + }, + To: to, + Value: 1, + }, + &transfer.TestTransferOptions{ + TokenAddress: eth.Address{0x4}, + }, + ) + + // Gas-fee-only transfer should be removed, so we get only 1 transfer again + entries, err = getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) + require.NoError(t, err) + require.Equal(t, 1, len(entries)) + require.Equal(t, contractTypeFromDBType("erc20"), entries[0].transferType) + + // Insert real 0-value ETH transfer + transfer.InsertTestTransfer(t, deps.db, to, &transfer.TestTransfer{ + TestTransaction: transfer.TestTransaction{ + ChainID: chainID, + From: from, + Hash: eth.Hash{0x5}, // another hash + BlkNumber: blkNum, + Nonce: nonce + 1, // another nonce + }, + To: to, + Value: 0, // 0-value as well + }) + + entries, err = getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(entries)) +} diff --git a/services/wallet/activity/filter.sql b/services/wallet/activity/filter.sql index 1b7612daa..27e2b21ac 100644 --- a/services/wallet/activity/filter.sql +++ b/services/wallet/activity/filter.sql @@ -157,6 +157,7 @@ layer2_networks(network_id) AS ( mint_methods(method_hash) AS ( %s ) + SELECT transfers.hash AS transfer_hash, NULL AS pending_hash, @@ -208,7 +209,7 @@ SELECT WHEN transfers.tx_from_address = zeroAddress AND transfers.type = "erc20" THEN substr(json_extract(tx, '$.input'), 1, 10) ELSE NULL END AS method_hash, - CASE + CASE WHEN transfers.tx_from_address = zeroAddress AND transfers.type = "erc20" THEN (SELECT 1 FROM json_each(transfers.receipt, '$.logs' ) WHERE json_extract( value, '$.topics[0]' ) = communityMintEvent) ELSE NULL END AS community_mint_event @@ -254,11 +255,11 @@ WHERE AND ( transfers.type = 'erc721' OR ( - transfers.type = 'erc20' + transfers.type = 'erc20' AND ( (method_hash IS NOT NULL AND method_hash IN mint_methods) OR community_mint_event IS NOT NULL - ) + ) ) ) ) @@ -281,11 +282,11 @@ WHERE AND ( transfers.type = 'erc721' OR ( - transfers.type = 'erc20' + transfers.type = 'erc20' AND ( (method_hash IS NOT NULL AND method_hash IN mint_methods) OR community_mint_event IS NOT NULL - ) + ) ) ) ) @@ -583,4 +584,4 @@ WHERE ORDER BY timestamp DESC LIMIT - ? OFFSET ? \ No newline at end of file + ? OFFSET ? diff --git a/services/wallet/transfer/block_ranges_sequential_dao.go b/services/wallet/transfer/block_ranges_sequential_dao.go index e7647de5d..a24279bf3 100644 --- a/services/wallet/transfer/block_ranges_sequential_dao.go +++ b/services/wallet/transfer/block_ranges_sequential_dao.go @@ -90,8 +90,8 @@ func (b *BlockRangeSequentialDAO) upsertRange(chainID uint64, account common.Add return err } - ethBlockRange := prepareUpdatedBlockRange(chainID, account, ethTokensBlockRange.eth, newBlockRange.eth) - tokensBlockRange := prepareUpdatedBlockRange(chainID, account, ethTokensBlockRange.tokens, newBlockRange.tokens) + ethBlockRange := prepareUpdatedBlockRange(ethTokensBlockRange.eth, newBlockRange.eth) + tokensBlockRange := prepareUpdatedBlockRange(ethTokensBlockRange.tokens, newBlockRange.tokens) log.Debug("update eth and tokens blocks range", "account", account, "chainID", chainID, "eth.start", ethBlockRange.Start, "eth.first", ethBlockRange.FirstKnown, "eth.last", ethBlockRange.LastKnown, @@ -117,7 +117,7 @@ func (b *BlockRangeSequentialDAO) upsertEthRange(chainID uint64, account common. return err } - blockRange := prepareUpdatedBlockRange(chainID, account, ethTokensBlockRange.eth, newBlockRange) + blockRange := prepareUpdatedBlockRange(ethTokensBlockRange.eth, newBlockRange) log.Debug("update eth blocks range", "account", account, "chainID", chainID, "start", blockRange.Start, "first", blockRange.FirstKnown, "last", blockRange.LastKnown, "old hash", ethTokensBlockRange.balanceCheckHash) @@ -146,7 +146,7 @@ func (b *BlockRangeSequentialDAO) updateTokenRange(chainID uint64, account commo return err } - blockRange := prepareUpdatedBlockRange(chainID, account, ethTokensBlockRange.tokens, newBlockRange) + blockRange := prepareUpdatedBlockRange(ethTokensBlockRange.tokens, newBlockRange) log.Debug("update tokens blocks range", "account", account, "chainID", chainID, "start", blockRange.Start, "first", blockRange.FirstKnown, "last", blockRange.LastKnown, "old hash", ethTokensBlockRange.balanceCheckHash) @@ -162,7 +162,7 @@ func (b *BlockRangeSequentialDAO) updateTokenRange(chainID uint64, account commo return err } -func prepareUpdatedBlockRange(chainID uint64, account common.Address, blockRange, newBlockRange *BlockRange) *BlockRange { +func prepareUpdatedBlockRange(blockRange, newBlockRange *BlockRange) *BlockRange { // Update existing range if blockRange != nil { if newBlockRange != nil { diff --git a/services/wallet/transfer/commands.go b/services/wallet/transfer/commands.go index 652175924..3cdf003ca 100644 --- a/services/wallet/transfer/commands.go +++ b/services/wallet/transfer/commands.go @@ -296,7 +296,7 @@ func (c *transfersCommand) saveAndConfirmPending(allTransfers []Transfer, blockN if resErr != nil { return resErr } - notifyFunctions := make([]func(), 0) + notifyFunctions := c.confirmPendingTransactions(tx, allTransfers) defer func() { if resErr == nil { commitErr := tx.Commit() @@ -314,6 +314,17 @@ func (c *transfersCommand) saveAndConfirmPending(allTransfers []Transfer, blockN } }() + resErr = saveTransfersMarkBlocksLoaded(tx, c.chainClient.NetworkID(), c.address, allTransfers, []*big.Int{blockNum}) + if resErr != nil { + log.Error("SaveTransfers error", "error", resErr) + } + + return resErr +} + +func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers []Transfer) (notifyFunctions []func()) { + notifyFunctions = make([]func(), 0) + // Confirm all pending transactions that are included in this block for i, tr := range allTransfers { chainID := w_common.ChainID(tr.NetworkID) @@ -351,13 +362,7 @@ func (c *transfersCommand) saveAndConfirmPending(allTransfers []Transfer, blockN notifyFunctions = append(notifyFunctions, notify) } } - - resErr = saveTransfersMarkBlocksLoaded(tx, c.chainClient.NetworkID(), c.address, allTransfers, []*big.Int{blockNum}) - if resErr != nil { - log.Error("SaveTransfers error", "error", resErr) - } - - return resErr + return notifyFunctions } // Mark all subTxs of a given Tx with the same multiTxID @@ -542,7 +547,7 @@ func (c *loadTransfersCommand) Command() async.Command { }.Run } -// This command always returs nil, even if there is an error in one of the commands. +// This command always returns nil, even if there is an error in one of the commands. // `transferCommand`s retry until maxError, but this command doesn't retry. // In case some transfer is not loaded after max retries, it will be retried only after restart of the app. // Currently there is no implementation to keep retrying until success. I think this should be implemented diff --git a/services/wallet/transfer/database.go b/services/wallet/transfer/database.go index b734ff537..b4445203a 100644 --- a/services/wallet/transfer/database.go +++ b/services/wallet/transfer/database.go @@ -492,6 +492,28 @@ func updateOrInsertTransfersDBFields(creator statementCreator, transfers []trans log.Error("can't save transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id) return err } + + err = removeGasOnlyEthTransfer(creator, t) + if err != nil { + log.Error("can't remove gas only eth transfer", "b-hash", t.blockHash, "b-n", t.blockNumber, "a", t.address, "h", t.id, "err", err) + // no return err, since it's not critical + } + } + return nil +} + +func removeGasOnlyEthTransfer(creator statementCreator, t transferDBFields) error { + if t.transferType != w_common.EthTransfer { + query, err := creator.Prepare(`DELETE FROM transfers WHERE tx_hash = ? AND address = ? AND network_id = ? + AND account_nonce = ? AND type = 'eth' AND amount_padded128hex = '00000000000000000000000000000000'`) + if err != nil { + return err + } + + _, err = query.Exec(t.txHash, t.address, t.chainID, t.txNonce) + if err != nil { + return err + } } return nil }