feat_: add support for approve and swap activity entries

This commit is contained in:
Dario Gabriel Lipicar 2024-07-17 05:09:49 -03:00 committed by dlipicar
parent 79d9262430
commit 67890853bf
7 changed files with 262 additions and 111 deletions

View File

@ -725,6 +725,8 @@ func getTrInAndOutAmounts(activityType Type, trAmount sql.NullString, pTrAmount
if ok {
switch activityType {
case ApproveAT:
fallthrough
case ContractDeploymentAT:
fallthrough
case SendAT:

View File

@ -108,6 +108,7 @@ type testData struct {
multiTx1Tr2 transfer.TestTransfer // index 5, USDC/Optimism
multiTx2Tr2 transfer.TestTransfer // index 6, SNT/Mainnet
multiTx2PendingTr transfer.TestTransfer // index 7, DAI/Mainnet
multiTx3Tr1 transfer.TestTransfer // index 8, DAI/Goerli
multiTx1 transfer.MultiTransaction
multiTx1ID common.MultiTransactionIDType
@ -115,14 +116,17 @@ type testData struct {
multiTx2 transfer.MultiTransaction
multiTx2ID common.MultiTransactionIDType
multiTx3 transfer.MultiTransaction
multiTx3ID common.MultiTransactionIDType
nextIndex int
}
// Generates and adds to the DB 7 transfers and 2 multitransactions.
// There are only 4 extractable activity entries (transactions + multi-transactions) with timestamps 1-4. The others are associated with a multi-transaction
// Generates and adds to the DB 8 transfers and 3 multitransactions.
// There are only 5 extractable activity entries (transactions + multi-transactions) with timestamps 1-5. The others are associated with a multi-transaction
func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddresses []eth.Address) {
// Generates ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet
trs, fromAddresses, toAddresses := transfer.GenerateTestTransfers(t, db, 1, 7)
// Generates ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet, DAI/Goerli
trs, fromAddresses, toAddresses := transfer.GenerateTestTransfers(t, db, 1, 8)
// Plain transfer
td.tr1 = trs[0]
@ -134,7 +138,7 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddre
// Send Multitransaction containing 2 x Plain transfers
td.multiTx1Tr1 = trs[2]
td.multiTx1Tr2 = trs[4]
td.multiTx1Tr2 = trs[7]
td.multiTx1 = transfer.GenerateTestSendMultiTransaction(td.multiTx1Tr1)
td.multiTx1.ToAsset = testutils.DaiSymbol
@ -165,7 +169,17 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddre
td.multiTx2PendingTr.MultiTransactionID = td.multiTx2ID
transfer.InsertTestPendingTransaction(t, db, &td.multiTx2PendingTr)
td.nextIndex = 8
// Approve Multitransaction containing 1 x Plain transfer
td.multiTx3Tr1 = trs[4]
td.multiTx3 = transfer.GenerateTestApproveMultiTransaction(td.multiTx3Tr1)
td.multiTx3ID = transfer.InsertTestMultiTransaction(t, db, &td.multiTx3)
td.multiTx3Tr1.MultiTransactionID = td.multiTx3ID
transfer.InsertTestTransfer(t, db, td.multiTx3Tr1.From, &td.multiTx3Tr1)
td.nextIndex = 9
return td, fromAddresses, toAddresses
}
@ -201,85 +215,107 @@ func TestGetActivityEntriesAll(t *testing.T) {
var filter Filter
entries, err := getActivityEntries(context.Background(), deps, append(toAddresses, fromAddresses...), true, []common.ChainID{}, filter, 0, 10)
require.NoError(t, err)
require.Equal(t, 4, len(entries))
require.Equal(t, 5, len(entries))
// Ensure we have the correct order
var curTimestamp int64 = 4
var curTimestamp int64 = 5
for _, entry := range entries {
require.Equal(t, curTimestamp, entry.timestamp, "entries are sorted by timestamp; expected %d, got %d", curTimestamp, entry.timestamp)
curTimestamp--
}
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
id: td.tr1.MultiTransactionID,
timestamp: td.tr1.Timestamp,
activityType: ReceiveAT,
activityStatus: FinalizedAS,
amountOut: (*hexutil.Big)(big.NewInt(0)),
amountIn: (*hexutil.Big)(big.NewInt(td.tr1.Value)),
tokenOut: nil,
tokenIn: TTrToToken(t, &td.tr1.TestTransaction),
symbolOut: nil,
symbolIn: common.NewAndSet("ETH"),
sender: &td.tr1.From,
recipient: &td.tr1.To,
chainIDOut: nil,
chainIDIn: &td.tr1.ChainID,
transferType: expectedTokenType(td.tr1.Token.Address),
}, entries[3])
require.Equal(t, Entry{
payloadType: PendingTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash},
id: td.pendingTr.MultiTransactionID,
timestamp: td.pendingTr.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
amountOut: (*hexutil.Big)(big.NewInt(td.pendingTr.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &td.pendingTr.TestTransaction),
tokenIn: nil,
symbolOut: common.NewAndSet("ETH"),
symbolIn: nil,
sender: &td.pendingTr.From,
recipient: &td.pendingTr.To,
chainIDOut: &td.pendingTr.ChainID,
chainIDIn: nil,
transferType: expectedTokenType(eth.Address{}),
}, entries[2])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx1ID,
timestamp: int64(td.multiTx1.Timestamp),
activityType: SendAT,
activityStatus: FinalizedAS,
amountOut: td.multiTx1.FromAmount,
amountIn: td.multiTx1.ToAmount,
tokenOut: tokenFromSymbol(nil, td.multiTx1.FromAsset),
tokenIn: tokenFromSymbol(nil, td.multiTx1.ToAsset),
symbolOut: common.NewAndSet("USDC"),
symbolIn: common.NewAndSet("DAI"),
sender: &td.multiTx1.FromAddress,
recipient: &td.multiTx1.ToAddress,
}, entries[1])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx2ID,
timestamp: int64(td.multiTx2.Timestamp),
activityType: SendAT,
activityStatus: PendingAS,
amountOut: td.multiTx2.FromAmount,
amountIn: td.multiTx2.ToAmount,
symbolOut: common.NewAndSet("USDC"),
symbolIn: common.NewAndSet("SNT"),
tokenOut: tokenFromSymbol(nil, td.multiTx2.FromAsset),
tokenIn: tokenFromSymbol(nil, td.multiTx2.ToAsset),
sender: &td.multiTx2.FromAddress,
recipient: &td.multiTx2.ToAddress,
}, entries[0])
expectedEntries := []Entry{
Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx3ID,
timestamp: int64(td.multiTx3.Timestamp),
activityType: ApproveAT,
activityStatus: FinalizedAS,
amountOut: td.multiTx3.FromAmount,
amountIn: td.multiTx3.ToAmount,
tokenOut: tokenFromSymbol(nil, td.multiTx3.FromAsset),
tokenIn: tokenFromSymbol(nil, td.multiTx3.ToAsset),
symbolOut: common.NewAndSet("USDC"),
symbolIn: common.NewAndSet("USDC"),
sender: &td.multiTx3.FromAddress,
recipient: &td.multiTx3.ToAddress,
},
Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx2ID,
timestamp: int64(td.multiTx2.Timestamp),
activityType: SendAT,
activityStatus: PendingAS,
amountOut: td.multiTx2.FromAmount,
amountIn: td.multiTx2.ToAmount,
symbolOut: common.NewAndSet("USDC"),
symbolIn: common.NewAndSet("SNT"),
tokenOut: tokenFromSymbol(nil, td.multiTx2.FromAsset),
tokenIn: tokenFromSymbol(nil, td.multiTx2.ToAsset),
sender: &td.multiTx2.FromAddress,
recipient: &td.multiTx2.ToAddress,
},
Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx1ID,
timestamp: int64(td.multiTx1.Timestamp),
activityType: SendAT,
activityStatus: FinalizedAS,
amountOut: td.multiTx1.FromAmount,
amountIn: td.multiTx1.ToAmount,
tokenOut: tokenFromSymbol(nil, td.multiTx1.FromAsset),
tokenIn: tokenFromSymbol(nil, td.multiTx1.ToAsset),
symbolOut: common.NewAndSet("USDC"),
symbolIn: common.NewAndSet("DAI"),
sender: &td.multiTx1.FromAddress,
recipient: &td.multiTx1.ToAddress,
},
Entry{
payloadType: PendingTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash},
id: td.pendingTr.MultiTransactionID,
timestamp: td.pendingTr.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
amountOut: (*hexutil.Big)(big.NewInt(td.pendingTr.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &td.pendingTr.TestTransaction),
tokenIn: nil,
symbolOut: common.NewAndSet("ETH"),
symbolIn: nil,
sender: &td.pendingTr.From,
recipient: &td.pendingTr.To,
chainIDOut: &td.pendingTr.ChainID,
chainIDIn: nil,
transferType: expectedTokenType(eth.Address{}),
},
Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
id: td.tr1.MultiTransactionID,
timestamp: td.tr1.Timestamp,
activityType: ReceiveAT,
activityStatus: FinalizedAS,
amountOut: (*hexutil.Big)(big.NewInt(0)),
amountIn: (*hexutil.Big)(big.NewInt(td.tr1.Value)),
tokenOut: nil,
tokenIn: TTrToToken(t, &td.tr1.TestTransaction),
symbolOut: nil,
symbolIn: common.NewAndSet("ETH"),
sender: &td.tr1.From,
recipient: &td.tr1.To,
chainIDOut: nil,
chainIDIn: &td.tr1.ChainID,
transferType: expectedTokenType(td.tr1.Token.Address),
},
}
for idx, expectedEntry := range expectedEntries {
require.Equal(t, expectedEntry, entries[idx], "entry %d", idx)
}
}
// TestGetActivityEntriesWithSenderFilter covers the corner-case of having both sender and receiver in the filter.
@ -323,7 +359,7 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
td, fromTds, toTds := fillTestData(t, deps.db)
// Add 6 extractable transactions with timestamps 6-12
// Add 6 extractable transactions with timestamps 7-13
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i])
@ -337,7 +373,7 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
filter.Period.EndTimestamp = NoLimitTimestampForPeriod
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 8, len(entries))
require.Equal(t, 9, len(entries))
const simpleTrIndex = 5
// Check start and end content
@ -378,13 +414,13 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
chainIDOut: nil,
chainIDIn: nil,
transferType: nil,
}, entries[7])
}, entries[8])
// Test complete interval
filter.Period.EndTimestamp = trs[2].Timestamp
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 5, len(entries))
require.Equal(t, 6, len(entries))
// Check start and end content
require.Equal(t, Entry{
@ -424,13 +460,13 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
chainIDOut: nil,
chainIDIn: nil,
transferType: nil,
}, entries[4])
}, entries[5])
// Test end only
filter.Period.StartTimestamp = NoLimitTimestampForPeriod
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 7, len(entries))
require.Equal(t, 8, len(entries))
// Check start and end content
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
@ -469,7 +505,7 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
chainIDOut: nil,
chainIDIn: &td.tr1.ChainID,
transferType: expectedTokenType(td.tr1.Token.Address),
}, entries[6])
}, entries[7])
}
func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
@ -821,7 +857,7 @@ func TestStatusMintCustomEvent(t *testing.T) {
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 7, len(entries))
require.Equal(t, 8, len(entries))
filter.Types = []Type{MintAT}
@ -849,7 +885,7 @@ func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
require.Equal(t, 11, len(entries))
addressesFilter := []eth.Address{td.multiTx1.ToAddress, td.multiTx2.FromAddress, trs[1].From, trs[4].From, trs[3].To}
// The td.multiTx1.ToAddress and trs[3].To are missing not having them as owner address
@ -944,7 +980,7 @@ func TestGetActivityEntriesFilterByStatus(t *testing.T) {
filter.Statuses = allActivityStatusesFilter()
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 11, len(entries))
require.Equal(t, 12, len(entries))
filter.Statuses = []Status{PendingAS}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
@ -967,7 +1003,7 @@ func TestGetActivityEntriesFilterByStatus(t *testing.T) {
filter.Statuses = []Status{FinalizedAS}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 4, len(entries))
require.Equal(t, 5, len(entries))
// Combined filter
filter.Statuses = []Status{FailedAS, PendingAS}
@ -1004,7 +1040,7 @@ func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
filter.Assets = allTokensFilter()
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 13, len(entries))
require.Equal(t, 14, len(entries))
// Native tokens are network agnostic, hence all are returned
filter.Assets = []Token{{TokenType: Native, ChainID: common.ChainID(transfer.EthMainnet.ChainID)}}
@ -1025,8 +1061,8 @@ func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
}}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
// Two MT for which ChainID is ignored and one transfer on the main net and the Goerli is ignored
require.Equal(t, 3, len(entries))
// Three MT for which ChainID is ignored and one transfer on the main net and the Goerli is ignored
require.Equal(t, 4, len(entries))
require.Equal(t, Erc20, entries[0].tokenIn.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[0].tokenIn.Address)
require.Nil(t, entries[0].tokenOut)
@ -1034,11 +1070,13 @@ func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
require.Equal(t, Erc20, entries[1].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
require.Equal(t, Erc20, entries[1].tokenIn.TokenType)
require.Equal(t, transfer.SntMainnet.Address, entries[1].tokenIn.Address)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenIn.Address)
require.Equal(t, Erc20, entries[2].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
require.Equal(t, transfer.UsdcMainnet.Address, entries[2].tokenOut.Address)
require.Equal(t, Erc20, entries[2].tokenIn.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
require.Equal(t, transfer.SntMainnet.Address, entries[2].tokenIn.Address)
require.Equal(t, Erc20, entries[3].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[3].tokenOut.Address)
filter.Assets = []Token{{
TokenType: Erc20,
@ -1051,8 +1089,8 @@ func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
}}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
// Two MT for which ChainID is ignored and two transfers on the main net and Goerli
require.Equal(t, 4, len(entries))
// Three MT for which ChainID is ignored and two transfers on the main net and Goerli
require.Equal(t, 5, len(entries))
require.Equal(t, Erc20, entries[0].tokenIn.TokenType)
require.Equal(t, transfer.UsdcGoerli.Address, entries[0].tokenIn.Address)
require.Nil(t, entries[0].tokenOut)
@ -1087,7 +1125,7 @@ func TestGetActivityEntriesFilterByCollectibles(t *testing.T) {
filter.Collectibles = allTokensFilter()
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 8, len(entries))
require.Equal(t, 9, len(entries))
// Search for a specific collectible
filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[0])}
@ -1134,7 +1172,7 @@ func TestGetActivityEntriesFilterByToAddresses(t *testing.T) {
filter.CounterpartyAddresses = allAddresses
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
require.Equal(t, 11, len(entries))
filter.CounterpartyAddresses = []eth.Address{eth.HexToAddress("0x567890")}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
@ -1185,11 +1223,12 @@ func TestGetActivityEntriesFilterByNetworks(t *testing.T) {
if td.multiTx2PendingTr.ChainID != td.multiTx2Tr1.ChainID && td.multiTx2PendingTr.ChainID != td.multiTx2Tr2.ChainID {
recordPresence(td.multiTx2PendingTr.ChainID, 3)
}
recordPresence(td.multiTx3Tr1.ChainID, 4)
// Add 6 extractable transactions
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
recordPresence(trs[i].ChainID, 4+i)
recordPresence(trs[i].ChainID, 5+i)
transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i])
}
allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...)
@ -1198,14 +1237,14 @@ func TestGetActivityEntriesFilterByNetworks(t *testing.T) {
chainIDs := allNetworksFilter()
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
require.Equal(t, 11, len(entries))
chainIDs = []common.ChainID{5674839210}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 0, len(entries))
chainIDs = []common.ChainID{td.pendingTr.ChainID, td.multiTx2Tr1.ChainID, trs[3].ChainID}
chainIDs = []common.ChainID{td.pendingTr.ChainID, td.multiTx2Tr1.ChainID, trs[3].ChainID, td.multiTx3Tr1.ChainID}
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15)
require.NoError(t, err)
expectedResults := make(map[int]int)

View File

@ -240,18 +240,20 @@ func (c *transfersCommand) Run(ctx context.Context) (err error) {
c.processUnknownErc20CommunityTransactions(ctx, allTransfers)
err = c.processMultiTransactions(ctx, allTransfers)
if err != nil {
log.Error("processMultiTransactions error", "error", err)
return err
}
if len(allTransfers) > 0 {
// First, try to match to any pre-existing pending/multi-transaction
err := c.saveAndConfirmPending(allTransfers, blockNum)
if err != nil {
log.Error("saveAndConfirmPending error", "error", err)
return err
}
// Check if multi transaction needs to be created
err = c.processMultiTransactions(ctx, allTransfers)
if err != nil {
log.Error("processMultiTransactions error", "error", err)
return err
}
} else {
// If no transfers found, that is suspecting, because downloader returned this block as containing transfers
log.Error("no transfers found in block", "chain", c.chainClient.NetworkID(), "address", c.address, "block", blockNum)
@ -467,6 +469,11 @@ func (c *transfersCommand) processMultiTransactions(ctx context.Context, allTran
// 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 {

View File

@ -102,6 +102,10 @@ func addSignaturesToTransactions(transactions map[common.Hash]*TransactionDescri
}
func multiTransactionFromCommand(command *MultiTransactionCommand) *MultiTransaction {
toAmount := new(hexutil.Big)
if command.ToAmount != nil {
toAmount = command.ToAmount
}
multiTransaction := NewMultiTransaction(
/* Timestamp: */ uint64(time.Now().Unix()),
/* FromNetworkID: */ 0,
@ -113,7 +117,7 @@ func multiTransactionFromCommand(command *MultiTransactionCommand) *MultiTransac
/* FromAsset: */ command.FromAsset,
/* ToAsset: */ command.ToAsset,
/* FromAmount: */ command.FromAmount,
/* ToAmount: */ new(hexutil.Big),
/* ToAmount: */ toAmount,
/* Type: */ command.Type,
/* CrossTxID: */ "",
)

View File

@ -111,6 +111,7 @@ type MultiTransactionCommand struct {
FromAsset string `json:"fromAsset"`
ToAsset string `json:"toAsset"`
FromAmount *hexutil.Big `json:"fromAmount"`
ToAmount *hexutil.Big `json:"toAmount"`
Type MultiTransactionType `json:"type"`
}

View File

@ -39,8 +39,12 @@ func (tm *TransactionManager) CreateMultiTransactionFromCommand(command *MultiTr
multiTransaction := multiTransactionFromCommand(command)
if multiTransaction.Type == MultiTransactionSend && multiTransaction.FromNetworkID == 0 && len(data) == 1 {
multiTransaction.FromNetworkID = data[0].ChainID
// Set network for single chain transactions
switch multiTransaction.Type {
case MultiTransactionSend, MultiTransactionApprove, MultiTransactionSwap:
if multiTransaction.FromNetworkID == wallet_common.UnknownChainID && len(data) == 1 {
multiTransaction.FromNetworkID = data[0].ChainID
}
}
return multiTransaction, nil

View File

@ -120,6 +120,44 @@ func setupTransactionData(_ *testing.T, transactor transactions.TransactorIface)
return &multiTransaction, data, bridges, expectedData
}
func setupApproveTransactionData(_ *testing.T, transactor transactions.TransactorIface) (*MultiTransaction, []*pathprocessor.MultipathProcessorTxArgs, map[string]pathprocessor.PathProcessor, []*pathprocessor.MultipathProcessorTxArgs) {
SetMultiTransactionIDGenerator(StaticIDCounter())
// Create mock data for the test
tokenTransfer := generateTestTransfer(4)
multiTransaction := GenerateTestApproveMultiTransaction(tokenTransfer)
// Initialize the bridges
var rpcClient *rpc.Client = nil
bridges := make(map[string]pathprocessor.PathProcessor)
transferBridge := pathprocessor.NewTransferProcessor(rpcClient, transactor)
bridges[transferBridge.Name()] = transferBridge
data := []*pathprocessor.MultipathProcessorTxArgs{
{
//ChainID: 1, // This will be set by transaction manager
Name: transferBridge.Name(),
TransferTx: &transactions.SendTxArgs{
From: types.Address(tokenTransfer.From),
To: (*types.Address)(&tokenTransfer.To),
Value: (*hexutil.Big)(big.NewInt(tokenTransfer.Value)),
Data: types.HexBytes("0x0"),
// Symbol: multiTransaction.FromAsset, // This will be set by transaction manager
// MultiTransactionID: multiTransaction.ID, // This will be set by transaction manager
},
},
}
expectedData := make([]*pathprocessor.MultipathProcessorTxArgs, 0)
for _, tx := range data {
txCopy := deepCopyTransactionBridgeWithTransferTx(tx)
updateDataFromMultiTx([]*pathprocessor.MultipathProcessorTxArgs{txCopy}, &multiTransaction)
expectedData = append(expectedData, txCopy)
}
return &multiTransaction, data, bridges, expectedData
}
func TestSendTransactionsETHSuccess(t *testing.T) {
tm, transactor, _ := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
@ -136,6 +174,22 @@ func TestSendTransactionsETHSuccess(t *testing.T) {
require.NoError(t, err)
}
func TestSendTransactionsApproveSuccess(t *testing.T) {
tm, transactor, _ := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
multiTransaction, data, bridges, expectedData := setupApproveTransactionData(t, transactor)
// Verify that the SendTransactionWithChainID method is called for each transaction with proper arguments
// Return values are not checked, because they must be checked in Transactor tests
for _, tx := range expectedData {
transactor.EXPECT().SendTransactionWithChainID(tx.ChainID, *(tx.TransferTx), account).Return(types.Hash{}, nil)
}
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)
require.NoError(t, err)
}
func TestSendTransactionsETHFailOnBridge(t *testing.T) {
tm, transactor, ctrl := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
@ -258,3 +312,43 @@ func TestWatchTransaction_Timeout(t *testing.T) {
err = tm.WatchTransaction(ctx, chainID, transactionHash)
require.ErrorIs(t, err, ErrWatchPendingTxTimeout)
}
func TestCreateMultiTransactionFromCommand(t *testing.T) {
tm, _, _ := setupTransactionManager(t)
var command *MultiTransactionCommand
// Test types that should get chainID from the data
mtTypes := []MultiTransactionType{MultiTransactionSend, MultiTransactionApprove, MultiTransactionSwap}
for _, mtType := range mtTypes {
fromAmount := hexutil.Big(*big.NewInt(1000000000000000000))
toAmount := hexutil.Big(*big.NewInt(123))
command = &MultiTransactionCommand{
Type: mtType,
FromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"),
ToAddress: common.HexToAddress("0xabcdef1234567890abcdef1234567890abcdef12"),
FromAsset: "DAI",
ToAsset: "USDT",
FromAmount: &fromAmount,
ToAmount: &toAmount,
}
data := make([]*pathprocessor.MultipathProcessorTxArgs, 0)
data = append(data, &pathprocessor.MultipathProcessorTxArgs{
ChainID: 1,
})
multiTransaction, err := tm.CreateMultiTransactionFromCommand(command, data)
require.NoError(t, err)
require.NotNil(t, multiTransaction)
require.Equal(t, command.FromAddress, multiTransaction.FromAddress)
require.Equal(t, command.ToAddress, multiTransaction.ToAddress)
require.Equal(t, command.FromAsset, multiTransaction.FromAsset)
require.Equal(t, command.ToAsset, multiTransaction.ToAsset)
require.Equal(t, command.FromAmount, multiTransaction.FromAmount)
require.Equal(t, command.ToAmount, multiTransaction.ToAmount)
require.Equal(t, command.Type, multiTransaction.Type)
require.Equal(t, data[0].ChainID, multiTransaction.FromNetworkID)
}
}