chore_: remove detection of external swaps and bridges

This commit is contained in:
Dario Gabriel Lipicar 2024-10-11 10:18:12 -03:00 committed by Anthony Laibe
parent c08e922f65
commit 5d4838a752
7 changed files with 118 additions and 747 deletions

View File

@ -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
}

View File

@ -208,7 +208,6 @@ type transfersCommand struct {
address common.Address
chainClient chain.ClientInterface
blocksLimit int
transactionManager *TransactionManager
pendingTxManager *transactions.PendingTxTracker
tokenManager *token.Manager
feed *event.Feed
@ -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 {
@ -596,7 +491,6 @@ type loadTransfersCommand struct {
blockDAO *BlockDAO
chainClient chain.ClientInterface
blocksByAddress map[common.Address][]*big.Int
transactionManager *TransactionManager
pendingTxManager *transactions.PendingTxTracker
blocksLimit int
tokenManager *token.Manager
@ -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()),
@ -647,7 +540,6 @@ func loadTransfers(ctx context.Context, blockDAO *BlockDAO, db *Database,
db: db,
},
blockNums: blocksByAddress[address],
transactionManager: transactionManager,
pendingTxManager: pendingTxManager,
tokenManager: tokenManager,
feed: feed,
@ -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
}

View File

@ -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,7 +1090,7 @@ 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 {
@ -1103,7 +1103,6 @@ func newLoadBlocksAndTransfersCommand(accounts []common.Address, db *Database, a
chainClient: chainClient,
feed: feed,
balanceCacher: balanceCacher,
transactionManager: transactionManager,
pendingTxManager: pendingTxManager,
tokenManager: tokenManager,
blocksLoadedCh: make(chan []*DBHeader, 100),
@ -1123,7 +1122,6 @@ 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
@ -1450,7 +1448,6 @@ func (c *loadBlocksAndTransfersCommand) startFetchingTransfersForLoadedBlocks(gr
db: c.db,
blockDAO: c.blockDAO,
chainClient: c.chainClient,
transactionManager: c.transactionManager,
pendingTxManager: c.pendingTxManager,
tokenManager: c.tokenManager,
blocksByAddress: blocksMap,

View File

@ -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)
@ -1403,7 +1402,6 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) {
chainClient: tc,
feed: &event.Feed{},
balanceCacher: balance.NewCacherWithTTL(5 * time.Minute),
transactionManager: tm,
pendingTxManager: tracker,
tokenManager: tokenManager,
blocksLoadedCh: blockChannel,

View File

@ -53,7 +53,6 @@ type Reactor struct {
blockRangesSeqDAO *BlockRangeSequentialDAO
accountsDB *accounts.Database
feed *event.Feed
transactionManager *TransactionManager
pendingTxManager *transactions.PendingTxTracker
tokenManager *token.Manager
strategy HistoryFetcher
@ -72,7 +71,6 @@ func NewReactor(db *Database, blockDAO *BlockDAO, blockRangesSeqDAO *BlockRangeS
blockDAO: blockDAO,
blockRangesSeqDAO: blockRangesSeqDAO,
feed: feed,
transactionManager: tm,
pendingTxManager: pendingTxManager,
tokenManager: tokenManager,
balanceCacher: balanceCacher,
@ -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,

View File

@ -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,
@ -36,7 +36,6 @@ func NewSequentialFetchStrategy(db *Database, blockDAO *BlockDAO, blockRangesSeq
blockRangesSeqDAO: blockRangesSeqDAO,
accountsDB: accountsDB,
feed: feed,
transactionManager: transactionManager,
pendingTxManager: pendingTxManager,
tokenManager: tokenManager,
chainClients: chainClients,
@ -55,7 +54,6 @@ type SequentialFetchStrategy struct {
feed *event.Feed
mu sync.Mutex
group *async.Group
transactionManager *TransactionManager
pendingTxManager *transactions.PendingTxTracker
tokenManager *token.Manager
chainClients map[uint64]chain.ClientInterface
@ -69,7 +67,7 @@ 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 {

View File

@ -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
}