github.com/status-im/status-go@v1.1.0/services/wallet/activity/activity_test.go (about) 1 package activity 2 3 import ( 4 "context" 5 "database/sql" 6 "encoding/hex" 7 "math/big" 8 "testing" 9 "time" 10 11 "github.com/status-im/status-go/services/wallet/common" 12 "github.com/status-im/status-go/services/wallet/testutils" 13 "github.com/status-im/status-go/services/wallet/transfer" 14 "github.com/status-im/status-go/t/helpers" 15 "github.com/status-im/status-go/walletdatabase" 16 17 eth "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/common/hexutil" 19 "github.com/ethereum/go-ethereum/core/types" 20 21 "github.com/stretchr/testify/require" 22 ) 23 24 var mockupTime = time.Unix(946724400, 0) // 2000-01-01 12:00:00 25 26 func tokenFromSymbol(chainID *common.ChainID, symbol string) *Token { 27 for i, t := range transfer.TestTokens { 28 if (chainID == nil || t.ChainID == uint64(*chainID)) && t.Symbol == symbol { 29 tokenType := Erc20 30 if testutils.SliceContains(transfer.NativeTokenIndices, i) { 31 tokenType = Native 32 } 33 return &Token{ 34 TokenType: tokenType, 35 ChainID: common.ChainID(t.ChainID), 36 Address: t.Address, 37 } 38 } 39 } 40 return nil 41 } 42 43 func tokenFromCollectible(c *transfer.TestCollectible) Token { 44 return Token{ 45 TokenType: Erc721, 46 ChainID: c.ChainID, 47 Address: c.TokenAddress, 48 TokenID: (*hexutil.Big)(c.TokenID), 49 } 50 } 51 52 func setupTestActivityDBStorageChoice(tb testing.TB, inMemory bool) (deps FilterDependencies, close func()) { 53 var db *sql.DB 54 var err error 55 cleanupDB := func() error { return nil } 56 cleanupWalletDB := func() error { return nil } 57 if inMemory { 58 db, err = helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{}) 59 require.NoError(tb, err) 60 } else { 61 db, cleanupWalletDB, err = helpers.SetupTestSQLDB(walletdatabase.DbInitializer{}, "wallet-activity-tests") 62 require.NoError(tb, err) 63 } 64 65 deps = FilterDependencies{ 66 db: db, 67 tokenSymbol: func(token Token) string { 68 switch token.TokenType { 69 case Native: 70 for i, t := range transfer.TestTokens { 71 if t.ChainID == uint64(token.ChainID) && testutils.SliceContains(transfer.NativeTokenIndices, i) { 72 return t.Symbol 73 } 74 } 75 case Erc20: 76 for _, t := range transfer.TestTokens { 77 if t.ChainID == uint64(token.ChainID) && t.Address == token.Address { 78 return t.Symbol 79 } 80 } 81 } 82 // In case of ERC721 and ERC1155 we don't have a symbol and they are not yet handled 83 return "" 84 }, 85 // tokenFromSymbol nil chainID accepts first symbol found 86 tokenFromSymbol: tokenFromSymbol, 87 currentTimestamp: func() int64 { 88 return mockupTime.Unix() 89 }, 90 } 91 92 return deps, func() { 93 require.NoError(tb, cleanupDB()) 94 require.NoError(tb, cleanupWalletDB()) 95 } 96 } 97 98 func setupTestActivityDB(tb testing.TB) (deps FilterDependencies, close func()) { 99 transfer.SetMultiTransactionIDGenerator(transfer.StaticIDCounter()) // to have different multi-transaction IDs even with fast execution 100 return setupTestActivityDBStorageChoice(tb, true) 101 } 102 103 type testData struct { 104 tr1 transfer.TestTransfer // index 1, ETH/Goerli 105 pendingTr transfer.TestTransfer // index 2, ETH/Optimism 106 multiTx1Tr1 transfer.TestTransfer // index 3, USDC/Mainnet 107 multiTx2Tr1 transfer.TestTransfer // index 4, USDC/Goerli 108 multiTx1Tr2 transfer.TestTransfer // index 5, USDC/Optimism 109 multiTx2Tr2 transfer.TestTransfer // index 6, SNT/Mainnet 110 multiTx2PendingTr transfer.TestTransfer // index 7, DAI/Mainnet 111 multiTx3Tr1 transfer.TestTransfer // index 8, DAI/Goerli 112 113 multiTx1 transfer.MultiTransaction 114 multiTx1ID common.MultiTransactionIDType 115 116 multiTx2 transfer.MultiTransaction 117 multiTx2ID common.MultiTransactionIDType 118 119 multiTx3 transfer.MultiTransaction 120 multiTx3ID common.MultiTransactionIDType 121 122 nextIndex int 123 } 124 125 // Generates and adds to the DB 8 transfers and 3 multitransactions. 126 // There are only 5 extractable activity entries (transactions + multi-transactions) with timestamps 1-5. The others are associated with a multi-transaction 127 func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddresses []eth.Address) { 128 // Generates ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet, DAI/Goerli 129 trs, fromAddresses, toAddresses := transfer.GenerateTestTransfers(t, db, 1, 8) 130 131 // Plain transfer 132 td.tr1 = trs[0] 133 transfer.InsertTestTransfer(t, db, td.tr1.To, &td.tr1) 134 135 // Pending transfer 136 td.pendingTr = trs[1] 137 transfer.InsertTestPendingTransaction(t, db, &td.pendingTr) 138 139 // Send Multitransaction containing 2 x Plain transfers 140 td.multiTx1Tr1 = trs[2] 141 td.multiTx1Tr2 = trs[7] 142 143 td.multiTx1 = transfer.GenerateTestSendMultiTransaction(td.multiTx1Tr1) 144 td.multiTx1.ToAsset = testutils.DaiSymbol 145 td.multiTx1ID = transfer.InsertTestMultiTransaction(t, db, &td.multiTx1) 146 147 td.multiTx1Tr1.MultiTransactionID = td.multiTx1ID 148 transfer.InsertTestTransfer(t, db, td.multiTx1Tr1.To, &td.multiTx1Tr1) 149 150 td.multiTx1Tr2.MultiTransactionID = td.multiTx1ID 151 transfer.InsertTestTransfer(t, db, td.multiTx1Tr2.To, &td.multiTx1Tr2) 152 153 // Send Multitransaction containing 2 x Plain transfers + 1 x Pending transfer 154 td.multiTx2Tr1 = trs[3] 155 td.multiTx2Tr2 = trs[5] 156 td.multiTx2PendingTr = trs[6] 157 158 td.multiTx2 = transfer.GenerateTestSendMultiTransaction(td.multiTx2Tr1) 159 td.multiTx2.ToAsset = testutils.SntSymbol 160 161 td.multiTx2ID = transfer.InsertTestMultiTransaction(t, db, &td.multiTx2) 162 163 td.multiTx2Tr1.MultiTransactionID = td.multiTx2ID 164 transfer.InsertTestTransfer(t, db, td.multiTx2Tr1.To, &td.multiTx2Tr1) 165 166 td.multiTx2Tr2.MultiTransactionID = td.multiTx2ID 167 transfer.InsertTestTransfer(t, db, td.multiTx2Tr2.To, &td.multiTx2Tr2) 168 169 td.multiTx2PendingTr.MultiTransactionID = td.multiTx2ID 170 transfer.InsertTestPendingTransaction(t, db, &td.multiTx2PendingTr) 171 172 // Approve Multitransaction containing 1 x Plain transfer 173 td.multiTx3Tr1 = trs[4] 174 175 td.multiTx3 = transfer.GenerateTestApproveMultiTransaction(td.multiTx3Tr1) 176 177 td.multiTx3ID = transfer.InsertTestMultiTransaction(t, db, &td.multiTx3) 178 179 td.multiTx3Tr1.MultiTransactionID = td.multiTx3ID 180 transfer.InsertTestTransfer(t, db, td.multiTx3Tr1.From, &td.multiTx3Tr1) 181 182 td.nextIndex = 9 183 return td, fromAddresses, toAddresses 184 } 185 186 func TTrToToken(t *testing.T, tt *transfer.TestTransaction) *Token { 187 token, isNative := transfer.TestTrToToken(t, tt) 188 tokenType := Erc20 189 if isNative { 190 tokenType = Native 191 } 192 return &Token{ 193 TokenType: tokenType, 194 ChainID: common.ChainID(token.ChainID), 195 Address: token.Address, 196 } 197 } 198 199 func expectedTokenType(tokenAddress eth.Address) *TransferType { 200 transferType := new(TransferType) 201 if (tokenAddress != eth.Address{}) { 202 *transferType = TransferTypeErc20 203 } else { 204 *transferType = TransferTypeEth 205 } 206 return transferType 207 } 208 209 func TestGetActivityEntriesAll(t *testing.T) { 210 deps, close := setupTestActivityDB(t) 211 defer close() 212 213 td, fromAddresses, toAddresses := fillTestData(t, deps.db) 214 215 var filter Filter 216 entries, err := getActivityEntries(context.Background(), deps, append(toAddresses, fromAddresses...), true, []common.ChainID{}, filter, 0, 10) 217 require.NoError(t, err) 218 require.Equal(t, 5, len(entries)) 219 220 // Ensure we have the correct order 221 var curTimestamp int64 = 5 222 for _, entry := range entries { 223 require.Equal(t, curTimestamp, entry.timestamp, "entries are sorted by timestamp; expected %d, got %d", curTimestamp, entry.timestamp) 224 curTimestamp-- 225 } 226 227 expectedEntries := []Entry{ 228 Entry{ 229 payloadType: MultiTransactionPT, 230 transaction: nil, 231 id: td.multiTx3ID, 232 timestamp: int64(td.multiTx3.Timestamp), 233 activityType: ApproveAT, 234 activityStatus: FinalizedAS, 235 amountOut: td.multiTx3.FromAmount, 236 amountIn: td.multiTx3.ToAmount, 237 tokenOut: tokenFromSymbol(nil, td.multiTx3.FromAsset), 238 tokenIn: tokenFromSymbol(nil, td.multiTx3.ToAsset), 239 symbolOut: common.NewAndSet("USDC"), 240 symbolIn: common.NewAndSet("USDC"), 241 sender: &td.multiTx3.FromAddress, 242 recipient: &td.multiTx3.ToAddress, 243 }, 244 Entry{ 245 payloadType: MultiTransactionPT, 246 transaction: nil, 247 id: td.multiTx2ID, 248 timestamp: int64(td.multiTx2.Timestamp), 249 activityType: SendAT, 250 activityStatus: PendingAS, 251 amountOut: td.multiTx2.FromAmount, 252 amountIn: td.multiTx2.ToAmount, 253 symbolOut: common.NewAndSet("USDC"), 254 symbolIn: common.NewAndSet("SNT"), 255 tokenOut: tokenFromSymbol(nil, td.multiTx2.FromAsset), 256 tokenIn: tokenFromSymbol(nil, td.multiTx2.ToAsset), 257 sender: &td.multiTx2.FromAddress, 258 recipient: &td.multiTx2.ToAddress, 259 }, 260 Entry{ 261 payloadType: MultiTransactionPT, 262 transaction: nil, 263 id: td.multiTx1ID, 264 timestamp: int64(td.multiTx1.Timestamp), 265 activityType: SendAT, 266 activityStatus: FinalizedAS, 267 amountOut: td.multiTx1.FromAmount, 268 amountIn: td.multiTx1.ToAmount, 269 tokenOut: tokenFromSymbol(nil, td.multiTx1.FromAsset), 270 tokenIn: tokenFromSymbol(nil, td.multiTx1.ToAsset), 271 symbolOut: common.NewAndSet("USDC"), 272 symbolIn: common.NewAndSet("DAI"), 273 sender: &td.multiTx1.FromAddress, 274 recipient: &td.multiTx1.ToAddress, 275 }, 276 Entry{ 277 payloadType: PendingTransactionPT, 278 transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash}, 279 id: td.pendingTr.MultiTransactionID, 280 timestamp: td.pendingTr.Timestamp, 281 activityType: SendAT, 282 activityStatus: PendingAS, 283 amountOut: (*hexutil.Big)(big.NewInt(td.pendingTr.Value)), 284 amountIn: (*hexutil.Big)(big.NewInt(0)), 285 tokenOut: TTrToToken(t, &td.pendingTr.TestTransaction), 286 tokenIn: nil, 287 symbolOut: common.NewAndSet("ETH"), 288 symbolIn: nil, 289 sender: &td.pendingTr.From, 290 recipient: &td.pendingTr.To, 291 chainIDOut: &td.pendingTr.ChainID, 292 chainIDIn: nil, 293 transferType: expectedTokenType(eth.Address{}), 294 }, 295 Entry{ 296 payloadType: SimpleTransactionPT, 297 transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To}, 298 id: td.tr1.MultiTransactionID, 299 timestamp: td.tr1.Timestamp, 300 activityType: ReceiveAT, 301 activityStatus: FinalizedAS, 302 amountOut: (*hexutil.Big)(big.NewInt(0)), 303 amountIn: (*hexutil.Big)(big.NewInt(td.tr1.Value)), 304 tokenOut: nil, 305 tokenIn: TTrToToken(t, &td.tr1.TestTransaction), 306 symbolOut: nil, 307 symbolIn: common.NewAndSet("ETH"), 308 sender: &td.tr1.From, 309 recipient: &td.tr1.To, 310 chainIDOut: nil, 311 chainIDIn: &td.tr1.ChainID, 312 transferType: expectedTokenType(td.tr1.Token.Address), 313 }, 314 } 315 316 for idx, expectedEntry := range expectedEntries { 317 require.Equal(t, expectedEntry, entries[idx], "entry %d", idx) 318 } 319 } 320 321 // TestGetActivityEntriesWithSenderFilter covers the corner-case of having both sender and receiver in the filter. 322 // In this specific case we expect that there will be two transactions (one probably backed by a multi-transaction) 323 // In case of both sender and receiver are included we validate we receive both entries otherwise only the "owned" 324 // transactions should be retrieved by the filter 325 func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testing.T) { 326 deps, close := setupTestActivityDB(t) 327 defer close() 328 329 // Add 4 extractable transactions with timestamps 1-4 330 td, _, _ := fillTestData(t, deps.db) 331 332 // Add another transaction with owner reversed 333 senderTr := td.tr1 334 // Ensure we have a consistent order 335 senderTr.Timestamp++ 336 // add sender as owner, fillTestData adds receiver as owner 337 transfer.InsertTestTransfer(t, deps.db, senderTr.From, &senderTr) 338 339 var filter Filter 340 entries, err := getActivityEntries(context.Background(), deps, []eth.Address{td.tr1.To, senderTr.From}, false, []common.ChainID{}, filter, 0, 10) 341 require.NoError(t, err) 342 require.Equal(t, 2, len(entries)) 343 344 // Check that the transaction are labeled alternatively as send and receive 345 require.Equal(t, SendAT, entries[0].activityType) 346 require.Equal(t, senderTr.From, entries[0].transaction.Address) 347 require.Equal(t, senderTr.From, *entries[0].sender) 348 require.Equal(t, senderTr.To, *entries[0].recipient) 349 350 require.Equal(t, ReceiveAT, entries[1].activityType) 351 require.Equal(t, td.tr1.To, *entries[1].recipient) 352 require.Equal(t, td.tr1.From, *entries[1].sender) 353 require.Equal(t, td.tr1.To, *entries[1].recipient) 354 } 355 356 func TestGetActivityEntriesFilterByTime(t *testing.T) { 357 deps, close := setupTestActivityDB(t) 358 defer close() 359 360 td, fromTds, toTds := fillTestData(t, deps.db) 361 362 // Add 6 extractable transactions with timestamps 7-13 363 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6) 364 for i := range trs { 365 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 366 } 367 368 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 369 370 // Test start only 371 var filter Filter 372 filter.Period.StartTimestamp = int64(td.multiTx1.Timestamp) 373 filter.Period.EndTimestamp = NoLimitTimestampForPeriod 374 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 375 require.NoError(t, err) 376 require.Equal(t, 9, len(entries)) 377 378 const simpleTrIndex = 5 379 // Check start and end content 380 require.Equal(t, Entry{ 381 payloadType: SimpleTransactionPT, 382 transaction: &transfer.TransactionIdentity{ChainID: trs[simpleTrIndex].ChainID, Hash: trs[simpleTrIndex].Hash, Address: trs[simpleTrIndex].To}, 383 id: 0, 384 timestamp: trs[simpleTrIndex].Timestamp, 385 activityType: ReceiveAT, 386 activityStatus: FinalizedAS, 387 amountOut: (*hexutil.Big)(big.NewInt(0)), 388 amountIn: (*hexutil.Big)(big.NewInt(trs[simpleTrIndex].Value)), 389 tokenOut: nil, 390 tokenIn: TTrToToken(t, &trs[simpleTrIndex].TestTransaction), 391 symbolOut: nil, 392 symbolIn: common.NewAndSet("USDC"), 393 sender: &trs[simpleTrIndex].From, 394 recipient: &trs[simpleTrIndex].To, 395 chainIDOut: nil, 396 chainIDIn: &trs[simpleTrIndex].ChainID, 397 transferType: expectedTokenType(trs[simpleTrIndex].Token.Address), 398 }, entries[0]) 399 require.Equal(t, Entry{ 400 payloadType: MultiTransactionPT, 401 transaction: nil, 402 id: td.multiTx1ID, 403 timestamp: int64(td.multiTx1.Timestamp), 404 activityType: SendAT, 405 activityStatus: FinalizedAS, 406 amountOut: td.multiTx1.FromAmount, 407 amountIn: td.multiTx1.ToAmount, 408 tokenOut: tokenFromSymbol(nil, td.multiTx1.FromAsset), 409 tokenIn: tokenFromSymbol(nil, td.multiTx1.ToAsset), 410 symbolOut: common.NewAndSet("USDC"), 411 symbolIn: common.NewAndSet("DAI"), 412 sender: &td.multiTx1.FromAddress, 413 recipient: &td.multiTx1.ToAddress, 414 chainIDOut: nil, 415 chainIDIn: nil, 416 transferType: nil, 417 }, entries[8]) 418 419 // Test complete interval 420 filter.Period.EndTimestamp = trs[2].Timestamp 421 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 422 require.NoError(t, err) 423 require.Equal(t, 6, len(entries)) 424 425 // Check start and end content 426 require.Equal(t, Entry{ 427 payloadType: SimpleTransactionPT, 428 transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To}, 429 id: 0, 430 timestamp: trs[2].Timestamp, 431 activityType: ReceiveAT, 432 activityStatus: FinalizedAS, 433 amountOut: (*hexutil.Big)(big.NewInt(0)), 434 amountIn: (*hexutil.Big)(big.NewInt(trs[2].Value)), 435 tokenOut: nil, 436 tokenIn: TTrToToken(t, &trs[2].TestTransaction), 437 symbolOut: nil, 438 symbolIn: common.NewAndSet("ETH"), 439 sender: &trs[2].From, 440 recipient: &trs[2].To, 441 chainIDOut: nil, 442 chainIDIn: &trs[2].ChainID, 443 transferType: expectedTokenType(trs[2].Token.Address), 444 }, entries[0]) 445 require.Equal(t, Entry{ 446 payloadType: MultiTransactionPT, 447 transaction: nil, 448 id: td.multiTx1ID, 449 timestamp: int64(td.multiTx1.Timestamp), 450 activityType: SendAT, 451 activityStatus: FinalizedAS, 452 amountOut: td.multiTx1.FromAmount, 453 amountIn: td.multiTx1.ToAmount, 454 tokenOut: tokenFromSymbol(nil, td.multiTx1.FromAsset), 455 tokenIn: tokenFromSymbol(nil, td.multiTx1.ToAsset), 456 symbolOut: common.NewAndSet("USDC"), 457 symbolIn: common.NewAndSet("DAI"), 458 sender: &td.multiTx1.FromAddress, 459 recipient: &td.multiTx1.ToAddress, 460 chainIDOut: nil, 461 chainIDIn: nil, 462 transferType: nil, 463 }, entries[5]) 464 465 // Test end only 466 filter.Period.StartTimestamp = NoLimitTimestampForPeriod 467 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 468 require.NoError(t, err) 469 require.Equal(t, 8, len(entries)) 470 // Check start and end content 471 require.Equal(t, Entry{ 472 payloadType: SimpleTransactionPT, 473 transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To}, 474 id: 0, 475 timestamp: trs[2].Timestamp, 476 activityType: ReceiveAT, 477 activityStatus: FinalizedAS, 478 amountOut: (*hexutil.Big)(big.NewInt(0)), 479 amountIn: (*hexutil.Big)(big.NewInt(trs[2].Value)), 480 tokenOut: nil, 481 tokenIn: TTrToToken(t, &trs[2].TestTransaction), 482 symbolOut: nil, 483 symbolIn: common.NewAndSet("ETH"), 484 sender: &trs[2].From, 485 recipient: &trs[2].To, 486 chainIDOut: nil, 487 chainIDIn: &trs[2].ChainID, 488 transferType: expectedTokenType(trs[2].Token.Address), 489 }, entries[0]) 490 require.Equal(t, Entry{ 491 payloadType: SimpleTransactionPT, 492 transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To}, 493 id: 0, 494 timestamp: td.tr1.Timestamp, 495 activityType: ReceiveAT, 496 activityStatus: FinalizedAS, 497 amountOut: (*hexutil.Big)(big.NewInt(0)), 498 amountIn: (*hexutil.Big)(big.NewInt(td.tr1.Value)), 499 tokenOut: nil, 500 tokenIn: TTrToToken(t, &td.tr1.TestTransaction), 501 symbolOut: nil, 502 symbolIn: common.NewAndSet("ETH"), 503 sender: &td.tr1.From, 504 recipient: &td.tr1.To, 505 chainIDOut: nil, 506 chainIDIn: &td.tr1.ChainID, 507 transferType: expectedTokenType(td.tr1.Token.Address), 508 }, entries[7]) 509 } 510 511 func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) { 512 deps, close := setupTestActivityDB(t) 513 defer close() 514 515 // Add 10 extractable transactions with timestamps 1-10 516 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, 1, 10) 517 for i := range trs { 518 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 519 } 520 521 allAddresses := append(fromTrs, toTrs...) 522 523 var filter Filter 524 // Get all 525 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 5) 526 require.NoError(t, err) 527 require.Equal(t, 5, len(entries)) 528 529 // Get time based interval 530 filter.Period.StartTimestamp = trs[2].Timestamp 531 filter.Period.EndTimestamp = trs[8].Timestamp 532 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 3) 533 require.NoError(t, err) 534 require.Equal(t, 3, len(entries)) 535 // Check start and end content 536 require.Equal(t, Entry{ 537 payloadType: SimpleTransactionPT, 538 transaction: &transfer.TransactionIdentity{ChainID: trs[8].ChainID, Hash: trs[8].Hash, Address: trs[8].To}, 539 id: 0, 540 timestamp: trs[8].Timestamp, 541 activityType: ReceiveAT, 542 activityStatus: FinalizedAS, 543 amountOut: (*hexutil.Big)(big.NewInt(0)), 544 amountIn: (*hexutil.Big)(big.NewInt(trs[8].Value)), 545 tokenOut: nil, 546 tokenIn: TTrToToken(t, &trs[8].TestTransaction), 547 symbolOut: nil, 548 symbolIn: common.NewAndSet("ETH"), 549 sender: &trs[8].From, 550 recipient: &trs[8].To, 551 chainIDOut: nil, 552 chainIDIn: &trs[8].ChainID, 553 transferType: expectedTokenType(trs[8].Token.Address), 554 }, entries[0]) 555 require.Equal(t, Entry{ 556 payloadType: SimpleTransactionPT, 557 transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To}, 558 id: 0, 559 timestamp: trs[6].Timestamp, 560 activityType: ReceiveAT, 561 activityStatus: FinalizedAS, 562 amountOut: (*hexutil.Big)(big.NewInt(0)), 563 amountIn: (*hexutil.Big)(big.NewInt(trs[6].Value)), 564 tokenOut: nil, 565 tokenIn: TTrToToken(t, &trs[6].TestTransaction), 566 symbolOut: nil, 567 symbolIn: common.NewAndSet("DAI"), 568 sender: &trs[6].From, 569 recipient: &trs[6].To, 570 chainIDOut: nil, 571 chainIDIn: &trs[6].ChainID, 572 transferType: expectedTokenType(trs[6].Token.Address), 573 }, entries[2]) 574 575 // Move window 2 entries forward 576 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 2, 3) 577 require.NoError(t, err) 578 require.Equal(t, 3, len(entries)) 579 // Check start and end content 580 require.Equal(t, Entry{ 581 payloadType: SimpleTransactionPT, 582 transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To}, 583 id: 0, 584 timestamp: trs[6].Timestamp, 585 activityType: ReceiveAT, 586 activityStatus: FinalizedAS, 587 amountOut: (*hexutil.Big)(big.NewInt(0)), 588 amountIn: (*hexutil.Big)(big.NewInt(trs[6].Value)), 589 tokenOut: nil, 590 tokenIn: TTrToToken(t, &trs[6].TestTransaction), 591 symbolOut: nil, 592 symbolIn: common.NewAndSet("DAI"), 593 sender: &trs[6].From, 594 recipient: &trs[6].To, 595 chainIDOut: nil, 596 chainIDIn: &trs[6].ChainID, 597 transferType: expectedTokenType(trs[6].Token.Address), 598 }, entries[0]) 599 require.Equal(t, Entry{ 600 payloadType: SimpleTransactionPT, 601 transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].To}, 602 id: 0, 603 timestamp: trs[4].Timestamp, 604 activityType: ReceiveAT, 605 activityStatus: FinalizedAS, 606 amountOut: (*hexutil.Big)(big.NewInt(0)), 607 amountIn: (*hexutil.Big)(big.NewInt(trs[4].Value)), 608 tokenOut: nil, 609 tokenIn: TTrToToken(t, &trs[4].TestTransaction), 610 symbolOut: nil, 611 symbolIn: common.NewAndSet("USDC"), 612 sender: &trs[4].From, 613 recipient: &trs[4].To, 614 chainIDOut: nil, 615 chainIDIn: &trs[4].ChainID, 616 transferType: expectedTokenType(trs[4].Token.Address), 617 }, entries[2]) 618 619 // Move window 4 more entries to test filter cap 620 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 6, 3) 621 require.NoError(t, err) 622 require.Equal(t, 1, len(entries)) 623 // Check start and end content 624 require.Equal(t, Entry{ 625 payloadType: SimpleTransactionPT, 626 transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To}, 627 id: 0, 628 timestamp: trs[2].Timestamp, 629 activityType: ReceiveAT, 630 activityStatus: FinalizedAS, 631 amountOut: (*hexutil.Big)(big.NewInt(0)), 632 amountIn: (*hexutil.Big)(big.NewInt(trs[2].Value)), 633 tokenOut: nil, 634 tokenIn: TTrToToken(t, &trs[2].TestTransaction), 635 symbolOut: nil, 636 symbolIn: common.NewAndSet("USDC"), 637 sender: &trs[2].From, 638 recipient: &trs[2].To, 639 chainIDOut: nil, 640 chainIDIn: &trs[2].ChainID, 641 transferType: expectedTokenType(trs[2].Token.Address), 642 }, entries[0]) 643 } 644 645 func countTypes(entries []Entry) (sendCount, receiveCount, contractCount, mintCount, swapCount, buyCount, bridgeCount, approveCount int) { 646 for _, entry := range entries { 647 switch entry.activityType { 648 case SendAT: 649 sendCount++ 650 case ReceiveAT: 651 receiveCount++ 652 case SwapAT: 653 swapCount++ 654 case BuyAT: 655 buyCount++ 656 case BridgeAT: 657 bridgeCount++ 658 case ContractDeploymentAT: 659 contractCount++ 660 case MintAT: 661 mintCount++ 662 case ApproveAT: 663 approveCount++ 664 } 665 } 666 return 667 } 668 669 func TestGetActivityEntriesFilterByType(t *testing.T) { 670 deps, close := setupTestActivityDB(t) 671 defer close() 672 673 // Adds 4 extractable transactions 674 td, tdFromAdds, tdToAddrs := fillTestData(t, deps.db) 675 // Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge, two MultiTransactionSend and one MultiTransactionApprove 676 multiTxs := make([]transfer.MultiTransaction, 6) 677 trs, fromAddrs, toAddrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, len(multiTxs)*2) 678 multiTxs[0] = transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1]) 679 multiTxs[1] = transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100) // trs[3] 680 multiTxs[2] = transfer.GenerateTestSendMultiTransaction(trs[4]) // trs[5] 681 multiTxs[3] = transfer.GenerateTestBridgeMultiTransaction(trs[6], trs[7]) 682 multiTxs[4] = transfer.GenerateTestSendMultiTransaction(trs[8]) // trs[9] 683 multiTxs[5] = transfer.GenerateTestApproveMultiTransaction(trs[10]) // trs[11] 684 685 var lastMT common.MultiTransactionIDType 686 for i := range trs { 687 if i%2 == 0 { 688 lastMT = transfer.InsertTestMultiTransaction(t, deps.db, &multiTxs[i/2]) 689 } 690 trs[i].MultiTransactionID = lastMT 691 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 692 } 693 694 trsSpecial, fromSpecial, toSpecial := transfer.GenerateTestTransfers(t, deps.db, 100, 3) 695 696 // Here not to include the modified To and From addresses 697 allAddresses := append(append(append(append(append(tdFromAdds, tdToAddrs...), fromAddrs...), toAddrs...), fromSpecial...), toSpecial...) 698 699 // Insert MintAT Collectible 700 trsSpecial[0].From = eth.HexToAddress("0x0") 701 transfer.InsertTestTransferWithOptions(t, deps.db, trsSpecial[0].To, &trsSpecial[0], &transfer.TestTransferOptions{ 702 TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), 703 TokenID: (big.NewInt(1318)), 704 }) 705 706 // Insert MintAT Token 707 trsSpecial[1].From = eth.HexToAddress("0x0") 708 inputMethod, err := hex.DecodeString("1b5ee6ae") 709 require.NoError(t, err) 710 transfer.InsertTestTransferWithOptions(t, deps.db, trsSpecial[1].To, &trsSpecial[1], &transfer.TestTransferOptions{ 711 TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb49"), 712 Tx: transfer.GenerateTxField(inputMethod), 713 }) 714 715 // Insert ContractDeploymentAt 716 trsSpecial[2].To = eth.HexToAddress("0x0") 717 transfer.InsertTestTransferWithOptions(t, deps.db, trsSpecial[2].From, &trsSpecial[2], &transfer.TestTransferOptions{ 718 NullifyAddresses: []eth.Address{trsSpecial[2].To}, 719 }) 720 721 // Test filtering out without address involved 722 var filter Filter 723 724 filter.Types = allActivityTypesFilter() 725 // Set tr1 to Receive and pendingTr to Send; rest of two MT remain default Send 726 addresses := []eth.Address{td.tr1.To, td.pendingTr.From, td.multiTx1.FromAddress, td.multiTx2.FromAddress, trs[0].From, trs[2].From, trs[4].From, trs[6].From, trs[8].From, trs[10].From, trsSpecial[0].To, trsSpecial[1].To, trsSpecial[2].From} 727 entries, err := getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 728 require.NoError(t, err) 729 require.Equal(t, 13, len(entries)) 730 731 filter.Types = []Type{SendAT, SwapAT} 732 entries, err = getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 733 require.NoError(t, err) 734 735 // 3 from td Send + 2 trs MT Send + 1 (swap) 736 require.Equal(t, 6, len(entries)) 737 738 sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount, approveCount := countTypes(entries) 739 740 require.Equal(t, 5, sendCount) 741 require.Equal(t, 0, receiveCount) 742 require.Equal(t, 0, contractCount) 743 require.Equal(t, 0, mintCount) 744 require.Equal(t, 1, swapCount) 745 require.Equal(t, 0, bridgeCount) 746 require.Equal(t, 0, approveCount) 747 748 filter.Types = []Type{BridgeAT, ReceiveAT} 749 entries, err = getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 750 require.NoError(t, err) 751 require.Equal(t, 3, len(entries)) 752 753 sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount, approveCount = countTypes(entries) 754 require.Equal(t, 0, sendCount) 755 require.Equal(t, 1, receiveCount) 756 require.Equal(t, 0, contractCount) 757 require.Equal(t, 0, mintCount) 758 require.Equal(t, 0, swapCount) 759 require.Equal(t, 2, bridgeCount) 760 require.Equal(t, 0, approveCount) 761 762 filter.Types = []Type{MintAT} 763 entries, err = getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 764 require.NoError(t, err) 765 require.Equal(t, 2, len(entries)) 766 767 sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount, approveCount = countTypes(entries) 768 require.Equal(t, 0, sendCount) 769 require.Equal(t, 0, receiveCount) 770 require.Equal(t, 0, contractCount) 771 require.Equal(t, 2, mintCount) 772 require.Equal(t, 0, swapCount) 773 require.Equal(t, 0, bridgeCount) 774 require.Equal(t, 0, approveCount) 775 776 filter.Types = []Type{ContractDeploymentAT} 777 entries, err = getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 778 require.NoError(t, err) 779 require.Equal(t, 1, len(entries)) 780 781 sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount, approveCount = countTypes(entries) 782 require.Equal(t, 0, sendCount) 783 require.Equal(t, 0, receiveCount) 784 require.Equal(t, 1, contractCount) 785 require.Equal(t, 0, mintCount) 786 require.Equal(t, 0, swapCount) 787 require.Equal(t, 0, bridgeCount) 788 require.Equal(t, 0, approveCount) 789 790 filter.Types = []Type{ApproveAT} 791 entries, err = getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 792 require.NoError(t, err) 793 require.Equal(t, 1, len(entries)) 794 795 sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount, approveCount = countTypes(entries) 796 require.Equal(t, 0, sendCount) 797 require.Equal(t, 0, receiveCount) 798 require.Equal(t, 0, contractCount) 799 require.Equal(t, 0, mintCount) 800 require.Equal(t, 0, swapCount) 801 require.Equal(t, 0, bridgeCount) 802 require.Equal(t, 1, approveCount) 803 804 // Filter with all addresses regression 805 filter.Types = []Type{SendAT} 806 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 807 require.NoError(t, err) 808 // We have 6 but one is not matched because is a receive, having owner the to address 809 require.Equal(t, 5, len(entries)) 810 } 811 812 func TestStatusMintCustomEvent(t *testing.T) { 813 deps, close := setupTestActivityDB(t) 814 defer close() 815 816 td, fromTds, toTds := fillTestData(t, deps.db) 817 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 3) 818 819 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 820 821 trs[0].From = eth.HexToAddress("0x0") 822 transfer.InsertTestTransferWithOptions(t, deps.db, trs[0].To, &trs[0], &transfer.TestTransferOptions{ 823 TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), 824 Receipt: &types.Receipt{ 825 Logs: []*types.Log{ 826 {Topics: []eth.Hash{eth.HexToHash("0xea667487ed28493de38fd2808b00affaee21d875a9e95aa01ef8352151292297")}}, 827 {Topics: []eth.Hash{eth.HexToHash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925")}}, 828 }, 829 }, 830 }) 831 // StatusMint - 0x28c427b0611d99da5c4f7368abe57e86b045b483c4689ae93e90745802335b87 832 trs[1].From = eth.HexToAddress("0x0") 833 transfer.InsertTestTransferWithOptions(t, deps.db, trs[1].To, &trs[1], &transfer.TestTransferOptions{ 834 TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb49"), 835 Receipt: &types.Receipt{ 836 Logs: []*types.Log{ 837 {Topics: []eth.Hash{eth.HexToHash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925")}}, 838 {Topics: []eth.Hash{eth.HexToHash("0x28c427b0611d99da5c4f7368abe57e86b045b483c4689ae93e90745802335b87")}}, 839 }, 840 }, 841 }) 842 843 // Log order should not matter 844 trs[2].From = eth.HexToAddress("0x0") 845 transfer.InsertTestTransferWithOptions(t, deps.db, trs[2].To, &trs[2], &transfer.TestTransferOptions{ 846 TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb49"), 847 Receipt: &types.Receipt{ 848 Logs: []*types.Log{ 849 {Topics: []eth.Hash{eth.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")}}, 850 {Topics: []eth.Hash{eth.HexToHash("0x28c427b0611d99da5c4f7368abe57e86b045b483c4689ae93e90745802335b87")}}, 851 {Topics: []eth.Hash{eth.HexToHash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925")}}, 852 }, 853 }, 854 }) 855 856 var filter Filter 857 858 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 859 require.NoError(t, err) 860 require.Equal(t, 8, len(entries)) 861 862 filter.Types = []Type{MintAT} 863 864 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 865 require.NoError(t, err) 866 require.Equal(t, 2, len(entries)) 867 require.Equal(t, trs[2].Hash, entries[0].transaction.Hash) 868 require.Equal(t, trs[1].Hash, entries[1].transaction.Hash) 869 } 870 871 func TestGetActivityEntriesFilterByAddresses(t *testing.T) { 872 deps, close := setupTestActivityDB(t) 873 defer close() 874 875 // Adds 4 extractable transactions 876 td, fromTds, toTds := fillTestData(t, deps.db) 877 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6) 878 for i := range trs { 879 transfer.InsertTestTransfer(t, deps.db, trs[i].From, &trs[i]) 880 } 881 882 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 883 884 var filter Filter 885 886 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 887 require.NoError(t, err) 888 require.Equal(t, 11, len(entries)) 889 890 addressesFilter := []eth.Address{td.multiTx1.ToAddress, td.multiTx2.FromAddress, trs[1].From, trs[4].From, trs[3].To} 891 // The td.multiTx1.ToAddress and trs[3].To are missing not having them as owner address 892 entries, err = getActivityEntries(context.Background(), deps, addressesFilter, false, []common.ChainID{}, filter, 0, 15) 893 require.NoError(t, err) 894 require.Equal(t, 3, len(entries)) 895 require.Equal(t, Entry{ 896 payloadType: SimpleTransactionPT, 897 transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].From}, 898 id: 0, 899 timestamp: trs[4].Timestamp, 900 activityType: SendAT, 901 activityStatus: FinalizedAS, 902 amountOut: (*hexutil.Big)(big.NewInt(trs[4].Value)), 903 amountIn: (*hexutil.Big)(big.NewInt(0)), 904 tokenOut: TTrToToken(t, &trs[4].TestTransaction), 905 tokenIn: nil, 906 symbolOut: common.NewAndSet("USDC"), 907 symbolIn: nil, 908 sender: &trs[4].From, 909 recipient: &trs[4].To, 910 chainIDOut: &trs[4].ChainID, 911 chainIDIn: nil, 912 transferType: expectedTokenType(trs[4].Token.Address), 913 }, entries[0]) 914 require.Equal(t, Entry{ 915 payloadType: SimpleTransactionPT, 916 transaction: &transfer.TransactionIdentity{ChainID: trs[1].ChainID, Hash: trs[1].Hash, Address: trs[1].From}, 917 id: 0, 918 timestamp: trs[1].Timestamp, 919 activityType: SendAT, 920 activityStatus: FinalizedAS, 921 amountOut: (*hexutil.Big)(big.NewInt(trs[1].Value)), 922 amountIn: (*hexutil.Big)(big.NewInt(0)), 923 tokenOut: TTrToToken(t, &trs[1].TestTransaction), 924 tokenIn: nil, 925 symbolOut: common.NewAndSet("ETH"), 926 symbolIn: nil, 927 sender: &trs[1].From, 928 recipient: &trs[1].To, 929 chainIDOut: &trs[1].ChainID, 930 chainIDIn: nil, 931 transferType: expectedTokenType(trs[1].Token.Address), 932 }, entries[1]) 933 require.Equal(t, Entry{ 934 payloadType: MultiTransactionPT, 935 transaction: nil, 936 id: td.multiTx2ID, 937 timestamp: int64(td.multiTx2.Timestamp), 938 activityType: SendAT, 939 activityStatus: PendingAS, 940 amountOut: td.multiTx2.FromAmount, 941 amountIn: td.multiTx2.ToAmount, 942 tokenOut: tokenFromSymbol(nil, td.multiTx2.FromAsset), 943 tokenIn: tokenFromSymbol(nil, td.multiTx2.ToAsset), 944 symbolOut: common.NewAndSet("USDC"), 945 symbolIn: common.NewAndSet("SNT"), 946 sender: &td.multiTx2.FromAddress, 947 recipient: &td.multiTx2.ToAddress, 948 chainIDOut: nil, 949 chainIDIn: nil, 950 }, entries[2]) 951 } 952 953 func TestGetActivityEntriesFilterByStatus(t *testing.T) { 954 deps, close := setupTestActivityDB(t) 955 defer close() 956 957 // Adds 4 extractable transactions: 1 T, 1 T pending, 1 MT pending, 1 MT with 2xT finalized 958 td, fromTds, toTds := fillTestData(t, deps.db) 959 // Add 7 extractable transactions: 1 pending, 1 Tr failed, 1 MT failed, 4 finalized 960 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 7) 961 multiTx := transfer.GenerateTestSendMultiTransaction(trs[6]) 962 failedMTID := transfer.InsertTestMultiTransaction(t, deps.db, &multiTx) 963 trs[6].MultiTransactionID = failedMTID 964 for i := range trs { 965 if i == 1 { 966 transfer.InsertTestPendingTransaction(t, deps.db, &trs[i]) 967 } else { 968 trs[i].Success = i != 3 && i != 6 969 if trs[i].Success && (i == 2 || i == 5) { 970 // Finalize status depends on timestamp 971 trs[i].Timestamp = mockupTime.Unix() - 10 972 } 973 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 974 } 975 } 976 977 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 978 979 var filter Filter 980 filter.Statuses = allActivityStatusesFilter() 981 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 982 require.NoError(t, err) 983 require.Equal(t, 12, len(entries)) 984 985 filter.Statuses = []Status{PendingAS} 986 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 987 require.NoError(t, err) 988 require.Equal(t, 3, len(entries)) 989 require.Equal(t, td.pendingTr.Hash, entries[2].transaction.Hash) 990 require.Equal(t, td.multiTx2ID, entries[1].id) 991 require.Equal(t, trs[1].Hash, entries[0].transaction.Hash) 992 993 filter.Statuses = []Status{FailedAS} 994 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 995 require.NoError(t, err) 996 require.Equal(t, 2, len(entries)) 997 998 filter.Statuses = []Status{CompleteAS} 999 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1000 require.NoError(t, err) 1001 require.Equal(t, 2, len(entries)) 1002 1003 filter.Statuses = []Status{FinalizedAS} 1004 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1005 require.NoError(t, err) 1006 require.Equal(t, 5, len(entries)) 1007 1008 // Combined filter 1009 filter.Statuses = []Status{FailedAS, PendingAS} 1010 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1011 require.NoError(t, err) 1012 require.Equal(t, 5, len(entries)) 1013 } 1014 1015 func TestGetActivityEntriesFilterByTokenType(t *testing.T) { 1016 deps, close := setupTestActivityDB(t) 1017 defer close() 1018 1019 // Adds 4 extractable transactions 2 transactions (ETH/Goerli, ETH/Optimism), one MT USDC to DAI and another MT USDC to SNT 1020 td, fromTds, toTds := fillTestData(t, deps.db) 1021 // Add 9 transactions DAI/Goerli, ETH/Mainnet, ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet 1022 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 9) 1023 for i := range trs { 1024 tokenAddr := transfer.TestTokens[i].Address 1025 trs[i].ChainID = common.ChainID(transfer.TestTokens[i].ChainID) 1026 transfer.InsertTestTransferWithOptions(t, deps.db, trs[i].To, &trs[i], &transfer.TestTransferOptions{ 1027 TokenAddress: tokenAddr, 1028 }) 1029 } 1030 1031 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 1032 1033 var filter Filter 1034 filter.FilterOutAssets = true 1035 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1036 require.NoError(t, err) 1037 require.Equal(t, 0, len(entries)) 1038 1039 filter.FilterOutAssets = false 1040 filter.Assets = allTokensFilter() 1041 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1042 require.NoError(t, err) 1043 require.Equal(t, 14, len(entries)) 1044 1045 // Native tokens are network agnostic, hence all are returned 1046 filter.Assets = []Token{{TokenType: Native, ChainID: common.ChainID(transfer.EthMainnet.ChainID)}} 1047 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1048 require.NoError(t, err) 1049 require.Equal(t, 5, len(entries)) 1050 1051 // Test that it doesn't break the filter 1052 filter.Assets = []Token{{TokenType: Erc1155}} 1053 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1054 require.NoError(t, err) 1055 require.Equal(t, 0, len(entries)) 1056 1057 filter.Assets = []Token{{ 1058 TokenType: Erc20, 1059 ChainID: common.ChainID(transfer.UsdcMainnet.ChainID), 1060 Address: transfer.UsdcMainnet.Address, 1061 }} 1062 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1063 require.NoError(t, err) 1064 // Three MT for which ChainID is ignored and one transfer on the main net and the Goerli is ignored 1065 require.Equal(t, 4, len(entries)) 1066 require.Equal(t, Erc20, entries[0].tokenIn.TokenType) 1067 require.Equal(t, transfer.UsdcMainnet.Address, entries[0].tokenIn.Address) 1068 require.Nil(t, entries[0].tokenOut) 1069 // MT has only symbol, the first token is lookup by symbol for both entries 1070 require.Equal(t, Erc20, entries[1].tokenOut.TokenType) 1071 require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address) 1072 require.Equal(t, Erc20, entries[1].tokenIn.TokenType) 1073 require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenIn.Address) 1074 require.Equal(t, Erc20, entries[2].tokenOut.TokenType) 1075 require.Equal(t, transfer.UsdcMainnet.Address, entries[2].tokenOut.Address) 1076 require.Equal(t, Erc20, entries[2].tokenIn.TokenType) 1077 require.Equal(t, transfer.SntMainnet.Address, entries[2].tokenIn.Address) 1078 require.Equal(t, Erc20, entries[3].tokenOut.TokenType) 1079 require.Equal(t, transfer.UsdcMainnet.Address, entries[3].tokenOut.Address) 1080 1081 filter.Assets = []Token{{ 1082 TokenType: Erc20, 1083 ChainID: common.ChainID(transfer.UsdcMainnet.ChainID), 1084 Address: transfer.UsdcMainnet.Address, 1085 }, { 1086 TokenType: Erc20, 1087 ChainID: common.ChainID(transfer.UsdcGoerli.ChainID), 1088 Address: transfer.UsdcGoerli.Address, 1089 }} 1090 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1091 require.NoError(t, err) 1092 // Three MT for which ChainID is ignored and two transfers on the main net and Goerli 1093 require.Equal(t, 5, len(entries)) 1094 require.Equal(t, Erc20, entries[0].tokenIn.TokenType) 1095 require.Equal(t, transfer.UsdcGoerli.Address, entries[0].tokenIn.Address) 1096 require.Nil(t, entries[0].tokenOut) 1097 } 1098 1099 func TestGetActivityEntriesFilterByCollectibles(t *testing.T) { 1100 deps, close := setupTestActivityDB(t) 1101 defer close() 1102 1103 // Adds 4 extractable transactions 2 transactions (ETH/Goerli, ETH/Optimism), one MT USDC to DAI and another MT USDC to SNT 1104 td, fromTds, toTds := fillTestData(t, deps.db) 1105 // Add 4 transactions with collectibles 1106 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 4) 1107 for i := range trs { 1108 collectibleData := transfer.TestCollectibles[i] 1109 trs[i].ChainID = collectibleData.ChainID 1110 transfer.InsertTestTransferWithOptions(t, deps.db, trs[i].To, &trs[i], &transfer.TestTransferOptions{ 1111 TokenAddress: collectibleData.TokenAddress, 1112 TokenID: collectibleData.TokenID, 1113 }) 1114 } 1115 1116 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 1117 1118 var filter Filter 1119 filter.FilterOutCollectibles = true 1120 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1121 require.NoError(t, err) 1122 require.Equal(t, 0, len(entries)) 1123 1124 filter.FilterOutCollectibles = false 1125 filter.Collectibles = allTokensFilter() 1126 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1127 require.NoError(t, err) 1128 require.Equal(t, 9, len(entries)) 1129 1130 // Search for a specific collectible 1131 filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[0])} 1132 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1133 require.NoError(t, err) 1134 require.Equal(t, 1, len(entries)) 1135 require.Equal(t, entries[0].tokenIn.Address, transfer.TestCollectibles[0].TokenAddress) 1136 require.Equal(t, entries[0].tokenIn.TokenID, (*hexutil.Big)(transfer.TestCollectibles[0].TokenID)) 1137 1138 // Search for a specific collectible 1139 filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[3])} 1140 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1141 require.NoError(t, err) 1142 require.Equal(t, 1, len(entries)) 1143 require.Equal(t, entries[0].tokenIn.Address, transfer.TestCollectibles[3].TokenAddress) 1144 require.Equal(t, entries[0].tokenIn.TokenID, (*hexutil.Big)(transfer.TestCollectibles[3].TokenID)) 1145 1146 // Search for a multiple collectibles 1147 filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[1]), tokenFromCollectible(&transfer.TestCollectibles[2])} 1148 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1149 require.NoError(t, err) 1150 require.Equal(t, 2, len(entries)) 1151 require.Equal(t, entries[0].tokenIn.Address, transfer.TestCollectibles[2].TokenAddress) 1152 require.True(t, (*big.Int)(entries[0].tokenIn.TokenID).Cmp(transfer.TestCollectibles[2].TokenID) == 0) 1153 require.Equal(t, entries[1].tokenIn.Address, transfer.TestCollectibles[1].TokenAddress) 1154 require.True(t, (*big.Int)(entries[1].tokenIn.TokenID).Cmp(transfer.TestCollectibles[1].TokenID) == 0) 1155 } 1156 1157 func TestGetActivityEntriesFilterByToAddresses(t *testing.T) { 1158 deps, close := setupTestActivityDB(t) 1159 defer close() 1160 1161 // Adds 4 extractable transactions 1162 td, fromTds, toTds := fillTestData(t, deps.db) 1163 // Add 6 extractable transactions 1164 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6) 1165 for i := range trs { 1166 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 1167 } 1168 1169 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 1170 1171 var filter Filter 1172 filter.CounterpartyAddresses = allAddresses 1173 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1174 require.NoError(t, err) 1175 require.Equal(t, 11, len(entries)) 1176 1177 filter.CounterpartyAddresses = []eth.Address{eth.HexToAddress("0x567890")} 1178 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1179 require.NoError(t, err) 1180 require.Equal(t, 0, len(entries)) 1181 1182 filter.CounterpartyAddresses = []eth.Address{td.pendingTr.To, td.multiTx2.ToAddress, trs[3].To} 1183 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1184 require.NoError(t, err) 1185 require.Equal(t, 3, len(entries)) 1186 1187 filter.CounterpartyAddresses = []eth.Address{td.tr1.To, td.pendingTr.From, trs[3].From, trs[5].To} 1188 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15) 1189 require.NoError(t, err) 1190 require.Equal(t, 2, len(entries)) 1191 } 1192 1193 func TestGetActivityEntriesFilterByNetworks(t *testing.T) { 1194 deps, close := setupTestActivityDB(t) 1195 defer close() 1196 1197 // Adds 4 extractable transactions 1198 td, fromTds, toTds := fillTestData(t, deps.db) 1199 1200 chainToEntryCount := make(map[common.ChainID]map[int]int) 1201 recordPresence := func(chainID common.ChainID, entry int) { 1202 if _, ok := chainToEntryCount[chainID]; !ok { 1203 chainToEntryCount[chainID] = make(map[int]int) 1204 chainToEntryCount[chainID][entry] = 1 1205 } else { 1206 if _, ok := chainToEntryCount[chainID][entry]; !ok { 1207 chainToEntryCount[chainID][entry] = 1 1208 } else { 1209 chainToEntryCount[chainID][entry]++ 1210 } 1211 } 1212 } 1213 recordPresence(td.tr1.ChainID, 0) 1214 recordPresence(td.pendingTr.ChainID, 1) 1215 recordPresence(td.multiTx1Tr1.ChainID, 2) 1216 if td.multiTx1Tr2.ChainID != td.multiTx1Tr1.ChainID { 1217 recordPresence(td.multiTx1Tr2.ChainID, 2) 1218 } 1219 recordPresence(td.multiTx2Tr1.ChainID, 3) 1220 if td.multiTx2Tr2.ChainID != td.multiTx2Tr1.ChainID { 1221 recordPresence(td.multiTx2Tr2.ChainID, 3) 1222 } 1223 if td.multiTx2PendingTr.ChainID != td.multiTx2Tr1.ChainID && td.multiTx2PendingTr.ChainID != td.multiTx2Tr2.ChainID { 1224 recordPresence(td.multiTx2PendingTr.ChainID, 3) 1225 } 1226 recordPresence(td.multiTx3Tr1.ChainID, 4) 1227 1228 // Add 6 extractable transactions 1229 trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6) 1230 for i := range trs { 1231 recordPresence(trs[i].ChainID, 5+i) 1232 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 1233 } 1234 allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...) 1235 1236 var filter Filter 1237 chainIDs := allNetworksFilter() 1238 entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15) 1239 require.NoError(t, err) 1240 require.Equal(t, 11, len(entries)) 1241 1242 chainIDs = []common.ChainID{5674839210} 1243 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15) 1244 require.NoError(t, err) 1245 require.Equal(t, 0, len(entries)) 1246 1247 chainIDs = []common.ChainID{td.pendingTr.ChainID, td.multiTx2Tr1.ChainID, trs[3].ChainID, td.multiTx3Tr1.ChainID} 1248 entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, chainIDs, filter, 0, 15) 1249 require.NoError(t, err) 1250 expectedResults := make(map[int]int) 1251 for _, chainID := range chainIDs { 1252 for entry := range chainToEntryCount[chainID] { 1253 if _, ok := expectedResults[entry]; !ok { 1254 expectedResults[entry]++ 1255 } 1256 } 1257 } 1258 require.Equal(t, len(expectedResults), len(entries)) 1259 } 1260 1261 func TestGetActivityEntriesFilterByNetworksOfSubTransactions(t *testing.T) { 1262 deps, close := setupTestActivityDB(t) 1263 defer close() 1264 1265 // Add 6 extractable transactions 1266 trs, _, toTrs := transfer.GenerateTestTransfers(t, deps.db, 0, 5) 1267 trs[0].ChainID = 1231 1268 trs[1].ChainID = 1232 1269 trs[2].ChainID = 1233 1270 mt1 := transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1]) 1271 trs[0].MultiTransactionID = transfer.InsertTestMultiTransaction(t, deps.db, &mt1) 1272 trs[1].MultiTransactionID = mt1.ID 1273 trs[2].MultiTransactionID = mt1.ID 1274 1275 trs[3].ChainID = 1234 1276 mt2 := transfer.GenerateTestSwapMultiTransaction(trs[3], testutils.SntSymbol, 100) 1277 // insertMultiTransaction will insert 0 instead of NULL 1278 mt2.FromNetworkID = 0 1279 mt2.ToNetworkID = 0 1280 trs[3].MultiTransactionID = transfer.InsertTestMultiTransaction(t, deps.db, &mt2) 1281 1282 for i := range trs { 1283 if i == 2 { 1284 transfer.InsertTestPendingTransaction(t, deps.db, &trs[i]) 1285 } else { 1286 transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 1287 } 1288 } 1289 1290 var filter Filter 1291 chainIDs := allNetworksFilter() 1292 entries, err := getActivityEntries(context.Background(), deps, toTrs, false, chainIDs, filter, 0, 15) 1293 require.NoError(t, err) 1294 require.Equal(t, 3, len(entries)) 1295 1296 chainIDs = []common.ChainID{trs[0].ChainID, trs[1].ChainID} 1297 entries, err = getActivityEntries(context.Background(), deps, toTrs, false, chainIDs, filter, 0, 15) 1298 require.NoError(t, err) 1299 require.Equal(t, 1, len(entries)) 1300 require.Equal(t, entries[0].id, mt1.ID) 1301 1302 // Filter by pending_transactions sub-transacitons 1303 chainIDs = []common.ChainID{trs[2].ChainID} 1304 entries, err = getActivityEntries(context.Background(), deps, toTrs, false, chainIDs, filter, 0, 15) 1305 require.NoError(t, err) 1306 require.Equal(t, 1, len(entries)) 1307 require.Equal(t, entries[0].id, mt1.ID) 1308 1309 chainIDs = []common.ChainID{trs[3].ChainID} 1310 entries, err = getActivityEntries(context.Background(), deps, toTrs, false, chainIDs, filter, 0, 15) 1311 require.NoError(t, err) 1312 require.Equal(t, 1, len(entries)) 1313 require.Equal(t, entries[0].id, mt2.ID) 1314 } 1315 1316 func TestGetActivityEntriesCheckToAndFrom(t *testing.T) { 1317 deps, close := setupTestActivityDB(t) 1318 defer close() 1319 1320 // Adds 6 transactions from which 4 are filtered out 1321 td, _, _ := fillTestData(t, deps.db) 1322 1323 // Add extra transactions to test To address 1324 trs, _, _ := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 2) 1325 transfer.InsertTestTransfer(t, deps.db, trs[0].To, &trs[0]) 1326 transfer.InsertTestPendingTransaction(t, deps.db, &trs[1]) 1327 1328 addresses := []eth.Address{td.tr1.To, td.pendingTr.To, 1329 td.multiTx1.FromAddress, td.multiTx2.FromAddress, trs[0].To, trs[1].To} 1330 1331 var filter Filter 1332 entries, err := getActivityEntries(context.Background(), deps, addresses, false, []common.ChainID{}, filter, 0, 15) 1333 require.NoError(t, err) 1334 require.Equal(t, 6, len(entries)) 1335 1336 require.Equal(t, ReceiveAT, entries[5].activityType) // td.tr1 1337 require.NotEqual(t, eth.Address{}, entries[5].transaction.Address) // td.tr1 1338 require.Equal(t, td.tr1.To, *entries[5].recipient) // td.tr1 1339 1340 require.Equal(t, ReceiveAT, entries[4].activityType) // td.pendingTr 1341 1342 // Multi-transactions are always considered as SendAT 1343 require.Equal(t, SendAT, entries[3].activityType) // td.multiTx1 1344 require.Equal(t, SendAT, entries[2].activityType) // td.multiTx2 1345 1346 require.Equal(t, ReceiveAT, entries[1].activityType) // trs[0] 1347 require.NotEqual(t, eth.Address{}, entries[1].transaction.Address) // trs[0] 1348 require.Equal(t, trs[0].To, entries[1].transaction.Address) // trs[0] 1349 1350 require.Equal(t, ReceiveAT, entries[0].activityType) // trs[1] (pending) 1351 } 1352 1353 func TestGetActivityEntriesCheckContextCancellation(t *testing.T) { 1354 deps, close := setupTestActivityDB(t) 1355 defer close() 1356 1357 _, fromAddresses, toAddresses := fillTestData(t, deps.db) 1358 1359 cancellableCtx, cancelFn := context.WithCancel(context.Background()) 1360 cancelFn() 1361 1362 activities, err := getActivityEntries(cancellableCtx, deps, append(fromAddresses, toAddresses...), true, []common.ChainID{}, Filter{}, 0, 10) 1363 require.ErrorIs(t, err, context.Canceled) 1364 require.Equal(t, 0, len(activities)) 1365 } 1366 1367 func TestGetActivityEntriesNullAddresses(t *testing.T) { 1368 deps, close := setupTestActivityDB(t) 1369 defer close() 1370 1371 trs, _, _ := transfer.GenerateTestTransfers(t, deps.db, 0, 4) 1372 multiTx := transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1]) 1373 multiTx.ToAddress = eth.Address{} 1374 1375 trs[0].MultiTransactionID = transfer.InsertTestMultiTransaction(t, deps.db, &multiTx) 1376 trs[1].MultiTransactionID = trs[0].MultiTransactionID 1377 1378 for i := 0; i < 3; i++ { 1379 transfer.InsertTestTransferWithOptions(t, deps.db, trs[i].From, &trs[i], &transfer.TestTransferOptions{ 1380 NullifyAddresses: []eth.Address{trs[i].To}, 1381 }) 1382 } 1383 1384 trs[3].To = eth.Address{} 1385 transfer.InsertTestPendingTransaction(t, deps.db, &trs[3]) 1386 1387 addresses := []eth.Address{trs[0].From, trs[1].From, trs[2].From, trs[3].From} 1388 1389 activities, err := getActivityEntries(context.Background(), deps, addresses, false, allNetworksFilter(), Filter{}, 0, 10) 1390 require.NoError(t, err) 1391 require.Equal(t, 3, len(activities)) 1392 } 1393 1394 func TestGetActivityEntries_ErrorIfNoAddress(t *testing.T) { 1395 _, err := getActivityEntries(context.Background(), FilterDependencies{}, []eth.Address{}, true, []common.ChainID{}, Filter{}, 0, 10) 1396 require.EqualError(t, err, "no addresses provided") 1397 } 1398 1399 func TestGetTxDetails(t *testing.T) { 1400 deps, close := setupTestActivityDB(t) 1401 defer close() 1402 1403 // Adds 4 extractable transactions 2 transactions (ETH/Goerli, ETH/Optimism), one MT USDC to DAI and another MT USDC to SNT 1404 td, _, _ := fillTestData(t, deps.db) 1405 1406 _, err := getTxDetails(context.Background(), deps.db, "") 1407 require.EqualError(t, err, "invalid tx id") 1408 1409 details, err := getTxDetails(context.Background(), deps.db, td.tr1.Hash.String()) 1410 require.NoError(t, err) 1411 1412 require.Equal(t, td.tr1.Hash.String(), details.ID) 1413 require.Equal(t, 0, details.MultiTxID) 1414 require.Equal(t, td.tr1.Nonce, details.Nonce) 1415 require.Equal(t, len(details.ChainDetails), 1) 1416 require.Equal(t, td.tr1.ChainID, common.ChainID(details.ChainDetails[0].ChainID)) 1417 require.Equal(t, td.tr1.BlkNumber, details.ChainDetails[0].BlockNumber) 1418 require.Equal(t, td.tr1.Hash, details.ChainDetails[0].Hash) 1419 require.Equal(t, td.tr1.Contract, *details.ChainDetails[0].Contract) 1420 } 1421 1422 func TestGetMultiTxDetails(t *testing.T) { 1423 deps, close := setupTestActivityDB(t) 1424 defer close() 1425 1426 // Adds 4 extractable transactions 2 transactions (ETH/Goerli, ETH/Optimism), one MT USDC to DAI and another MT USDC to SNT 1427 td, _, _ := fillTestData(t, deps.db) 1428 1429 _, err := getMultiTxDetails(context.Background(), deps.db, 0) 1430 require.EqualError(t, err, "invalid tx id") 1431 1432 details, err := getMultiTxDetails(context.Background(), deps.db, int(td.multiTx1.ID)) 1433 require.NoError(t, err) 1434 1435 require.Equal(t, "", details.ID) 1436 require.Equal(t, int(td.multiTx1.ID), details.MultiTxID) 1437 require.Equal(t, td.multiTx1Tr2.Nonce, details.Nonce) 1438 require.Equal(t, 2, len(details.ChainDetails)) 1439 require.Equal(t, td.multiTx1Tr1.ChainID, common.ChainID(details.ChainDetails[0].ChainID)) 1440 require.Equal(t, td.multiTx1Tr1.BlkNumber, details.ChainDetails[0].BlockNumber) 1441 require.Equal(t, td.multiTx1Tr1.Hash, details.ChainDetails[0].Hash) 1442 require.Equal(t, td.multiTx1Tr1.Contract, *details.ChainDetails[0].Contract) 1443 require.Equal(t, td.multiTx1Tr2.ChainID, common.ChainID(details.ChainDetails[1].ChainID)) 1444 require.Equal(t, td.multiTx1Tr2.BlkNumber, details.ChainDetails[1].BlockNumber) 1445 require.Equal(t, td.multiTx1Tr2.Hash, details.ChainDetails[1].Hash) 1446 require.Equal(t, td.multiTx1Tr2.Contract, *details.ChainDetails[1].Contract) 1447 } 1448 1449 func TestGetActivityEntriesSkipEthGasFeeOnlyTransfers(t *testing.T) { 1450 deps, close := setupTestActivityDB(t) 1451 defer close() 1452 1453 to := eth.Address{0x1} 1454 from := eth.Address{0x2} 1455 hash := eth.Hash{0x3} 1456 blkNum := int64(1) 1457 chainID := common.ChainID(1) 1458 nonce := uint64(1) 1459 1460 // Insert 0-value gas-only ETH transfer as a result of token transfer's gas fee 1461 transfer.InsertTestTransfer(t, deps.db, to, &transfer.TestTransfer{ 1462 TestTransaction: transfer.TestTransaction{ 1463 ChainID: chainID, 1464 From: from, 1465 Hash: hash, 1466 BlkNumber: blkNum, 1467 Nonce: nonce, 1468 }, 1469 To: to, 1470 Value: 0, 1471 }) 1472 1473 entries, err := getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) 1474 require.NoError(t, err) 1475 require.Equal(t, 1, len(entries)) 1476 require.Equal(t, hash, entries[0].transaction.Hash) 1477 1478 // Insert token transfer 1479 transfer.InsertTestTransferWithOptions(t, deps.db, to, 1480 &transfer.TestTransfer{ 1481 TestTransaction: transfer.TestTransaction{ 1482 ChainID: chainID, 1483 From: from, 1484 Hash: hash, 1485 BlkNumber: blkNum, 1486 Nonce: nonce, 1487 }, 1488 To: to, 1489 Value: 1, 1490 }, 1491 &transfer.TestTransferOptions{ 1492 TokenAddress: eth.Address{0x4}, 1493 }, 1494 ) 1495 1496 // Gas-fee-only transfer should be removed, so we get only 1 transfer again 1497 entries, err = getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) 1498 require.NoError(t, err) 1499 require.Equal(t, 1, len(entries)) 1500 require.Equal(t, contractTypeFromDBType("erc20"), entries[0].transferType) 1501 1502 // Insert real 0-value ETH transfer 1503 transfer.InsertTestTransfer(t, deps.db, to, &transfer.TestTransfer{ 1504 TestTransaction: transfer.TestTransaction{ 1505 ChainID: chainID, 1506 From: from, 1507 Hash: eth.Hash{0x5}, // another hash 1508 BlkNumber: blkNum, 1509 Nonce: nonce + 1, // another nonce 1510 }, 1511 To: to, 1512 Value: 0, // 0-value as well 1513 }) 1514 1515 entries, err = getActivityEntries(context.Background(), deps, []eth.Address{to}, true, []common.ChainID{chainID}, Filter{}, 0, 10) 1516 require.NoError(t, err) 1517 require.Equal(t, 2, len(entries)) 1518 }