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

View File

@ -620,7 +620,7 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
}, entries[0]) }, 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 { for _, entry := range entries {
switch entry.activityType { switch entry.activityType {
case SendAT: case SendAT:
@ -633,6 +633,10 @@ func countTypes(entries []Entry) (sendCount, receiveCount, swapCount, buyCount,
buyCount++ buyCount++
case BridgeAT: case BridgeAT:
bridgeCount++ bridgeCount++
case ContractDeploymentAT:
contractCount++
case MintAT:
mintCount++
} }
} }
return return
@ -662,15 +666,29 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
transfer.InsertTestTransfer(t, deps.db, trs[i].To, &trs[i]) 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 // Test filtering out without address involved
var filter Filter var filter Filter
filter.Types = allActivityTypesFilter() filter.Types = allActivityTypesFilter()
// Set tr1 to Receive and pendingTr to Send; rest of two MT remain default Send // 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) entries, err := getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 9, len(entries)) require.Equal(t, 11, len(entries))
filter.Types = []Type{SendAT, SwapAT} filter.Types = []Type{SendAT, SwapAT}
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15) 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) // 3 from td Send + 2 trs MT Send + 1 (swap)
require.Equal(t, 6, len(entries)) 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, 5, sendCount)
require.Equal(t, 0, receiveCount) require.Equal(t, 0, receiveCount)
require.Equal(t, 0, contractCount)
require.Equal(t, 0, mintCount)
require.Equal(t, 1, swapCount) require.Equal(t, 1, swapCount)
require.Equal(t, 0, bridgeCount) require.Equal(t, 0, bridgeCount)
@ -690,11 +710,39 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(entries)) 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, 0, sendCount)
require.Equal(t, 1, receiveCount) require.Equal(t, 1, receiveCount)
require.Equal(t, 0, contractCount)
require.Equal(t, 0, mintCount)
require.Equal(t, 0, swapCount) require.Equal(t, 0, swapCount)
require.Equal(t, 2, bridgeCount) 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) { 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 -- 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 -- 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 -- 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 -- 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 filterActivityTypeAll,
? AS filterActivityTypeSend, ? AS filterActivityTypeSend,
? AS filterActivityTypeReceive, ? AS filterActivityTypeReceive,
? AS filterActivityTypeContractDeployment,
? AS filterActivityTypeMint,
? AS fromTrType, ? AS fromTrType,
? AS toTrType, ? AS toTrType,
? AS filterAllAddresses, ? AS filterAllAddresses,
@ -37,7 +41,8 @@ WITH filter_conditions AS (
? AS statusPending, ? AS statusPending,
? AS includeAllTokenTypeAssets, ? AS includeAllTokenTypeAssets,
? AS includeAllNetworks, ? AS includeAllNetworks,
? AS pendingStatus ? AS pendingStatus,
"0000000000000000000000000000000000000000" AS zeroAddress
), ),
filter_addresses(address) AS ( filter_addresses(address) AS (
SELECT SELECT
@ -195,15 +200,16 @@ WHERE
) )
) )
AND ( AND (
-- Check description at the top of the file why code below is duplicated
filterActivityTypeAll filterActivityTypeAll
OR ( OR (
filterActivityTypeSend filterActivityTypeSend
AND ( AND (
filterAllAddresses filterAllAddresses
OR ( OR (HEX(transfers.tx_from_address) IN filter_addresses)
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 ( OR (
filterActivityTypeReceive filterActivityTypeReceive
@ -211,6 +217,28 @@ WHERE
filterAllAddresses filterAllAddresses
OR (HEX(transfers.tx_to_address) IN filter_addresses) 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 ( AND (

View File

@ -380,6 +380,9 @@ func isMetadataEmpty(asset thirdparty.CollectibleData) bool {
} }
func (o *Manager) fetchTokenURI(id thirdparty.CollectibleUniqueID) (string, error) { 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)) backend, err := o.rpcClient.EthClient(uint64(id.ContractID.ChainID))
if err != nil { if err != nil {
return "", err return "", err

View File

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