github.com/status-im/status-go@v1.1.0/services/wallet/activity/filter.go (about) 1 package activity 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 8 // used for embedding the sql query in the binary 9 _ "embed" 10 11 eth "github.com/ethereum/go-ethereum/common" 12 "github.com/ethereum/go-ethereum/common/hexutil" 13 "github.com/status-im/status-go/services/wallet/common" 14 "github.com/status-im/status-go/services/wallet/thirdparty" 15 "github.com/status-im/status-go/transactions" 16 ) 17 18 const NoLimitTimestampForPeriod = 0 19 20 //go:embed get_collectibles.sql 21 var getCollectiblesQueryFormatString string 22 23 //go:embed oldest_timestamp.sql 24 var oldestTimestampQueryFormatString string 25 26 //go:embed recipients.sql 27 var recipientsQueryFormatString string 28 29 type Period struct { 30 StartTimestamp int64 `json:"startTimestamp"` 31 EndTimestamp int64 `json:"endTimestamp"` 32 } 33 34 type Type int 35 36 const ( 37 SendAT Type = iota 38 ReceiveAT 39 BuyAT 40 SwapAT 41 BridgeAT 42 ContractDeploymentAT 43 MintAT 44 ApproveAT 45 ) 46 47 func allActivityTypesFilter() []Type { 48 return []Type{} 49 } 50 51 type Status int 52 53 const ( 54 FailedAS Status = iota // failed status or at least one failed transaction for multi-transactions 55 PendingAS // in pending DB or at least one transaction in pending for multi-transactions 56 CompleteAS // success status 57 FinalizedAS // all multi-transactions have success status 58 ) 59 60 func allActivityStatusesFilter() []Status { 61 return []Status{} 62 } 63 64 type TokenType int 65 66 const ( 67 Native TokenType = iota 68 Erc20 69 Erc721 70 Erc1155 71 ) 72 73 // Token supports all tokens. Some fields might be optional, depending on the TokenType 74 type Token struct { 75 TokenType TokenType `json:"tokenType"` 76 // ChainID is used for TokenType.Native only to lookup the symbol, all chains will be included in the token filter 77 ChainID common.ChainID `json:"chainId"` 78 Address eth.Address `json:"address,omitempty"` 79 TokenID *hexutil.Big `json:"tokenId,omitempty"` 80 } 81 82 func allTokensFilter() []Token { 83 return []Token{} 84 } 85 86 func allNetworksFilter() []common.ChainID { 87 return []common.ChainID{} 88 } 89 90 type Filter struct { 91 Period Period `json:"period"` 92 Types []Type `json:"types"` 93 Statuses []Status `json:"statuses"` 94 CounterpartyAddresses []eth.Address `json:"counterpartyAddresses"` 95 96 // Tokens 97 Assets []Token `json:"assets"` 98 Collectibles []Token `json:"collectibles"` 99 FilterOutAssets bool `json:"filterOutAssets"` 100 FilterOutCollectibles bool `json:"filterOutCollectibles"` 101 } 102 103 func (f *Filter) IsEmpty() bool { 104 return f.Period.StartTimestamp == NoLimitTimestampForPeriod && 105 f.Period.EndTimestamp == NoLimitTimestampForPeriod && 106 len(f.Types) == 0 && 107 len(f.Statuses) == 0 && 108 len(f.CounterpartyAddresses) == 0 && 109 len(f.Assets) == 0 && 110 len(f.Collectibles) == 0 && 111 !f.FilterOutAssets && 112 !f.FilterOutCollectibles 113 } 114 115 func GetRecipients(ctx context.Context, db *sql.DB, chainIDs []common.ChainID, addresses []eth.Address, offset int, limit int) (recipients []eth.Address, hasMore bool, err error) { 116 filterAllAddresses := len(addresses) == 0 117 involvedAddresses := noEntriesInTmpTableSQLValues 118 if !filterAllAddresses { 119 involvedAddresses = joinAddresses(addresses) 120 } 121 122 includeAllNetworks := len(chainIDs) == 0 123 networks := noEntriesInTmpTableSQLValues 124 if !includeAllNetworks { 125 networks = joinItems(chainIDs, nil) 126 } 127 128 queryString := fmt.Sprintf(recipientsQueryFormatString, involvedAddresses, networks) 129 130 rows, err := db.QueryContext(ctx, queryString, filterAllAddresses, includeAllNetworks, transactions.Pending, limit, offset) 131 if err != nil { 132 return nil, false, err 133 } 134 defer rows.Close() 135 136 var entries []eth.Address 137 for rows.Next() { 138 var toAddress eth.Address 139 var timestamp int64 140 err := rows.Scan(&toAddress, ×tamp) 141 if err != nil { 142 return nil, false, err 143 } 144 entries = append(entries, toAddress) 145 } 146 147 if err = rows.Err(); err != nil { 148 return nil, false, err 149 } 150 151 hasMore = len(entries) == limit 152 153 return entries, hasMore, nil 154 } 155 156 func GetOldestTimestamp(ctx context.Context, db *sql.DB, addresses []eth.Address) (timestamp uint64, err error) { 157 filterAllAddresses := len(addresses) == 0 158 involvedAddresses := noEntriesInTmpTableSQLValues 159 if !filterAllAddresses { 160 involvedAddresses = joinAddresses(addresses) 161 } 162 163 queryString := fmt.Sprintf(oldestTimestampQueryFormatString, involvedAddresses) 164 165 row := db.QueryRowContext(ctx, queryString, filterAllAddresses) 166 var fromAddress, toAddress sql.NullString 167 err = row.Scan(&fromAddress, &toAddress, ×tamp) 168 if err == sql.ErrNoRows { 169 return 0, nil 170 } 171 172 if err != nil { 173 return 0, err 174 } 175 176 return timestamp, nil 177 } 178 179 func GetActivityCollectibles(ctx context.Context, db *sql.DB, chainIDs []common.ChainID, owners []eth.Address, offset int, limit int) ([]thirdparty.CollectibleUniqueID, error) { 180 filterAllAddresses := len(owners) == 0 181 involvedAddresses := noEntriesInTmpTableSQLValues 182 if !filterAllAddresses { 183 involvedAddresses = joinAddresses(owners) 184 } 185 186 includeAllNetworks := len(chainIDs) == 0 187 networks := noEntriesInTmpTableSQLValues 188 if !includeAllNetworks { 189 networks = joinItems(chainIDs, nil) 190 } 191 192 queryString := fmt.Sprintf(getCollectiblesQueryFormatString, involvedAddresses, networks) 193 194 rows, err := db.QueryContext(ctx, queryString, filterAllAddresses, includeAllNetworks, limit, offset) 195 if err != nil { 196 return nil, err 197 } 198 defer rows.Close() 199 200 return thirdparty.RowsToCollectibles(rows) 201 }