Stefan 21e6914a3c fix(wallet) fix reading amount for pending transactions
The reading of the amount for pending transactions was done in the same
way as for transfers table. However, the transfers table has a string
hex representation of the amount, while the pending transactions table
has a binary representation of the amount (*big.Int). This was
triggering the not int warning and value was missing.

Updates status-desktop #12120
2024-02-01 18:28:55 +01:00

383 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/bigint"
"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, (*bigint.SQLBigIntBytes)(big.NewInt(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)
}