feat: Filter by collectibles (#4028)
This commit is contained in:
parent
b3213172a7
commit
bc4093299e
|
@ -311,8 +311,9 @@ const (
|
|||
fromTrType = byte(1)
|
||||
toTrType = byte(2)
|
||||
|
||||
noEntriesInTmpTableSQLValues = "(NULL)"
|
||||
noEntriesInTwoColumnsTmpTableSQLValues = "(NULL, NULL)"
|
||||
noEntriesInTmpTableSQLValues = "(NULL)"
|
||||
noEntriesInTwoColumnsTmpTableSQLValues = "(NULL, NULL)"
|
||||
noEntriesInThreeColumnsTmpTableSQLValues = "(NULL, NULL, NULL)"
|
||||
)
|
||||
|
||||
//go:embed filter.sql
|
||||
|
@ -374,6 +375,21 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
}
|
||||
}
|
||||
|
||||
includeAllCollectibles := len(filter.Collectibles) == 0 && !filter.FilterOutCollectibles
|
||||
assetsERC721 := noEntriesInThreeColumnsTmpTableSQLValues
|
||||
if !includeAllCollectibles && !filter.FilterOutCollectibles {
|
||||
assetsERC721 = joinItems(filter.Collectibles, func(item Token) string {
|
||||
// SQL HEX() (Blob->Hex) conversion returns uppercase digits with no 0x prefix
|
||||
tokenID := strings.ToUpper(item.TokenID.String()[2:])
|
||||
address := strings.ToUpper(item.Address.Hex()[2:])
|
||||
if len(tokenID)%2 == 1 {
|
||||
// Hex length must be divisable by 2, otherwise append '0' at the beginning
|
||||
tokenID = "0" + tokenID
|
||||
}
|
||||
return fmt.Sprintf("%d, '%s', '%s'", item.ChainID, tokenID, address)
|
||||
})
|
||||
}
|
||||
|
||||
// construct chain IDs
|
||||
includeAllNetworks := len(chainIDs) == 0
|
||||
networks := noEntriesInTmpTableSQLValues
|
||||
|
@ -414,7 +430,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
return strconv.Itoa(int(t))
|
||||
})
|
||||
|
||||
queryString := fmt.Sprintf(queryFormatString, involvedAddresses, toAddresses, assetsTokenCodes, assetsERC20, networks,
|
||||
queryString := fmt.Sprintf(queryFormatString, involvedAddresses, toAddresses, assetsTokenCodes, assetsERC20, assetsERC721, networks,
|
||||
layer2Networks, joinedMTTypes)
|
||||
|
||||
// The duplicated temporary table UNION with CTE acts as an optimization
|
||||
|
@ -435,6 +451,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
|
|||
includeAllStatuses, filterStatusCompleted, filterStatusFailed, filterStatusFinalized, filterStatusPending,
|
||||
FailedAS, CompleteAS, FinalizedAS, PendingAS,
|
||||
includeAllTokenTypeAssets,
|
||||
includeAllCollectibles,
|
||||
includeAllNetworks,
|
||||
transactions.Pending,
|
||||
deps.currentTimestamp(),
|
||||
|
|
|
@ -38,6 +38,15 @@ func tokenFromSymbol(chainID *common.ChainID, symbol string) *Token {
|
|||
return nil
|
||||
}
|
||||
|
||||
func tokenFromCollectible(c *transfer.TestCollectible) Token {
|
||||
return Token{
|
||||
TokenType: Erc721,
|
||||
ChainID: c.ChainID,
|
||||
Address: c.TokenAddress,
|
||||
TokenID: (*hexutil.Big)(c.TokenID),
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestActivityDBStorageChoice(tb testing.TB, inMemory bool) (deps FilterDependencies, close func()) {
|
||||
var db *sql.DB
|
||||
var err error
|
||||
|
@ -951,6 +960,60 @@ func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
|
|||
require.Nil(t, entries[0].tokenOut)
|
||||
}
|
||||
|
||||
func TestGetActivityEntriesFilterByCollectibles(t *testing.T) {
|
||||
deps, close := setupTestActivityDB(t)
|
||||
defer close()
|
||||
|
||||
// Adds 4 extractable transactions 2 transactions (ETH/Goerli, ETH/Optimism), one MT USDC to DAI and another MT USDC to SNT
|
||||
td, fromTds, toTds := fillTestData(t, deps.db)
|
||||
// Add 4 transactions with collectibles
|
||||
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 4)
|
||||
for i := range trs {
|
||||
collectibleData := transfer.TestCollectibles[i]
|
||||
trs[i].ChainID = collectibleData.ChainID
|
||||
transfer.InsertTestTransferWithOptions(t, deps.db, trs[i].To, &trs[i], &transfer.TestTransferOptions{
|
||||
TokenAddress: collectibleData.TokenAddress,
|
||||
TokenID: collectibleData.TokenID,
|
||||
})
|
||||
}
|
||||
|
||||
allAddresses := append(append(append(fromTds, toTds...), fromTrs...), toTrs...)
|
||||
|
||||
var filter Filter
|
||||
filter.FilterOutCollectibles = true
|
||||
entries, err := getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(entries))
|
||||
|
||||
filter.FilterOutCollectibles = false
|
||||
filter.Collectibles = allTokensFilter()
|
||||
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 8, len(entries))
|
||||
|
||||
// Search for a specific collectible
|
||||
filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[0])}
|
||||
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(entries))
|
||||
require.Equal(t, entries[0].tokenIn.Address, transfer.TestCollectibles[0].TokenAddress)
|
||||
require.Equal(t, entries[0].tokenIn.TokenID, (*hexutil.Big)(transfer.TestCollectibles[0].TokenID))
|
||||
|
||||
// Search for a specific collectible
|
||||
filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[3])}
|
||||
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(entries))
|
||||
require.Equal(t, entries[0].tokenIn.Address, transfer.TestCollectibles[3].TokenAddress)
|
||||
require.Equal(t, entries[0].tokenIn.TokenID, (*hexutil.Big)(transfer.TestCollectibles[3].TokenID))
|
||||
|
||||
// Search for a multiple collectibles
|
||||
filter.Collectibles = []Token{tokenFromCollectible(&transfer.TestCollectibles[1]), tokenFromCollectible(&transfer.TestCollectibles[2])}
|
||||
entries, err = getActivityEntries(context.Background(), deps, allAddresses, true, []common.ChainID{}, filter, 0, 15)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(entries))
|
||||
}
|
||||
|
||||
func TestGetActivityEntriesFilterByToAddresses(t *testing.T) {
|
||||
deps, close := setupTestActivityDB(t)
|
||||
defer close()
|
||||
|
|
|
@ -38,6 +38,7 @@ WITH filter_conditions AS (
|
|||
? AS statusFinalized,
|
||||
? AS statusPending,
|
||||
? AS includeAllTokenTypeAssets,
|
||||
? AS includeAllCollectibles,
|
||||
? AS includeAllNetworks,
|
||||
? AS pendingStatus,
|
||||
? AS nowTimestamp,
|
||||
|
@ -87,6 +88,10 @@ assets_erc20(chain_id, token_address) AS (
|
|||
VALUES
|
||||
%s
|
||||
),
|
||||
assets_erc721(chain_id, token_id, token_address) AS (
|
||||
VALUES
|
||||
%s
|
||||
),
|
||||
filter_networks(network_id) AS (
|
||||
VALUES
|
||||
%s
|
||||
|
@ -291,6 +296,19 @@ WHERE
|
|||
)
|
||||
)
|
||||
)
|
||||
AND (
|
||||
includeAllCollectibles
|
||||
OR (
|
||||
transfers.type = "erc721"
|
||||
AND (
|
||||
(
|
||||
transfers.network_id,
|
||||
HEX(transfers.token_id),
|
||||
HEX(transfers.token_address)
|
||||
) IN assets_erc721
|
||||
)
|
||||
)
|
||||
)
|
||||
AND (
|
||||
includeAllNetworks
|
||||
OR (transfers.network_id IN filter_networks)
|
||||
|
@ -360,6 +378,7 @@ WHERE
|
|||
filterAllActivityStatus
|
||||
OR filterStatusPending
|
||||
)
|
||||
AND includeAllCollectibles
|
||||
AND (
|
||||
(
|
||||
startFilterDisabled
|
||||
|
@ -458,6 +477,7 @@ WHERE
|
|||
OR multi_transactions.timestamp <= endTimestamp
|
||||
)
|
||||
)
|
||||
AND includeAllCollectibles
|
||||
AND (
|
||||
filterActivityTypeAll
|
||||
OR (multi_transactions.type IN (%s))
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
eth_common "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/common"
|
||||
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||
"github.com/status-im/status-go/services/wallet/testutils"
|
||||
|
@ -141,6 +142,35 @@ func GenerateTestTransfers(tb testing.TB, db *sql.DB, firstStartIndex int, count
|
|||
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",
|
||||
|
|
Loading…
Reference in New Issue