mirror of
https://github.com/status-im/status-go.git
synced 2025-01-10 06:36:32 +00:00
82185b54b5
`getLogs` for multiple accounts simultaneously. For now only used for new transfers detection. Detection of `new` transfers has been changed, now they are searched from head and forward. Previously they were searched from last scanned block forward.
382 lines
11 KiB
Go
382 lines
11 KiB
Go
package transfer
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
|
|
eth_common "github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/status-im/status-go/services/wallet/common"
|
|
"github.com/status-im/status-go/services/wallet/testutils"
|
|
"github.com/status-im/status-go/services/wallet/token"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type TestTransaction struct {
|
|
Hash eth_common.Hash
|
|
ChainID common.ChainID
|
|
From eth_common.Address // [sender]
|
|
Timestamp int64
|
|
BlkNumber int64
|
|
Success bool
|
|
Nonce uint64
|
|
Contract eth_common.Address
|
|
MultiTransactionID MultiTransactionIDType
|
|
}
|
|
|
|
type TestTransfer struct {
|
|
TestTransaction
|
|
To eth_common.Address // [address]
|
|
Value int64
|
|
Token *token.Token
|
|
}
|
|
|
|
type TestMultiTransaction struct {
|
|
MultiTransactionID MultiTransactionIDType
|
|
MultiTransactionType MultiTransactionType
|
|
FromAddress eth_common.Address
|
|
ToAddress eth_common.Address
|
|
FromToken string
|
|
ToToken string
|
|
FromAmount int64
|
|
ToAmount int64
|
|
Timestamp int64
|
|
FromNetworkID *uint64
|
|
ToNetworkID *uint64
|
|
}
|
|
|
|
func SeedToToken(seed int) *token.Token {
|
|
tokenIndex := seed % len(TestTokens)
|
|
return TestTokens[tokenIndex]
|
|
}
|
|
|
|
func TestTrToToken(t *testing.T, tt *TestTransaction) (token *token.Token, isNative bool) {
|
|
// Sanity check that none of the markers changed and they should be equal to seed
|
|
require.Equal(t, tt.Timestamp, tt.BlkNumber)
|
|
|
|
tokenIndex := int(tt.Timestamp) % len(TestTokens)
|
|
isNative = testutils.SliceContains(NativeTokenIndices, tokenIndex)
|
|
|
|
return TestTokens[tokenIndex], isNative
|
|
}
|
|
|
|
func generateTestTransaction(seed int) TestTransaction {
|
|
token := SeedToToken(seed)
|
|
return TestTransaction{
|
|
Hash: eth_common.HexToHash(fmt.Sprintf("0x1%d", seed)),
|
|
ChainID: common.ChainID(token.ChainID),
|
|
From: eth_common.HexToAddress(fmt.Sprintf("0x2%d", seed)),
|
|
Timestamp: int64(seed),
|
|
BlkNumber: int64(seed),
|
|
Success: true,
|
|
Nonce: uint64(seed),
|
|
// In practice this is last20Bytes(Keccak256(RLP(From, nonce)))
|
|
Contract: eth_common.HexToAddress(fmt.Sprintf("0x4%d", seed)),
|
|
MultiTransactionID: NoMultiTransactionID,
|
|
}
|
|
}
|
|
|
|
func generateTestTransfer(seed int) TestTransfer {
|
|
tokenIndex := seed % len(TestTokens)
|
|
token := TestTokens[tokenIndex]
|
|
return TestTransfer{
|
|
TestTransaction: generateTestTransaction(seed),
|
|
To: eth_common.HexToAddress(fmt.Sprintf("0x3%d", seed)),
|
|
Value: int64(seed),
|
|
Token: token,
|
|
}
|
|
}
|
|
|
|
func GenerateTestSendMultiTransaction(tr TestTransfer) TestMultiTransaction {
|
|
return TestMultiTransaction{
|
|
MultiTransactionType: MultiTransactionSend,
|
|
FromAddress: tr.From,
|
|
ToAddress: tr.To,
|
|
FromToken: tr.Token.Symbol,
|
|
ToToken: tr.Token.Symbol,
|
|
FromAmount: tr.Value,
|
|
ToAmount: 0,
|
|
Timestamp: tr.Timestamp,
|
|
}
|
|
}
|
|
|
|
func GenerateTestSwapMultiTransaction(tr TestTransfer, toToken string, toAmount int64) TestMultiTransaction {
|
|
return TestMultiTransaction{
|
|
MultiTransactionType: MultiTransactionSwap,
|
|
FromAddress: tr.From,
|
|
ToAddress: tr.To,
|
|
FromToken: tr.Token.Symbol,
|
|
ToToken: toToken,
|
|
FromAmount: tr.Value,
|
|
ToAmount: toAmount,
|
|
Timestamp: tr.Timestamp,
|
|
}
|
|
}
|
|
|
|
func GenerateTestBridgeMultiTransaction(fromTr, toTr TestTransfer) TestMultiTransaction {
|
|
return TestMultiTransaction{
|
|
MultiTransactionType: MultiTransactionBridge,
|
|
FromAddress: fromTr.From,
|
|
ToAddress: toTr.To,
|
|
FromToken: fromTr.Token.Symbol,
|
|
ToToken: toTr.Token.Symbol,
|
|
FromAmount: fromTr.Value,
|
|
ToAmount: toTr.Value,
|
|
Timestamp: fromTr.Timestamp,
|
|
}
|
|
}
|
|
|
|
// GenerateTestTransfers will generate transaction based on the TestTokens index and roll over if there are more than
|
|
// len(TestTokens) transactions
|
|
func GenerateTestTransfers(tb testing.TB, db *sql.DB, firstStartIndex int, count int) (result []TestTransfer, fromAddresses, toAddresses []eth_common.Address) {
|
|
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
|
|
tr := generateTestTransfer(i)
|
|
fromAddresses = append(fromAddresses, tr.From)
|
|
toAddresses = append(toAddresses, tr.To)
|
|
result = append(result, tr)
|
|
}
|
|
return
|
|
}
|
|
|
|
type TestCollectible struct {
|
|
TokenAddress eth_common.Address
|
|
TokenID *big.Int
|
|
ChainID common.ChainID
|
|
}
|
|
|
|
var TestCollectibles = []TestCollectible{
|
|
TestCollectible{
|
|
TokenAddress: eth_common.HexToAddress("0x97a04fda4d97c6e3547d66b572e29f4a4ff40392"),
|
|
TokenID: big.NewInt(1),
|
|
ChainID: 1,
|
|
},
|
|
TestCollectible{ // Same token ID as above but different address
|
|
TokenAddress: eth_common.HexToAddress("0x2cec8879915cdbd80c88d8b1416aa9413a24ddfa"),
|
|
TokenID: big.NewInt(1),
|
|
ChainID: 1,
|
|
},
|
|
TestCollectible{
|
|
TokenAddress: eth_common.HexToAddress("0x1dea7a3e04849840c0eb15fd26a55f6c40c4a69b"),
|
|
TokenID: big.NewInt(11),
|
|
ChainID: 5,
|
|
},
|
|
TestCollectible{ // Same address as above but different token ID
|
|
TokenAddress: eth_common.HexToAddress("0x1dea7a3e04849840c0eb15fd26a55f6c40c4a69b"),
|
|
TokenID: big.NewInt(12),
|
|
ChainID: 5,
|
|
},
|
|
}
|
|
|
|
var EthMainnet = token.Token{
|
|
Address: eth_common.HexToAddress("0x"),
|
|
Name: "Ether",
|
|
Symbol: "ETH",
|
|
ChainID: 1,
|
|
}
|
|
|
|
var EthGoerli = token.Token{
|
|
Address: eth_common.HexToAddress("0x"),
|
|
Name: "Ether",
|
|
Symbol: "ETH",
|
|
ChainID: 5,
|
|
}
|
|
|
|
var EthOptimism = token.Token{
|
|
Address: eth_common.HexToAddress("0x"),
|
|
Name: "Ether",
|
|
Symbol: "ETH",
|
|
ChainID: 10,
|
|
}
|
|
|
|
var UsdcMainnet = token.Token{
|
|
Address: eth_common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
|
|
Name: "USD Coin",
|
|
Symbol: "USDC",
|
|
ChainID: 1,
|
|
}
|
|
|
|
var UsdcGoerli = token.Token{
|
|
Address: eth_common.HexToAddress("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"),
|
|
Name: "USD Coin",
|
|
Symbol: "USDC",
|
|
ChainID: 5,
|
|
}
|
|
|
|
var UsdcOptimism = token.Token{
|
|
Address: eth_common.HexToAddress("0x7f5c764cbc14f9669b88837ca1490cca17c31607"),
|
|
Name: "USD Coin",
|
|
Symbol: "USDC",
|
|
ChainID: 10,
|
|
}
|
|
|
|
var SntMainnet = token.Token{
|
|
Address: eth_common.HexToAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"),
|
|
Name: "Status Network Token",
|
|
Symbol: "SNT",
|
|
ChainID: 1,
|
|
}
|
|
|
|
var DaiMainnet = token.Token{
|
|
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
|
|
Name: "DAI Stablecoin",
|
|
Symbol: "DAI",
|
|
ChainID: 5,
|
|
}
|
|
|
|
var DaiGoerli = token.Token{
|
|
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
|
|
Name: "DAI Stablecoin",
|
|
Symbol: "DAI",
|
|
ChainID: 5,
|
|
}
|
|
|
|
// TestTokens contains ETH/Mainnet, ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet, DAI/Goerli
|
|
var TestTokens = []*token.Token{
|
|
&EthMainnet, &EthGoerli, &EthOptimism, &UsdcMainnet, &UsdcGoerli, &UsdcOptimism, &SntMainnet, &DaiMainnet, &DaiGoerli,
|
|
}
|
|
|
|
var NativeTokenIndices = []int{0, 1, 2}
|
|
|
|
func InsertTestTransfer(tb testing.TB, db *sql.DB, address eth_common.Address, tr *TestTransfer) {
|
|
token := TestTokens[int(tr.Timestamp)%len(TestTokens)]
|
|
InsertTestTransferWithOptions(tb, db, address, tr, &TestTransferOptions{
|
|
TokenAddress: token.Address,
|
|
})
|
|
}
|
|
|
|
type TestTransferOptions struct {
|
|
TokenAddress eth_common.Address
|
|
TokenID *big.Int
|
|
NullifyAddresses []eth_common.Address
|
|
Tx *types.Transaction
|
|
Receipt *types.Receipt
|
|
}
|
|
|
|
func GenerateTxField(data []byte) *types.Transaction {
|
|
return types.NewTx(&types.DynamicFeeTx{
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common.Address, tr *TestTransfer, opt *TestTransferOptions) {
|
|
var (
|
|
tx *sql.Tx
|
|
)
|
|
tx, err := db.Begin()
|
|
require.NoError(tb, err)
|
|
defer func() {
|
|
if err == nil {
|
|
err = tx.Commit()
|
|
return
|
|
}
|
|
_ = tx.Rollback()
|
|
}()
|
|
|
|
blkHash := eth_common.HexToHash("4")
|
|
|
|
block := blockDBFields{
|
|
chainID: uint64(tr.ChainID),
|
|
account: address,
|
|
blockNumber: big.NewInt(tr.BlkNumber),
|
|
blockHash: blkHash,
|
|
}
|
|
|
|
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
|
|
err = insertBlockDBFields(tx, block)
|
|
require.NoError(tb, err)
|
|
|
|
receiptStatus := uint64(0)
|
|
if tr.Success {
|
|
receiptStatus = 1
|
|
}
|
|
|
|
tokenType := "eth"
|
|
if (opt.TokenAddress != eth_common.Address{}) {
|
|
if opt.TokenID == nil {
|
|
tokenType = "erc20"
|
|
} else {
|
|
tokenType = "erc721"
|
|
}
|
|
}
|
|
|
|
// Workaround to simulate writing of NULL values for addresses
|
|
txTo := &tr.To
|
|
txFrom := &tr.From
|
|
for i := 0; i < len(opt.NullifyAddresses); i++ {
|
|
if opt.NullifyAddresses[i] == tr.To {
|
|
txTo = nil
|
|
}
|
|
if opt.NullifyAddresses[i] == tr.From {
|
|
txFrom = nil
|
|
}
|
|
}
|
|
|
|
transfer := transferDBFields{
|
|
chainID: uint64(tr.ChainID),
|
|
id: tr.Hash,
|
|
txHash: &tr.Hash,
|
|
address: address,
|
|
blockHash: blkHash,
|
|
blockNumber: big.NewInt(tr.BlkNumber),
|
|
sender: tr.From,
|
|
transferType: common.Type(tokenType),
|
|
timestamp: uint64(tr.Timestamp),
|
|
multiTransactionID: tr.MultiTransactionID,
|
|
baseGasFees: "0x0",
|
|
receiptStatus: &receiptStatus,
|
|
txValue: big.NewInt(tr.Value),
|
|
txFrom: txFrom,
|
|
txTo: txTo,
|
|
txNonce: &tr.Nonce,
|
|
tokenAddress: &opt.TokenAddress,
|
|
contractAddress: &tr.Contract,
|
|
tokenID: opt.TokenID,
|
|
transaction: opt.Tx,
|
|
receipt: opt.Receipt,
|
|
}
|
|
err = updateOrInsertTransfersDBFields(tx, []transferDBFields{transfer})
|
|
require.NoError(tb, err)
|
|
}
|
|
|
|
func InsertTestPendingTransaction(tb testing.TB, db *sql.DB, tr *TestTransfer) {
|
|
_, err := db.Exec(`
|
|
INSERT INTO pending_transactions (network_id, hash, timestamp, from_address, to_address,
|
|
symbol, gas_price, gas_limit, value, data, type, additional_data, multi_transaction_id
|
|
) VALUES (?, ?, ?, ?, ?, 'ETH', 0, 0, ?, '', 'eth', '', ?)`,
|
|
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, tr.Value, tr.MultiTransactionID)
|
|
require.NoError(tb, err)
|
|
}
|
|
|
|
func InsertTestMultiTransaction(tb testing.TB, db *sql.DB, tr *TestMultiTransaction) MultiTransactionIDType {
|
|
fromTokenType := tr.FromToken
|
|
if tr.FromToken == "" {
|
|
fromTokenType = testutils.EthSymbol
|
|
}
|
|
toTokenType := tr.ToToken
|
|
if tr.ToToken == "" {
|
|
toTokenType = testutils.EthSymbol
|
|
}
|
|
fromAmount := (*hexutil.Big)(big.NewInt(tr.FromAmount))
|
|
toAmount := (*hexutil.Big)(big.NewInt(tr.ToAmount))
|
|
|
|
result, err := db.Exec(`
|
|
INSERT INTO multi_transactions (from_address, from_asset, from_amount, to_address, to_asset, to_amount, type, timestamp, from_network_id, to_network_id
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
tr.FromAddress, fromTokenType, fromAmount.String(), tr.ToAddress, toTokenType, toAmount.String(), tr.MultiTransactionType, tr.Timestamp, tr.FromNetworkID, tr.ToNetworkID)
|
|
require.NoError(tb, err)
|
|
rowID, err := result.LastInsertId()
|
|
require.NoError(tb, err)
|
|
tr.MultiTransactionID = MultiTransactionIDType(rowID)
|
|
return tr.MultiTransactionID
|
|
}
|
|
|
|
// For using in tests only outside the package
|
|
func SaveTransfersMarkBlocksLoaded(database *Database, chainID uint64, address eth_common.Address, transfers []Transfer, blocks []*big.Int) error {
|
|
return saveTransfersMarkBlocksLoaded(database.client, chainID, address, transfers, blocks)
|
|
}
|