feat(Wallet) complete the filter API
It uses the current data only and doesn't extend with new types or include new features in activity sources DBs. Major changes: - Partially filter by chain IDs - Partially filter by Status if it is the case - Partially filter by token types - Filter by counterparty addresses - Use wallet accounts for TO/FROM instead of filters Closes: #10634
This commit is contained in:
parent
5777bb429a
commit
e78a73bd9f
|
@ -0,0 +1,19 @@
|
||||||
|
package accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddTestAccounts(t *testing.T, db *sql.DB, accounts []*Account) {
|
||||||
|
d, err := NewDB(db)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = d.SaveAccounts(accounts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := d.GetAccounts()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, accounts, res)
|
||||||
|
}
|
|
@ -9,12 +9,19 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/services/wallet/common"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PayloadType = int
|
type PayloadType = int
|
||||||
|
|
||||||
|
// Beware if adding/removing please check if affected and update the functions below
|
||||||
|
// - NewActivityEntryWithTransaction
|
||||||
|
// - multiTransactionTypeToActivityType
|
||||||
const (
|
const (
|
||||||
MultiTransactionPT PayloadType = iota + 1
|
MultiTransactionPT PayloadType = iota + 1
|
||||||
SimpleTransactionPT
|
SimpleTransactionPT
|
||||||
|
@ -22,29 +29,34 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
// TODO: rename in payloadType
|
payloadType PayloadType
|
||||||
transactionType PayloadType
|
transaction *transfer.TransactionIdentity
|
||||||
transaction *transfer.TransactionIdentity
|
id transfer.MultiTransactionIDType
|
||||||
id transfer.MultiTransactionIDType
|
timestamp int64
|
||||||
timestamp int64
|
activityType Type
|
||||||
activityType Type
|
activityStatus Status
|
||||||
|
tokenType TokenType
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonSerializationTemplate struct {
|
type jsonSerializationTemplate struct {
|
||||||
TransactionType PayloadType `json:"transactionType"`
|
PayloadType PayloadType `json:"payloadType"`
|
||||||
Transaction *transfer.TransactionIdentity `json:"transaction"`
|
Transaction *transfer.TransactionIdentity `json:"transaction"`
|
||||||
ID transfer.MultiTransactionIDType `json:"id"`
|
ID transfer.MultiTransactionIDType `json:"id"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
ActivityType Type `json:"activityType"`
|
ActivityType Type `json:"activityType"`
|
||||||
|
ActivityStatus Status `json:"activityStatus"`
|
||||||
|
TokenType TokenType `json:"tokenType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Entry) MarshalJSON() ([]byte, error) {
|
func (e *Entry) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(jsonSerializationTemplate{
|
return json.Marshal(jsonSerializationTemplate{
|
||||||
TransactionType: e.transactionType,
|
PayloadType: e.payloadType,
|
||||||
Transaction: e.transaction,
|
Transaction: e.transaction,
|
||||||
ID: e.id,
|
ID: e.id,
|
||||||
Timestamp: e.timestamp,
|
Timestamp: e.timestamp,
|
||||||
ActivityType: e.activityType,
|
ActivityType: e.activityType,
|
||||||
|
ActivityStatus: e.activityStatus,
|
||||||
|
TokenType: e.tokenType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +67,7 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.transactionType = aux.TransactionType
|
e.payloadType = aux.PayloadType
|
||||||
e.transaction = aux.Transaction
|
e.transaction = aux.Transaction
|
||||||
e.id = aux.ID
|
e.id = aux.ID
|
||||||
e.timestamp = aux.Timestamp
|
e.timestamp = aux.Timestamp
|
||||||
|
@ -63,31 +75,35 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewActivityEntryWithTransaction(transactionType PayloadType, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type) Entry {
|
func NewActivityEntryWithTransaction(payloadType PayloadType, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status) Entry {
|
||||||
if transactionType != SimpleTransactionPT && transactionType != PendingTransactionPT {
|
if payloadType != SimpleTransactionPT && payloadType != PendingTransactionPT {
|
||||||
panic("invalid transaction type")
|
panic("invalid transaction type")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Entry{
|
return Entry{
|
||||||
transactionType: transactionType,
|
payloadType: payloadType,
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
activityType: activityType,
|
activityType: activityType,
|
||||||
|
activityStatus: activityStatus,
|
||||||
|
tokenType: AssetTT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewActivityEntryWithMultiTransaction(id transfer.MultiTransactionIDType, timestamp int64, activityType Type) Entry {
|
func NewActivityEntryWithMultiTransaction(id transfer.MultiTransactionIDType, timestamp int64, activityType Type, activityStatus Status) Entry {
|
||||||
return Entry{
|
return Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
id: id,
|
id: id,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
activityType: activityType,
|
activityType: activityType,
|
||||||
|
activityStatus: activityStatus,
|
||||||
|
tokenType: AssetTT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Entry) TransactionType() PayloadType {
|
func (e *Entry) PayloadType() PayloadType {
|
||||||
return e.transactionType
|
return e.payloadType
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiTransactionTypeToActivityType(mtType transfer.MultiTransactionType) Type {
|
func multiTransactionTypeToActivityType(mtType transfer.MultiTransactionType) Type {
|
||||||
|
@ -101,7 +117,7 @@ func multiTransactionTypeToActivityType(mtType transfer.MultiTransactionType) Ty
|
||||||
panic("unknown multi transaction type")
|
panic("unknown multi transaction type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func typesContain(slice []Type, item Type) bool {
|
func sliceContains[T constraints.Ordered](slice []T, item T) bool {
|
||||||
for _, a := range slice {
|
for _, a := range slice {
|
||||||
if a == item {
|
if a == item {
|
||||||
return true
|
return true
|
||||||
|
@ -110,31 +126,33 @@ func typesContain(slice []Type, item Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinMTTypes(types []transfer.MultiTransactionType) string {
|
func joinItems[T interface{}](items []T, itemConversion func(T) string) string {
|
||||||
var sb strings.Builder
|
if len(items) == 0 {
|
||||||
for i, val := range types {
|
return ""
|
||||||
if i > 0 {
|
|
||||||
sb.WriteString(",")
|
|
||||||
}
|
|
||||||
sb.WriteString(strconv.Itoa(int(val)))
|
|
||||||
}
|
}
|
||||||
|
var sb strings.Builder
|
||||||
|
if itemConversion == nil {
|
||||||
|
itemConversion = func(item T) string {
|
||||||
|
return fmt.Sprintf("%v", item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, item := range items {
|
||||||
|
if i == 0 {
|
||||||
|
sb.WriteString("(")
|
||||||
|
} else {
|
||||||
|
sb.WriteString("),(")
|
||||||
|
}
|
||||||
|
sb.WriteString(itemConversion(item))
|
||||||
|
}
|
||||||
|
sb.WriteString(")")
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinAddresses(addresses []common.Address) string {
|
func joinAddresses(addresses []eth.Address) string {
|
||||||
var sb strings.Builder
|
return joinItems(addresses, func(a eth.Address) string {
|
||||||
for i, address := range addresses {
|
return fmt.Sprintf("'%s'", strings.ToUpper(hex.EncodeToString(a[:])))
|
||||||
if i == 0 {
|
})
|
||||||
sb.WriteString("('")
|
|
||||||
} else {
|
|
||||||
sb.WriteString("'),('")
|
|
||||||
}
|
|
||||||
sb.WriteString(strings.ToUpper(hex.EncodeToString(address[:])))
|
|
||||||
}
|
|
||||||
sb.WriteString("')")
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func activityTypesToMultiTransactionTypes(trTypes []Type) []transfer.MultiTransactionType {
|
func activityTypesToMultiTransactionTypes(trTypes []Type) []transfer.MultiTransactionType {
|
||||||
|
@ -155,11 +173,25 @@ func activityTypesToMultiTransactionTypes(trTypes []Type) []transfer.MultiTransa
|
||||||
return mtTypes
|
return mtTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: extend with SEND/RECEIVE for transfers and pending_transactions
|
const (
|
||||||
// TODO: clarify if we include sender and receiver in pending_transactions as we do for transfers
|
fromTrType = byte(1)
|
||||||
// TODO optimization: consider implementing nullable []byte instead of using strings for addresses
|
//toTrType = byte(2)
|
||||||
// Query includes duplicates, will return multiple rows for the same transaction
|
|
||||||
const queryFormatString = `
|
// TODO: Multi-transaction network information is missing in filtering
|
||||||
|
// TODO: extract token code for non transfer type eth
|
||||||
|
// TODO optimization: consider implementing nullable []byte instead of using strings for addresses
|
||||||
|
//
|
||||||
|
// Query includes duplicates, will return multiple rows for the same transaction if both to and from addresses
|
||||||
|
// are in the address list.
|
||||||
|
//
|
||||||
|
// The addresses list will have priority in deciding the source of the duplicate transaction. However, if the
|
||||||
|
// if the addresses list is empty, and all addresses should be included, the accounts table will be used
|
||||||
|
// see filter_addresses temp table is used
|
||||||
|
// The switch for tr_type is used to de-conflict the source for the two entries for the same transaction
|
||||||
|
//
|
||||||
|
// UNION ALL is used to avoid the overhead of DISTINCT given that we don't expect to have duplicate entries outside
|
||||||
|
// the sender and receiver addresses being in the list which is handled separately
|
||||||
|
queryFormatString = `
|
||||||
WITH filter_conditions AS (
|
WITH filter_conditions AS (
|
||||||
SELECT
|
SELECT
|
||||||
? AS startFilterDisabled,
|
? AS startFilterDisabled,
|
||||||
|
@ -171,9 +203,26 @@ const queryFormatString = `
|
||||||
? AS filterActivityTypeSend,
|
? AS filterActivityTypeSend,
|
||||||
? AS filterActivityTypeReceive,
|
? AS filterActivityTypeReceive,
|
||||||
|
|
||||||
? AS filterAllAddresses
|
? AS filterAllAddresses,
|
||||||
|
? AS filterAllToAddresses,
|
||||||
|
? AS filterAllActivityStatus,
|
||||||
|
? AS includeAllTokenTypeAssets,
|
||||||
|
? AS statusIsPending,
|
||||||
|
|
||||||
|
? AS includeAllNetworks
|
||||||
),
|
),
|
||||||
filter_addresses(address) AS (
|
filter_addresses(address) AS (
|
||||||
|
SELECT HEX(address) FROM accounts WHERE (SELECT filterAllAddresses FROM filter_conditions) != 0
|
||||||
|
UNION ALL
|
||||||
|
SELECT * FROM (VALUES %s) WHERE (SELECT filterAllAddresses FROM filter_conditions) = 0
|
||||||
|
),
|
||||||
|
filter_to_addresses(address) AS (
|
||||||
|
VALUES %s
|
||||||
|
),
|
||||||
|
filter_assets(token_code) AS (
|
||||||
|
VALUES %s
|
||||||
|
),
|
||||||
|
filter_networks(network_id) AS (
|
||||||
VALUES %s
|
VALUES %s
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -183,12 +232,48 @@ const queryFormatString = `
|
||||||
0 AS multi_tx_id,
|
0 AS multi_tx_id,
|
||||||
transfers.timestamp AS timestamp,
|
transfers.timestamp AS timestamp,
|
||||||
NULL AS mt_type,
|
NULL AS mt_type,
|
||||||
HEX(transfers.address) AS owner_address
|
|
||||||
|
CASE
|
||||||
|
WHEN from_join.address IS NOT NULL AND to_join.address IS NULL THEN 1
|
||||||
|
WHEN to_join.address IS NOT NULL AND from_join.address IS NULL THEN 2
|
||||||
|
WHEN from_join.address IS NOT NULL AND to_join.address IS NOT NULL THEN
|
||||||
|
CASE
|
||||||
|
WHEN from_join.address < to_join.address THEN 1
|
||||||
|
ELSE 2
|
||||||
|
END
|
||||||
|
ELSE NULL
|
||||||
|
END as tr_type,
|
||||||
|
|
||||||
|
transfers.sender AS from_address,
|
||||||
|
transfers.address AS to_address
|
||||||
FROM transfers, filter_conditions
|
FROM transfers, filter_conditions
|
||||||
|
LEFT JOIN
|
||||||
|
filter_addresses from_join ON HEX(transfers.sender) = from_join.address
|
||||||
|
LEFT JOIN
|
||||||
|
filter_addresses to_join ON HEX(transfers.address) = to_join.address
|
||||||
WHERE transfers.multi_transaction_id = 0
|
WHERE transfers.multi_transaction_id = 0
|
||||||
AND ((startFilterDisabled OR timestamp >= startTimestamp) AND (endFilterDisabled OR timestamp <= endTimestamp))
|
AND ((startFilterDisabled OR timestamp >= startTimestamp)
|
||||||
AND (filterActivityTypeAll OR (filterActivityTypeSend AND (filterAllAddresses OR (HEX(transfers.sender) IN filter_addresses))) OR (filterActivityTypeReceive AND (filterAllAddresses OR (HEX(transfers.address) IN filter_addresses))))
|
AND (endFilterDisabled OR timestamp <= endTimestamp)
|
||||||
AND (filterAllAddresses OR (HEX(transfers.sender) IN filter_addresses) OR (HEX(transfers.address) IN filter_addresses))
|
)
|
||||||
|
AND (filterActivityTypeAll
|
||||||
|
OR (filterActivityTypeSend
|
||||||
|
AND (filterAllAddresses
|
||||||
|
OR (HEX(transfers.sender) IN filter_addresses)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
OR (filterActivityTypeReceive
|
||||||
|
AND (filterAllAddresses OR (HEX(transfers.address) IN filter_addresses))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND (filterAllAddresses
|
||||||
|
OR (HEX(transfers.sender) IN filter_addresses)
|
||||||
|
OR (HEX(transfers.address) IN filter_addresses)
|
||||||
|
)
|
||||||
|
AND (filterAllToAddresses
|
||||||
|
OR (HEX(transfers.address) IN filter_to_addresses)
|
||||||
|
)
|
||||||
|
AND (includeAllTokenTypeAssets OR (transfers.type = "eth" AND ("ETH" IN filter_assets)))
|
||||||
|
AND (includeAllNetworks OR (transfers.network_id IN filter_networks))
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
|
@ -199,12 +284,40 @@ const queryFormatString = `
|
||||||
0 AS multi_tx_id,
|
0 AS multi_tx_id,
|
||||||
pending_transactions.timestamp AS timestamp,
|
pending_transactions.timestamp AS timestamp,
|
||||||
NULL AS mt_type,
|
NULL AS mt_type,
|
||||||
NULL AS owner_address
|
|
||||||
|
CASE
|
||||||
|
WHEN from_join.address IS NOT NULL AND to_join.address IS NULL THEN 1
|
||||||
|
WHEN to_join.address IS NOT NULL AND from_join.address IS NULL THEN 2
|
||||||
|
WHEN from_join.address IS NOT NULL AND to_join.address IS NOT NULL THEN
|
||||||
|
CASE
|
||||||
|
WHEN from_join.address < to_join.address THEN 1
|
||||||
|
ELSE 2
|
||||||
|
END
|
||||||
|
ELSE NULL
|
||||||
|
END as tr_type,
|
||||||
|
|
||||||
|
pending_transactions.from_address AS from_address,
|
||||||
|
pending_transactions.to_address AS to_address
|
||||||
FROM pending_transactions, filter_conditions
|
FROM pending_transactions, filter_conditions
|
||||||
|
LEFT JOIN
|
||||||
|
filter_addresses from_join ON HEX(pending_transactions.from_address) = from_join.address
|
||||||
|
LEFT JOIN
|
||||||
|
filter_addresses to_join ON HEX(pending_transactions.to_address) = to_join.address
|
||||||
WHERE pending_transactions.multi_transaction_id = 0
|
WHERE pending_transactions.multi_transaction_id = 0
|
||||||
AND ((startFilterDisabled OR timestamp >= startTimestamp) AND (endFilterDisabled OR timestamp <= endTimestamp))
|
AND (filterAllActivityStatus OR statusIsPending)
|
||||||
|
AND ((startFilterDisabled OR timestamp >= startTimestamp)
|
||||||
|
AND (endFilterDisabled OR timestamp <= endTimestamp)
|
||||||
|
)
|
||||||
AND (filterActivityTypeAll OR filterActivityTypeSend)
|
AND (filterActivityTypeAll OR filterActivityTypeSend)
|
||||||
AND (filterAllAddresses OR (HEX(pending_transactions.from_address) IN filter_addresses) OR (HEX(pending_transactions.to_address) IN filter_addresses))
|
AND (filterAllAddresses
|
||||||
|
OR (HEX(pending_transactions.from_address) IN filter_addresses)
|
||||||
|
OR (HEX(pending_transactions.to_address) IN filter_addresses)
|
||||||
|
)
|
||||||
|
AND (filterAllToAddresses
|
||||||
|
OR (HEX(pending_transactions.to_address) IN filter_to_addresses)
|
||||||
|
)
|
||||||
|
AND (includeAllTokenTypeAssets OR (UPPER(pending_transactions.symbol) IN filter_assets))
|
||||||
|
AND (includeAllNetworks OR (pending_transactions.network_id IN filter_networks))
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
|
@ -215,41 +328,89 @@ const queryFormatString = `
|
||||||
multi_transactions.ROWID AS multi_tx_id,
|
multi_transactions.ROWID AS multi_tx_id,
|
||||||
multi_transactions.timestamp AS timestamp,
|
multi_transactions.timestamp AS timestamp,
|
||||||
multi_transactions.type AS mt_type,
|
multi_transactions.type AS mt_type,
|
||||||
NULL AS owner_address
|
NULL as tr_type,
|
||||||
|
multi_transactions.from_address AS from_address,
|
||||||
|
multi_transactions.to_address AS to_address
|
||||||
FROM multi_transactions, filter_conditions
|
FROM multi_transactions, filter_conditions
|
||||||
WHERE ((startFilterDisabled OR timestamp >= startTimestamp) AND (endFilterDisabled OR timestamp <= endTimestamp))
|
WHERE ((startFilterDisabled OR timestamp >= startTimestamp)
|
||||||
|
AND (endFilterDisabled OR timestamp <= endTimestamp)
|
||||||
|
)
|
||||||
AND (filterActivityTypeAll OR (multi_transactions.type IN (%s)))
|
AND (filterActivityTypeAll OR (multi_transactions.type IN (%s)))
|
||||||
AND (filterAllAddresses OR (HEX(multi_transactions.from_address) IN filter_addresses) OR (HEX(multi_transactions.to_address) IN filter_addresses))
|
AND (filterAllAddresses
|
||||||
|
OR (HEX(multi_transactions.from_address) IN filter_addresses)
|
||||||
|
OR (HEX(multi_transactions.to_address) IN filter_addresses)
|
||||||
|
)
|
||||||
|
AND (filterAllToAddresses
|
||||||
|
OR (HEX(multi_transactions.to_address) IN filter_to_addresses)
|
||||||
|
)
|
||||||
|
AND (includeAllTokenTypeAssets OR (UPPER(multi_transactions.from_asset) IN filter_assets) OR (UPPER(multi_transactions.to_asset) IN filter_assets))
|
||||||
|
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT ? OFFSET ?`
|
LIMIT ? OFFSET ?`
|
||||||
|
|
||||||
func GetActivityEntries(db *sql.DB, addresses []common.Address, chainIDs []uint64, filter Filter, offset int, limit int) ([]Entry, error) {
|
noEntriesInTmpTableSQLValues = "(NULL)"
|
||||||
// Query the transfers, pending_transactions, and multi_transactions tables ordered by timestamp column
|
)
|
||||||
|
|
||||||
|
// GetActivityEntries returns query the transfers, pending_transactions, and multi_transactions tables
|
||||||
|
// based on filter parameters and arguments
|
||||||
|
// it returns metadata for all entries ordered by timestamp column
|
||||||
|
//
|
||||||
|
// Adding a no-limit option was never considered or required.
|
||||||
|
func GetActivityEntries(db *sql.DB, addresses []eth.Address, chainIDs []common.ChainID, filter Filter, offset int, limit int) ([]Entry, error) {
|
||||||
|
// TODO: filter collectibles after they are added to multi_transactions table
|
||||||
|
if len(filter.Tokens.EnabledTypes) > 0 && !sliceContains(filter.Tokens.EnabledTypes, AssetTT) {
|
||||||
|
// For now we deal only with assets so return empty result
|
||||||
|
return []Entry{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
includeAllTokenTypeAssets := (len(filter.Tokens.EnabledTypes) == 0 ||
|
||||||
|
sliceContains(filter.Tokens.EnabledTypes, AssetTT)) && len(filter.Tokens.Assets) == 0
|
||||||
|
|
||||||
|
assets := noEntriesInTmpTableSQLValues
|
||||||
|
if !includeAllTokenTypeAssets {
|
||||||
|
assets = joinItems(filter.Tokens.Assets, func(item TokenCode) string { return fmt.Sprintf("'%v'", item) })
|
||||||
|
}
|
||||||
|
|
||||||
|
includeAllNetworks := len(chainIDs) == 0
|
||||||
|
networks := noEntriesInTmpTableSQLValues
|
||||||
|
if !includeAllNetworks {
|
||||||
|
networks = joinItems(chainIDs, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: finish filter: chainIDs, statuses, tokenTypes, counterpartyAddresses
|
|
||||||
// TODO: use all accounts list for detecting SEND/RECEIVE instead of the current addresses list; also change activityType detection in transfer part
|
|
||||||
startFilterDisabled := !(filter.Period.StartTimestamp > 0)
|
startFilterDisabled := !(filter.Period.StartTimestamp > 0)
|
||||||
endFilterDisabled := !(filter.Period.EndTimestamp > 0)
|
endFilterDisabled := !(filter.Period.EndTimestamp > 0)
|
||||||
filterActivityTypeAll := typesContain(filter.Types, AllAT) || len(filter.Types) == 0
|
filterActivityTypeAll := len(filter.Types) == 0
|
||||||
filterAllAddresses := len(addresses) == 0
|
filterAllAddresses := len(addresses) == 0
|
||||||
|
filterAllToAddresses := len(filter.CounterpartyAddresses) == 0
|
||||||
|
includeAllStatuses := len(filter.Statuses) == 0
|
||||||
|
|
||||||
//fmt.Println("@dd filter: timeEnabled", filter.Period.StartTimestamp, filter.Period.EndTimestamp, "; type", filter.Types, "offset", offset, "limit", limit)
|
statusIsPending := false
|
||||||
|
if !includeAllStatuses {
|
||||||
|
statusIsPending = sliceContains(filter.Statuses, PendingAS)
|
||||||
|
}
|
||||||
|
|
||||||
joinedAddresses := "(NULL)"
|
involvedAddresses := noEntriesInTmpTableSQLValues
|
||||||
if !filterAllAddresses {
|
if !filterAllAddresses {
|
||||||
joinedAddresses = joinAddresses(addresses)
|
involvedAddresses = joinAddresses(addresses)
|
||||||
|
}
|
||||||
|
toAddresses := noEntriesInTmpTableSQLValues
|
||||||
|
if !filterAllToAddresses {
|
||||||
|
toAddresses = joinAddresses(filter.CounterpartyAddresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
mtTypes := activityTypesToMultiTransactionTypes(filter.Types)
|
mtTypes := activityTypesToMultiTransactionTypes(filter.Types)
|
||||||
joinedMTTypes := joinMTTypes(mtTypes)
|
joinedMTTypes := joinItems(mtTypes, func(t transfer.MultiTransactionType) string {
|
||||||
|
return strconv.Itoa(int(t))
|
||||||
|
})
|
||||||
|
|
||||||
queryString := fmt.Sprintf(queryFormatString, joinedAddresses, joinedMTTypes)
|
queryString := fmt.Sprintf(queryFormatString, involvedAddresses, toAddresses, assets, networks,
|
||||||
|
joinedMTTypes)
|
||||||
|
|
||||||
rows, err := db.Query(queryString,
|
rows, err := db.Query(queryString,
|
||||||
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
|
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
|
||||||
filterActivityTypeAll, typesContain(filter.Types, SendAT), typesContain(filter.Types, ReceiveAT),
|
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
|
||||||
filterAllAddresses,
|
filterAllAddresses, filterAllToAddresses, includeAllStatuses, includeAllTokenTypeAssets, statusIsPending,
|
||||||
|
includeAllNetworks,
|
||||||
limit, offset)
|
limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -261,30 +422,41 @@ func GetActivityEntries(db *sql.DB, addresses []common.Address, chainIDs []uint6
|
||||||
var transferHash, pendingHash []byte
|
var transferHash, pendingHash []byte
|
||||||
var chainID, multiTxID sql.NullInt64
|
var chainID, multiTxID sql.NullInt64
|
||||||
var timestamp int64
|
var timestamp int64
|
||||||
var dbActivityType sql.NullByte
|
var dbMtType, dbTrType sql.NullByte
|
||||||
var dbAddress sql.NullString
|
var toAddress, fromAddress eth.Address
|
||||||
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, ×tamp, &dbActivityType, &dbAddress)
|
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, ×tamp, &dbMtType, &dbTrType, &fromAddress, &toAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getActivityType := func(trType sql.NullByte) (activityType Type, filteredAddress eth.Address) {
|
||||||
|
if trType.Valid && trType.Byte == fromTrType {
|
||||||
|
return SendAT, fromAddress
|
||||||
|
}
|
||||||
|
// Don't expect this to happen due to trType = NULL outside of tests
|
||||||
|
return ReceiveAT, toAddress
|
||||||
|
}
|
||||||
|
|
||||||
var entry Entry
|
var entry Entry
|
||||||
if transferHash != nil && chainID.Valid {
|
if transferHash != nil && chainID.Valid {
|
||||||
var activityType Type = SendAT
|
// TODO: extend DB with status in order to filter by status. The status has to be extracted from the receipt upon downloading
|
||||||
thisAddress := common.HexToAddress(dbAddress.String)
|
activityStatus := FinalizedAS
|
||||||
for _, address := range addresses {
|
activityType, filteredAddress := getActivityType(dbTrType)
|
||||||
if address == thisAddress {
|
entry = NewActivityEntryWithTransaction(SimpleTransactionPT,
|
||||||
activityType = ReceiveAT
|
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(transferHash), Address: filteredAddress},
|
||||||
}
|
timestamp, activityType, activityStatus)
|
||||||
}
|
|
||||||
entry = NewActivityEntryWithTransaction(SimpleTransactionPT, &transfer.TransactionIdentity{ChainID: uint64(chainID.Int64), Hash: common.BytesToHash(transferHash), Address: thisAddress}, timestamp, activityType)
|
|
||||||
} else if pendingHash != nil && chainID.Valid {
|
} else if pendingHash != nil && chainID.Valid {
|
||||||
var activityType Type = SendAT
|
activityStatus := PendingAS
|
||||||
entry = NewActivityEntryWithTransaction(PendingTransactionPT, &transfer.TransactionIdentity{ChainID: uint64(chainID.Int64), Hash: common.BytesToHash(pendingHash)}, timestamp, activityType)
|
activityType, _ := getActivityType(dbTrType)
|
||||||
|
entry = NewActivityEntryWithTransaction(PendingTransactionPT,
|
||||||
|
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(pendingHash)},
|
||||||
|
timestamp, activityType, activityStatus)
|
||||||
} else if multiTxID.Valid {
|
} else if multiTxID.Valid {
|
||||||
activityType := multiTransactionTypeToActivityType(transfer.MultiTransactionType(dbActivityType.Byte))
|
activityType := multiTransactionTypeToActivityType(transfer.MultiTransactionType(dbMtType.Byte))
|
||||||
|
// TODO: aggregate status from all sub-transactions
|
||||||
|
activityStatus := FinalizedAS
|
||||||
entry = NewActivityEntryWithMultiTransaction(transfer.MultiTransactionIDType(multiTxID.Int64),
|
entry = NewActivityEntryWithMultiTransaction(transfer.MultiTransactionIDType(multiTxID.Int64),
|
||||||
timestamp, activityType)
|
timestamp, activityType, activityStatus)
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("invalid row data")
|
return nil, errors.New("invalid row data")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/status-im/status-go/appdatabase"
|
"github.com/status-im/status-go/appdatabase"
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
|
"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/testutils"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
|
eth_common "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -22,15 +26,6 @@ func setupTestActivityDB(t *testing.T) (db *sql.DB, close func()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestPendingTransaction(t *testing.T, db *sql.DB, tr *transfer.TestTransaction) {
|
|
||||||
_, 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, ?, '', 'test', '', ?)`,
|
|
||||||
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, tr.Value, tr.MultiTransactionID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type testData struct {
|
type testData struct {
|
||||||
tr1 transfer.TestTransaction // index 1
|
tr1 transfer.TestTransaction // index 1
|
||||||
pendingTr transfer.TestTransaction // index 2
|
pendingTr transfer.TestTransaction // index 2
|
||||||
|
@ -51,12 +46,15 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData) {
|
||||||
transfer.InsertTestTransfer(t, db, &td.tr1)
|
transfer.InsertTestTransfer(t, db, &td.tr1)
|
||||||
|
|
||||||
td.pendingTr = trs[1]
|
td.pendingTr = trs[1]
|
||||||
insertTestPendingTransaction(t, db, &td.pendingTr)
|
transfer.InsertTestPendingTransaction(t, db, &td.pendingTr)
|
||||||
|
|
||||||
td.singletonMTr = trs[2]
|
td.singletonMTr = trs[2]
|
||||||
|
td.singletonMTr.FromToken = testutils.SntSymbol
|
||||||
|
td.singletonMTr.ToToken = testutils.DaiSymbol
|
||||||
td.singletonMTID = transfer.InsertTestMultiTransaction(t, db, &td.singletonMTr)
|
td.singletonMTID = transfer.InsertTestMultiTransaction(t, db, &td.singletonMTr)
|
||||||
|
|
||||||
td.mTr = trs[3]
|
td.mTr = trs[3]
|
||||||
|
td.mTr.ToToken = testutils.SntSymbol
|
||||||
td.mTrID = transfer.InsertTestMultiTransaction(t, db, &td.mTr)
|
td.mTrID = transfer.InsertTestMultiTransaction(t, db, &td.mTr)
|
||||||
|
|
||||||
td.subTr = trs[4]
|
td.subTr = trs[4]
|
||||||
|
@ -65,7 +63,7 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData) {
|
||||||
|
|
||||||
td.subPendingTr = trs[5]
|
td.subPendingTr = trs[5]
|
||||||
td.subPendingTr.MultiTransactionID = td.mTrID
|
td.subPendingTr.MultiTransactionID = td.mTrID
|
||||||
insertTestPendingTransaction(t, db, &td.subPendingTr)
|
transfer.InsertTestPendingTransaction(t, db, &td.subPendingTr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +74,7 @@ func TestGetActivityEntriesAll(t *testing.T) {
|
||||||
td := fillTestData(t, db)
|
td := fillTestData(t, db)
|
||||||
|
|
||||||
var filter Filter
|
var filter Filter
|
||||||
entries, err := GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 10)
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 10)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 4, len(entries))
|
require.Equal(t, 4, len(entries))
|
||||||
|
|
||||||
|
@ -88,48 +86,60 @@ func TestGetActivityEntriesAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, testutils.StructExistsInSlice(Entry{
|
require.True(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
|
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
|
||||||
id: td.tr1.MultiTransactionID,
|
id: td.tr1.MultiTransactionID,
|
||||||
timestamp: td.tr1.Timestamp,
|
timestamp: td.tr1.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
require.True(t, testutils.StructExistsInSlice(Entry{
|
require.True(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: PendingTransactionPT,
|
payloadType: PendingTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash},
|
transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash},
|
||||||
id: td.pendingTr.MultiTransactionID,
|
id: td.pendingTr.MultiTransactionID,
|
||||||
timestamp: td.pendingTr.Timestamp,
|
timestamp: td.pendingTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: PendingAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
require.True(t, testutils.StructExistsInSlice(Entry{
|
require.True(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
transaction: nil,
|
transaction: nil,
|
||||||
id: td.singletonMTID,
|
id: td.singletonMTID,
|
||||||
timestamp: td.singletonMTr.Timestamp,
|
timestamp: td.singletonMTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
require.True(t, testutils.StructExistsInSlice(Entry{
|
require.True(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
transaction: nil,
|
transaction: nil,
|
||||||
id: td.mTrID,
|
id: td.mTrID,
|
||||||
timestamp: td.mTr.Timestamp,
|
timestamp: td.mTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
|
|
||||||
// Ensure the sub-transactions of the multi-transactions are not returned
|
// Ensure the sub-transactions of the multi-transactions are not returned
|
||||||
require.False(t, testutils.StructExistsInSlice(Entry{
|
require.False(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: td.subTr.ChainID, Hash: td.subTr.Hash, Address: td.subTr.To},
|
transaction: &transfer.TransactionIdentity{ChainID: td.subTr.ChainID, Hash: td.subTr.Hash, Address: td.subTr.To},
|
||||||
id: td.subTr.MultiTransactionID,
|
id: td.subTr.MultiTransactionID,
|
||||||
timestamp: td.subTr.Timestamp,
|
timestamp: td.subTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
require.False(t, testutils.StructExistsInSlice(Entry{
|
require.False(t, testutils.StructExistsInSlice(Entry{
|
||||||
transactionType: PendingTransactionPT,
|
payloadType: PendingTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: td.subPendingTr.ChainID, Hash: td.subPendingTr.Hash},
|
transaction: &transfer.TransactionIdentity{ChainID: td.subPendingTr.ChainID, Hash: td.subPendingTr.Hash},
|
||||||
id: td.subPendingTr.MultiTransactionID,
|
id: td.subPendingTr.MultiTransactionID,
|
||||||
timestamp: td.subPendingTr.Timestamp,
|
timestamp: td.subPendingTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: PendingAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries))
|
}, entries))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,14 +156,40 @@ func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testin
|
||||||
prevTo := receiverTr.To
|
prevTo := receiverTr.To
|
||||||
receiverTr.To = td.tr1.From
|
receiverTr.To = td.tr1.From
|
||||||
receiverTr.From = prevTo
|
receiverTr.From = prevTo
|
||||||
|
|
||||||
|
// TODO: test also when there is a transaction in the other direction
|
||||||
|
|
||||||
|
// Ensure they are the oldest transactions (last in the list) and we have a consistent order
|
||||||
|
receiverTr.Timestamp--
|
||||||
transfer.InsertTestTransfer(t, db, &receiverTr)
|
transfer.InsertTestTransfer(t, db, &receiverTr)
|
||||||
|
|
||||||
var filter Filter
|
var filter Filter
|
||||||
entries, err := GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 10)
|
entries, err := GetActivityEntries(db, []eth.Address{td.tr1.From, receiverTr.From}, []common.ChainID{}, filter, 0, 10)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(entries))
|
||||||
|
|
||||||
|
// Check that the transaction are labeled alternatively as send and receive
|
||||||
|
require.Equal(t, ReceiveAT, entries[1].activityType)
|
||||||
|
require.NotEqual(t, eth.Address{}, entries[1].transaction.Address)
|
||||||
|
require.Equal(t, receiverTr.To, entries[1].transaction.Address)
|
||||||
|
|
||||||
|
require.Equal(t, SendAT, entries[0].activityType)
|
||||||
|
require.NotEqual(t, eth.Address{}, entries[0].transaction.Address)
|
||||||
|
require.Equal(t, td.tr1.From, entries[0].transaction.Address)
|
||||||
|
|
||||||
|
// add accounts to DB for proper detection of sender/receiver in all cases
|
||||||
|
accounts.AddTestAccounts(t, db, []*accounts.Account{
|
||||||
|
{Address: types.Address(td.tr1.From), Chat: false, Wallet: true},
|
||||||
|
{Address: types.Address(receiverTr.From)},
|
||||||
|
})
|
||||||
|
|
||||||
|
entries, err = GetActivityEntries(db, []eth.Address{}, []common.ChainID{}, filter, 0, 10)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// TODO: decide how should we handle this case filter out or include it in the result
|
|
||||||
// For now we include both. Can be changed by using UNION instead of UNION ALL in the query or by filtering out
|
|
||||||
require.Equal(t, 5, len(entries))
|
require.Equal(t, 5, len(entries))
|
||||||
|
|
||||||
|
// Check that the transaction are labeled alternatively as send and receive
|
||||||
|
require.Equal(t, ReceiveAT, entries[4].activityType)
|
||||||
|
require.Equal(t, SendAT, entries[3].activityType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetActivityEntriesFilterByTime(t *testing.T) {
|
func TestGetActivityEntriesFilterByTime(t *testing.T) {
|
||||||
|
@ -170,65 +206,78 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
|
||||||
// Test start only
|
// Test start only
|
||||||
var filter Filter
|
var filter Filter
|
||||||
filter.Period.StartTimestamp = td.singletonMTr.Timestamp
|
filter.Period.StartTimestamp = td.singletonMTr.Timestamp
|
||||||
entries, err := GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 15)
|
filter.Period.EndTimestamp = NoLimitTimestampForPeriod
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 8, len(entries))
|
require.Equal(t, 8, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[5].ChainID, Hash: trs[5].Hash, Address: trs[5].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[5].ChainID, Hash: trs[5].Hash, Address: trs[5].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[5].Timestamp,
|
timestamp: trs[5].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
transaction: nil,
|
transaction: nil,
|
||||||
id: td.singletonMTID,
|
id: td.singletonMTID,
|
||||||
timestamp: td.singletonMTr.Timestamp,
|
timestamp: td.singletonMTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[7])
|
}, entries[7])
|
||||||
|
|
||||||
// Test complete interval
|
// Test complete interval
|
||||||
filter.Period.EndTimestamp = trs[2].Timestamp
|
filter.Period.EndTimestamp = trs[2].Timestamp
|
||||||
entries, err = GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 15)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 5, len(entries))
|
require.Equal(t, 5, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[2].Timestamp,
|
timestamp: trs[2].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
transaction: nil,
|
transaction: nil,
|
||||||
id: td.singletonMTID,
|
id: td.singletonMTID,
|
||||||
timestamp: td.singletonMTr.Timestamp,
|
timestamp: td.singletonMTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[4])
|
}, entries[4])
|
||||||
|
|
||||||
// Test end only
|
// Test end only
|
||||||
filter.Period.StartTimestamp = 0
|
filter.Period.StartTimestamp = NoLimitTimestampForPeriod
|
||||||
entries, err = GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 15)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 7, len(entries))
|
require.Equal(t, 7, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[2].Timestamp,
|
timestamp: trs[2].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
|
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: td.tr1.Timestamp,
|
timestamp: td.tr1.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[6])
|
}, entries[6])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,63 +293,73 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
|
||||||
|
|
||||||
var filter Filter
|
var filter Filter
|
||||||
// Get all
|
// Get all
|
||||||
entries, err := GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 5)
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 5)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 5, len(entries))
|
require.Equal(t, 5, len(entries))
|
||||||
|
|
||||||
// Get time based interval
|
// Get time based interval
|
||||||
filter.Period.StartTimestamp = trs[2].Timestamp
|
filter.Period.StartTimestamp = trs[2].Timestamp
|
||||||
filter.Period.EndTimestamp = trs[8].Timestamp
|
filter.Period.EndTimestamp = trs[8].Timestamp
|
||||||
entries, err = GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 3)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(entries))
|
require.Equal(t, 3, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[8].ChainID, Hash: trs[8].Hash, Address: trs[8].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[8].ChainID, Hash: trs[8].Hash, Address: trs[8].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[8].Timestamp,
|
timestamp: trs[8].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[6].Timestamp,
|
timestamp: trs[6].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[2])
|
}, entries[2])
|
||||||
|
|
||||||
// Move window 2 entries forward
|
// Move window 2 entries forward
|
||||||
entries, err = GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 2, 3)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 2, 3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(entries))
|
require.Equal(t, 3, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[6].ChainID, Hash: trs[6].Hash, Address: trs[6].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[6].Timestamp,
|
timestamp: trs[6].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[4].Timestamp,
|
timestamp: trs[4].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[2])
|
}, entries[2])
|
||||||
|
|
||||||
// Move window 4 more entries to test filter cap
|
// Move window 4 more entries to test filter cap
|
||||||
entries, err = GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 6, 3)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 6, 3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(entries))
|
require.Equal(t, 1, len(entries))
|
||||||
// Check start and end content
|
// Check start and end content
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[2].ChainID, Hash: trs[2].Hash, Address: trs[2].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[2].Timestamp,
|
timestamp: trs[2].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +369,7 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
||||||
|
|
||||||
// Adds 4 extractable transactions
|
// Adds 4 extractable transactions
|
||||||
fillTestData(t, db)
|
fillTestData(t, db)
|
||||||
// Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge rest Send
|
// Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge rest MultiTransactionSend
|
||||||
trs := transfer.GenerateTestTransactions(t, db, 6, 6)
|
trs := transfer.GenerateTestTransactions(t, db, 6, 6)
|
||||||
trs[1].MultiTransactionType = transfer.MultiTransactionBridge
|
trs[1].MultiTransactionType = transfer.MultiTransactionBridge
|
||||||
trs[3].MultiTransactionType = transfer.MultiTransactionSwap
|
trs[3].MultiTransactionType = transfer.MultiTransactionSwap
|
||||||
|
@ -326,33 +385,43 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
||||||
|
|
||||||
// Test filtering out without address involved
|
// Test filtering out without address involved
|
||||||
var filter Filter
|
var filter Filter
|
||||||
// TODO: add more types to cover all cases
|
|
||||||
|
filter.Types = allActivityTypesFilter()
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
filter.Types = []Type{SendAT, SwapAT}
|
filter.Types = []Type{SendAT, SwapAT}
|
||||||
entries, err := GetActivityEntries(db, []common.Address{}, []uint64{}, filter, 0, 15)
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 8, len(entries))
|
require.Equal(t, 8, len(entries))
|
||||||
swapCount := 0
|
swapCount := 0
|
||||||
sendCount := 0
|
sendCount := 0
|
||||||
|
receiveCount := 0
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.activityType == SendAT {
|
if entry.activityType == SendAT {
|
||||||
sendCount++
|
sendCount++
|
||||||
}
|
}
|
||||||
|
if entry.activityType == ReceiveAT {
|
||||||
|
receiveCount++
|
||||||
|
}
|
||||||
if entry.activityType == SwapAT {
|
if entry.activityType == SwapAT {
|
||||||
swapCount++
|
swapCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require.Equal(t, 7, sendCount)
|
require.Equal(t, 2, sendCount)
|
||||||
|
require.Equal(t, 5, receiveCount)
|
||||||
require.Equal(t, 1, swapCount)
|
require.Equal(t, 1, swapCount)
|
||||||
|
|
||||||
// Test filtering out with address involved
|
// Test filtering out with address involved
|
||||||
filter.Types = []Type{BridgeAT, ReceiveAT}
|
filter.Types = []Type{BridgeAT, ReceiveAT}
|
||||||
// Include one "to" from transfers to be detected as receive
|
// Include one "to" from transfers to be detected as receive
|
||||||
addresses := []common.Address{trs[0].To, trs[1].To, trs[2].From, trs[3].From, trs[5].From}
|
addresses := []eth_common.Address{trs[0].To, trs[1].To, trs[2].From, trs[3].From, trs[5].From}
|
||||||
entries, err = GetActivityEntries(db, addresses, []uint64{}, filter, 0, 15)
|
entries, err = GetActivityEntries(db, addresses, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(entries))
|
require.Equal(t, 3, len(entries))
|
||||||
bridgeCount := 0
|
bridgeCount := 0
|
||||||
receiveCount := 0
|
receiveCount = 0
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.activityType == BridgeAT {
|
if entry.activityType == BridgeAT {
|
||||||
bridgeCount++
|
bridgeCount++
|
||||||
|
@ -365,42 +434,241 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
|
||||||
require.Equal(t, 1, receiveCount)
|
require.Equal(t, 1, receiveCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetActivityEntriesFilterByAddress(t *testing.T) {
|
func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
|
||||||
db, close := setupTestActivityDB(t)
|
db, close := setupTestActivityDB(t)
|
||||||
defer close()
|
defer close()
|
||||||
|
|
||||||
// Adds 4 extractable transactions
|
// Adds 4 extractable transactions
|
||||||
td := fillTestData(t, db)
|
td := fillTestData(t, db)
|
||||||
// Add 6 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge rest Send
|
|
||||||
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
||||||
for i := range trs {
|
for i := range trs {
|
||||||
transfer.InsertTestTransfer(t, db, &trs[i])
|
transfer.InsertTestTransfer(t, db, &trs[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
var filter Filter
|
var filter Filter
|
||||||
addressesFilter := []common.Address{td.mTr.To, trs[1].From, trs[4].To}
|
|
||||||
entries, err := GetActivityEntries(db, addressesFilter, []uint64{}, filter, 0, 15)
|
addressesFilter := allAddressesFilter()
|
||||||
|
entries, err := GetActivityEntries(db, addressesFilter, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
addressesFilter = []eth_common.Address{td.mTr.To, trs[1].From, trs[4].To}
|
||||||
|
entries, err = GetActivityEntries(db, addressesFilter, []common.ChainID{}, filter, 0, 15)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 3, len(entries))
|
require.Equal(t, 3, len(entries))
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[4].ChainID, Hash: trs[4].Hash, Address: trs[4].To},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[4].Timestamp,
|
timestamp: trs[4].Timestamp,
|
||||||
activityType: ReceiveAT,
|
activityType: ReceiveAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[0])
|
}, entries[0])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: SimpleTransactionPT,
|
payloadType: SimpleTransactionPT,
|
||||||
transaction: &transfer.TransactionIdentity{ChainID: trs[1].ChainID, Hash: trs[1].Hash, Address: trs[1].To},
|
transaction: &transfer.TransactionIdentity{ChainID: trs[1].ChainID, Hash: trs[1].Hash, Address: trs[1].From},
|
||||||
id: 0,
|
id: 0,
|
||||||
timestamp: trs[1].Timestamp,
|
timestamp: trs[1].Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[1])
|
}, entries[1])
|
||||||
require.Equal(t, Entry{
|
require.Equal(t, Entry{
|
||||||
transactionType: MultiTransactionPT,
|
payloadType: MultiTransactionPT,
|
||||||
transaction: nil,
|
transaction: nil,
|
||||||
id: td.mTrID,
|
id: td.mTrID,
|
||||||
timestamp: td.mTr.Timestamp,
|
timestamp: td.mTr.Timestamp,
|
||||||
activityType: SendAT,
|
activityType: SendAT,
|
||||||
|
activityStatus: FinalizedAS,
|
||||||
|
tokenType: AssetTT,
|
||||||
}, entries[2])
|
}, entries[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetActivityEntriesFilterByStatus(t *testing.T) {
|
||||||
|
db, close := setupTestActivityDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Adds 4 extractable transactions
|
||||||
|
fillTestData(t, db)
|
||||||
|
// Add 6 extractable transactions
|
||||||
|
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
||||||
|
for i := range trs {
|
||||||
|
transfer.InsertTestTransfer(t, db, &trs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter Filter
|
||||||
|
filter.Statuses = []Status{}
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
filter.Statuses = allActivityStatusesFilter()
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
// TODO: enabled and finish tests after extending DB with transaction status
|
||||||
|
//
|
||||||
|
// filter.Statuses = []Status{PendingAS}
|
||||||
|
// entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// require.Equal(t, 1, len(entries))
|
||||||
|
|
||||||
|
// filter.Statuses = []Status{FailedAS, CompleteAS}
|
||||||
|
// entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// require.Equal(t, 9, len(entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
|
||||||
|
db, close := setupTestActivityDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Adds 4 extractable transactions 2 transactions ETH, one MT SNT to DAI and another MT ETH to SNT
|
||||||
|
fillTestData(t, db)
|
||||||
|
// Add 6 extractable transactions with USDC (only erc20 as type in DB)
|
||||||
|
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
||||||
|
for i := range trs {
|
||||||
|
trs[i].FromToken = "USDC"
|
||||||
|
transfer.InsertTestTransfer(t, db, &trs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter Filter
|
||||||
|
filter.Tokens = noAssetsFilter()
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(entries))
|
||||||
|
|
||||||
|
filter.Tokens = allTokensFilter()
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
// Regression when collectibles is nil
|
||||||
|
filter.Tokens = Tokens{[]TokenCode{}, nil, []TokenType{}}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
filter.Tokens = Tokens{Assets: []TokenCode{"ETH"}, EnabledTypes: []TokenType{AssetTT}}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 3, len(entries))
|
||||||
|
|
||||||
|
// TODO: update tests after adding token type to transfers
|
||||||
|
filter.Tokens = Tokens{Assets: []TokenCode{"USDC", "DAI"}, EnabledTypes: []TokenType{AssetTT}}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(entries))
|
||||||
|
|
||||||
|
// Regression when EnabledTypes ar empty
|
||||||
|
filter.Tokens = Tokens{Assets: []TokenCode{"USDC", "DAI"}, EnabledTypes: []TokenType{}}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetActivityEntriesFilterByToAddresses(t *testing.T) {
|
||||||
|
db, close := setupTestActivityDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Adds 4 extractable transactions
|
||||||
|
td := fillTestData(t, db)
|
||||||
|
// Add 6 extractable transactions
|
||||||
|
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
||||||
|
for i := range trs {
|
||||||
|
transfer.InsertTestTransfer(t, db, &trs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter Filter
|
||||||
|
filter.CounterpartyAddresses = allAddressesFilter()
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
filter.CounterpartyAddresses = []eth_common.Address{eth_common.HexToAddress("0x567890")}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(entries))
|
||||||
|
|
||||||
|
filter.CounterpartyAddresses = []eth_common.Address{td.pendingTr.To, td.mTr.To, trs[3].To}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 3, len(entries))
|
||||||
|
|
||||||
|
filter.CounterpartyAddresses = []eth_common.Address{td.tr1.To, td.pendingTr.From, trs[3].From, trs[5].To}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(entries))
|
||||||
|
}
|
||||||
|
func TestGetActivityEntriesFilterByNetworks(t *testing.T) {
|
||||||
|
db, close := setupTestActivityDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Adds 4 extractable transactions
|
||||||
|
td := fillTestData(t, db)
|
||||||
|
// Add 6 extractable transactions
|
||||||
|
trs := transfer.GenerateTestTransactions(t, db, 7, 6)
|
||||||
|
for i := range trs {
|
||||||
|
transfer.InsertTestTransfer(t, db, &trs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter Filter
|
||||||
|
chainIDs := allNetworksFilter()
|
||||||
|
entries, err := GetActivityEntries(db, []eth_common.Address{}, chainIDs, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 10, len(entries))
|
||||||
|
|
||||||
|
chainIDs = []common.ChainID{5674839210}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, chainIDs, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// TODO: update after multi-transactions are filterable by ChainID
|
||||||
|
require.Equal(t, 2 /*0*/, len(entries))
|
||||||
|
|
||||||
|
chainIDs = []common.ChainID{td.pendingTr.ChainID, td.mTr.ChainID, trs[3].ChainID}
|
||||||
|
entries, err = GetActivityEntries(db, []eth_common.Address{}, chainIDs, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// TODO: update after multi-transactions are filterable by ChainID
|
||||||
|
require.Equal(t, 4 /*3*/, len(entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetActivityEntriesCheckToAndFrom(t *testing.T) {
|
||||||
|
db, close := setupTestActivityDB(t)
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
// Adds 6 transactions from which 4 are filered out
|
||||||
|
td := fillTestData(t, db)
|
||||||
|
|
||||||
|
// Add extra transactions to test To address
|
||||||
|
trs := transfer.GenerateTestTransactions(t, db, 7, 2)
|
||||||
|
transfer.InsertTestTransfer(t, db, &trs[0])
|
||||||
|
transfer.InsertTestPendingTransaction(t, db, &trs[1])
|
||||||
|
|
||||||
|
addresses := []eth_common.Address{td.tr1.From, td.pendingTr.From,
|
||||||
|
td.singletonMTr.From, td.mTr.To, trs[0].To, trs[1].To}
|
||||||
|
|
||||||
|
var filter Filter
|
||||||
|
entries, err := GetActivityEntries(db, addresses, []common.ChainID{}, filter, 0, 15)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 6, len(entries))
|
||||||
|
|
||||||
|
require.Equal(t, SendAT, entries[5].activityType) // td.tr1
|
||||||
|
require.NotEqual(t, eth.Address{}, entries[5].transaction.Address) // td.tr1
|
||||||
|
require.Equal(t, td.tr1.From, entries[5].transaction.Address) // td.tr1
|
||||||
|
|
||||||
|
require.Equal(t, SendAT, entries[4].activityType) // td.pendingTr
|
||||||
|
|
||||||
|
// Multi-transactions are always considered as SendAT
|
||||||
|
require.Equal(t, SendAT, entries[3].activityType) // td.singletonMTr
|
||||||
|
require.Equal(t, SendAT, entries[2].activityType) // td.mTr
|
||||||
|
|
||||||
|
require.Equal(t, ReceiveAT, entries[1].activityType) // trs[0]
|
||||||
|
require.NotEqual(t, eth.Address{}, entries[1].transaction.Address) // trs[0]
|
||||||
|
require.Equal(t, trs[0].To, entries[1].transaction.Address) // trs[0]
|
||||||
|
|
||||||
|
require.Equal(t, ReceiveAT, entries[0].activityType) // trs[1] (pending)
|
||||||
|
|
||||||
|
// TODO: add accounts to DB for proper detection of sender/receiver
|
||||||
|
// TODO: Test with all addresses
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package activity
|
package activity
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/common"
|
import (
|
||||||
|
eth_common "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/status-im/status-go/services/wallet/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const NoLimitTimestampForPeriod = 0
|
||||||
|
|
||||||
type Period struct {
|
type Period struct {
|
||||||
// 0 means no limit
|
|
||||||
StartTimestamp int64 `json:"startTimestamp"`
|
StartTimestamp int64 `json:"startTimestamp"`
|
||||||
EndTimestamp int64 `json:"endTimestamp"`
|
EndTimestamp int64 `json:"endTimestamp"`
|
||||||
}
|
}
|
||||||
|
@ -11,36 +15,69 @@ type Period struct {
|
||||||
type Type int
|
type Type int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AllAT Type = iota
|
SendAT Type = iota
|
||||||
SendAT
|
|
||||||
ReceiveAT
|
ReceiveAT
|
||||||
BuyAT
|
BuyAT
|
||||||
SwapAT
|
SwapAT
|
||||||
BridgeAT
|
BridgeAT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func allActivityTypesFilter() []Type {
|
||||||
|
return []Type{}
|
||||||
|
}
|
||||||
|
|
||||||
type Status int
|
type Status int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AllAS Status = iota
|
FailedAS Status = iota
|
||||||
FailedAS
|
|
||||||
PendingAS
|
PendingAS
|
||||||
CompleteAS
|
CompleteAS
|
||||||
FinalizedAS
|
FinalizedAS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func allActivityStatusesFilter() []Status {
|
||||||
|
return []Status{}
|
||||||
|
}
|
||||||
|
|
||||||
type TokenType int
|
type TokenType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AllTT TokenType = iota
|
AssetTT TokenType = iota
|
||||||
AssetTT
|
|
||||||
CollectiblesTT
|
CollectiblesTT
|
||||||
)
|
)
|
||||||
|
|
||||||
type Filter struct {
|
type TokenCode string
|
||||||
Period Period `json:"period"`
|
|
||||||
Types []Type `json:"types"`
|
// Tokens the following rules apply for its members:
|
||||||
Statuses []Status `json:"statuses"`
|
// empty member: none is selected
|
||||||
TokenTypes []TokenType `json:"tokenTypes"`
|
// nil means all
|
||||||
CounterpartyAddresses []common.Address `json:"counterpartyAddresses"`
|
// see allTokensFilter and noTokensFilter
|
||||||
|
type Tokens struct {
|
||||||
|
Assets []TokenCode `json:"assets"`
|
||||||
|
Collectibles []eth_common.Address `json:"collectibles"`
|
||||||
|
EnabledTypes []TokenType `json:"enabledTypes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func noAssetsFilter() Tokens {
|
||||||
|
return Tokens{[]TokenCode{}, []eth_common.Address{}, []TokenType{CollectiblesTT}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allTokensFilter() Tokens {
|
||||||
|
return Tokens{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allAddressesFilter() []eth_common.Address {
|
||||||
|
return []eth_common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allNetworksFilter() []common.ChainID {
|
||||||
|
return []common.ChainID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
Period Period `json:"period"`
|
||||||
|
Types []Type `json:"types"`
|
||||||
|
Statuses []Status `json:"statuses"`
|
||||||
|
Tokens Tokens `json:"tokens"`
|
||||||
|
CounterpartyAddresses []eth_common.Address `json:"counterpartyAddresses"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"github.com/status-im/status-go/services/wallet/thirdparty/opensea"
|
"github.com/status-im/status-go/services/wallet/thirdparty/opensea"
|
||||||
"github.com/status-im/status-go/services/wallet/token"
|
"github.com/status-im/status-go/services/wallet/token"
|
||||||
"github.com/status-im/status-go/services/wallet/transfer"
|
"github.com/status-im/status-go/services/wallet/transfer"
|
||||||
|
|
||||||
|
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAPI(s *Service) *API {
|
func NewAPI(s *Service) *API {
|
||||||
|
@ -247,7 +249,7 @@ func (api *API) GetPendingTransactionsForIdentities(ctx context.Context, identit
|
||||||
result = make([]*transfer.PendingTransaction, 0, len(identities))
|
result = make([]*transfer.PendingTransaction, 0, len(identities))
|
||||||
var pt *transfer.PendingTransaction
|
var pt *transfer.PendingTransaction
|
||||||
for _, identity := range identities {
|
for _, identity := range identities {
|
||||||
pt, err = api.s.transactionManager.GetPendingEntry(identity.ChainID, identity.Hash)
|
pt, err = api.s.transactionManager.GetPendingEntry(uint64(identity.ChainID), identity.Hash)
|
||||||
result = append(result, pt)
|
result = append(result, pt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +527,7 @@ func (api *API) FetchAllCurrencyFormats() (currency.FormatPerSymbol, error) {
|
||||||
return api.s.currency.FetchAllCurrencyFormats()
|
return api.s.currency.FetchAllCurrencyFormats()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetActivityEntries(addresses []common.Address, chainIDs []uint64, filter activity.Filter, offset int, limit int) ([]activity.Entry, error) {
|
func (api *API) GetActivityEntries(addresses []common.Address, chainIDs []wallet_common.ChainID, filter activity.Filter, offset int, limit int) ([]activity.Entry, error) {
|
||||||
log.Debug("call to GetActivityEntries")
|
log.Debug("call to GetActivityEntries")
|
||||||
return activity.GetActivityEntries(api.s.db, addresses, chainIDs, filter, offset, limit)
|
return activity.GetActivityEntries(api.s.db, addresses, chainIDs, filter, offset, limit)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ package testutils
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
|
const EthSymbol = "ETH"
|
||||||
|
const SntSymbol = "SNT"
|
||||||
|
const DaiSymbol = "DAI"
|
||||||
|
|
||||||
func StructExistsInSlice[T any](target T, slice []T) bool {
|
func StructExistsInSlice[T any](target T, slice []T) bool {
|
||||||
for _, item := range slice {
|
for _, item := range slice {
|
||||||
if reflect.DeepEqual(target, item) {
|
if reflect.DeepEqual(target, item) {
|
||||||
|
|
|
@ -279,7 +279,7 @@ func (db *Database) GetTransfersForIdentities(ctx context.Context, identities []
|
||||||
for _, identity := range identities {
|
for _, identity := range identities {
|
||||||
subQuery := newSubQuery()
|
subQuery := newSubQuery()
|
||||||
// TODO optimization: consider using tuples in sqlite and IN operator
|
// TODO optimization: consider using tuples in sqlite and IN operator
|
||||||
subQuery = subQuery.FilterNetwork(identity.ChainID).FilterTransactionHash(identity.Hash).FilterAddress(identity.Address)
|
subQuery = subQuery.FilterNetwork(uint64(identity.ChainID)).FilterTransactionHash(identity.Hash).FilterAddress(identity.Address)
|
||||||
query.addSubQuery(subQuery, OrSeparator)
|
query.addSubQuery(subQuery, OrSeparator)
|
||||||
}
|
}
|
||||||
rows, err := db.client.QueryContext(ctx, query.String(), query.Args()...)
|
rows, err := db.client.QueryContext(ctx, query.String(), query.Args()...)
|
||||||
|
|
|
@ -223,8 +223,8 @@ func TestGetTransfersForIdentities(t *testing.T) {
|
||||||
require.Equal(t, big.NewInt(trs[3].BlkNumber), entries[1].BlockNumber)
|
require.Equal(t, big.NewInt(trs[3].BlkNumber), entries[1].BlockNumber)
|
||||||
require.Equal(t, uint64(trs[1].Timestamp), entries[0].Timestamp)
|
require.Equal(t, uint64(trs[1].Timestamp), entries[0].Timestamp)
|
||||||
require.Equal(t, uint64(trs[3].Timestamp), entries[1].Timestamp)
|
require.Equal(t, uint64(trs[3].Timestamp), entries[1].Timestamp)
|
||||||
require.Equal(t, trs[1].ChainID, entries[0].NetworkID)
|
require.Equal(t, uint64(trs[1].ChainID), entries[0].NetworkID)
|
||||||
require.Equal(t, trs[3].ChainID, entries[1].NetworkID)
|
require.Equal(t, uint64(trs[3].ChainID), entries[1].NetworkID)
|
||||||
require.Equal(t, MultiTransactionIDType(0), entries[0].MultiTransactionID)
|
require.Equal(t, MultiTransactionIDType(0), entries[0].MultiTransactionID)
|
||||||
require.Equal(t, MultiTransactionIDType(0), entries[1].MultiTransactionID)
|
require.Equal(t, MultiTransactionIDType(0), entries[1].MultiTransactionID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ func (q *transfersQuery) addWhereSeparator(separator SeparatorType) {
|
||||||
|
|
||||||
type SeparatorType int
|
type SeparatorType int
|
||||||
|
|
||||||
|
// Beware if changing this enum please update addWhereSeparator as well
|
||||||
const (
|
const (
|
||||||
NoSeparator SeparatorType = iota + 1
|
NoSeparator SeparatorType = iota + 1
|
||||||
OrSeparator
|
OrSeparator
|
||||||
|
|
|
@ -3,18 +3,23 @@ package transfer
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
eth_common "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/status-im/status-go/services/wallet/common"
|
||||||
|
"github.com/status-im/status-go/services/wallet/testutils"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestTransaction struct {
|
type TestTransaction struct {
|
||||||
Hash common.Hash
|
Hash eth_common.Hash
|
||||||
ChainID uint64
|
ChainID common.ChainID
|
||||||
From common.Address // [sender]
|
From eth_common.Address // [sender]
|
||||||
To common.Address // [address]
|
To eth_common.Address // [address]
|
||||||
|
FromToken string // used to detect type in transfers table
|
||||||
|
ToToken string // only used in multi_transactions table
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
Value int64
|
Value int64
|
||||||
BlkNumber int64
|
BlkNumber int64
|
||||||
|
@ -25,10 +30,10 @@ type TestTransaction struct {
|
||||||
func GenerateTestTransactions(t *testing.T, db *sql.DB, firstStartIndex int, count int) (result []TestTransaction) {
|
func GenerateTestTransactions(t *testing.T, db *sql.DB, firstStartIndex int, count int) (result []TestTransaction) {
|
||||||
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
|
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
|
||||||
tr := TestTransaction{
|
tr := TestTransaction{
|
||||||
Hash: common.HexToHash(fmt.Sprintf("0x1%d", i)),
|
Hash: eth_common.HexToHash(fmt.Sprintf("0x1%d", i)),
|
||||||
ChainID: uint64(i),
|
ChainID: common.ChainID(i),
|
||||||
From: common.HexToAddress(fmt.Sprintf("0x2%d", i)),
|
From: eth_common.HexToAddress(fmt.Sprintf("0x2%d", i)),
|
||||||
To: common.HexToAddress(fmt.Sprintf("0x3%d", i)),
|
To: eth_common.HexToAddress(fmt.Sprintf("0x3%d", i)),
|
||||||
Timestamp: int64(i),
|
Timestamp: int64(i),
|
||||||
Value: int64(i),
|
Value: int64(i),
|
||||||
BlkNumber: int64(i),
|
BlkNumber: int64(i),
|
||||||
|
@ -42,7 +47,11 @@ func GenerateTestTransactions(t *testing.T, db *sql.DB, firstStartIndex int, cou
|
||||||
|
|
||||||
func InsertTestTransfer(t *testing.T, db *sql.DB, tr *TestTransaction) {
|
func InsertTestTransfer(t *testing.T, db *sql.DB, tr *TestTransaction) {
|
||||||
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
|
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
|
||||||
blkHash := common.HexToHash("4")
|
tokenType := "eth"
|
||||||
|
if tr.FromToken != "" && strings.ToUpper(tr.FromToken) != testutils.EthSymbol {
|
||||||
|
tokenType = "erc20"
|
||||||
|
}
|
||||||
|
blkHash := eth_common.HexToHash("4")
|
||||||
_, err := db.Exec(`
|
_, err := db.Exec(`
|
||||||
INSERT OR IGNORE INTO blocks(
|
INSERT OR IGNORE INTO blocks(
|
||||||
network_id, address, blk_number, blk_hash
|
network_id, address, blk_number, blk_hash
|
||||||
|
@ -50,17 +59,34 @@ func InsertTestTransfer(t *testing.T, db *sql.DB, tr *TestTransaction) {
|
||||||
INSERT INTO transfers (network_id, hash, address, blk_hash, tx,
|
INSERT INTO transfers (network_id, hash, address, blk_hash, tx,
|
||||||
sender, receipt, log, type, blk_number, timestamp, loaded,
|
sender, receipt, log, type, blk_number, timestamp, loaded,
|
||||||
multi_transaction_id, base_gas_fee
|
multi_transaction_id, base_gas_fee
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "test", ?, ?, 0, ?, 0)`,
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, 0)`,
|
||||||
tr.ChainID, tr.To, tr.BlkNumber, blkHash,
|
tr.ChainID, tr.To, tr.BlkNumber, blkHash,
|
||||||
tr.ChainID, tr.Hash, tr.To, blkHash, &JSONBlob{}, tr.From, &JSONBlob{}, &JSONBlob{}, tr.BlkNumber, tr.Timestamp, tr.MultiTransactionID)
|
tr.ChainID, tr.Hash, tr.To, blkHash, &JSONBlob{}, tr.From, &JSONBlob{}, &JSONBlob{}, tokenType, tr.BlkNumber, tr.Timestamp, tr.MultiTransactionID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InsertTestPendingTransaction(t *testing.T, db *sql.DB, tr *TestTransaction) {
|
||||||
|
_, 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, ?, '', 'test', '', ?)`,
|
||||||
|
tr.ChainID, tr.Hash, tr.Timestamp, tr.From, tr.To, tr.Value, tr.MultiTransactionID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InsertTestMultiTransaction(t *testing.T, db *sql.DB, tr *TestTransaction) MultiTransactionIDType {
|
func InsertTestMultiTransaction(t *testing.T, db *sql.DB, tr *TestTransaction) MultiTransactionIDType {
|
||||||
|
fromTokenType := tr.FromToken
|
||||||
|
if tr.FromToken == "" {
|
||||||
|
fromTokenType = testutils.EthSymbol
|
||||||
|
}
|
||||||
|
toTokenType := tr.ToToken
|
||||||
|
if tr.ToToken == "" {
|
||||||
|
toTokenType = testutils.EthSymbol
|
||||||
|
}
|
||||||
result, err := db.Exec(`
|
result, err := db.Exec(`
|
||||||
INSERT INTO multi_transactions (from_address, from_asset, from_amount, to_address, to_asset, type, timestamp
|
INSERT INTO multi_transactions (from_address, from_asset, from_amount, to_address, to_asset, type, timestamp
|
||||||
) VALUES (?, 'ETH', 0, ?, 'SNT', ?, ?)`,
|
) VALUES (?, ?, 0, ?, ?, ?, ?)`,
|
||||||
tr.From, tr.To, tr.MultiTransactionType, tr.Timestamp)
|
tr.From, fromTokenType, tr.To, toTokenType, tr.MultiTransactionType, tr.Timestamp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rowID, err := result.LastInsertId()
|
rowID, err := result.LastInsertId()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/status-im/status-go/services/wallet/async"
|
"github.com/status-im/status-go/services/wallet/async"
|
||||||
"github.com/status-im/status-go/services/wallet/bigint"
|
"github.com/status-im/status-go/services/wallet/bigint"
|
||||||
"github.com/status-im/status-go/services/wallet/bridge"
|
"github.com/status-im/status-go/services/wallet/bridge"
|
||||||
|
wallet_common "github.com/status-im/status-go/services/wallet/common"
|
||||||
"github.com/status-im/status-go/transactions"
|
"github.com/status-im/status-go/transactions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,9 +97,9 @@ type PendingTransaction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TransactionIdentity struct {
|
type TransactionIdentity struct {
|
||||||
ChainID uint64 `json:"chainId"`
|
ChainID wallet_common.ChainID `json:"chainId"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
Address common.Address `json:"address"`
|
Address common.Address `json:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectFromPending = `SELECT hash, timestamp, value, from_address, to_address, data,
|
const selectFromPending = `SELECT hash, timestamp, value, from_address, to_address, data,
|
||||||
|
|
Loading…
Reference in New Issue