From 5d4838a752d2575c76d0d48669f94e5b47ef02db Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Fri, 11 Oct 2024 10:18:12 -0300 Subject: [PATCH] chore_: remove detection of external swaps and bridges --- services/wallet/transfer/bridge_identifier.go | 221 --------------- services/wallet/transfer/commands.go | 186 ++----------- .../wallet/transfer/commands_sequential.go | 63 ++--- .../transfer/commands_sequential_test.go | 28 +- services/wallet/transfer/reactor.go | 47 ++-- .../transfer/sequential_fetch_strategy.go | 58 ++-- services/wallet/transfer/swap_identifier.go | 262 ------------------ 7 files changed, 118 insertions(+), 747 deletions(-) delete mode 100644 services/wallet/transfer/bridge_identifier.go delete mode 100644 services/wallet/transfer/swap_identifier.go diff --git a/services/wallet/transfer/bridge_identifier.go b/services/wallet/transfer/bridge_identifier.go deleted file mode 100644 index 76207e30d..000000000 --- a/services/wallet/transfer/bridge_identifier.go +++ /dev/null @@ -1,221 +0,0 @@ -package transfer - -import ( - "context" - "encoding/hex" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/status-im/status-go/rpc/chain" - w_common "github.com/status-im/status-go/services/wallet/common" - "github.com/status-im/status-go/services/wallet/token" -) - -// TODO: Find proper way to uniquely match Origin and Destination transactions (some sort of hash or uniqueID) -// Current approach is not failsafe (for example, if multiple identical bridge operations are triggered -// at the same time) -// Recipient + Relayer + Data should match in both Origin and Destination transactions -func getHopBridgeFromL1CrossTxID(recipient common.Address, relayer common.Address, logData []byte) string { - return fmt.Sprintf("FromL1_%s_%s_%s", recipient.String(), relayer.String(), hex.EncodeToString(logData)) -} - -func getHopBridgeFromL2CrossTxID(transferID *big.Int) string { - return fmt.Sprintf("FromL2_0x%s", transferID.Text(16)) -} - -type originTxParams struct { - fromNetworkID uint64 - fromTxHash common.Hash - fromAddress common.Address - fromAsset string - fromAmount *big.Int - toNetworkID uint64 - toAddress common.Address - crossTxID string - timestamp uint64 -} - -func upsertHopBridgeOriginTx(ctx context.Context, transactionManager *TransactionManager, params originTxParams) (*MultiTransaction, error) { - // Try to find "destination" half of the multiTx - multiTx, err := transactionManager.GetBridgeDestinationMultiTransaction(ctx, params.toNetworkID, params.crossTxID) - if err != nil { - return nil, err - } - - if multiTx == nil { - multiTx = NewMultiTransaction( - /* Timestamp: */ params.timestamp, // Common data - /* FromNetworkID: */ params.fromNetworkID, // Data from "origin" transaction - /* ToNetworkID: */ params.toNetworkID, // Data from "origin" transaction - /* FromTxHash: */ params.fromTxHash, // Data from "origin" transaction - /* ToTxHash: */ common.Hash{}, - /* FromAddress: */ params.fromAddress, // Data from "origin" transaction - /* ToAddress: */ params.toAddress, // Data from "origin" transaction - /* FromAsset: */ params.fromAsset, // Data from "origin" transaction - /* ToAsset: */ params.fromAsset, // To be replaced by "destination" transaction, need to be non-null - /* FromAmount: */ (*hexutil.Big)(params.fromAmount), // Data from "origin" transaction - /* ToAmount: */ (*hexutil.Big)(params.fromAmount), // To be replaced by "destination" transaction, need to be non-null - /* Type: */ MultiTransactionBridge, // Common data - /* CrossTxID: */ params.crossTxID, // Common data - ) - - _, err := transactionManager.InsertMultiTransaction(multiTx) - if err != nil { - return nil, err - } - - } else { - multiTx.FromNetworkID = params.fromNetworkID - multiTx.FromTxHash = params.fromTxHash - multiTx.FromAddress = params.fromAddress - multiTx.FromAsset = params.fromAsset - multiTx.FromAmount = (*hexutil.Big)(params.fromAmount) - multiTx.Timestamp = params.timestamp - - err := transactionManager.UpdateMultiTransaction(multiTx) - if err != nil { - return nil, err - } - } - return multiTx, nil -} - -type destinationTxParams struct { - toNetworkID uint64 - toTxHash common.Hash - toAddress common.Address - toAsset string - toAmount *big.Int - crossTxID string - timestamp uint64 -} - -func upsertHopBridgeDestinationTx(ctx context.Context, transactionManager *TransactionManager, params destinationTxParams) (*MultiTransaction, error) { - // Try to find "origin" half of the multiTx - multiTx, err := transactionManager.GetBridgeOriginMultiTransaction(ctx, params.toNetworkID, params.crossTxID) - if err != nil { - return nil, err - } - - if multiTx == nil { - multiTx = NewMultiTransaction( - /* Timestamp: */ params.timestamp, // Common data - /* FromNetworkID: */ 0, // not set - /* ToNetworkID: */ params.toNetworkID, // Data from "destination" transaction - /* FromTxHash: */ common.Hash{}, - /* ToTxHash: */ params.toTxHash, // Data from "destination" transaction - /* FromAddress: */ params.toAddress, // To be replaced by "origin" transaction, need to be non-null - /* ToAddress: */ params.toAddress, // Data from "destination" transaction - /* FromAsset: */ params.toAsset, // To be replaced by "origin" transaction, need to be non-null - /* ToAsset: */ params.toAsset, // Data from "destination" transaction - /* FromAmount: */ (*hexutil.Big)(params.toAmount), // To be replaced by "origin" transaction, need to be non-null - /* ToAmount: */ (*hexutil.Big)(params.toAmount), // Data from "destination" transaction - /* Type: */ MultiTransactionBridge, // Common data - /* CrossTxID: */ params.crossTxID, // Common data - ) - - _, err := transactionManager.InsertMultiTransaction(multiTx) - if err != nil { - return nil, err - } - } else { - multiTx.ToTxHash = params.toTxHash - multiTx.ToAsset = params.toAsset - multiTx.ToAmount = (*hexutil.Big)(params.toAmount) - multiTx.Timestamp = params.timestamp - - err := transactionManager.UpdateMultiTransaction(multiTx) - if err != nil { - return nil, err - } - } - return multiTx, nil -} - -func buildHopBridgeMultitransaction(ctx context.Context, client chain.ClientInterface, transactionManager *TransactionManager, tokenManager *token.Manager, subTx *Transfer) (*MultiTransaction, error) { - // Identify if it's from/to transaction - switch w_common.GetEventType(subTx.Log) { - case w_common.HopBridgeTransferSentToL2EventType: - // L1->L2 Origin transaction - toChainID, recipient, relayer, fromAmount, err := w_common.ParseHopBridgeTransferSentToL2Log(subTx.Log) - if err != nil { - return nil, err - } - - params := originTxParams{ - fromNetworkID: subTx.NetworkID, - fromTxHash: subTx.Receipt.TxHash, - fromAddress: subTx.From, - fromAsset: "ETH", - fromAmount: fromAmount, - toNetworkID: toChainID, - toAddress: recipient, - crossTxID: getHopBridgeFromL1CrossTxID(recipient, relayer, subTx.Log.Data), - timestamp: subTx.Timestamp, - } - - return upsertHopBridgeOriginTx(ctx, transactionManager, params) - - case w_common.HopBridgeTransferFromL1CompletedEventType: - // L1->L2 Destination transaction - recipient, relayer, toAmount, err := w_common.ParseHopBridgeTransferFromL1CompletedLog(subTx.Log) - if err != nil { - return nil, err - } - - params := destinationTxParams{ - toNetworkID: subTx.NetworkID, - toTxHash: subTx.Receipt.TxHash, - toAddress: recipient, - toAsset: "ETH", - toAmount: toAmount, - crossTxID: getHopBridgeFromL1CrossTxID(recipient, relayer, subTx.Log.Data), - timestamp: subTx.Timestamp, - } - - return upsertHopBridgeDestinationTx(ctx, transactionManager, params) - - case w_common.HopBridgeTransferSentEventType: - // L2->L1 / L2->L2 Origin transaction - transferID, toChainID, recipient, fromAmount, _, _, _, _, _, err := w_common.ParseHopBridgeTransferSentLog(subTx.Log) - if err != nil { - return nil, err - } - - params := originTxParams{ - fromNetworkID: subTx.NetworkID, - fromTxHash: subTx.Receipt.TxHash, - fromAddress: subTx.From, - fromAsset: "ETH", - fromAmount: fromAmount, - toNetworkID: toChainID, - toAddress: recipient, - crossTxID: getHopBridgeFromL2CrossTxID(transferID), - timestamp: subTx.Timestamp, - } - - return upsertHopBridgeOriginTx(ctx, transactionManager, params) - - case w_common.HopBridgeWithdrawalBondedEventType: - // L2->L1 / L2->L2 Destination transaction - transferID, toAmount, err := w_common.ParseHopWithdrawalBondedLog(subTx.Log) - if err != nil { - return nil, err - } - - params := destinationTxParams{ - toNetworkID: subTx.NetworkID, - toTxHash: subTx.Receipt.TxHash, - toAddress: subTx.Address, - toAsset: "ETH", - toAmount: toAmount, - crossTxID: getHopBridgeFromL2CrossTxID(transferID), - timestamp: subTx.Timestamp, - } - - return upsertHopBridgeDestinationTx(ctx, transactionManager, params) - } - return nil, nil -} diff --git a/services/wallet/transfer/commands.go b/services/wallet/transfer/commands.go index 5e880de03..d836ff556 100644 --- a/services/wallet/transfer/commands.go +++ b/services/wallet/transfer/commands.go @@ -201,17 +201,16 @@ func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) { } type transfersCommand struct { - db *Database - blockDAO *BlockDAO - eth *ETHDownloader - blockNums []*big.Int - address common.Address - chainClient chain.ClientInterface - blocksLimit int - transactionManager *TransactionManager - pendingTxManager *transactions.PendingTxTracker - tokenManager *token.Manager - feed *event.Feed + db *Database + blockDAO *BlockDAO + eth *ETHDownloader + blockNums []*big.Int + address common.Address + chainClient chain.ClientInterface + blocksLimit int + pendingTxManager *transactions.PendingTxTracker + tokenManager *token.Manager + feed *event.Feed // result fetchedTransfers []Transfer @@ -274,13 +273,6 @@ func (c *transfersCommand) Run(ctx context.Context) (err error) { logutils.ZapLogger().Error("saveAndConfirmPending error", zap.Error(err)) return err } - - // Check if multi transaction needs to be created - err = c.processMultiTransactions(ctx, allTransfers) - if err != nil { - logutils.ZapLogger().Error("processMultiTransactions error", zap.Error(err)) - return err - } } else { // If no transfers found, that is suspecting, because downloader returned this block as containing transfers logutils.ZapLogger().Error("no transfers found in block", @@ -422,73 +414,6 @@ func (c *transfersCommand) confirmPendingTransactions(tx *sql.Tx, allTransfers [ return notifyFunctions } -// Mark all subTxs of a given Tx with the same multiTxID -func setMultiTxID(tx Transaction, multiTxID w_common.MultiTransactionIDType) { - for _, subTx := range tx { - subTx.MultiTransactionID = multiTxID - } -} - -func (c *transfersCommand) markMultiTxTokensAsPreviouslyOwned(ctx context.Context, multiTransaction *MultiTransaction, ownerAddress common.Address) { - if multiTransaction == nil { - return - } - if len(multiTransaction.ToAsset) > 0 && multiTransaction.ToNetworkID > 0 { - token := c.tokenManager.GetToken(multiTransaction.ToNetworkID, multiTransaction.ToAsset) - _, _ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress) - } - if len(multiTransaction.FromAsset) > 0 && multiTransaction.FromNetworkID > 0 { - token := c.tokenManager.GetToken(multiTransaction.FromNetworkID, multiTransaction.FromAsset) - _, _ = c.tokenManager.MarkAsPreviouslyOwnedToken(token, ownerAddress) - } -} - -func (c *transfersCommand) checkAndProcessSwapMultiTx(ctx context.Context, tx Transaction) (bool, error) { - for _, subTx := range tx { - switch subTx.Type { - // If the Tx contains any uniswapV2Swap/uniswapV3Swap subTx, generate a Swap multiTx - case w_common.UniswapV2Swap, w_common.UniswapV3Swap: - multiTransaction, err := buildUniswapSwapMultitransaction(ctx, c.chainClient, c.tokenManager, subTx) - if err != nil { - return false, err - } - - if multiTransaction != nil { - id, err := c.transactionManager.InsertMultiTransaction(multiTransaction) - if err != nil { - return false, err - } - setMultiTxID(tx, id) - c.markMultiTxTokensAsPreviouslyOwned(ctx, multiTransaction, subTx.Address) - return true, nil - } - } - } - - return false, nil -} - -func (c *transfersCommand) checkAndProcessBridgeMultiTx(ctx context.Context, tx Transaction) (bool, error) { - for _, subTx := range tx { - switch subTx.Type { - // If the Tx contains any hopBridge subTx, create/update Bridge multiTx - case w_common.HopBridgeFrom, w_common.HopBridgeTo: - multiTransaction, err := buildHopBridgeMultitransaction(ctx, c.chainClient, c.transactionManager, c.tokenManager, subTx) - if err != nil { - return false, err - } - - if multiTransaction != nil { - setMultiTxID(tx, multiTransaction.ID) - c.markMultiTxTokensAsPreviouslyOwned(ctx, multiTransaction, subTx.Address) - return true, nil - } - } - } - - return false, nil -} - func (c *transfersCommand) processUnknownErc20CommunityTransactions(ctx context.Context, allTransfers []Transfer) { for _, tx := range allTransfers { // To can be nil in case of erc20 contract creation @@ -508,36 +433,6 @@ func (c *transfersCommand) processUnknownErc20CommunityTransactions(ctx context. } } -func (c *transfersCommand) processMultiTransactions(ctx context.Context, allTransfers []Transfer) error { - txByTxHash := subTransactionListToTransactionsByTxHash(allTransfers) - - // Detect / Generate multitransactions - // Iterate over all detected transactions - for _, tx := range txByTxHash { - // Check if already matched to a multi transaction - if tx[0].MultiTransactionID > 0 { - continue - } - - // Then check for a Swap transaction - txProcessed, err := c.checkAndProcessSwapMultiTx(ctx, tx) - if err != nil { - return err - } - if txProcessed { - continue - } - - // Then check for a Bridge transaction - _, err = c.checkAndProcessBridgeMultiTx(ctx, tx) - if err != nil { - return err - } - } - - return nil -} - func (c *transfersCommand) notifyOfNewTransfers(blockNum *big.Int, transfers []Transfer) { if c.feed != nil { if len(transfers) > 0 { @@ -591,16 +486,15 @@ func (c *transfersCommand) notifyOfLatestTransfers(transfers []Transfer, transfe } type loadTransfersCommand struct { - accounts []common.Address - db *Database - blockDAO *BlockDAO - chainClient chain.ClientInterface - blocksByAddress map[common.Address][]*big.Int - transactionManager *TransactionManager - pendingTxManager *transactions.PendingTxTracker - blocksLimit int - tokenManager *token.Manager - feed *event.Feed + accounts []common.Address + db *Database + blockDAO *BlockDAO + chainClient chain.ClientInterface + blocksByAddress map[common.Address][]*big.Int + pendingTxManager *transactions.PendingTxTracker + blocksLimit int + tokenManager *token.Manager + feed *event.Feed } func (c *loadTransfersCommand) Command() async.Command { @@ -617,13 +511,12 @@ func (c *loadTransfersCommand) Command() async.Command { // in `transferCommand` with exponential backoff instead of `loadTransfersCommand` (issue #4608). func (c *loadTransfersCommand) Run(parent context.Context) (err error) { return loadTransfers(parent, c.blockDAO, c.db, c.chainClient, c.blocksLimit, c.blocksByAddress, - c.transactionManager, c.pendingTxManager, c.tokenManager, c.feed) + c.pendingTxManager, c.tokenManager, c.feed) } func loadTransfers(ctx context.Context, blockDAO *BlockDAO, db *Database, chainClient chain.ClientInterface, blocksLimitPerAccount int, blocksByAddress map[common.Address][]*big.Int, - transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker, - tokenManager *token.Manager, feed *event.Feed) error { + pendingTxManager *transactions.PendingTxTracker, tokenManager *token.Manager, feed *event.Feed) error { logutils.ZapLogger().Debug("loadTransfers start", zap.Uint64("chain", chainClient.NetworkID()), @@ -646,11 +539,10 @@ func loadTransfers(ctx context.Context, blockDAO *BlockDAO, db *Database, signer: types.LatestSignerForChainID(chainClient.ToBigInt()), db: db, }, - blockNums: blocksByAddress[address], - transactionManager: transactionManager, - pendingTxManager: pendingTxManager, - tokenManager: tokenManager, - feed: feed, + blockNums: blocksByAddress[address], + pendingTxManager: pendingTxManager, + tokenManager: tokenManager, + feed: feed, } group.Add(transfers.Command()) } @@ -692,31 +584,3 @@ func uniqueHeaderPerBlockHash(allHeaders []*DBHeader) []*DBHeader { return uniqHeaders } - -// Organize subTransactions by Transaction Hash -func subTransactionListToTransactionsByTxHash(subTransactions []Transfer) map[common.Hash]Transaction { - rst := map[common.Hash]Transaction{} - - for index := range subTransactions { - subTx := &subTransactions[index] - txHash := subTx.Transaction.Hash() - - if _, ok := rst[txHash]; !ok { - rst[txHash] = make([]*Transfer, 0) - } - rst[txHash] = append(rst[txHash], subTx) - } - - return rst -} - -func IsTransferDetectionEvent(ev walletevent.EventType) bool { - if ev == EventInternalETHTransferDetected || - ev == EventInternalERC20TransferDetected || - ev == EventInternalERC721TransferDetected || - ev == EventInternalERC1155TransferDetected { - return true - } - - return false -} diff --git a/services/wallet/transfer/commands_sequential.go b/services/wallet/transfer/commands_sequential.go index b9e9893c6..19961aa74 100644 --- a/services/wallet/transfer/commands_sequential.go +++ b/services/wallet/transfer/commands_sequential.go @@ -1081,7 +1081,7 @@ func (c *loadBlocksAndTransfersCommand) startTransfersLoop(ctx context.Context) go func() { defer gocommon.LogOnPanic() _ = loadTransfers(ctx, c.blockDAO, c.db, c.chainClient, noBlockLimit, - blocksByAddress, c.transactionManager, c.pendingTxManager, c.tokenManager, c.feed) + blocksByAddress, c.pendingTxManager, c.tokenManager, c.feed) }() } } @@ -1090,26 +1090,25 @@ func (c *loadBlocksAndTransfersCommand) startTransfersLoop(ctx context.Context) func newLoadBlocksAndTransfersCommand(accounts []common.Address, db *Database, accountsDB *accounts.Database, blockDAO *BlockDAO, blockRangesSeqDAO BlockRangeDAOer, chainClient chain.ClientInterface, feed *event.Feed, - transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker, + pendingTxManager *transactions.PendingTxTracker, tokenManager *token.Manager, balanceCacher balance.Cacher, omitHistory bool, blockChainState *blockchainstate.BlockChainState) *loadBlocksAndTransfersCommand { return &loadBlocksAndTransfersCommand{ - accounts: accounts, - db: db, - blockRangeDAO: blockRangesSeqDAO, - accountsDB: accountsDB, - blockDAO: blockDAO, - chainClient: chainClient, - feed: feed, - balanceCacher: balanceCacher, - transactionManager: transactionManager, - pendingTxManager: pendingTxManager, - tokenManager: tokenManager, - blocksLoadedCh: make(chan []*DBHeader, 100), - omitHistory: omitHistory, - contractMaker: tokenManager.ContractMaker, - blockChainState: blockChainState, + accounts: accounts, + db: db, + blockRangeDAO: blockRangesSeqDAO, + accountsDB: accountsDB, + blockDAO: blockDAO, + chainClient: chainClient, + feed: feed, + balanceCacher: balanceCacher, + pendingTxManager: pendingTxManager, + tokenManager: tokenManager, + blocksLoadedCh: make(chan []*DBHeader, 100), + omitHistory: omitHistory, + contractMaker: tokenManager.ContractMaker, + blockChainState: blockChainState, } } @@ -1123,13 +1122,12 @@ type loadBlocksAndTransfersCommand struct { feed *event.Feed balanceCacher balance.Cacher // nonArchivalRPCNode bool // TODO Make use of it - transactionManager *TransactionManager - pendingTxManager *transactions.PendingTxTracker - tokenManager *token.Manager - blocksLoadedCh chan []*DBHeader - omitHistory bool - contractMaker *contracts.ContractMaker - blockChainState *blockchainstate.BlockChainState + pendingTxManager *transactions.PendingTxTracker + tokenManager *token.Manager + blocksLoadedCh chan []*DBHeader + omitHistory bool + contractMaker *contracts.ContractMaker + blockChainState *blockchainstate.BlockChainState // Not to be set by the caller transfersLoaded map[common.Address]bool // For event RecentHistoryReady to be sent only once per account during app lifetime @@ -1446,15 +1444,14 @@ func (c *loadBlocksAndTransfersCommand) startFetchingTransfersForLoadedBlocks(gr go func() { defer gocommon.LogOnPanic() txCommand := &loadTransfersCommand{ - accounts: c.accounts, - db: c.db, - blockDAO: c.blockDAO, - chainClient: c.chainClient, - transactionManager: c.transactionManager, - pendingTxManager: c.pendingTxManager, - tokenManager: c.tokenManager, - blocksByAddress: blocksMap, - feed: c.feed, + accounts: c.accounts, + db: c.db, + blockDAO: c.blockDAO, + chainClient: c.chainClient, + pendingTxManager: c.pendingTxManager, + tokenManager: c.tokenManager, + blocksByAddress: blocksMap, + feed: c.feed, } group.Add(txCommand.Command()) diff --git a/services/wallet/transfer/commands_sequential_test.go b/services/wallet/transfer/commands_sequential_test.go index 426d14668..ec43c77e7 100644 --- a/services/wallet/transfer/commands_sequential_test.go +++ b/services/wallet/transfer/commands_sequential_test.go @@ -1335,7 +1335,6 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) { db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{}) require.NoError(t, err) - tm := &TransactionManager{NewMultiTransactionDB(db), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} mediaServer, err := server.NewMediaServer(appdb, nil, nil, db) require.NoError(t, err) @@ -1395,20 +1394,19 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) { require.NoError(t, err) cmd := &loadBlocksAndTransfersCommand{ - accounts: []common.Address{address}, - db: wdb, - blockRangeDAO: &BlockRangeSequentialDAO{wdb.client}, - blockDAO: &BlockDAO{db}, - accountsDB: accDB, - chainClient: tc, - feed: &event.Feed{}, - balanceCacher: balance.NewCacherWithTTL(5 * time.Minute), - transactionManager: tm, - pendingTxManager: tracker, - tokenManager: tokenManager, - blocksLoadedCh: blockChannel, - omitHistory: true, - contractMaker: tokenManager.ContractMaker, + accounts: []common.Address{address}, + db: wdb, + blockRangeDAO: &BlockRangeSequentialDAO{wdb.client}, + blockDAO: &BlockDAO{db}, + accountsDB: accDB, + chainClient: tc, + feed: &event.Feed{}, + balanceCacher: balance.NewCacherWithTTL(5 * time.Minute), + pendingTxManager: tracker, + tokenManager: tokenManager, + blocksLoadedCh: blockChannel, + omitHistory: true, + contractMaker: tokenManager.ContractMaker, } tc.prepareBalanceHistory(int(tc.currentBlock)) diff --git a/services/wallet/transfer/reactor.go b/services/wallet/transfer/reactor.go index 7d63d59eb..f058df0cf 100644 --- a/services/wallet/transfer/reactor.go +++ b/services/wallet/transfer/reactor.go @@ -48,36 +48,34 @@ type HistoryFetcher interface { // Reactor listens to new blocks and stores transfers into the database. type Reactor struct { - db *Database - blockDAO *BlockDAO - blockRangesSeqDAO *BlockRangeSequentialDAO - accountsDB *accounts.Database - feed *event.Feed - transactionManager *TransactionManager - pendingTxManager *transactions.PendingTxTracker - tokenManager *token.Manager - strategy HistoryFetcher - balanceCacher balance.Cacher - omitHistory bool - blockChainState *blockchainstate.BlockChainState - chainIDs []uint64 + db *Database + blockDAO *BlockDAO + blockRangesSeqDAO *BlockRangeSequentialDAO + accountsDB *accounts.Database + feed *event.Feed + pendingTxManager *transactions.PendingTxTracker + tokenManager *token.Manager + strategy HistoryFetcher + balanceCacher balance.Cacher + omitHistory bool + blockChainState *blockchainstate.BlockChainState + chainIDs []uint64 } func NewReactor(db *Database, blockDAO *BlockDAO, blockRangesSeqDAO *BlockRangeSequentialDAO, accountsDB *accounts.Database, feed *event.Feed, tm *TransactionManager, pendingTxManager *transactions.PendingTxTracker, tokenManager *token.Manager, balanceCacher balance.Cacher, omitHistory bool, blockChainState *blockchainstate.BlockChainState) *Reactor { return &Reactor{ - db: db, - accountsDB: accountsDB, - blockDAO: blockDAO, - blockRangesSeqDAO: blockRangesSeqDAO, - feed: feed, - transactionManager: tm, - pendingTxManager: pendingTxManager, - tokenManager: tokenManager, - balanceCacher: balanceCacher, - omitHistory: omitHistory, - blockChainState: blockChainState, + db: db, + accountsDB: accountsDB, + blockDAO: blockDAO, + blockRangesSeqDAO: blockRangesSeqDAO, + feed: feed, + pendingTxManager: pendingTxManager, + tokenManager: tokenManager, + balanceCacher: balanceCacher, + omitHistory: omitHistory, + blockChainState: blockChainState, } } @@ -114,7 +112,6 @@ func (r *Reactor) createFetchStrategy(chainClients map[uint64]chain.ClientInterf r.blockRangesSeqDAO, r.accountsDB, r.feed, - r.transactionManager, r.pendingTxManager, r.tokenManager, chainClients, diff --git a/services/wallet/transfer/sequential_fetch_strategy.go b/services/wallet/transfer/sequential_fetch_strategy.go index 44db61833..3de56144d 100644 --- a/services/wallet/transfer/sequential_fetch_strategy.go +++ b/services/wallet/transfer/sequential_fetch_strategy.go @@ -21,7 +21,7 @@ import ( ) func NewSequentialFetchStrategy(db *Database, blockDAO *BlockDAO, blockRangesSeqDAO *BlockRangeSequentialDAO, accountsDB *accounts.Database, feed *event.Feed, - transactionManager *TransactionManager, pendingTxManager *transactions.PendingTxTracker, + pendingTxManager *transactions.PendingTxTracker, tokenManager *token.Manager, chainClients map[uint64]chain.ClientInterface, accounts []common.Address, @@ -31,45 +31,43 @@ func NewSequentialFetchStrategy(db *Database, blockDAO *BlockDAO, blockRangesSeq ) *SequentialFetchStrategy { return &SequentialFetchStrategy{ - db: db, - blockDAO: blockDAO, - blockRangesSeqDAO: blockRangesSeqDAO, - accountsDB: accountsDB, - feed: feed, - transactionManager: transactionManager, - pendingTxManager: pendingTxManager, - tokenManager: tokenManager, - chainClients: chainClients, - accounts: accounts, - balanceCacher: balanceCacher, - omitHistory: omitHistory, - blockChainState: blockChainState, + db: db, + blockDAO: blockDAO, + blockRangesSeqDAO: blockRangesSeqDAO, + accountsDB: accountsDB, + feed: feed, + pendingTxManager: pendingTxManager, + tokenManager: tokenManager, + chainClients: chainClients, + accounts: accounts, + balanceCacher: balanceCacher, + omitHistory: omitHistory, + blockChainState: blockChainState, } } type SequentialFetchStrategy struct { - db *Database - blockDAO *BlockDAO - blockRangesSeqDAO *BlockRangeSequentialDAO - accountsDB *accounts.Database - feed *event.Feed - mu sync.Mutex - group *async.Group - transactionManager *TransactionManager - pendingTxManager *transactions.PendingTxTracker - tokenManager *token.Manager - chainClients map[uint64]chain.ClientInterface - accounts []common.Address - balanceCacher balance.Cacher - omitHistory bool - blockChainState *blockchainstate.BlockChainState + db *Database + blockDAO *BlockDAO + blockRangesSeqDAO *BlockRangeSequentialDAO + accountsDB *accounts.Database + feed *event.Feed + mu sync.Mutex + group *async.Group + pendingTxManager *transactions.PendingTxTracker + tokenManager *token.Manager + chainClients map[uint64]chain.ClientInterface + accounts []common.Address + balanceCacher balance.Cacher + omitHistory bool + blockChainState *blockchainstate.BlockChainState } func (s *SequentialFetchStrategy) newCommand(chainClient chain.ClientInterface, accounts []common.Address) async.Commander { return newLoadBlocksAndTransfersCommand(accounts, s.db, s.accountsDB, s.blockDAO, s.blockRangesSeqDAO, chainClient, s.feed, - s.transactionManager, s.pendingTxManager, s.tokenManager, s.balanceCacher, s.omitHistory, s.blockChainState) + s.pendingTxManager, s.tokenManager, s.balanceCacher, s.omitHistory, s.blockChainState) } func (s *SequentialFetchStrategy) start() error { diff --git a/services/wallet/transfer/swap_identifier.go b/services/wallet/transfer/swap_identifier.go deleted file mode 100644 index 09ec01db8..000000000 --- a/services/wallet/transfer/swap_identifier.go +++ /dev/null @@ -1,262 +0,0 @@ -package transfer - -import ( - "context" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - - uniswapv2 "github.com/status-im/status-go/contracts/uniswapV2" - uniswapv3 "github.com/status-im/status-go/contracts/uniswapV3" - "github.com/status-im/status-go/rpc/chain" - w_common "github.com/status-im/status-go/services/wallet/common" - "github.com/status-im/status-go/services/wallet/token" -) - -const ETHSymbol string = "ETH" -const WETHSymbol string = "WETH" - -func fetchUniswapV2PairInfo(ctx context.Context, client chain.ClientInterface, pairAddress common.Address) (*common.Address, *common.Address, error) { - caller, err := uniswapv2.NewUniswapv2Caller(pairAddress, client) - if err != nil { - return nil, nil, err - } - - token0Address, err := caller.Token0(&bind.CallOpts{ - Context: ctx, - }) - if err != nil { - return nil, nil, err - } - - token1Address, err := caller.Token1(&bind.CallOpts{ - Context: ctx, - }) - if err != nil { - return nil, nil, err - } - - return &token0Address, &token1Address, nil -} - -func fetchUniswapV3PoolInfo(ctx context.Context, client chain.ClientInterface, poolAddress common.Address) (*common.Address, *common.Address, error) { - caller, err := uniswapv3.NewUniswapv3Caller(poolAddress, client) - if err != nil { - return nil, nil, err - } - - token0Address, err := caller.Token0(&bind.CallOpts{ - Context: ctx, - }) - if err != nil { - return nil, nil, err - } - - token1Address, err := caller.Token1(&bind.CallOpts{ - Context: ctx, - }) - if err != nil { - return nil, nil, err - } - - return &token0Address, &token1Address, nil -} - -func identifyUniswapV2Asset(tokenManager *token.Manager, chainID uint64, amount0 *big.Int, contractAddress0 common.Address, amount1 *big.Int, contractAddress1 common.Address) (token *token.Token, amount *big.Int, err error) { - // Either amount0 or amount1 should be 0 - if amount1.Sign() == 0 && amount0.Sign() != 0 { - token = tokenManager.FindTokenByAddress(chainID, contractAddress0) - if token == nil { - err = fmt.Errorf("couldn't find symbol for token0 %v", contractAddress0) - return - } - amount = amount0 - } else if amount0.Sign() == 0 && amount1.Sign() != 0 { - token = tokenManager.FindTokenByAddress(chainID, contractAddress1) - if token == nil { - err = fmt.Errorf("couldn't find symbol for token1 %v", contractAddress1) - return - } - amount = amount1 - } else { - err = fmt.Errorf("couldn't identify token %v %v %v %v", contractAddress0, amount0, contractAddress1, amount1) - return - } - - return -} - -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) - if err != nil { - return - } - - token0ContractAddress, token1ContractAddress, err := fetchUniswapV2PairInfo(ctx, client, pairAddress) - if err != nil { - return - } - - fromToken, fromAmountInt, err := identifyUniswapV2Asset(tokenManager, client.NetworkID(), amount0In, *token0ContractAddress, amount1In, *token1ContractAddress) - if err != nil { - // "Soft" error, allow to continue with unknown asset - fromAsset = "" - fromAmount = (*hexutil.Big)(big.NewInt(0)) - } else { - fromAsset = fromToken.Symbol - fromAmount = (*hexutil.Big)(fromAmountInt) - } - - toToken, toAmountInt, err := identifyUniswapV2Asset(tokenManager, client.NetworkID(), amount0Out, *token0ContractAddress, amount1Out, *token1ContractAddress) - if err != nil { - // "Soft" error, allow to continue with unknown asset - toAsset = "" - toAmount = (*hexutil.Big)(big.NewInt(0)) - } else { - toAsset = toToken.Symbol - toAmount = (*hexutil.Big)(toAmountInt) - } - - err = nil - return -} - -func identifyUniswapV3Assets(tokenManager *token.Manager, chainID uint64, amount0 *big.Int, contractAddress0 common.Address, amount1 *big.Int, contractAddress1 common.Address) (fromToken *token.Token, fromAmount *big.Int, toToken *token.Token, toAmount *big.Int, err error) { - token0 := tokenManager.FindTokenByAddress(chainID, contractAddress0) - if token0 == nil { - err = fmt.Errorf("couldn't find symbol for token0 %v", contractAddress0) - return - } - - token1 := tokenManager.FindTokenByAddress(chainID, contractAddress1) - if token1 == nil { - err = fmt.Errorf("couldn't find symbol for token1 %v", contractAddress1) - return - } - - // amount0 and amount1 are the balance deltas of the pool - // The positive amount is how much the sender spent - // The negative amount is how much the recipent got - if amount0.Sign() > 0 && amount1.Sign() < 0 { - fromToken = token0 - fromAmount = amount0 - toToken = token1 - toAmount = new(big.Int).Neg(amount1) - } else if amount0.Sign() < 0 && amount1.Sign() > 0 { - fromToken = token1 - fromAmount = amount1 - toToken = token0 - toAmount = new(big.Int).Neg(amount0) - } else { - err = fmt.Errorf("couldn't identify tokens %v %v %v %v", contractAddress0, amount0, contractAddress1, amount1) - return - } - - return -} - -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) - if err != nil { - return - } - - token0ContractAddress, token1ContractAddress, err := fetchUniswapV3PoolInfo(ctx, client, poolAddress) - if err != nil { - return - } - - fromToken, fromAmountInt, toToken, toAmountInt, err := identifyUniswapV3Assets(tokenManager, client.NetworkID(), amount0, *token0ContractAddress, amount1, *token1ContractAddress) - if err != nil { - // "Soft" error, allow to continue with unknown asset - err = nil - fromAsset = "" - fromAmount = (*hexutil.Big)(big.NewInt(0)) - toAsset = "" - toAmount = (*hexutil.Big)(big.NewInt(0)) - } else { - fromAsset = fromToken.Symbol - fromAmount = (*hexutil.Big)(fromAmountInt) - toAsset = toToken.Symbol - toAmount = (*hexutil.Big)(toAmountInt) - } - - return -} - -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 { - case w_common.UniswapV2SwapEventType: - return fetchUniswapV2Info(ctx, client, tokenManager, log) - case w_common.UniswapV3SwapEventType: - return fetchUniswapV3Info(ctx, client, tokenManager, log) - } - err = fmt.Errorf("wrong log type %s", logType) - return -} - -// 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 -func buildUniswapSwapMultitransaction(ctx context.Context, client chain.ClientInterface, tokenManager *token.Manager, transfer *Transfer) (*MultiTransaction, error) { - multiTransaction := MultiTransaction{ - Type: MultiTransactionSwap, - FromNetworkID: transfer.NetworkID, - FromTxHash: transfer.Receipt.TxHash, - FromAddress: transfer.Address, - ToNetworkID: transfer.NetworkID, - ToTxHash: transfer.Receipt.TxHash, - ToAddress: transfer.Address, - Timestamp: transfer.Timestamp, - } - - var firstSwapLog, lastSwapLog *types.Log - var firstSwapLogType, lastSwapLogType w_common.EventType - hasWETHDepositLog := false - hasWETHWithdrawalLog := false - - for _, ethlog := range transfer.Receipt.Logs { - logType := w_common.GetEventType(ethlog) - switch logType { - case w_common.WETHDepositEventType: - hasWETHDepositLog = true - case w_common.WETHWithdrawalEventType: - hasWETHWithdrawalLog = true - case w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType: - if firstSwapLog == nil { - firstSwapLog = ethlog - firstSwapLogType = logType - } - lastSwapLog = ethlog - lastSwapLogType = logType - } - } - - var err error - - multiTransaction.FromAsset, multiTransaction.FromAmount, multiTransaction.ToAsset, multiTransaction.ToAmount, err = fetchUniswapInfo(ctx, client, tokenManager, firstSwapLog, firstSwapLogType) - if err != nil { - return nil, err - } - - if firstSwapLog != lastSwapLog { - _, _, multiTransaction.ToAsset, multiTransaction.ToAmount, err = fetchUniswapInfo(ctx, client, tokenManager, lastSwapLog, lastSwapLogType) - if err != nil { - return nil, err - } - } - - // WETH and ETH have same decimals value, no need to change From/To Amount - if multiTransaction.FromAsset == WETHSymbol && hasWETHDepositLog { - multiTransaction.FromAsset = ETHSymbol - } - - if multiTransaction.ToAsset == WETHSymbol && hasWETHWithdrawalLog { - multiTransaction.ToAsset = ETHSymbol - } - - return &multiTransaction, nil -}