feat: Add activity filtering by contract deploy and minting (#4009)

This commit is contained in:
Cuteivist 2023-09-11 11:54:37 +02:00 committed by GitHub
parent 8ba9f38ce7
commit e77fc59f5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 11 deletions

View File

@ -420,6 +420,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
rows, err := deps.db.QueryContext(ctx, queryString,
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
sliceContains(filter.Types, ContractDeploymentAT), sliceContains(filter.Types, MintAT),
fromTrType, toTrType,
filterAllAddresses, filterAllToAddresses,
includeAllStatuses, filterStatusCompleted, filterStatusFailed, filterStatusFinalized, filterStatusPending,
@ -519,7 +520,7 @@ func getActivityEntries(ctx context.Context, deps FilterDependencies, addresses
)
// Extract tokens
if activityType == SendAT {
if activityType == SendAT || activityType == ContractDeploymentAT {
entry.tokenOut = involvedToken
outChainID = new(common.ChainID)
*outChainID = common.ChainID(chainID.Int64)
@ -624,10 +625,14 @@ func getTrInAndOutAmounts(activityType Type, trAmount sql.NullString) (inAmount
amount, ok := new(big.Int).SetString(trAmount.String, 16)
if ok {
switch activityType {
case ContractDeploymentAT:
fallthrough
case SendAT:
inAmount = (*hexutil.Big)(big.NewInt(0))
outAmount = (*hexutil.Big)(amount)
return
case MintAT:
fallthrough
case ReceiveAT:
inAmount = (*hexutil.Big)(amount)
outAmount = (*hexutil.Big)(big.NewInt(0))

View File

@ -620,7 +620,7 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
}, entries[0])
}
func countTypes(entries []Entry) (sendCount, receiveCount, swapCount, buyCount, bridgeCount int) {
func countTypes(entries []Entry) (sendCount, receiveCount, contractCount, mintCount, swapCount, buyCount, bridgeCount int) {
for _, entry := range entries {
switch entry.activityType {
case SendAT:
@ -633,6 +633,10 @@ func countTypes(entries []Entry) (sendCount, receiveCount, swapCount, buyCount,
buyCount++
case BridgeAT:
bridgeCount++
case ContractDeploymentAT:
contractCount++
case MintAT:
mintCount++
}
}
return
@ -662,15 +666,29 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i])
}
trsSpecial, _, _ := transfer.GenerateTestTransfers(t, deps.db, 100, 2)
// Insert MintAT
trsSpecial[0].From = eth.HexToAddress("0x0")
transfer.InsertTestTransferWithOptions(t, deps.db, trsSpecial[0].To, &trsSpecial[0], &transfer.TestTransferOptions{
TokenAddress: eth.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
TokenID: (big.NewInt(1318)),
})
// Insert ContractDeploymentAt
trsSpecial[1].To = eth.HexToAddress("0x0")
transfer.InsertTestTransferWithOptions(t, deps.db, trsSpecial[1].From, &trsSpecial[1], &transfer.TestTransferOptions{
NullifyAddresses: []eth.Address{trsSpecial[1].To},
})
// Test filtering out without address involved
var filter Filter
filter.Types = allActivityTypesFilter()
// Set tr1 to Receive and pendingTr to Send; rest of two MT remain default Send
addresses := []eth.Address{td.tr1.To, td.pendingTr.From, td.multiTx1.FromAddress, td.multiTx2.FromAddress, trs[0].From, trs[2].From, trs[4].From, trs[6].From, trs[8].From}
addresses := []eth.Address{td.tr1.To, td.pendingTr.From, td.multiTx1.FromAddress, td.multiTx2.FromAddress, trs[0].From, trs[2].From, trs[4].From, trs[6].From, trs[8].From, trsSpecial[0].To, trsSpecial[1].From}
entries, err := getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 9, len(entries))
require.Equal(t, 11, len(entries))
filter.Types = []Type{SendAT, SwapAT}
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
@ -678,10 +696,12 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
// 3 from td Send + 2 trs MT Send + 1 (swap)
require.Equal(t, 6, len(entries))
sendCount, receiveCount, swapCount, _, bridgeCount := countTypes(entries)
sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount := countTypes(entries)
require.Equal(t, 5, sendCount)
require.Equal(t, 0, receiveCount)
require.Equal(t, 0, contractCount)
require.Equal(t, 0, mintCount)
require.Equal(t, 1, swapCount)
require.Equal(t, 0, bridgeCount)
@ -690,11 +710,39 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 3, len(entries))
sendCount, receiveCount, swapCount, _, bridgeCount = countTypes(entries)
sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount = countTypes(entries)
require.Equal(t, 0, sendCount)
require.Equal(t, 1, receiveCount)
require.Equal(t, 0, contractCount)
require.Equal(t, 0, mintCount)
require.Equal(t, 0, swapCount)
require.Equal(t, 2, bridgeCount)
filter.Types = []Type{MintAT}
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 1, len(entries))
sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount = countTypes(entries)
require.Equal(t, 0, sendCount)
require.Equal(t, 0, receiveCount)
require.Equal(t, 0, contractCount)
require.Equal(t, 1, mintCount)
require.Equal(t, 0, swapCount)
require.Equal(t, 0, bridgeCount)
filter.Types = []Type{ContractDeploymentAT}
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 1, len(entries))
sendCount, receiveCount, contractCount, mintCount, swapCount, _, bridgeCount = countTypes(entries)
require.Equal(t, 0, sendCount)
require.Equal(t, 0, receiveCount)
require.Equal(t, 1, contractCount)
require.Equal(t, 0, mintCount)
require.Equal(t, 0, swapCount)
require.Equal(t, 0, bridgeCount)
}
func TestGetActivityEntriesFilterByAddresses(t *testing.T) {

View File

@ -10,6 +10,8 @@
--
-- Only status FailedAS, PendingAS and CompleteAS are returned. FinalizedAS requires correlation with blockchain current state. As an optimization we approximate it by using timestamp information; see startTimestamp and endTimestamp
--
-- ContractDeploymentAT is subtype of SendAT and MintAT is subtype of ReceiveAT. It means query must prevent returning MintAT when filtering by ReceiveAT or ContractDeploymentAT when filtering by SendAT. That required duplicated code in filter by type query, to maintain perforamnce.
--
-- Token filtering has two parts
-- 1. Filtering by symbol (multi_transactions and pending_transactions tables) where the chain ID is ignored, basically the filter_networks will account for that
-- 2. Filtering by token identity (chain and address for transfers table) where the symbol is ignored and all the token identities must be provided
@ -23,6 +25,8 @@ WITH filter_conditions AS (
? AS filterActivityTypeAll,
? AS filterActivityTypeSend,
? AS filterActivityTypeReceive,
? AS filterActivityTypeContractDeployment,
? AS filterActivityTypeMint,
? AS fromTrType,
? AS toTrType,
? AS filterAllAddresses,
@ -37,7 +41,8 @@ WITH filter_conditions AS (
? AS statusPending,
? AS includeAllTokenTypeAssets,
? AS includeAllNetworks,
? AS pendingStatus
? AS pendingStatus,
"0000000000000000000000000000000000000000" AS zeroAddress
),
filter_addresses(address) AS (
SELECT
@ -195,15 +200,16 @@ WHERE
)
)
AND (
-- Check description at the top of the file why code below is duplicated
filterActivityTypeAll
OR (
filterActivityTypeSend
AND (
filterAllAddresses
OR (
HEX(transfers.tx_from_address) IN filter_addresses
)
OR (HEX(transfers.tx_from_address) IN filter_addresses)
)
AND NOT (tr_type = fromTrType and transfers.tx_to_address IS NULL AND transfers.type = "eth"
AND transfers.contract_address IS NOT NULL AND HEX(transfers.contract_address) != zeroAddress)
)
OR (
filterActivityTypeReceive
@ -211,6 +217,28 @@ WHERE
filterAllAddresses
OR (HEX(transfers.tx_to_address) IN filter_addresses)
)
AND NOT (tr_type = toTrType AND transfers.type = "erc721" AND (
transfers.tx_from_address IS NULL
OR HEX(transfers.tx_from_address) = zeroAddress))
)
OR (
filterActivityTypeContractDeployment
AND tr_type = fromTrType AND transfers.tx_to_address IS NULL AND transfers.type = "eth"
AND transfers.contract_address IS NOT NULL AND HEX(transfers.contract_address) != zeroAddress
AND (
filterAllAddresses
OR (HEX(transfers.address) IN filter_addresses)
)
)
OR (
filterActivityTypeMint
AND tr_type = toTrType AND transfers.type = "erc721" AND (
transfers.tx_from_address IS NULL
OR HEX(transfers.tx_from_address) = zeroAddress
) AND (
filterAllAddresses
OR (HEX(transfers.address) IN filter_addresses)
)
)
)
AND (

View File

@ -380,6 +380,9 @@ func isMetadataEmpty(asset thirdparty.CollectibleData) bool {
}
func (o *Manager) fetchTokenURI(id thirdparty.CollectibleUniqueID) (string, error) {
if id.TokenID == nil {
return "", errors.New("empty token ID")
}
backend, err := o.rpcClient.EthClient(uint64(id.ContractID.ChainID))
if err != nil {
return "", err

View File

@ -242,7 +242,7 @@ func InsertTestTransferWithOptions(tb testing.TB, db *sql.DB, address eth_common
block := blockDBFields{
chainID: uint64(tr.ChainID),
account: tr.To,
account: address,
blockNumber: big.NewInt(tr.BlkNumber),
blockHash: blkHash,
}