feat(wallet) filter activity by erc20

Main changes:

- Refactor activity API to propagate token identities.
- Extend service to convert token identities to symbols for filtering
  multi-transaction
- Filter transfers, pending_transactions and multi-transactions based
  on the provided token identities
- Return involved token identities in activity API
- Test token filtering

Also:

- Fixed calling cancel on a filer activity completed task to release
  resources

Notes:

- Found limitations with the token identity which complicates things
  by not allowing to filter by token groups (like token-code does)

Updates status-desktop #11025
This commit is contained in:
Stefan 2023-06-13 11:25:23 +02:00 committed by Stefan Dunca
parent bf64f97d5a
commit 8e63f44735
16 changed files with 1076 additions and 721 deletions

View File

@ -1 +1 @@
0.159.1
0.159.2

View File

@ -317,22 +317,14 @@ func migrateWalletJSONBlobs(sqlTx *sql.Tx) error {
}
if nullableTx.Valid {
var tokenAddress *common.Address
var correctType w_common.Type
var tokenID, value *big.Int
if nullableL.Valid {
correctType, tokenAddress, tokenID, value = w_common.ExtractTokenIdentity(w_common.Type(entryType), l, tx)
} else {
correctType = w_common.Type(entryType)
value = new(big.Int).Set(tx.Value())
}
correctType, tokenID, value, dbAddress := extractToken(entryType, tx, l, nullableL.Valid)
gasPrice := sqlite.BigIntToClampedInt64(tx.GasPrice())
gasTipCap := sqlite.BigIntToClampedInt64(tx.GasTipCap())
gasFeeCap := sqlite.BigIntToClampedInt64(tx.GasFeeCap())
valueStr := sqlite.BigIntToPadded128BitsStr(value)
currentRow = append(currentRow, tx.Type(), tx.Protected(), tx.Gas(), gasPrice, gasTipCap, gasFeeCap, valueStr, tx.Nonce(), int64(tx.Size()), &sqlite.JSONBlob{Data: tokenAddress}, (*bigint.SQLBigIntBytes)(tokenID), correctType)
currentRow = append(currentRow, tx.Type(), tx.Protected(), tx.Gas(), gasPrice, gasTipCap, gasFeeCap, valueStr, tx.Nonce(), int64(tx.Size()), dbAddress, (*bigint.SQLBigIntBytes)(tokenID), correctType)
} else {
for i := 0; i < 11; i++ {
currentRow = append(currentRow, nil)
@ -377,3 +369,18 @@ func migrateWalletJSONBlobs(sqlTx *sql.Tx) error {
return nil
}
func extractToken(entryType string, tx *types.Transaction, l *types.Log, logValid bool) (correctType w_common.Type, tokenID *big.Int, value *big.Int, dbAddress *string) {
if logValid {
var tokenAddress *common.Address
correctType, tokenAddress, tokenID, value = w_common.ExtractTokenIdentity(w_common.Type(entryType), l, tx)
if tokenAddress != nil {
dbAddress = new(string)
*dbAddress = tokenAddress.Hex()
}
} else {
correctType = w_common.Type(entryType)
value = new(big.Int).Set(tx.Value())
}
return
}

View File

@ -219,7 +219,7 @@ func TestMigrateWalletJsonBlobs(t *testing.T) {
entryType string
isTokenIDNull bool
)
dbTokenAddress := sqlite.JSONBlob{Data: &tokenAddress}
var dbTokenAddress sql.NullString
tokenID := new(big.Int)
rows, err := db.Query(`SELECT status, receipt_type, tx_hash, log_index, block_hash, cumulative_gas_used, contract_address, gas_used, tx_index,
tx_type, protected, gas_limit, gas_price_clamped64, gas_tip_cap_clamped64, gas_fee_cap_clamped64, amount_padded128hex, account_nonce, size, token_address, token_id, type,
@ -242,6 +242,9 @@ func TestMigrateWalletJsonBlobs(t *testing.T) {
if err != nil {
return err
}
if dbTokenAddress.Valid {
tokenAddress = common.HexToAddress(dbTokenAddress.String)
}
if dbContractAddress.Valid {
contractAddress = common.HexToAddress(dbContractAddress.String)
}

View File

@ -1,6 +1,5 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// .DS_Store (6.148kB)
// 1640111208_dummy.up.sql (258B)
// 1642666031_add_removed_clock_to_bookmarks.up.sql (117B)
// 1643644541_gif_api_key_setting.up.sql (108B)
@ -70,6 +69,7 @@
// 1686041510_add_idx_transfers_blkno_loaded.up.sql (71B)
// 1686048341_transfers_receipt_json_blob_out.up.sql.down.sql (104B)
// 1686048341_transfers_receipt_json_blob_out.up.sql.up.sql (1.5kB)
// 1686825075_cleanup_token_address.up.sql (273B)
// doc.go (74B)
package migrations
@ -139,26 +139,6 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _Ds_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xd8\x31\x0a\x02\x31\x10\x85\xe1\x37\x31\x45\xc0\x26\xa5\x65\x1a\x0f\xe0\x0d\xc2\xb2\x9e\xc0\x0b\x58\x78\x05\xfb\x1c\x5d\x96\x79\x60\x60\xd5\x4e\x8c\xcb\xfb\x40\xfe\x05\x37\x2a\x16\x31\x23\x00\x9b\xee\xb7\x13\x90\x01\x24\x78\x71\xc4\x4b\x89\x8f\x95\xd0\x5d\x1b\x5f\x43\x44\x44\x44\xc6\x66\x9e\xb4\xff\xf5\x07\x11\x91\xe1\x2c\xfb\x43\x61\x2b\xdb\xbc\xc6\xe7\x03\x1b\xbb\x35\x99\x2d\x6c\x65\x9b\xd7\x78\x5f\x60\x23\x9b\xd8\xcc\x16\xb6\xb2\xcd\xcb\x4d\xcb\x38\x7c\x18\xdf\xd9\x38\xa1\x18\xa7\x10\x2b\x6c\xfd\xce\x77\x23\xf2\xef\x76\x9e\xbc\xfc\xfe\x9f\xdf\xcf\xff\x22\xb2\x61\x16\xe7\xcb\x3c\x3d\x07\x82\xf5\x0d\x00\xae\xdd\xf5\xa7\x43\x40\xf0\x3f\x0b\x0f\xdd\x5a\x1d\x04\x44\x06\xf3\x08\x00\x00\xff\xff\x6a\x00\x88\x6d\x04\x18\x00\x00")
func Ds_storeBytes() ([]byte, error) {
return bindataRead(
_Ds_store,
".DS_Store",
)
}
func Ds_store() (*asset, error) {
bytes, err := Ds_storeBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: ".DS_Store", size: 6148, mode: os.FileMode(0644), modTime: time.Unix(1685384027, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0x51, 0x65, 0x27, 0x91, 0x5, 0xca, 0x67, 0x73, 0x18, 0x5, 0x0, 0x68, 0x8d, 0xf4, 0xbd, 0xc6, 0x9a, 0x2c, 0x7b, 0x77, 0x17, 0x52, 0xf0, 0xa4, 0x6e, 0xf1, 0x20, 0xb7, 0xfd, 0x8e, 0xc3}}
return a, nil
}
var __1640111208_dummyUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8e\xb1\x4e\x04\x31\x0c\x44\x7b\xbe\x62\x3a\x40\x22\x7f\x71\x57\x50\xc0\x4a\xe8\x44\x9f\x4b\x26\x1b\x8b\x5d\x1b\x62\xa3\xe5\xf3\x51\x44\x45\x73\x6e\x3d\xef\xcd\xa4\x84\xd3\x82\xd7\xe5\x82\xb7\xf3\xcb\xf2\x7e\xbe\x4b\x09\xe9\xd6\xcd\xc0\xa5\x8b\xa3\xc9\x46\x54\xa3\x43\x2d\xba\xe8\x3a\x3f\xcf\x71\xef\x28\x83\x39\x58\xe1\x86\xe8\xc4\x2e\xeb\xc8\x21\xa6\xf8\x50\x3b\x1c\x6d\xd8\x8e\xa3\x4b\xe9\xf0\xaf\xed\xcf\x13\x86\x62\x1a\xa2\xdf\x84\x8b\x16\x62\xda\xfe\xd3\x3d\x3b\xae\xa4\xc2\x3f\x37\x89\x59\x20\x8a\x38\x0c\x0f\x57\x36\x1b\x7c\x42\xd6\x8a\xdc\x82\x03\x6a\x95\xa5\xad\xe0\x4f\x8c\x5c\x26\xfe\x38\x85\x27\x9b\x63\x91\x6b\x45\xb1\x4a\x74\x0e\xfe\x06\x00\x00\xff\xff\x9b\xc1\xf3\x13\x02\x01\x00\x00")
func _1640111208_dummyUpSqlBytes() ([]byte, error) {
@ -174,7 +154,7 @@ func _1640111208_dummyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xf0, 0xae, 0x20, 0x6e, 0x75, 0xd1, 0x36, 0x14, 0xf2, 0x40, 0xe5, 0xd6, 0x7a, 0xc4, 0xa5, 0x72, 0xaa, 0xb5, 0x4d, 0x71, 0x97, 0xb8, 0xe8, 0x95, 0x22, 0x95, 0xa2, 0xac, 0xaf, 0x48, 0x58}}
return a, nil
}
@ -194,7 +174,7 @@ func _1642666031_add_removed_clock_to_bookmarksUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x4e, 0x38, 0x99, 0x7a, 0xc, 0x90, 0x13, 0xec, 0xfe, 0x2f, 0x55, 0xff, 0xb7, 0xb6, 0xaa, 0x96, 0xc6, 0x92, 0x79, 0xcc, 0xee, 0x4e, 0x99, 0x53, 0xfe, 0x1c, 0xbb, 0x32, 0x2, 0xa4, 0x27}}
return a, nil
}
@ -214,7 +194,7 @@ func _1643644541_gif_api_key_settingUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1b, 0x94, 0x28, 0xfb, 0x66, 0xd1, 0x7c, 0xb8, 0x89, 0xe2, 0xb4, 0x71, 0x65, 0x24, 0x57, 0x22, 0x95, 0x38, 0x97, 0x3, 0x9b, 0xc6, 0xa4, 0x41, 0x7b, 0xba, 0xf7, 0xdb, 0x70, 0xf7, 0x20, 0x3a}}
return a, nil
}
@ -234,7 +214,7 @@ func _1644188994_recent_stickersUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1e, 0xad, 0xaa, 0x30, 0xbf, 0x4, 0x7, 0xf8, 0xc3, 0x3, 0xb8, 0x97, 0x23, 0x2b, 0xbd, 0x1c, 0x60, 0x69, 0xb0, 0x42, 0x5e, 0x6b, 0xd, 0xa7, 0xa3, 0x6b, 0x2e, 0xdc, 0x70, 0x13, 0x72, 0x7}}
return a, nil
}
@ -254,7 +234,7 @@ func _1646659233_add_address_to_dapp_permisssionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xed, 0xb0, 0x35, 0xcc, 0x2e, 0x16, 0xe6, 0x15, 0x86, 0x2c, 0x37, 0x80, 0xae, 0xa3, 0xc5, 0x31, 0x78, 0x5, 0x9d, 0xcd, 0x7b, 0xeb, 0x5f, 0xf2, 0xb3, 0x74, 0x72, 0xdf, 0xcf, 0x88, 0xb, 0x40}}
return a, nil
}
@ -274,7 +254,7 @@ func _1646841105_add_emoji_accountUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe6, 0x77, 0x29, 0x95, 0x18, 0x64, 0x82, 0x63, 0xe7, 0xaf, 0x6c, 0xa9, 0x15, 0x7d, 0x46, 0xa6, 0xbc, 0xdf, 0xa7, 0xd, 0x2b, 0xd2, 0x2d, 0x97, 0x4d, 0xa, 0x6b, 0xd, 0x6e, 0x90, 0x42, 0x5c}}
return a, nil
}
@ -294,7 +274,7 @@ func _1647278782_display_nameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xa1, 0x1f, 0x3e, 0x61, 0x65, 0x8d, 0xff, 0xee, 0xde, 0xc5, 0x91, 0xd9, 0x5c, 0xb5, 0xe2, 0xf0, 0xb7, 0xe7, 0x5c, 0x5c, 0x16, 0x25, 0x89, 0xee, 0x78, 0x12, 0xea, 0x3e, 0x48, 0x41, 0xa6}}
return a, nil
}
@ -314,7 +294,7 @@ func _1647862838_reset_last_backupUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0xe3, 0xd5, 0xf6, 0x5f, 0xfe, 0x65, 0xfa, 0x1d, 0x88, 0xf8, 0x5f, 0x24, 0x71, 0x34, 0x68, 0x96, 0x2a, 0x60, 0x87, 0x15, 0x82, 0x4d, 0x8a, 0x59, 0x3d, 0x1f, 0xd8, 0x56, 0xd4, 0xfb, 0xda}}
return a, nil
}
@ -334,7 +314,7 @@ func _1647871652_add_settings_sync_clock_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd8, 0x58, 0xec, 0x85, 0x90, 0xfa, 0x30, 0x98, 0x98, 0x9a, 0xa6, 0xa8, 0x96, 0x2b, 0x38, 0x93, 0xf3, 0xae, 0x46, 0x74, 0xa4, 0x41, 0x62, 0x9b, 0x2, 0x86, 0xbf, 0xe5, 0x2a, 0xce, 0xe2, 0xc0}}
return a, nil
}
@ -354,7 +334,7 @@ func _1647880168_add_torrent_configUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0x92, 0x22, 0x37, 0x96, 0xf3, 0xb5, 0x5b, 0x27, 0xd0, 0x7d, 0x43, 0x5, 0x4e, 0x9d, 0xe2, 0x49, 0xbe, 0x86, 0x31, 0xa1, 0x89, 0xff, 0xd6, 0x51, 0xe0, 0x9c, 0xb, 0xda, 0xfc, 0xf2, 0x93}}
return a, nil
}
@ -374,7 +354,7 @@ func _1647882837_add_communities_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xbd, 0x87, 0x78, 0x99, 0xd9, 0x5d, 0xbd, 0xf7, 0x57, 0x9c, 0xca, 0x97, 0xbd, 0xb3, 0xe9, 0xb5, 0x89, 0x31, 0x3f, 0xf6, 0x5c, 0x13, 0xb, 0xc3, 0x54, 0x93, 0x18, 0x40, 0x7, 0x82, 0xfe, 0x7e}}
return a, nil
}
@ -394,7 +374,7 @@ func _1647956635_add_waku_messages_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xe, 0xe1, 0xdc, 0xda, 0x2e, 0x89, 0x8d, 0xdc, 0x2a, 0x1c, 0x13, 0xa1, 0xfc, 0xfe, 0xf, 0xb2, 0xb9, 0x85, 0xc8, 0x45, 0xd6, 0xd1, 0x7, 0x5c, 0xa3, 0x8, 0x47, 0x44, 0x6d, 0x96, 0xe0}}
return a, nil
}
@ -414,7 +394,7 @@ func _1648554928_network_testUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9a, 0xc5, 0x7f, 0x87, 0xf3, 0x2c, 0xf7, 0xbb, 0xd3, 0x3a, 0x4e, 0x76, 0x88, 0xca, 0xaf, 0x73, 0xce, 0x8f, 0xa1, 0xf6, 0x3d, 0x4d, 0xed, 0x6f, 0x49, 0xf2, 0xfe, 0x56, 0x2a, 0x60, 0x68, 0xca}}
return a, nil
}
@ -434,7 +414,7 @@ func _1649174829_add_visitble_tokenUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa3, 0x22, 0xc0, 0x2b, 0x3f, 0x4f, 0x3d, 0x5e, 0x4c, 0x68, 0x7c, 0xd0, 0x15, 0x36, 0x9f, 0xec, 0xa1, 0x2a, 0x7b, 0xb4, 0xe3, 0xc6, 0xc9, 0xb4, 0x81, 0x50, 0x4a, 0x11, 0x3b, 0x35, 0x7, 0xcf}}
return a, nil
}
@ -454,7 +434,7 @@ func _1649882262_add_derived_from_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x11, 0xb9, 0x44, 0x4d, 0x85, 0x8d, 0x7f, 0xb4, 0xae, 0x4f, 0x5c, 0x66, 0x64, 0xb6, 0xe2, 0xe, 0x3d, 0xad, 0x9d, 0x8, 0x4f, 0xab, 0x6e, 0xa8, 0x7d, 0x76, 0x3, 0xad, 0x96, 0x1, 0xee, 0x5c}}
return a, nil
}
@ -474,7 +454,7 @@ func _1650612625_add_community_message_archive_hashes_tableUpSql() (*asset, erro
return nil, err
}
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x48, 0x31, 0xb3, 0x75, 0x23, 0xe2, 0x45, 0xe, 0x47, 0x1b, 0x35, 0xa5, 0x6e, 0x83, 0x4e, 0x64, 0x7d, 0xd7, 0xa2, 0xda, 0xe9, 0x53, 0xf1, 0x16, 0x86, 0x2c, 0x57, 0xad, 0xfa, 0xca, 0x39, 0xde}}
return a, nil
}
@ -494,7 +474,7 @@ func _1650616788_add_communities_archives_info_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0x4f, 0x80, 0x45, 0xb9, 0xd9, 0x15, 0xe2, 0x78, 0xd0, 0xcb, 0x71, 0xc1, 0x1b, 0xb7, 0x1b, 0x1b, 0x97, 0xfe, 0x47, 0x53, 0x3c, 0x62, 0xbc, 0xdd, 0x3a, 0x94, 0x1a, 0xc, 0x48, 0x76, 0xe}}
return a, nil
}
@ -514,7 +494,7 @@ func _1652715604_add_clock_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb6, 0xd9, 0x8d, 0x73, 0xc9, 0xef, 0xfa, 0xb1, 0x4b, 0xa5, 0xf3, 0x5, 0x19, 0x26, 0x46, 0xf8, 0x47, 0x93, 0xdb, 0xac, 0x2, 0xef, 0xf9, 0x71, 0x56, 0x83, 0xe6, 0x2d, 0xb0, 0xd7, 0x83, 0x5c}}
return a, nil
}
@ -534,7 +514,7 @@ func _1653037334_add_notifications_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4b, 0xc4, 0x65, 0xac, 0xa, 0xf2, 0xef, 0xb6, 0x39, 0x3c, 0xc5, 0xb1, 0xb2, 0x9c, 0x86, 0x58, 0xe0, 0x38, 0xcb, 0x57, 0x3c, 0x76, 0x73, 0x87, 0x79, 0x4e, 0xf6, 0xed, 0xb0, 0x8e, 0x9e, 0xa}}
return a, nil
}
@ -554,7 +534,7 @@ func _1654702119_add_mutual_contact_settingsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x26, 0x66, 0x67, 0x50, 0xfe, 0xd7, 0xe3, 0x29, 0x8b, 0xff, 0x9d, 0x5a, 0x87, 0xa7, 0x99, 0x6e, 0xd6, 0xcd, 0x2e, 0xbb, 0x17, 0xdf, 0x7f, 0xf7, 0xa3, 0xfa, 0x32, 0x7c, 0x2d, 0x92, 0xc8, 0x74}}
return a, nil
}
@ -574,7 +554,7 @@ func _1655375270_add_clock_field_to_communities_settings_tableUpSql() (*asset, e
return nil, err
}
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x19, 0xc5, 0xc0, 0xf9, 0x84, 0x53, 0xdf, 0x83, 0xcf, 0xb6, 0x40, 0x6d, 0xf5, 0xdc, 0x77, 0x37, 0xb7, 0xe3, 0xa, 0x75, 0xe7, 0x6, 0x11, 0xca, 0x2b, 0x51, 0x92, 0xdd, 0x7d, 0xdb, 0xc3, 0xf5}}
return a, nil
}
@ -594,7 +574,7 @@ func _1655385721_drop_networks_configUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfc, 0xa7, 0x20, 0xbb, 0x67, 0x21, 0xe, 0xc6, 0xc8, 0x21, 0x74, 0xe0, 0xce, 0xc8, 0xe2, 0x2, 0xb4, 0xea, 0xf0, 0xe5, 0xc4, 0x4d, 0xdd, 0xd4, 0x52, 0x31, 0xa9, 0x3d, 0xcd, 0xd8, 0x9b, 0xab}}
return a, nil
}
@ -614,7 +594,7 @@ func _1655385724_networks_chaincolor_shortnameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd9, 0xe7, 0x84, 0xbb, 0x5f, 0xd2, 0x2c, 0x42, 0x88, 0x62, 0x52, 0xb6, 0x58, 0x31, 0xac, 0xc, 0x96, 0x2b, 0x1b, 0xe5, 0x4e, 0x9a, 0x3a, 0xf6, 0xf6, 0xfc, 0xa9, 0x1a, 0x35, 0x62, 0x28, 0x88}}
return a, nil
}
@ -634,7 +614,7 @@ func _1655456688_add_deleted_at_field_to_bookmarks_tableUpSql() (*asset, error)
return nil, err
}
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0x9a, 0xbd, 0x9a, 0xc9, 0xf, 0xdf, 0x90, 0x0, 0x5d, 0xea, 0x6e, 0x7d, 0x51, 0x95, 0xcd, 0x90, 0xd3, 0x1a, 0x36, 0x6c, 0xf4, 0xbd, 0xa7, 0x6b, 0xbf, 0xe5, 0xdb, 0xa3, 0x88, 0xe3, 0x50}}
return a, nil
}
@ -654,7 +634,7 @@ func _1655462032_create_bookmarks_deleted_at_indexUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf, 0x8e, 0x20, 0x6b, 0x14, 0x9e, 0xcd, 0x97, 0xd3, 0xfe, 0x62, 0x3, 0x26, 0x59, 0x1, 0x6c, 0x99, 0xef, 0x6d, 0x21, 0xd4, 0xb5, 0xa3, 0xf4, 0x39, 0x40, 0x54, 0x6, 0xd, 0x60, 0x13, 0x38}}
return a, nil
}
@ -674,7 +654,7 @@ func _1657617291_add_multi_transactions_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x86, 0xb0, 0x4e, 0x8c, 0x4, 0x82, 0xb4, 0x43, 0xaa, 0xd0, 0x16, 0xdd, 0xcb, 0x88, 0x81, 0xac, 0x4, 0x34, 0x1a, 0x8f, 0x2e, 0xc5, 0x69, 0xb, 0xf0, 0x17, 0xf7, 0xe3, 0x9, 0xe, 0x54, 0xe0}}
return a, nil
}
@ -694,7 +674,7 @@ func _1660134042_add_social_links_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x73, 0xb6, 0xe7, 0x3f, 0xaa, 0x39, 0x9a, 0x56, 0x56, 0x31, 0xf1, 0x8e, 0x26, 0x23, 0x1, 0xe4, 0xfa, 0x98, 0xfe, 0x78, 0x87, 0x20, 0xcb, 0x52, 0xf4, 0x38, 0x7f, 0xc4, 0x1c, 0x4, 0x22}}
return a, nil
}
@ -714,7 +694,7 @@ func _1660134060_settings_bioUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x46, 0x25, 0xa0, 0xa6, 0x47, 0xff, 0xbc, 0x2a, 0x0, 0xff, 0x59, 0x4b, 0xb0, 0xc9, 0x4e, 0x15, 0xe4, 0xd9, 0xda, 0xeb, 0xfe, 0x55, 0x98, 0xc3, 0x9d, 0x96, 0xe7, 0xf, 0xd1, 0x5c, 0x93, 0x73}}
return a, nil
}
@ -734,7 +714,7 @@ func _1660134070_add_wakuv2_storeUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1d, 0xe6, 0xc3, 0x9, 0xef, 0xdc, 0xae, 0x49, 0x30, 0x78, 0x54, 0xd6, 0xdb, 0xbf, 0xc0, 0x8e, 0x25, 0x8f, 0xfc, 0x67, 0x80, 0x39, 0x37, 0xd4, 0x86, 0xc1, 0x85, 0xc8, 0x99, 0xc4, 0x59, 0xd4}}
return a, nil
}
@ -754,7 +734,7 @@ func _1660134072_waku2_store_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xeb, 0xb4, 0xa0, 0xa1, 0x2b, 0xcb, 0x4c, 0x3c, 0xc6, 0xd0, 0xe8, 0x96, 0xe3, 0x96, 0xf1, 0x4f, 0x1f, 0xe0, 0xe7, 0x1f, 0x85, 0xa3, 0xe, 0xf7, 0x52, 0x56, 0x63, 0x2b, 0xb0, 0x87, 0x7b}}
return a, nil
}
@ -774,7 +754,7 @@ func _1662365868_add_key_uid_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc6, 0xd8, 0x2f, 0x2f, 0x3b, 0xa8, 0xbd, 0x6d, 0xf6, 0x87, 0x7e, 0xd2, 0xf1, 0xa2, 0xf7, 0x81, 0x6a, 0x23, 0x10, 0xbc, 0xbf, 0x5b, 0xe7, 0x2b, 0x9c, 0xa9, 0x8a, 0x18, 0xbb, 0xd0, 0x86, 0x91}}
return a, nil
}
@ -794,7 +774,7 @@ func _1662447680_add_keypairs_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdc, 0x25, 0xa9, 0xc7, 0x63, 0x27, 0x97, 0x35, 0x5f, 0x6b, 0xab, 0x26, 0xcb, 0xf9, 0xbd, 0x5e, 0xac, 0x3, 0xa0, 0x5e, 0xb9, 0x71, 0xa3, 0x1f, 0xb3, 0x4f, 0x7f, 0x79, 0x28, 0x48, 0xbe, 0xc}}
return a, nil
}
@ -814,7 +794,7 @@ func _1662460056_move_favourites_to_saved_addressesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x10, 0xa2, 0x8c, 0xa3, 0xec, 0xad, 0xdf, 0xc3, 0x48, 0x5, 0x9b, 0x50, 0x25, 0x59, 0xae, 0x7d, 0xee, 0x58, 0xd2, 0x41, 0x27, 0xf2, 0x22, 0x2e, 0x9a, 0xb9, 0x4a, 0xcc, 0x38, 0x6e, 0x3a, 0xb2}}
return a, nil
}
@ -834,7 +814,7 @@ func _1662738097_add_base_fee_transactionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xfb, 0x10, 0xae, 0xfc, 0x77, 0x70, 0x98, 0x6f, 0xec, 0xaa, 0xcd, 0x7, 0xc7, 0x74, 0x23, 0xc, 0xd5, 0x1e, 0x82, 0xdd, 0xfe, 0xff, 0x3b, 0xd2, 0x49, 0x10, 0x5b, 0x30, 0xc, 0x2d, 0xb0}}
return a, nil
}
@ -854,7 +834,7 @@ func _1662972194_add_keypairs_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xab, 0x76, 0xf2, 0x86, 0xe1, 0x7e, 0xe9, 0x47, 0x32, 0x48, 0xd5, 0x6b, 0xe5, 0xd, 0xab, 0xb7, 0xf1, 0xd4, 0xf1, 0xad, 0x38, 0xa6, 0x11, 0xe7, 0xce, 0x5c, 0x11, 0x11, 0xf, 0x47, 0xb2, 0x4}}
return a, nil
}
@ -874,7 +854,7 @@ func _1664392661_add_third_party_id_to_waku_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x67, 0x66, 0x9e, 0x66, 0x74, 0xce, 0x1c, 0xb, 0x1b, 0x9d, 0xd5, 0xfc, 0x65, 0xe, 0x83, 0x90, 0x4c, 0x61, 0x4e, 0x6b, 0xe7, 0x86, 0xbe, 0x36, 0x4f, 0x91, 0x36, 0x4, 0x47, 0x7b, 0x82}}
return a, nil
}
@ -894,7 +874,7 @@ func _1664783660_add_sync_info_to_saved_addressesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x67, 0x7c, 0x3a, 0x95, 0x4e, 0x55, 0xb2, 0xbd, 0xb4, 0x18, 0x93, 0xc1, 0xcf, 0x9f, 0x12, 0xbb, 0x49, 0x8a, 0x2a, 0x6a, 0x2a, 0x7f, 0xad, 0x44, 0xc3, 0xf, 0x3a, 0x79, 0x18, 0xb9, 0x4c, 0x64}}
return a, nil
}
@ -914,7 +894,7 @@ func _1668109917_wakunodesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x29, 0xaa, 0x9e, 0x2, 0x66, 0x85, 0x69, 0xa8, 0xd9, 0xe2, 0x4b, 0x8d, 0x2a, 0x9c, 0xdf, 0xd2, 0xef, 0x64, 0x58, 0xe3, 0xa6, 0xe7, 0xc1, 0xd1, 0xc8, 0x9c, 0xc0, 0x2c, 0x1, 0xa8, 0x7b, 0x81}}
return a, nil
}
@ -934,7 +914,7 @@ func _1670249678_display_name_to_settings_sync_clock_tableUpSql() (*asset, error
return nil, err
}
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0x18, 0xdc, 0xc4, 0x1f, 0x79, 0x22, 0x16, 0x4d, 0xdf, 0x6c, 0x66, 0xd5, 0xa4, 0x88, 0x5d, 0x5, 0x37, 0xa7, 0x41, 0x5, 0x50, 0xae, 0x12, 0xfa, 0x7e, 0x89, 0x24, 0x5c, 0xae, 0x30, 0xfc}}
return a, nil
}
@ -954,7 +934,7 @@ func _1670836810_add_imported_flag_to_community_archive_hashesUpSql() (*asset, e
return nil, err
}
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6f, 0xf, 0xf0, 0xbd, 0xfe, 0x63, 0x25, 0x8f, 0x5e, 0x46, 0x4b, 0x45, 0x31, 0x8b, 0x3e, 0xd8, 0x6b, 0x5d, 0x9d, 0x6d, 0x10, 0x9a, 0x87, 0x4b, 0x18, 0xc6, 0x39, 0x81, 0x6e, 0xe4, 0x75, 0xfb}}
return a, nil
}
@ -974,7 +954,7 @@ func _1671438731_add_magnetlink_uri_to_communities_archive_infoUpSql() (*asset,
return nil, err
}
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xda, 0x8b, 0x4b, 0xd6, 0xd8, 0xe2, 0x3d, 0xf7, 0x6b, 0xcd, 0x1e, 0x70, 0x9, 0x2e, 0x35, 0x4, 0x61, 0xc3, 0xb5, 0x9d, 0xc5, 0x27, 0x21, 0xa, 0x5a, 0xd6, 0x3e, 0xa6, 0x24, 0xa2, 0x12, 0xdf}}
return a, nil
}
@ -994,7 +974,7 @@ func _1672933930_switcher_cardUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0xba, 0xdc, 0xbb, 0x40, 0x4, 0xf2, 0x10, 0xdf, 0xb4, 0xd2, 0x80, 0x8a, 0x74, 0x4d, 0xf6, 0xbc, 0x50, 0x7, 0xd, 0x22, 0x7f, 0xc4, 0xaf, 0xaa, 0xde, 0xdc, 0x71, 0xe9, 0x42, 0x98, 0x36}}
return a, nil
}
@ -1014,7 +994,7 @@ func _1674056187_add_price_cacheUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb7, 0x79, 0x6a, 0x9b, 0x28, 0xd1, 0x22, 0xf0, 0x84, 0x76, 0x40, 0x39, 0x49, 0x15, 0x5d, 0xaa, 0xfd, 0x11, 0xff, 0x13, 0x27, 0x42, 0x12, 0xfa, 0x82, 0xe6, 0x7a, 0xf0, 0x5e, 0x1f, 0xe3, 0xba}}
return a, nil
}
@ -1034,7 +1014,7 @@ func _1674136690_ens_usernamesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x7a, 0xf3, 0xa8, 0x88, 0x99, 0xd6, 0x9c, 0x69, 0x48, 0x3c, 0x10, 0xda, 0x72, 0xdc, 0x14, 0xd, 0x6e, 0x8c, 0x82, 0x92, 0x2d, 0x2c, 0xee, 0x4c, 0x70, 0xa4, 0xdc, 0x5c, 0x5, 0x2, 0xc3}}
return a, nil
}
@ -1054,7 +1034,7 @@ func _1674232431_add_balance_historyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf7, 0xb5, 0x18, 0xca, 0x4a, 0x93, 0xbb, 0x6f, 0xa4, 0xee, 0xe4, 0x3e, 0xff, 0x6a, 0x4b, 0xe2, 0xe1, 0x61, 0x28, 0xee, 0xc5, 0x26, 0x57, 0x61, 0x5e, 0x6d, 0x44, 0x1e, 0x85, 0x43, 0x70, 0xa2}}
return a, nil
}
@ -1074,7 +1054,7 @@ func _1676368933_keypairs_to_keycardsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x93, 0x27, 0x2, 0xf0, 0x37, 0x81, 0x65, 0xa4, 0xb3, 0x5b, 0x60, 0x36, 0x95, 0xfc, 0x81, 0xf0, 0x3b, 0x7c, 0xc3, 0x2c, 0x85, 0xbd, 0x38, 0x46, 0xa4, 0x95, 0x4a, 0x6, 0x3e, 0x74, 0xd5}}
return a, nil
}
@ -1094,7 +1074,7 @@ func _1676951398_add_currency_format_cacheUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xa3, 0x76, 0x35, 0xca, 0xf, 0xe8, 0xdf, 0xd9, 0x61, 0xf9, 0xed, 0xfc, 0x6d, 0xf5, 0xe, 0x11, 0x88, 0xbd, 0x14, 0x92, 0xc6, 0x57, 0x53, 0xe, 0xcd, 0x52, 0xf4, 0xa9, 0xb1, 0xdd, 0xfd}}
return a, nil
}
@ -1114,7 +1094,7 @@ func _1676968196_keycards_add_clock_columnUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4c, 0xf, 0x1c, 0x28, 0x41, 0x57, 0x57, 0x6c, 0xe, 0x75, 0x6b, 0x75, 0x12, 0x0, 0x18, 0x1e, 0x88, 0x1e, 0x45, 0xe0, 0x32, 0xb9, 0xd4, 0xd9, 0x2e, 0xc8, 0xb, 0x80, 0x6, 0x51, 0x3d, 0x28}}
return a, nil
}
@ -1134,7 +1114,7 @@ func _1676968197_add_fallback_rpc_to_networksUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676968197_add_fallback_rpc_to_networks.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1676968197_add_fallback_rpc_to_networks.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6a, 0xc6, 0x45, 0xfa, 0x62, 0x84, 0x74, 0x6d, 0x7c, 0xd7, 0x1d, 0x79, 0xb6, 0x38, 0x43, 0xa8, 0x8, 0x6b, 0x75, 0x3d, 0x9, 0x2, 0xc5, 0x9f, 0xbb, 0x45, 0x56, 0x4c, 0x4e, 0x17, 0x89}}
return a, nil
}
@ -1154,7 +1134,7 @@ func _1677674090_add_chains_ens_istest_to_saved_addressesUpSql() (*asset, error)
return nil, err
}
info := bindataFileInfo{name: "1677674090_add_chains_ens_istest_to_saved_addresses.up.sql", size: 638, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1677674090_add_chains_ens_istest_to_saved_addresses.up.sql", size: 638, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0x2d, 0xa4, 0x1b, 0xf6, 0x6a, 0x13, 0x7b, 0xe, 0x59, 0xcd, 0xe2, 0x4e, 0x81, 0x99, 0xc4, 0x33, 0x84, 0xde, 0x66, 0xca, 0xac, 0x2f, 0x5, 0x90, 0xac, 0xfd, 0x4e, 0xfc, 0x55, 0x44, 0xe5}}
return a, nil
}
@ -1174,7 +1154,7 @@ func _1677681143_accounts_table_type_column_updateUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1677681143_accounts_table_type_column_update.up.sql", size: 135, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1677681143_accounts_table_type_column_update.up.sql", size: 135, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xc4, 0x6, 0x42, 0x50, 0x1d, 0xf4, 0x48, 0x55, 0xbc, 0xa2, 0x19, 0xdd, 0xad, 0xc8, 0xc, 0xa7, 0x30, 0xb6, 0xaf, 0xe, 0x2b, 0xaa, 0x2a, 0xa4, 0xe1, 0xb9, 0x41, 0x23, 0x66, 0xd3, 0x3}}
return a, nil
}
@ -1194,7 +1174,7 @@ func _1678264207_accounts_table_new_columns_addedUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1678264207_accounts_table_new_columns_added.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1678264207_accounts_table_new_columns_added.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xd4, 0xf3, 0x35, 0xef, 0x5c, 0x19, 0x3c, 0x15, 0x90, 0x60, 0xbd, 0x1f, 0x81, 0xf0, 0x86, 0x73, 0x89, 0xa0, 0x70, 0xf2, 0x46, 0xae, 0xea, 0xd0, 0xc6, 0x9e, 0x55, 0x4a, 0x54, 0x62, 0xbb}}
return a, nil
}
@ -1214,7 +1194,7 @@ func _1680770368_add_bio_to_settings_sync_clock_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1680770368_add_bio_to_settings_sync_clock_table.up.sql", size: 75, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1680770368_add_bio_to_settings_sync_clock_table.up.sql", size: 75, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4a, 0x52, 0xf6, 0x3f, 0xaa, 0xd, 0xa0, 0xee, 0xe8, 0xe6, 0x16, 0x21, 0x80, 0x61, 0xe4, 0x7a, 0x4e, 0x37, 0x8d, 0x30, 0x51, 0x20, 0x4d, 0x15, 0x47, 0xfb, 0x6, 0xa1, 0xce, 0xc8, 0x27, 0x5a}}
return a, nil
}
@ -1234,7 +1214,7 @@ func _1681110436_add_mnemonic_to_settings_sync_clock_tableUpSql() (*asset, error
return nil, err
}
info := bindataFileInfo{name: "1681110436_add_mnemonic_to_settings_sync_clock_table.up.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1681110436_add_mnemonic_to_settings_sync_clock_table.up.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3d, 0x74, 0x81, 0x7d, 0x9e, 0x77, 0xb6, 0xfe, 0xe3, 0xcb, 0x48, 0xe5, 0x5f, 0x39, 0x23, 0xa1, 0x7d, 0x53, 0x22, 0xe8, 0x96, 0x15, 0x8a, 0x1e, 0x8e, 0xbc, 0xe2, 0x1d, 0xc4, 0xc2, 0x56, 0x34}}
return a, nil
}
@ -1254,7 +1234,7 @@ func _1681392602_9d_sync_periodUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1681392602_9d_sync_period.up.sql", size: 60, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1681392602_9d_sync_period.up.sql", size: 60, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc9, 0xa, 0x90, 0x29, 0x7f, 0x76, 0x98, 0xa7, 0x71, 0x80, 0x5a, 0x2f, 0xbe, 0x23, 0x9a, 0xd4, 0xf4, 0x39, 0x19, 0xd3, 0xa5, 0x34, 0x6e, 0x67, 0x6a, 0xbe, 0x8a, 0xad, 0x21, 0xc7, 0xba, 0x88}}
return a, nil
}
@ -1274,7 +1254,7 @@ func _1681762078_default_sync_period_9dUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1681762078_default_sync_period_9d.up.sql", size: 3002, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1681762078_default_sync_period_9d.up.sql", size: 3002, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xd9, 0x26, 0xfc, 0xa9, 0x45, 0xc1, 0x81, 0xa8, 0xe2, 0x2c, 0xe9, 0x3c, 0xea, 0x1d, 0x37, 0x11, 0x45, 0x8c, 0x6c, 0xbc, 0xc2, 0x6, 0x69, 0x2, 0x75, 0x29, 0x40, 0x9f, 0xc5, 0xbb, 0x36}}
return a, nil
}
@ -1294,7 +1274,7 @@ func _1681780680_add_clock_to_social_links_settingsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1681780680_add_clock_to_social_links_settings.up.sql", size: 137, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1681780680_add_clock_to_social_links_settings.up.sql", size: 137, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x63, 0x11, 0xf5, 0x41, 0xe5, 0x5a, 0xf4, 0xe3, 0xf3, 0x14, 0x87, 0x28, 0xd8, 0xf0, 0x52, 0x31, 0x8, 0xd5, 0xbb, 0xf4, 0xff, 0x55, 0x5f, 0x42, 0x90, 0xcb, 0xf7, 0x46, 0x2, 0x6, 0xbe, 0x42}}
return a, nil
}
@ -1314,7 +1294,7 @@ func _1682073779_settings_table_remove_latest_derived_path_columnUpSql() (*asset
return nil, err
}
info := bindataFileInfo{name: "1682073779_settings_table_remove_latest_derived_path_column.up.sql", size: 4470, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1682073779_settings_table_remove_latest_derived_path_column.up.sql", size: 4470, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7a, 0x36, 0x2, 0x41, 0xd, 0x5c, 0xd1, 0x92, 0x85, 0x6d, 0x84, 0xff, 0x67, 0xa7, 0x4c, 0x67, 0xa4, 0xef, 0x52, 0x69, 0x1f, 0x22, 0x25, 0x92, 0xc, 0xb3, 0x89, 0x50, 0x91, 0xc, 0x49, 0xf9}}
return a, nil
}
@ -1334,7 +1314,7 @@ func _1682146075_add_created_at_to_saved_addressesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1682146075_add_created_at_to_saved_addresses.up.sql", size: 107, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1682146075_add_created_at_to_saved_addresses.up.sql", size: 107, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0xfe, 0x35, 0x9c, 0x6b, 0xdf, 0x67, 0x18, 0x16, 0xe4, 0xc9, 0xd4, 0x77, 0x7c, 0x4, 0xe2, 0x6c, 0x41, 0xd9, 0x53, 0x97, 0xfe, 0x5, 0xa3, 0x23, 0xce, 0x82, 0xad, 0x92, 0x5e, 0xd7, 0x7d}}
return a, nil
}
@ -1354,7 +1334,7 @@ func _1682393575_sync_ens_nameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1682393575_sync_ens_name.up.sql", size: 713, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1682393575_sync_ens_name.up.sql", size: 713, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfb, 0xea, 0xcb, 0x4d, 0x71, 0x5a, 0x49, 0x19, 0x8b, 0xef, 0x66, 0x27, 0x33, 0x89, 0xb0, 0xe, 0x37, 0x1b, 0x41, 0x8, 0x12, 0xcc, 0x56, 0xd8, 0x1b, 0xf, 0xf8, 0x50, 0x4b, 0x93, 0xf1, 0x29}}
return a, nil
}
@ -1374,7 +1354,7 @@ func _1683457503_add_blocks_ranges_sequential_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1683457503_add_blocks_ranges_sequential_table.up.sql", size: 263, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1683457503_add_blocks_ranges_sequential_table.up.sql", size: 263, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfe, 0x57, 0x2e, 0x0, 0x6a, 0x6e, 0xd7, 0xeb, 0xe6, 0x66, 0x79, 0x32, 0x22, 0x82, 0x92, 0xf4, 0xc9, 0xf1, 0x58, 0x1a, 0x45, 0x60, 0x77, 0x50, 0xe7, 0x54, 0x4a, 0xc0, 0x42, 0x3a, 0x4f, 0x35}}
return a, nil
}
@ -1394,7 +1374,7 @@ func _1683627613_accounts_and_keycards_improvementsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1683627613_accounts_and_keycards_improvements.up.sql", size: 3640, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1683627613_accounts_and_keycards_improvements.up.sql", size: 3640, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8e, 0xbe, 0x62, 0xf5, 0x9, 0x42, 0x8c, 0x8f, 0xa8, 0x45, 0xe7, 0x36, 0xc9, 0xde, 0xf4, 0xe2, 0xfd, 0xc4, 0x8, 0xd0, 0xa3, 0x8, 0x64, 0xe2, 0x56, 0xcc, 0xa7, 0x6d, 0xc5, 0xcc, 0x82, 0x2c}}
return a, nil
}
@ -1414,7 +1394,7 @@ func _1685041348_settings_table_add_latest_derived_path_columnUpSql() (*asset, e
return nil, err
}
info := bindataFileInfo{name: "1685041348_settings_table_add_latest_derived_path_column.up.sql", size: 115, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1685041348_settings_table_add_latest_derived_path_column.up.sql", size: 115, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0xd4, 0x1b, 0xbf, 0x8, 0xf9, 0xd4, 0xb0, 0xa0, 0x6, 0x5b, 0xfb, 0x7e, 0xff, 0xfa, 0xbf, 0xcc, 0x64, 0x47, 0x81, 0x8b, 0x5e, 0x17, 0x6a, 0xa7, 0xa4, 0x35, 0x8f, 0x30, 0x4f, 0xd9, 0xd}}
return a, nil
}
@ -1434,7 +1414,7 @@ func _1685440989_update_color_id_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1685440989_update_color_id_accounts.up.sql", size: 918, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1685440989_update_color_id_accounts.up.sql", size: 918, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x10, 0x2e, 0x51, 0x1d, 0x2d, 0x16, 0x84, 0xd6, 0xe8, 0xbc, 0x20, 0x53, 0x47, 0xb8, 0x40, 0x21, 0x52, 0x5c, 0xd9, 0xbb, 0xea, 0xe2, 0xa5, 0x77, 0xc8, 0x35, 0x4c, 0xe0, 0x9d, 0x42, 0x44, 0x50}}
return a, nil
}
@ -1454,7 +1434,7 @@ func _1685463947_add_to_asset_to_multitransactionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1685463947_add_to_asset_to_multitransaction.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1685463947_add_to_asset_to_multitransaction.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd3, 0x66, 0x15, 0x10, 0xfa, 0x66, 0x81, 0x68, 0xd9, 0xb4, 0x93, 0x9e, 0x11, 0xed, 0x1d, 0x16, 0x9d, 0x5a, 0xf8, 0xd7, 0x8, 0xea, 0x7a, 0xaf, 0xe4, 0xb3, 0x22, 0x19, 0xca, 0xff, 0x75, 0x7c}}
return a, nil
}
@ -1474,7 +1454,7 @@ func _1685880973_add_profile_links_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1685880973_add_profile_links_settings_table.up.sql", size: 1656, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1685880973_add_profile_links_settings_table.up.sql", size: 1656, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x13, 0x23, 0x7b, 0x1e, 0x82, 0x61, 0xcc, 0x76, 0xd6, 0xc7, 0x42, 0x6e, 0x69, 0x21, 0x1b, 0xfd, 0x7d, 0xda, 0xd7, 0xb7, 0xc7, 0xd3, 0x22, 0x63, 0xfe, 0xc6, 0xd3, 0xdf, 0xc8, 0x5f, 0x50, 0xcc}}
return a, nil
}
@ -1494,7 +1474,7 @@ func _1686041510_add_idx_transfers_blkno_loadedUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1686041510_add_idx_transfers_blkno_loaded.up.sql", size: 71, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1686041510_add_idx_transfers_blkno_loaded.up.sql", size: 71, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe2, 0x5d, 0x7e, 0x43, 0x14, 0x3c, 0x50, 0x44, 0x25, 0xd0, 0xe1, 0x75, 0xba, 0x61, 0x7b, 0x68, 0x2e, 0x43, 0x74, 0x1d, 0x10, 0x61, 0x8e, 0x45, 0xe6, 0x25, 0x78, 0x81, 0x68, 0x6, 0x24, 0x5b}}
return a, nil
}
@ -1514,7 +1494,7 @@ func _1686048341_transfers_receipt_json_blob_outUpSqlDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1686048341_transfers_receipt_json_blob_out.up.sql.down.sql", size: 104, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1686048341_transfers_receipt_json_blob_out.up.sql.down.sql", size: 104, mode: os.FileMode(0644), modTime: time.Unix(1686217919, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9f, 0x6c, 0xd9, 0x76, 0x83, 0x64, 0xf0, 0xf2, 0x74, 0x97, 0xca, 0xd7, 0xaa, 0x4, 0x74, 0x7c, 0x34, 0x56, 0x88, 0x10, 0xa9, 0x4d, 0x1d, 0x8e, 0x85, 0xc3, 0x66, 0x1, 0x2b, 0x30, 0x90, 0xf4}}
return a, nil
}
@ -1534,11 +1514,31 @@ func _1686048341_transfers_receipt_json_blob_outUpSqlUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1686048341_transfers_receipt_json_blob_out.up.sql.up.sql", size: 1500, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "1686048341_transfers_receipt_json_blob_out.up.sql.up.sql", size: 1500, mode: os.FileMode(0644), modTime: time.Unix(1686217919, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xcd, 0xe3, 0xa6, 0x8c, 0x53, 0x51, 0xe6, 0x3c, 0x64, 0xcb, 0x3, 0x3, 0xb, 0x4d, 0x52, 0xa5, 0x1c, 0xcc, 0xe1, 0x23, 0x94, 0x14, 0x79, 0xd7, 0x56, 0x58, 0xef, 0xcc, 0x1a, 0x6, 0xa4}}
return a, nil
}
var __1686825075_cleanup_token_addressUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x8e\xcd\x4a\xc4\x40\x10\x84\xef\x79\x8a\x62\x40\x46\x21\x59\xe2\xcf\x4d\x3c\x08\x8e\x28\x7a\x10\x5d\xf1\x28\x9d\x49\xc7\x0c\x66\x67\xa4\xbb\x85\xe4\xed\x45\x17\x41\x73\xea\x86\xaf\xf8\xaa\x9a\x06\xd7\x69\x86\x8d\x0c\x2b\xef\x9c\x5f\xa9\xef\x85\x55\x11\xcb\xf4\xb9\xcb\x48\x79\xcf\x84\xb2\x0e\x2c\x0a\xa3\x6e\x62\xd8\x48\x86\x91\x14\x1d\x73\xae\x9a\x06\x29\xc7\x22\xc2\xd1\xa6\x05\xca\x06\x2b\x20\xa8\x49\xca\x6f\x28\xc3\x8f\x63\x28\xb2\x83\x6b\xe7\xe3\x13\xea\x36\x9b\xd3\xb3\xd8\x3b\xa4\xac\xc6\xd4\x7f\x47\x08\xdd\x62\x0c\x12\xa1\xa5\x7a\x7e\xb8\xba\xdc\x86\x3f\xb5\x4f\x61\xbb\x1a\x78\x01\xe1\x8f\x89\x22\x1f\xfe\xde\x7f\xbc\x86\x6f\x67\x5f\xc3\xfb\xa3\x1a\xde\xed\x3f\xbc\xdc\x84\xc7\xb0\x12\xdd\xdf\xde\x05\x78\xd7\xce\x07\xce\x9f\x57\x5f\x01\x00\x00\xff\xff\xf9\xa8\x19\x33\x11\x01\x00\x00")
func _1686825075_cleanup_token_addressUpSqlBytes() ([]byte, error) {
return bindataRead(
__1686825075_cleanup_token_addressUpSql,
"1686825075_cleanup_token_address.up.sql",
)
}
func _1686825075_cleanup_token_addressUpSql() (*asset, error) {
bytes, err := _1686825075_cleanup_token_addressUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1686825075_cleanup_token_address.up.sql", size: 273, mode: os.FileMode(0644), modTime: time.Unix(1686994151, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x14, 0x72, 0x10, 0xec, 0x97, 0xc9, 0x3a, 0xdb, 0x39, 0x33, 0xc9, 0x6, 0x92, 0xbe, 0xe4, 0xc2, 0x5c, 0xb6, 0xaa, 0xe5, 0x25, 0x21, 0x4d, 0x74, 0x18, 0x94, 0xc, 0x33, 0x2f, 0xa4, 0x9, 0x99}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
func docGoBytes() ([]byte, error) {
@ -1554,7 +1554,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1686648985, 0)}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1686145633, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
return a, nil
}
@ -1650,8 +1650,6 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
".DS_Store": Ds_store,
"1640111208_dummy.up.sql": _1640111208_dummyUpSql,
"1642666031_add_removed_clock_to_bookmarks.up.sql": _1642666031_add_removed_clock_to_bookmarksUpSql,
@ -1790,6 +1788,8 @@ var _bindata = map[string]func() (*asset, error){
"1686048341_transfers_receipt_json_blob_out.up.sql.up.sql": _1686048341_transfers_receipt_json_blob_outUpSqlUpSql,
"1686825075_cleanup_token_address.up.sql": _1686825075_cleanup_token_addressUpSql,
"doc.go": docGo,
}
@ -1834,8 +1834,7 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
".DS_Store": &bintree{Ds_store, map[string]*bintree{}},
"1640111208_dummy.up.sql": &bintree{_1640111208_dummyUpSql, map[string]*bintree{}},
"1640111208_dummy.up.sql": &bintree{_1640111208_dummyUpSql, map[string]*bintree{}},
"1642666031_add_removed_clock_to_bookmarks.up.sql": &bintree{_1642666031_add_removed_clock_to_bookmarksUpSql, map[string]*bintree{}},
"1643644541_gif_api_key_setting.up.sql": &bintree{_1643644541_gif_api_key_settingUpSql, map[string]*bintree{}},
"1644188994_recent_stickers.up.sql": &bintree{_1644188994_recent_stickersUpSql, map[string]*bintree{}},
@ -1904,6 +1903,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1686041510_add_idx_transfers_blkno_loaded.up.sql": &bintree{_1686041510_add_idx_transfers_blkno_loadedUpSql, map[string]*bintree{}},
"1686048341_transfers_receipt_json_blob_out.up.sql.down.sql": &bintree{_1686048341_transfers_receipt_json_blob_outUpSqlDownSql, map[string]*bintree{}},
"1686048341_transfers_receipt_json_blob_out.up.sql.up.sql": &bintree{_1686048341_transfers_receipt_json_blob_outUpSqlUpSql, map[string]*bintree{}},
"1686825075_cleanup_token_address.up.sql": &bintree{_1686825075_cleanup_token_addressUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}

View File

@ -0,0 +1,3 @@
-- Fix the token_address column in the transfers table that has been
-- incorrectly set to a string of the form "0x12ab..34cd" instead of a byte array
UPDATE transfers SET token_address = replace(replace(token_address, '0x', ''), '"', '') WHERE token_address LIKE '"0x%"';

View File

@ -40,7 +40,7 @@ Filter requirements
- [x] `chainID`: aggregate data for activity entries `Bridge`, `Buy`, `Swap`
- [x] `tokenCode` for activity entries `Send`, `Receive`
- For `Bridge` its already there and `Buy`, `Swap` is coming soon
- [ ] `collectibles`: require adding collectible attributes to activity data (probably `token_address` and `tokenId`)
- [ ] `collectibles`: require adding collectible attributes to activity data (`token_address` and `tokenId`)
UX requirements

View File

@ -37,9 +37,10 @@ type Entry struct {
timestamp int64
activityType Type
activityStatus Status
tokenType TokenType
amountOut *hexutil.Big // Used for activityType SendAT, SwapAT, BridgeAT
amountIn *hexutil.Big // Used for activityType ReceiveAT, BuyAT, SwapAT, BridgeAT
tokenOut *Token // Used for activityType SendAT, SwapAT, BridgeAT
tokenIn *Token // Used for activityType ReceiveAT, BuyAT, SwapAT, BridgeAT
}
type jsonSerializationTemplate struct {
@ -49,9 +50,10 @@ type jsonSerializationTemplate struct {
Timestamp int64 `json:"timestamp"`
ActivityType Type `json:"activityType"`
ActivityStatus Status `json:"activityStatus"`
TokenType TokenType `json:"tokenType"`
AmountOut *hexutil.Big `json:"amountOut"`
AmountIn *hexutil.Big `json:"amountIn"`
TokenOut *Token `json:"tokenOut,omitempty"`
TokenIn *Token `json:"tokenIn,omitempty"`
}
func (e *Entry) MarshalJSON() ([]byte, error) {
@ -62,9 +64,10 @@ func (e *Entry) MarshalJSON() ([]byte, error) {
Timestamp: e.timestamp,
ActivityType: e.activityType,
ActivityStatus: e.activityStatus,
TokenType: e.tokenType,
AmountOut: e.amountOut,
AmountIn: e.amountIn,
TokenOut: e.tokenOut,
TokenIn: e.tokenIn,
})
}
@ -85,15 +88,15 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
return nil
}
func newActivityEntryWithPendingTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big) Entry {
return newActivityEntryWithTransaction(true, transaction, timestamp, activityType, activityStatus, amountIn, amountOut)
func newActivityEntryWithPendingTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big, tokenOut *Token, tokenIn *Token) Entry {
return newActivityEntryWithTransaction(true, transaction, timestamp, activityType, activityStatus, amountIn, amountOut, tokenOut, tokenIn)
}
func newActivityEntryWithSimpleTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big) Entry {
return newActivityEntryWithTransaction(false, transaction, timestamp, activityType, activityStatus, amountIn, amountOut)
func newActivityEntryWithSimpleTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big, tokenOut *Token, tokenIn *Token) Entry {
return newActivityEntryWithTransaction(false, transaction, timestamp, activityType, activityStatus, amountIn, amountOut, tokenOut, tokenIn)
}
func newActivityEntryWithTransaction(pending bool, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big) Entry {
func newActivityEntryWithTransaction(pending bool, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big, tokenOut *Token, tokenIn *Token) Entry {
payloadType := SimpleTransactionPT
if pending {
payloadType = PendingTransactionPT
@ -106,22 +109,24 @@ func newActivityEntryWithTransaction(pending bool, transaction *transfer.Transac
timestamp: timestamp,
activityType: activityType,
activityStatus: activityStatus,
tokenType: AssetTT,
amountIn: amountIn,
amountOut: amountOut,
tokenOut: tokenOut,
tokenIn: tokenIn,
}
}
func NewActivityEntryWithMultiTransaction(id transfer.MultiTransactionIDType, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big) Entry {
func NewActivityEntryWithMultiTransaction(id transfer.MultiTransactionIDType, timestamp int64, activityType Type, activityStatus Status, amountIn *hexutil.Big, amountOut *hexutil.Big, tokenOut *Token, tokenIn *Token) Entry {
return Entry{
payloadType: MultiTransactionPT,
id: id,
timestamp: timestamp,
activityType: activityType,
activityStatus: activityStatus,
tokenType: AssetTT,
amountIn: amountIn,
amountOut: amountOut,
tokenOut: tokenOut,
tokenIn: tokenIn,
}
}
@ -149,6 +154,15 @@ func sliceContains[T constraints.Ordered](slice []T, item T) bool {
return false
}
func sliceChecksCondition[T any](slice []T, condition func(*T) bool) bool {
for i := range slice {
if condition(&slice[i]) {
return true
}
}
return false
}
func joinItems[T interface{}](items []T, itemConversion func(T) string) string {
if len(items) == 0 {
return ""
@ -203,6 +217,7 @@ const (
// 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
// or insert binary (X'...' syntax) directly into the query
//
// Query includes duplicates, will return multiple rows for the same transaction if both to and from addresses
// are in the address list.
@ -217,236 +232,461 @@ const (
//
// Only status FailedAS, PendingAS and CompleteAS are returned. FinalizedAS requires correlation with blockchain
// current state. As an optimization we can approximate it by using timestamp information or last known block number
//
// 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
queryFormatString = `
WITH filter_conditions AS (
SELECT
? AS startFilterDisabled,
? AS startTimestamp,
? AS endFilterDisabled,
? AS endTimestamp,
WITH filter_conditions AS (
SELECT
? AS startFilterDisabled,
? AS startTimestamp,
? AS endFilterDisabled,
? AS endTimestamp,
? AS filterActivityTypeAll,
? AS filterActivityTypeSend,
? AS filterActivityTypeReceive,
? AS filterActivityTypeAll,
? AS filterActivityTypeSend,
? AS filterActivityTypeReceive,
? AS fromTrType,
? AS toTrType,
? AS fromTrType,
? AS toTrType,
? AS filterAllAddresses,
? AS filterAllToAddresses,
? AS filterAllAddresses,
? AS filterAllToAddresses,
? AS filterAllActivityStatus,
? AS filterStatusCompleted,
? AS filterStatusFailed,
? AS filterStatusFinalized,
? AS filterStatusPending,
? AS filterAllActivityStatus,
? AS filterStatusCompleted,
? AS filterStatusFailed,
? AS filterStatusFinalized,
? AS filterStatusPending,
? AS statusFailed,
? AS statusSuccess,
? AS statusPending,
? AS statusFailed,
? AS statusSuccess,
? AS statusPending,
? AS includeAllTokenTypeAssets,
? AS includeAllTokenTypeAssets,
? AS includeAllNetworks
),
filter_addresses(address) AS (
SELECT HEX(address) FROM keypairs_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
),
tr_status AS (
SELECT
multi_transaction_id,
MIN(status) AS min_status,
COUNT(*) AS count
FROM
transfers
WHERE transfers.multi_transaction_id != 0
GROUP BY
transfers.multi_transaction_id
),
pending_status AS (
SELECT
multi_transaction_id,
COUNT(*) AS count
FROM
pending_transactions
WHERE pending_transactions.multi_transaction_id != 0
GROUP BY pending_transactions.multi_transaction_id
)
SELECT
transfers.hash AS transfer_hash,
NULL AS pending_hash,
transfers.network_id AS network_id,
0 AS multi_tx_id,
transfers.timestamp AS timestamp,
NULL AS mt_type,
? AS includeAllNetworks
),
filter_addresses(address) AS (
SELECT HEX(address) FROM keypairs_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
),
assets_token_codes(token_code) AS (
VALUES %s
),
assets_erc20(chain_id, token_address) AS (
VALUES %s
),
filter_networks(network_id) AS (
VALUES %s
),
tr_status AS (
SELECT
multi_transaction_id,
MIN(status) AS min_status,
COUNT(*) AS count
FROM
transfers
WHERE transfers.multi_transaction_id != 0
GROUP BY
transfers.multi_transaction_id
),
pending_status AS (
SELECT
multi_transaction_id,
COUNT(*) AS count
FROM
pending_transactions
WHERE pending_transactions.multi_transaction_id != 0
GROUP BY pending_transactions.multi_transaction_id
)
SELECT
transfers.hash AS transfer_hash,
NULL AS pending_hash,
transfers.network_id AS network_id,
0 AS multi_tx_id,
transfers.timestamp AS timestamp,
NULL AS mt_type,
CASE
WHEN from_join.address IS NOT NULL AND to_join.address IS NULL THEN fromTrType
WHEN to_join.address IS NOT NULL AND from_join.address IS NULL THEN toTrType
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,
CASE
WHEN from_join.address IS NOT NULL AND to_join.address IS NULL THEN fromTrType
WHEN to_join.address IS NOT NULL AND from_join.address IS NULL THEN toTrType
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,
transfers.amount_padded128hex AS tr_amount,
NULL AS mt_from_amount,
NULL AS mt_to_amount,
transfers.sender AS from_address,
transfers.address AS to_address,
transfers.amount_padded128hex AS tr_amount,
NULL AS mt_from_amount,
NULL AS mt_to_amount,
CASE
WHEN transfers.status IS 1 THEN statusSuccess
ELSE statusFailed
END AS agg_status,
CASE
WHEN transfers.status IS 1 THEN statusSuccess
ELSE statusFailed
END AS agg_status,
1 AS agg_count
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
AND ((startFilterDisabled OR timestamp >= startTimestamp)
AND (endFilterDisabled OR timestamp <= endTimestamp)
)
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))
AND (filterAllActivityStatus OR ((filterStatusCompleted OR filterStatusFinalized) AND transfers.status = 1)
OR (filterStatusFailed AND transfers.status = 0)
)
1 AS agg_count,
UNION ALL
transfers.token_address AS token_address,
NULL AS token_code,
NULL AS from_token_code,
NULL AS to_token_code
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
AND ((startFilterDisabled OR timestamp >= startTimestamp)
AND (endFilterDisabled OR timestamp <= endTimestamp)
)
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 assets_token_codes))
OR (transfers.type = "erc20" AND ((transfers.network_id, transfers.token_address) IN assets_erc20)))
AND (includeAllNetworks OR (transfers.network_id IN filter_networks))
AND (filterAllActivityStatus OR ((filterStatusCompleted OR filterStatusFinalized) AND transfers.status = 1)
OR (filterStatusFailed AND transfers.status = 0)
)
SELECT
NULL AS transfer_hash,
pending_transactions.hash AS pending_hash,
pending_transactions.network_id AS network_id,
0 AS multi_tx_id,
pending_transactions.timestamp AS timestamp,
NULL AS mt_type,
UNION ALL
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,
SELECT
NULL AS transfer_hash,
pending_transactions.hash AS pending_hash,
pending_transactions.network_id AS network_id,
0 AS multi_tx_id,
pending_transactions.timestamp AS timestamp,
NULL AS mt_type,
pending_transactions.from_address AS from_address,
pending_transactions.to_address AS to_address,
pending_transactions.value AS tr_amount,
NULL AS mt_from_amount,
NULL AS mt_to_amount,
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,
statusPending AS agg_status,
1 AS agg_count
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
AND (filterAllActivityStatus OR filterStatusPending)
AND ((startFilterDisabled OR timestamp >= startTimestamp)
AND (endFilterDisabled OR timestamp <= endTimestamp)
)
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 (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))
pending_transactions.from_address AS from_address,
pending_transactions.to_address AS to_address,
pending_transactions.value AS tr_amount,
NULL AS mt_from_amount,
NULL AS mt_to_amount,
UNION ALL
statusPending AS agg_status,
1 AS agg_count,
SELECT
NULL AS transfer_hash,
NULL AS pending_hash,
NULL AS network_id,
multi_transactions.ROWID AS multi_tx_id,
multi_transactions.timestamp AS timestamp,
multi_transactions.type AS mt_type,
NULL as tr_type,
multi_transactions.from_address AS from_address,
multi_transactions.to_address AS to_address,
NULL AS tr_amount,
multi_transactions.from_amount AS mt_from_amount,
multi_transactions.to_amount AS mt_to_amount,
NULL AS token_address,
pending_transactions.symbol AS token_code,
NULL AS from_token_code,
NULL AS to_token_code
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
AND (filterAllActivityStatus OR filterStatusPending)
AND ((startFilterDisabled OR timestamp >= startTimestamp)
AND (endFilterDisabled OR timestamp <= endTimestamp)
)
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 (filterAllToAddresses
OR (HEX(pending_transactions.to_address) IN filter_to_addresses)
)
AND (includeAllTokenTypeAssets OR (UPPER(pending_transactions.symbol) IN assets_token_codes))
AND (includeAllNetworks OR (pending_transactions.network_id IN filter_networks))
CASE
WHEN tr_status.min_status = 1 AND pending_status.count IS NULL THEN statusSuccess
WHEN tr_status.min_status = 0 THEN statusFailed
ELSE statusPending
END AS agg_status,
UNION ALL
COALESCE(tr_status.count, 0) + COALESCE(pending_status.count, 0) AS agg_count
SELECT
NULL AS transfer_hash,
NULL AS pending_hash,
NULL AS network_id,
multi_transactions.ROWID AS multi_tx_id,
multi_transactions.timestamp AS timestamp,
multi_transactions.type AS mt_type,
NULL as tr_type,
multi_transactions.from_address AS from_address,
multi_transactions.to_address AS to_address,
NULL AS tr_amount,
multi_transactions.from_amount AS mt_from_amount,
multi_transactions.to_amount AS mt_to_amount,
FROM multi_transactions, filter_conditions
JOIN tr_status ON multi_transactions.ROWID = tr_status.multi_transaction_id
LEFT JOIN pending_status ON multi_transactions.ROWID = pending_status.multi_transaction_id
WHERE
((startFilterDisabled OR multi_transactions.timestamp >= startTimestamp)
AND (endFilterDisabled OR multi_transactions.timestamp <= endTimestamp)
)
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 (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)
)
AND (filterAllActivityStatus OR ((filterStatusCompleted OR filterStatusFinalized) AND agg_status = statusSuccess)
OR (filterStatusFailed AND agg_status = statusFailed) OR (filterStatusPending AND agg_status = statusPending))
CASE
WHEN tr_status.min_status = 1 AND pending_status.count IS NULL THEN statusSuccess
WHEN tr_status.min_status = 0 THEN statusFailed
ELSE statusPending
END AS agg_status,
ORDER BY timestamp DESC
LIMIT ? OFFSET ?`
COALESCE(tr_status.count, 0) + COALESCE(pending_status.count, 0) AS agg_count,
noEntriesInTmpTableSQLValues = "(NULL)"
NULL AS token_address,
NULL AS token_code,
multi_transactions.from_asset AS from_token_code,
multi_transactions.to_asset AS to_token_code
FROM multi_transactions, filter_conditions
JOIN tr_status ON multi_transactions.ROWID = tr_status.multi_transaction_id
LEFT JOIN pending_status ON multi_transactions.ROWID = pending_status.multi_transaction_id
WHERE
((startFilterDisabled OR multi_transactions.timestamp >= startTimestamp)
AND (endFilterDisabled OR multi_transactions.timestamp <= endTimestamp)
)
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 (filterAllToAddresses
OR (HEX(multi_transactions.to_address) IN filter_to_addresses)
)
AND (includeAllTokenTypeAssets
OR (multi_transactions.from_asset != '' AND (UPPER(multi_transactions.from_asset) IN assets_token_codes))
OR (multi_transactions.to_asset != '' AND (UPPER(multi_transactions.to_asset) IN assets_token_codes))
)
AND (filterAllActivityStatus OR ((filterStatusCompleted OR filterStatusFinalized) AND agg_status = statusSuccess)
OR (filterStatusFailed AND agg_status = statusFailed) OR (filterStatusPending AND agg_status = statusPending))
ORDER BY timestamp DESC
LIMIT ? OFFSET ?`
noEntriesInTmpTableSQLValues = "(NULL)"
noEntriesInTwoColumnsTmpTableSQLValues = "(NULL, NULL)"
)
type FilterDependencies struct {
db *sql.DB
tokenSymbol func(token Token) string
tokenFromSymbol func(chainID *common.ChainID, symbol string) *Token
}
// getActivityEntries queries 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(ctx context.Context, deps FilterDependencies, addresses []eth.Address, chainIDs []common.ChainID, filter Filter, offset int, limit int) ([]Entry, error) {
includeAllTokenTypeAssets := len(filter.Assets) == 0 && !filter.FilterOutAssets
// Used for symbol bearing tables multi_transactions and pending_transactions
assetsTokenCodes := noEntriesInTmpTableSQLValues
// Used for identity bearing tables transfers
assetsERC20 := noEntriesInTwoColumnsTmpTableSQLValues
if !includeAllTokenTypeAssets && !filter.FilterOutAssets {
symbolsSet := make(map[string]struct{})
var symbols []string
for _, item := range filter.Assets {
symbol := deps.tokenSymbol(item)
if _, ok := symbolsSet[symbol]; !ok {
symbols = append(symbols, symbol)
symbolsSet[symbol] = struct{}{}
}
}
assetsTokenCodes = joinItems(symbols, func(s string) string {
return fmt.Sprintf("'%s'", s)
})
if sliceChecksCondition(filter.Assets, func(item *Token) bool { return item.TokenType == Erc20 }) {
assetsERC20 = joinItems(filter.Assets, func(item Token) string {
if item.TokenType == Erc20 {
return fmt.Sprintf("%d, '%s'", item.ChainID, item.Address.Hex())
}
return ""
})
}
}
// construct chain IDs
includeAllNetworks := len(chainIDs) == 0
networks := noEntriesInTmpTableSQLValues
if !includeAllNetworks {
networks = joinItems(chainIDs, nil)
}
startFilterDisabled := !(filter.Period.StartTimestamp > 0)
endFilterDisabled := !(filter.Period.EndTimestamp > 0)
filterActivityTypeAll := len(filter.Types) == 0
filterAllAddresses := len(addresses) == 0
filterAllToAddresses := len(filter.CounterpartyAddresses) == 0
includeAllStatuses := len(filter.Statuses) == 0
filterStatusPending := false
filterStatusCompleted := false
filterStatusFailed := false
filterStatusFinalized := false
if !includeAllStatuses {
filterStatusPending = sliceContains(filter.Statuses, PendingAS)
filterStatusCompleted = sliceContains(filter.Statuses, CompleteAS)
filterStatusFailed = sliceContains(filter.Statuses, FailedAS)
filterStatusFinalized = sliceContains(filter.Statuses, FinalizedAS)
}
involvedAddresses := noEntriesInTmpTableSQLValues
if !filterAllAddresses {
involvedAddresses = joinAddresses(addresses)
}
toAddresses := noEntriesInTmpTableSQLValues
if !filterAllToAddresses {
toAddresses = joinAddresses(filter.CounterpartyAddresses)
}
mtTypes := activityTypesToMultiTransactionTypes(filter.Types)
joinedMTTypes := joinItems(mtTypes, func(t transfer.MultiTransactionType) string {
return strconv.Itoa(int(t))
})
queryString := fmt.Sprintf(queryFormatString, involvedAddresses, toAddresses, assetsTokenCodes, assetsERC20, networks,
joinedMTTypes)
rows, err := deps.db.QueryContext(ctx, queryString,
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
fromTrType, toTrType,
filterAllAddresses, filterAllToAddresses,
includeAllStatuses, filterStatusCompleted, filterStatusFailed, filterStatusFinalized, filterStatusPending,
FailedAS, CompleteAS, PendingAS,
includeAllTokenTypeAssets,
includeAllNetworks,
limit, offset)
if err != nil {
return nil, err
}
defer rows.Close()
var entries []Entry
for rows.Next() {
var transferHash, pendingHash []byte
var chainID, multiTxID, aggregatedCount sql.NullInt64
var timestamp int64
var dbMtType, dbTrType sql.NullByte
var toAddress, fromAddress eth.Address
var aggregatedStatus int
var dbTrAmount sql.NullString
var dbMtFromAmount, dbMtToAmount sql.NullString
var tokenAddress, tokenCode, fromTokenCode, toTokenCode sql.NullString
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, &timestamp, &dbMtType, &dbTrType, &fromAddress,
&toAddress, &dbTrAmount, &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount,
&tokenAddress, &tokenCode, &fromTokenCode, &toTokenCode)
if err != nil {
return nil, err
}
getActivityType := func(trType sql.NullByte) (activityType Type, filteredAddress eth.Address) {
if trType.Valid {
if trType.Byte == fromTrType {
return SendAT, fromAddress
} else if trType.Byte == toTrType {
return ReceiveAT, toAddress
}
}
log.Warn(fmt.Sprintf("unexpected activity type. Missing [%s, %s] in the addresses table?", fromAddress, toAddress))
return ReceiveAT, toAddress
}
// Can be mapped directly because the values are injected into the query
activityStatus := Status(aggregatedStatus)
var tokenOut, tokenIn *Token
var entry Entry
if transferHash != nil && chainID.Valid {
// Extract activity type: SendAT/ReceiveAT
activityType, filteredAddress := getActivityType(dbTrType)
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount)
// Extract tokens
var involvedToken *Token
if tokenAddress.Valid && eth.HexToAddress(tokenAddress.String) != eth.HexToAddress("0x") {
involvedToken = &Token{TokenType: Erc20, ChainID: common.ChainID(chainID.Int64), Address: eth.HexToAddress(tokenAddress.String)}
} else {
involvedToken = &Token{TokenType: Native, ChainID: common.ChainID(chainID.Int64)}
}
if activityType == SendAT {
tokenOut = involvedToken
} else {
tokenIn = involvedToken
}
entry = newActivityEntryWithSimpleTransaction(
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(transferHash), Address: filteredAddress},
timestamp, activityType, activityStatus, inAmount, outAmount, tokenOut, tokenIn)
} else if pendingHash != nil && chainID.Valid {
// Extract activity type: PendingAT
activityType, _ := getActivityType(dbTrType)
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount)
// Extract tokens
if tokenCode.Valid {
cID := common.ChainID(chainID.Int64)
tokenOut = deps.tokenFromSymbol(&cID, tokenCode.String)
}
entry = newActivityEntryWithPendingTransaction(&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(pendingHash)},
timestamp, activityType, activityStatus, inAmount, outAmount, tokenOut, tokenIn)
} else if multiTxID.Valid {
mtInAmount, mtOutAmount := getMtInAndOutAmounts(dbMtFromAmount, dbMtToAmount)
// Extract activity type: SendAT/SwapAT/BridgeAT
activityType := multiTransactionTypeToActivityType(transfer.MultiTransactionType(dbMtType.Byte))
// Extract tokens
if fromTokenCode.Valid {
tokenOut = deps.tokenFromSymbol(nil, fromTokenCode.String)
}
if toTokenCode.Valid {
tokenIn = deps.tokenFromSymbol(nil, toTokenCode.String)
}
entry = NewActivityEntryWithMultiTransaction(transfer.MultiTransactionIDType(multiTxID.Int64),
timestamp, activityType, activityStatus, mtInAmount, mtOutAmount, tokenOut, tokenIn)
} else {
return nil, errors.New("invalid row data")
}
entries = append(entries, entry)
}
if err = rows.Err(); err != nil {
return nil, err
}
return entries, nil
}
func getTrInAndOutAmounts(activityType Type, trAmount sql.NullString) (inAmount *hexutil.Big, outAmount *hexutil.Big) {
if trAmount.Valid {
amount, ok := new(big.Int).SetString(trAmount.String, 16)
@ -495,140 +735,3 @@ func getMtInAndOutAmounts(dbFromAmount sql.NullString, dbToAmount sql.NullString
outAmount = (*hexutil.Big)(big.NewInt(0))
return
}
// getActivityEntries queries 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(ctx context.Context, 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)
}
startFilterDisabled := !(filter.Period.StartTimestamp > 0)
endFilterDisabled := !(filter.Period.EndTimestamp > 0)
filterActivityTypeAll := len(filter.Types) == 0
filterAllAddresses := len(addresses) == 0
filterAllToAddresses := len(filter.CounterpartyAddresses) == 0
includeAllStatuses := len(filter.Statuses) == 0
filterStatusPending := false
filterStatusCompleted := false
filterStatusFailed := false
filterStatusFinalized := false
if !includeAllStatuses {
filterStatusPending = sliceContains(filter.Statuses, PendingAS)
filterStatusCompleted = sliceContains(filter.Statuses, CompleteAS)
filterStatusFailed = sliceContains(filter.Statuses, FailedAS)
filterStatusFinalized = sliceContains(filter.Statuses, FinalizedAS)
}
involvedAddresses := noEntriesInTmpTableSQLValues
if !filterAllAddresses {
involvedAddresses = joinAddresses(addresses)
}
toAddresses := noEntriesInTmpTableSQLValues
if !filterAllToAddresses {
toAddresses = joinAddresses(filter.CounterpartyAddresses)
}
mtTypes := activityTypesToMultiTransactionTypes(filter.Types)
joinedMTTypes := joinItems(mtTypes, func(t transfer.MultiTransactionType) string {
return strconv.Itoa(int(t))
})
queryString := fmt.Sprintf(queryFormatString, involvedAddresses, toAddresses, assets, networks,
joinedMTTypes)
rows, err := db.QueryContext(ctx, queryString,
startFilterDisabled, filter.Period.StartTimestamp, endFilterDisabled, filter.Period.EndTimestamp,
filterActivityTypeAll, sliceContains(filter.Types, SendAT), sliceContains(filter.Types, ReceiveAT),
fromTrType, toTrType,
filterAllAddresses, filterAllToAddresses,
includeAllStatuses, filterStatusCompleted, filterStatusFailed, filterStatusFinalized, filterStatusPending,
FailedAS, CompleteAS, PendingAS,
includeAllTokenTypeAssets,
includeAllNetworks,
limit, offset)
if err != nil {
return nil, err
}
defer rows.Close()
var entries []Entry
for rows.Next() {
var transferHash, pendingHash []byte
var chainID, multiTxID, aggregatedCount sql.NullInt64
var timestamp int64
var dbMtType, dbTrType sql.NullByte
var toAddress, fromAddress eth.Address
var aggregatedStatus int
var dbTrAmount sql.NullString
var dbMtFromAmount, dbMtToAmount sql.NullString
err := rows.Scan(&transferHash, &pendingHash, &chainID, &multiTxID, &timestamp, &dbMtType, &dbTrType, &fromAddress, &toAddress, &dbTrAmount, &dbMtFromAmount, &dbMtToAmount, &aggregatedStatus, &aggregatedCount)
if err != nil {
return nil, err
}
getActivityType := func(trType sql.NullByte) (activityType Type, filteredAddress eth.Address) {
if trType.Valid {
if trType.Byte == fromTrType {
return SendAT, fromAddress
} else if trType.Byte == toTrType {
return ReceiveAT, toAddress
}
}
log.Warn(fmt.Sprintf("unexpected activity type. Missing [%s, %s] in the addresses table?", fromAddress, toAddress))
return ReceiveAT, toAddress
}
// Can be mapped directly because the values are injected into the query
activityStatus := Status(aggregatedStatus)
var entry Entry
if transferHash != nil && chainID.Valid {
activityType, filteredAddress := getActivityType(dbTrType)
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount)
entry = newActivityEntryWithSimpleTransaction(
&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(transferHash), Address: filteredAddress},
timestamp, activityType, activityStatus, inAmount, outAmount)
} else if pendingHash != nil && chainID.Valid {
activityType, _ := getActivityType(dbTrType)
inAmount, outAmount := getTrInAndOutAmounts(activityType, dbTrAmount)
entry = newActivityEntryWithPendingTransaction(&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(pendingHash)},
timestamp, activityType, activityStatus, inAmount, outAmount)
} else if multiTxID.Valid {
mtInAmount, mtOutAmount := getMtInAndOutAmounts(dbMtFromAmount, dbMtToAmount)
activityType := multiTransactionTypeToActivityType(transfer.MultiTransactionType(dbMtType.Byte))
fmt.Println("getMtInAndOutAmounts", multiTxID.Int64, activityType, mtInAmount, mtOutAmount)
entry = NewActivityEntryWithMultiTransaction(transfer.MultiTransactionIDType(multiTxID.Int64),
timestamp, activityType, activityStatus, mtInAmount, mtOutAmount)
} else {
return nil, errors.New("invalid row data")
}
entries = append(entries, entry)
}
if err = rows.Err(); err != nil {
return nil, err
}
return entries, nil
}

View File

@ -20,23 +20,63 @@ import (
"github.com/stretchr/testify/require"
)
func setupTestActivityDB(t *testing.T) (db *sql.DB, close func()) {
func tokenFromSymbol(chainID *common.ChainID, symbol string) *Token {
for i, t := range transfer.TestTokens {
if (chainID == nil || t.ChainID == uint64(*chainID)) && t.Symbol == symbol {
tokenType := Erc20
if testutils.SliceContains(transfer.NativeTokenIndices, i) {
tokenType = Native
}
return &Token{
TokenType: tokenType,
ChainID: common.ChainID(t.ChainID),
Address: t.Address,
}
}
}
return nil
}
func setupTestActivityDB(t *testing.T) (deps FilterDependencies, close func()) {
db, err := appdatabase.SetupTestMemorySQLDB("wallet-activity-tests")
require.NoError(t, err)
deps = FilterDependencies{
db: db,
tokenSymbol: func(token Token) string {
switch token.TokenType {
case Native:
for i, t := range transfer.TestTokens {
if t.ChainID == uint64(token.ChainID) && testutils.SliceContains(transfer.NativeTokenIndices, i) {
return t.Symbol
}
}
case Erc20:
for _, t := range transfer.TestTokens {
if t.ChainID == uint64(token.ChainID) && t.Address == token.Address {
return t.Symbol
}
}
}
// In case of ERC721 and ERC1155 we don't have a symbol and they are not yet handled
return ""
},
// tokenFromSymbol nil chainID accepts first symbol found
tokenFromSymbol: tokenFromSymbol,
}
return db, func() {
return deps, func() {
require.NoError(t, db.Close())
}
}
type testData struct {
tr1 transfer.TestTransfer // index 1
pendingTr transfer.TestTransfer // index 2
multiTx1Tr1 transfer.TestTransfer // index 3
multiTx2Tr1 transfer.TestTransfer // index 4
multiTx1Tr2 transfer.TestTransfer // index 5
multiTx2Tr2 transfer.TestTransfer // index 6
multiTx2PendingTr transfer.TestTransfer // index 7
tr1 transfer.TestTransfer // index 1, ETH/Goerli
pendingTr transfer.TestTransfer // index 2, ETH/Optimism
multiTx1Tr1 transfer.TestTransfer // index 3, USDC/Mainnet
multiTx2Tr1 transfer.TestTransfer // index 4, USDC/Goerli
multiTx1Tr2 transfer.TestTransfer // index 5, USDC/Optimism
multiTx2Tr2 transfer.TestTransfer // index 6, SNT/Mainnet
multiTx2PendingTr transfer.TestTransfer // index 7, DAI/Mainnet
multiTx1 transfer.TestMultiTransaction
multiTx1ID transfer.MultiTransactionIDType
@ -61,6 +101,7 @@ func mockTestAccountsWithAddresses(t *testing.T, db *sql.DB, addresses []eth_com
// Generates and adds to the DB 7 transfers and 2 multitransactions.
// There are only 4 extractable activity entries (transactions + multi-transactions) with timestamps 1-4. The others are associated with a multi-transaction
func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddresses []eth_common.Address) {
// Generates ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet
trs, fromAddresses, toAddresses := transfer.GenerateTestTransfers(t, db, 1, 7)
// Plain transfer
@ -75,7 +116,8 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddre
td.multiTx1Tr1 = trs[2]
td.multiTx1Tr2 = trs[4]
td.multiTx1Tr1.Token = testutils.SntSymbol
// TODO: This got automatically set by GenerateTestTransfers to USDC/Mainnet
//td.multiTx1Tr1.Token = &transfer.SntMainnet
td.multiTx1 = transfer.GenerateTestSendMultiTransaction(td.multiTx1Tr1)
td.multiTx1.ToToken = testutils.DaiSymbol
@ -93,7 +135,7 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddre
td.multiTx2PendingTr = trs[6]
td.multiTx2 = transfer.GenerateTestSendMultiTransaction(td.multiTx2Tr1)
td.multiTx1.ToToken = testutils.SntSymbol
td.multiTx2.ToToken = testutils.SntSymbol
td.multiTx2ID = transfer.InsertTestMultiTransaction(t, db, &td.multiTx2)
td.multiTx2Tr1.MultiTransactionID = td.multiTx2ID
@ -109,14 +151,27 @@ func fillTestData(t *testing.T, db *sql.DB) (td testData, fromAddresses, toAddre
return td, fromAddresses, toAddresses
}
func TTrToToken(t *testing.T, tt *transfer.TestTransaction) *Token {
token, isNative := transfer.TestTrToToken(t, tt)
tokenType := Erc20
if isNative {
tokenType = Native
}
return &Token{
TokenType: tokenType,
ChainID: common.ChainID(token.ChainID),
Address: token.Address,
}
}
func TestGetActivityEntriesAll(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
td, fromAddresses, toAddresses := fillTestData(t, db)
td, fromAddresses, toAddresses := fillTestData(t, deps.db)
var filter Filter
entries, err := getActivityEntries(context.Background(), db, append(toAddresses, fromAddresses...), []common.ChainID{}, filter, 0, 10)
entries, err := getActivityEntries(context.Background(), deps, append(toAddresses, fromAddresses...), []common.ChainID{}, filter, 0, 10)
require.NoError(t, err)
require.Equal(t, 4, len(entries))
@ -127,119 +182,66 @@ func TestGetActivityEntriesAll(t *testing.T) {
curTimestamp--
}
require.True(t, testutils.StructExistsInSlice(Entry{
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.tr1.ChainID, Hash: td.tr1.Hash, Address: td.tr1.From},
id: td.tr1.MultiTransactionID,
timestamp: td.tr1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.tr1.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.True(t, testutils.StructExistsInSlice(Entry{
tokenOut: TTrToToken(t, &td.tr1.TestTransaction),
tokenIn: nil,
}, entries[3])
require.Equal(t, Entry{
payloadType: PendingTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.pendingTr.ChainID, Hash: td.pendingTr.Hash},
id: td.pendingTr.MultiTransactionID,
timestamp: td.pendingTr.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.pendingTr.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.True(t, testutils.StructExistsInSlice(Entry{
tokenOut: TTrToToken(t, &td.pendingTr.TestTransaction),
tokenIn: nil,
}, entries[2])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx1ID,
timestamp: td.multiTx1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx1.FromAmount)),
amountIn: (*hexutil.Big)(big.NewInt(td.multiTx1.ToAmount)),
}, entries))
require.True(t, testutils.StructExistsInSlice(Entry{
tokenOut: tokenFromSymbol(nil, td.multiTx1.FromToken),
tokenIn: tokenFromSymbol(nil, td.multiTx1.ToToken),
}, entries[1])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
transaction: nil,
id: td.multiTx2ID,
timestamp: td.multiTx2.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx2.FromAmount)),
amountIn: (*hexutil.Big)(big.NewInt(td.multiTx2.ToAmount)),
}, entries))
// Ensure the sub-transactions of the multi-transactions are not returned
require.False(t, testutils.StructExistsInSlice(Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.multiTx1Tr1.ChainID, Hash: td.multiTx1Tr1.Hash, Address: td.multiTx1Tr1.To},
id: td.multiTx1Tr1.MultiTransactionID,
timestamp: td.multiTx1Tr1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx1Tr1.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.False(t, testutils.StructExistsInSlice(Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.multiTx1Tr2.ChainID, Hash: td.multiTx1Tr2.Hash, Address: td.multiTx1Tr2.To},
id: td.multiTx1Tr2.MultiTransactionID,
timestamp: td.multiTx1Tr2.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx1Tr2.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.False(t, testutils.StructExistsInSlice(Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.multiTx2Tr1.ChainID, Hash: td.multiTx2Tr1.Hash, Address: td.multiTx2Tr1.To},
id: td.multiTx2Tr1.MultiTransactionID,
timestamp: td.multiTx2Tr1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx2Tr1.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.False(t, testutils.StructExistsInSlice(Entry{
payloadType: SimpleTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.multiTx2Tr2.ChainID, Hash: td.multiTx2Tr2.Hash, Address: td.multiTx2Tr2.To},
id: td.multiTx2Tr2.MultiTransactionID,
timestamp: td.multiTx2Tr2.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx2Tr2.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
require.False(t, testutils.StructExistsInSlice(Entry{
payloadType: PendingTransactionPT,
transaction: &transfer.TransactionIdentity{ChainID: td.multiTx2PendingTr.ChainID, Hash: td.multiTx2PendingTr.Hash},
id: td.multiTx2PendingTr.MultiTransactionID,
timestamp: td.multiTx2PendingTr.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx2PendingTr.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
}, entries))
tokenOut: tokenFromSymbol(nil, td.multiTx2.FromToken),
tokenIn: tokenFromSymbol(nil, td.multiTx2.ToToken),
}, entries[0])
}
// TestGetActivityEntriesWithSenderFilter covers the issue with returning the same transaction
// twice when the sender and receiver have entries in the transfers table
func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Add 4 extractable transactions with timestamps 1-4
td, fromAddresses, toAddresses := fillTestData(t, db)
td, fromAddresses, toAddresses := fillTestData(t, deps.db)
mockTestAccountsWithAddresses(t, db, append(fromAddresses, toAddresses...))
mockTestAccountsWithAddresses(t, deps.db, append(fromAddresses, toAddresses...))
// Add another transaction with sender and receiver reversed
receiverTr := td.tr1
@ -251,10 +253,10 @@ func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testin
// 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, deps.db, &receiverTr)
var filter Filter
entries, err := getActivityEntries(context.Background(), db, []eth.Address{td.tr1.From, receiverTr.From}, []common.ChainID{}, filter, 0, 10)
entries, err := getActivityEntries(context.Background(), deps, []eth.Address{td.tr1.From, receiverTr.From}, []common.ChainID{}, filter, 0, 10)
require.NoError(t, err)
require.Equal(t, 2, len(entries))
@ -267,7 +269,7 @@ func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testin
require.NotEqual(t, eth.Address{}, entries[0].transaction.Address)
require.Equal(t, td.tr1.From, entries[0].transaction.Address)
entries, err = getActivityEntries(context.Background(), db, []eth.Address{}, []common.ChainID{}, filter, 0, 10)
entries, err = getActivityEntries(context.Background(), deps, []eth.Address{}, []common.ChainID{}, filter, 0, 10)
require.NoError(t, err)
require.Equal(t, 5, len(entries))
@ -277,24 +279,24 @@ func TestGetActivityEntriesWithSameTransactionForSenderAndReceiverInDB(t *testin
}
func TestGetActivityEntriesFilterByTime(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
td, fromTds, toTds := fillTestData(t, db)
td, fromTds, toTds := fillTestData(t, deps.db)
// Add 6 extractable transactions with timestamps 6-12
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 6)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
// Test start only
var filter Filter
filter.Period.StartTimestamp = td.multiTx1.Timestamp
filter.Period.EndTimestamp = NoLimitTimestampForPeriod
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 8, len(entries))
// Check start and end content
@ -305,9 +307,10 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: trs[5].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[5].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[5].TestTransaction),
tokenIn: nil,
}, entries[0])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
@ -316,14 +319,15 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: td.multiTx1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx1.FromAmount)),
amountIn: (*hexutil.Big)(big.NewInt(td.multiTx1.ToAmount)),
tokenOut: tokenFromSymbol(nil, td.multiTx1.FromToken),
tokenIn: tokenFromSymbol(nil, td.multiTx1.ToToken),
}, entries[7])
// Test complete interval
filter.Period.EndTimestamp = trs[2].Timestamp
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 5, len(entries))
// Check start and end content
@ -334,9 +338,10 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: trs[2].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[2].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[2].TestTransaction),
tokenIn: nil,
}, entries[0])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
@ -345,14 +350,15 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: td.multiTx1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx1.FromAmount)),
amountIn: (*hexutil.Big)(big.NewInt(td.multiTx1.ToAmount)),
tokenOut: tokenFromSymbol(nil, td.multiTx1.FromToken),
tokenIn: tokenFromSymbol(nil, td.multiTx1.ToToken),
}, entries[4])
// Test end only
filter.Period.StartTimestamp = NoLimitTimestampForPeriod
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 7, len(entries))
// Check start and end content
@ -363,9 +369,10 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: trs[2].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[2].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[2].TestTransaction),
tokenIn: nil,
}, entries[0])
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
@ -374,34 +381,35 @@ func TestGetActivityEntriesFilterByTime(t *testing.T) {
timestamp: td.tr1.Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.tr1.Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &td.tr1.TestTransaction),
tokenIn: nil,
}, entries[6])
}
func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Add 10 extractable transactions with timestamps 1-10
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, 1, 10)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, 1, 10)
for i := range trs {
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
mockTestAccountsWithAddresses(t, db, append(fromTrs, toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(fromTrs, toTrs...))
var filter Filter
// Get all
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 5)
entries, err := getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 5)
require.NoError(t, err)
require.Equal(t, 5, len(entries))
// Get time based interval
filter.Period.StartTimestamp = trs[2].Timestamp
filter.Period.EndTimestamp = trs[8].Timestamp
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 3)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 3)
require.NoError(t, err)
require.Equal(t, 3, len(entries))
// Check start and end content
@ -412,9 +420,10 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
timestamp: trs[8].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[8].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[8].TestTransaction),
tokenIn: nil,
}, entries[0])
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
@ -423,13 +432,14 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
timestamp: trs[6].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[6].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[6].TestTransaction),
tokenIn: nil,
}, entries[2])
// Move window 2 entries forward
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 2, 3)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 2, 3)
require.NoError(t, err)
require.Equal(t, 3, len(entries))
// Check start and end content
@ -440,9 +450,10 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
timestamp: trs[6].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[6].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[6].TestTransaction),
tokenIn: nil,
}, entries[0])
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
@ -451,13 +462,14 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
timestamp: trs[4].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[4].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[4].TestTransaction),
tokenIn: nil,
}, entries[2])
// Move window 4 more entries to test filter cap
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 6, 3)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 6, 3)
require.NoError(t, err)
require.Equal(t, 1, len(entries))
// Check start and end content
@ -468,9 +480,10 @@ func TestGetActivityEntriesCheckOffsetAndLimit(t *testing.T) {
timestamp: trs[2].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[2].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[2].TestTransaction),
tokenIn: nil,
}, entries[0])
}
@ -493,14 +506,14 @@ func countTypes(entries []Entry) (sendCount, receiveCount, swapCount, buyCount,
}
func TestGetActivityEntriesFilterByType(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions
td, _, _ := fillTestData(t, db)
td, _, _ := fillTestData(t, deps.db)
// Add 5 extractable transactions: one MultiTransactionSwap, two MultiTransactionBridge and two MultiTransactionSend
multiTxs := make([]transfer.TestMultiTransaction, 5)
trs, _, _ := transfer.GenerateTestTransfers(t, db, td.nextIndex, len(multiTxs)*2)
trs, _, _ := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, len(multiTxs)*2)
multiTxs[0] = transfer.GenerateTestBridgeMultiTransaction(trs[0], trs[1])
multiTxs[1] = transfer.GenerateTestSwapMultiTransaction(trs[2], testutils.SntSymbol, 100) // trs[3]
multiTxs[2] = transfer.GenerateTestSendMultiTransaction(trs[4]) // trs[5]
@ -510,10 +523,10 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
var lastMT transfer.MultiTransactionIDType
for i := range trs {
if i%2 == 0 {
lastMT = transfer.InsertTestMultiTransaction(t, db, &multiTxs[i/2])
lastMT = transfer.InsertTestMultiTransaction(t, deps.db, &multiTxs[i/2])
}
trs[i].MultiTransactionID = lastMT
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
// Test filtering out without address involved
@ -522,12 +535,12 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
filter.Types = allActivityTypesFilter()
// Set tr1 to Receive and pendingTr to Send; rest of two MT remain default Send
addresses := []eth_common.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}
entries, err := getActivityEntries(context.Background(), db, addresses, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 9, len(entries))
filter.Types = []Type{SendAT, SwapAT}
entries, err = getActivityEntries(context.Background(), db, addresses, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
// 3 from td Send + 2 trs MT Send + 1 (swap)
require.Equal(t, 6, len(entries))
@ -540,7 +553,7 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
require.Equal(t, 0, bridgeCount)
filter.Types = []Type{BridgeAT, ReceiveAT}
entries, err = getActivityEntries(context.Background(), db, addresses, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 3, len(entries))
@ -552,27 +565,27 @@ func TestGetActivityEntriesFilterByType(t *testing.T) {
}
func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions
td, fromTds, toTds := fillTestData(t, db)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 6)
td, fromTds, toTds := fillTestData(t, deps.db)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
var filter Filter
addressesFilter := allAddressesFilter()
entries, err := getActivityEntries(context.Background(), db, addressesFilter, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, addressesFilter, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
addressesFilter = []eth_common.Address{td.multiTx2.ToAddress, trs[1].From, trs[4].To}
entries, err = getActivityEntries(context.Background(), db, addressesFilter, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, addressesFilter, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 3, len(entries))
require.Equal(t, Entry{
@ -582,9 +595,10 @@ func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
timestamp: trs[4].Timestamp,
activityType: ReceiveAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(0)),
amountIn: (*hexutil.Big)(big.NewInt(trs[4].Value)),
tokenOut: nil,
tokenIn: TTrToToken(t, &trs[4].TestTransaction),
}, entries[0])
require.Equal(t, Entry{
payloadType: SimpleTransactionPT,
@ -593,9 +607,10 @@ func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
timestamp: trs[1].Timestamp,
activityType: SendAT,
activityStatus: CompleteAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(trs[1].Value)),
amountIn: (*hexutil.Big)(big.NewInt(0)),
tokenOut: TTrToToken(t, &trs[1].TestTransaction),
tokenIn: nil,
}, entries[1])
require.Equal(t, Entry{
payloadType: MultiTransactionPT,
@ -604,42 +619,43 @@ func TestGetActivityEntriesFilterByAddresses(t *testing.T) {
timestamp: td.multiTx2.Timestamp,
activityType: SendAT,
activityStatus: PendingAS,
tokenType: AssetTT,
amountOut: (*hexutil.Big)(big.NewInt(td.multiTx2.FromAmount)),
amountIn: (*hexutil.Big)(big.NewInt(td.multiTx2.ToAmount)),
tokenOut: tokenFromSymbol(nil, td.multiTx2.FromToken),
tokenIn: tokenFromSymbol(nil, td.multiTx2.ToToken),
}, entries[2])
}
func TestGetActivityEntriesFilterByStatus(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions: 1 T, 1 T pending, 1 MT pending, 1 MT with 2xT success
td, fromTds, toTds := fillTestData(t, db)
td, fromTds, toTds := fillTestData(t, deps.db)
// Add 7 extractable transactions: 1 pending, 1 Tr failed, 1 MT failed, 4 success
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 7)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 7)
multiTx := transfer.GenerateTestSendMultiTransaction(trs[6])
failedMTID := transfer.InsertTestMultiTransaction(t, db, &multiTx)
failedMTID := transfer.InsertTestMultiTransaction(t, deps.db, &multiTx)
trs[6].MultiTransactionID = failedMTID
for i := range trs {
if i == 1 {
transfer.InsertTestPendingTransaction(t, db, &trs[i])
transfer.InsertTestPendingTransaction(t, deps.db, &trs[i])
} else {
trs[i].Success = i != 3 && i != 6
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
var filter Filter
filter.Statuses = allActivityStatusesFilter()
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 11, len(entries))
filter.Statuses = []Status{PendingAS}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 3, len(entries))
require.Equal(t, td.pendingTr.Hash, entries[2].transaction.Hash)
@ -647,162 +663,192 @@ func TestGetActivityEntriesFilterByStatus(t *testing.T) {
require.Equal(t, trs[1].Hash, entries[0].transaction.Hash)
filter.Statuses = []Status{FailedAS}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 2, len(entries))
filter.Statuses = []Status{CompleteAS}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 6, len(entries))
// Finalized is treated as Complete, would need dynamic blockchain status to track the Finalized level
filter.Statuses = []Status{FinalizedAS}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 6, len(entries))
// Combined filter
filter.Statuses = []Status{FailedAS, PendingAS}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 5, len(entries))
}
func TestGetActivityEntriesFilterByTokenType(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions 2 transactions ETH, one MT SNT to DAI and another MT ETH to SNT
td, fromTds, toTds := fillTestData(t, db)
// Add 6 extractable transactions with USDC (only erc20 as type in DB)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 6)
// 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 9 transactions DAI/Goerli, ETH/Mainnet, ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 9)
for i := range trs {
trs[i].Token = "USDC"
transfer.InsertTestTransfer(t, db, &trs[i])
tokenAddr := transfer.TestTokens[i].Address
trs[i].ChainID = common.ChainID(transfer.TestTokens[i].ChainID)
transfer.InsertTestTransferWithToken(t, deps.db, &trs[i], tokenAddr)
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
var filter Filter
filter.Tokens = noAssetsFilter()
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
filter.FilterOutAssets = true
entries, err := getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 0, len(entries))
filter.Tokens = allTokensFilter()
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
filter.FilterOutAssets = false
filter.Assets = allTokensFilter()
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
require.Equal(t, 13, len(entries))
// Regression when collectibles is nil
filter.Tokens = Tokens{[]TokenCode{}, nil, []TokenType{}}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
// Native tokens are network agnostic, hence all are returned
filter.Assets = []Token{{TokenType: Native, ChainID: common.ChainID(transfer.EthMainnet.ChainID)}}
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
require.Equal(t, 5, len(entries))
filter.Tokens = Tokens{Assets: []TokenCode{"ETH"}, EnabledTypes: []TokenType{AssetTT}}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
// Test that it doesn't break the filter
filter.Assets = []Token{{TokenType: Erc1155}}
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 0, len(entries))
filter.Assets = []Token{{
TokenType: Erc20,
ChainID: common.ChainID(transfer.UsdcMainnet.ChainID),
Address: transfer.UsdcMainnet.Address,
}}
entries, err = getActivityEntries(context.Background(), deps, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
// Two MT for which ChainID is ignored and one transfer on the main net and the Goerli is ignored
require.Equal(t, 3, len(entries))
require.Equal(t, Erc20, entries[0].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[0].tokenOut.Address)
require.Nil(t, entries[0].tokenIn)
// MT has only symbol, the first token is lookup by symbol for both entries
require.Equal(t, Erc20, entries[1].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
require.Equal(t, Erc20, entries[1].tokenIn.TokenType)
require.Equal(t, transfer.SntMainnet.Address, entries[1].tokenIn.Address)
require.Equal(t, Erc20, entries[2].tokenOut.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
require.Equal(t, Erc20, entries[2].tokenIn.TokenType)
require.Equal(t, transfer.UsdcMainnet.Address, entries[1].tokenOut.Address)
// TODO: update tests after adding token type to transfers
filter.Tokens = Tokens{Assets: []TokenCode{"USDC", "DAI"}, EnabledTypes: []TokenType{AssetTT}}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
filter.Assets = []Token{{
TokenType: Erc20,
ChainID: common.ChainID(transfer.UsdcMainnet.ChainID),
Address: transfer.UsdcMainnet.Address,
}, {
TokenType: Erc20,
ChainID: common.ChainID(transfer.UsdcGoerli.ChainID),
Address: transfer.UsdcGoerli.Address,
}}
entries, err = getActivityEntries(context.Background(), deps, []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(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 1, len(entries))
// Two MT for which ChainID is ignored and two transfers on the main net and Goerli
require.Equal(t, 4, len(entries))
require.Equal(t, Erc20, entries[0].tokenOut.TokenType)
require.Equal(t, transfer.UsdcGoerli.Address, entries[0].tokenOut.Address)
require.Nil(t, entries[0].tokenIn)
}
func TestGetActivityEntriesFilterByToAddresses(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions
td, fromTds, toTds := fillTestData(t, db)
td, fromTds, toTds := fillTestData(t, deps.db)
// Add 6 extractable transactions
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 6)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
var filter Filter
filter.CounterpartyAddresses = allAddressesFilter()
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, []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(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []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.multiTx2.ToAddress, trs[3].To}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []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(context.Background(), db, []eth_common.Address{}, []common.ChainID{}, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []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)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 4 extractable transactions
td, fromTds, toTds := fillTestData(t, db)
td, fromTds, toTds := fillTestData(t, deps.db)
// Add 6 extractable transactions
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, db, td.nextIndex, 6)
trs, fromTrs, toTrs := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 6)
for i := range trs {
transfer.InsertTestTransfer(t, db, &trs[i])
transfer.InsertTestTransfer(t, deps.db, &trs[i])
}
mockTestAccountsWithAddresses(t, db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
mockTestAccountsWithAddresses(t, deps.db, append(append(append(fromTds, toTds...), fromTrs...), toTrs...))
var filter Filter
chainIDs := allNetworksFilter()
entries, err := getActivityEntries(context.Background(), db, []eth_common.Address{}, chainIDs, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, []eth_common.Address{}, chainIDs, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 10, len(entries))
chainIDs = []common.ChainID{5674839210}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, chainIDs, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []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.multiTx2Tr1.ChainID, trs[3].ChainID}
entries, err = getActivityEntries(context.Background(), db, []eth_common.Address{}, chainIDs, filter, 0, 15)
entries, err = getActivityEntries(context.Background(), deps, []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))
require.Equal(t, 8 /*6*/, len(entries))
}
func TestGetActivityEntriesCheckToAndFrom(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
// Adds 6 transactions from which 4 are filered out
td, _, _ := fillTestData(t, db)
td, _, _ := fillTestData(t, deps.db)
// Add extra transactions to test To address
trs, _, _ := transfer.GenerateTestTransfers(t, db, td.nextIndex, 2)
transfer.InsertTestTransfer(t, db, &trs[0])
transfer.InsertTestPendingTransaction(t, db, &trs[1])
trs, _, _ := transfer.GenerateTestTransfers(t, deps.db, td.nextIndex, 2)
transfer.InsertTestTransfer(t, deps.db, &trs[0])
transfer.InsertTestPendingTransaction(t, deps.db, &trs[1])
addresses := []eth_common.Address{td.tr1.From, td.pendingTr.From,
td.multiTx1.FromAddress, td.multiTx2.ToAddress, trs[0].To, trs[1].To}
var filter Filter
entries, err := getActivityEntries(context.Background(), db, addresses, []common.ChainID{}, filter, 0, 15)
entries, err := getActivityEntries(context.Background(), deps, addresses, []common.ChainID{}, filter, 0, 15)
require.NoError(t, err)
require.Equal(t, 6, len(entries))
@ -829,15 +875,15 @@ func TestGetActivityEntriesCheckToAndFrom(t *testing.T) {
// TODO test sub-transaction count for multi-transactions
func TestGetActivityEntriesCheckContextCancellation(t *testing.T) {
db, close := setupTestActivityDB(t)
deps, close := setupTestActivityDB(t)
defer close()
_, _, _ = fillTestData(t, db)
_, _, _ = fillTestData(t, deps.db)
cancellableCtx, cancelFn := context.WithCancel(context.Background())
cancelFn()
activities, err := getActivityEntries(cancellableCtx, db, []eth.Address{}, []common.ChainID{}, Filter{}, 0, 10)
activities, err := getActivityEntries(cancellableCtx, deps, []eth.Address{}, []common.ChainID{}, Filter{}, 0, 10)
require.ErrorIs(t, err, context.Canceled)
require.Equal(t, 0, len(activities))
}

View File

@ -5,6 +5,7 @@ import (
"database/sql"
eth "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/services/wallet/common"
)
@ -45,28 +46,25 @@ func allActivityStatusesFilter() []Status {
type TokenType int
const (
AssetTT TokenType = iota
CollectiblesTT
Native TokenType = iota
Erc20
Erc721
Erc1155
)
type TokenCode string
type TokenID *hexutil.Big
// Tokens the following rules apply for its members:
// empty member: none is selected
// nil means all
// see allTokensFilter and noTokensFilter
type Tokens struct {
Assets []TokenCode `json:"assets"`
Collectibles []eth.Address `json:"collectibles"`
EnabledTypes []TokenType `json:"enabledTypes"`
// Token supports all tokens. Some fields might be optional, depending on the TokenType
type Token struct {
TokenType TokenType `json:"tokenType"`
// ChainID is used for TokenType.Native only to lookup the symbol, all chains will be included in the token filter
ChainID common.ChainID `json:"chainId"`
Address eth.Address `json:"address,omitempty"`
TokenID TokenID `json:"tokenId,omitempty"`
}
func noAssetsFilter() Tokens {
return Tokens{[]TokenCode{}, []eth.Address{}, []TokenType{CollectiblesTT}}
}
func allTokensFilter() Tokens {
return Tokens{}
func allTokensFilter() []Token {
return []Token{}
}
func allAddressesFilter() []eth.Address {
@ -81,8 +79,13 @@ type Filter struct {
Period Period `json:"period"`
Types []Type `json:"types"`
Statuses []Status `json:"statuses"`
Tokens Tokens `json:"tokens"`
CounterpartyAddresses []eth.Address `json:"counterpartyAddresses"`
// Tokens
Assets []Token `json:"assets"`
Collectibles []Token `json:"collectibles"`
FilterOutAssets bool `json:"filterOutAssets"`
FilterOutCollectibles bool `json:"filterOutCollectibles"`
}
// TODO: consider sorting by saved address and contacts to offload the client from doing it at runtime

View File

@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/log"
w_common "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/services/wallet/walletevent"
)
@ -22,19 +23,21 @@ const (
)
type Service struct {
db *sql.DB
eventFeed *event.Feed
db *sql.DB
tokenManager *token.Manager
eventFeed *event.Feed
context context.Context
cancelFn context.CancelFunc
wg sync.WaitGroup
mu sync.Mutex
context context.Context
cancelFn context.CancelFunc
wg sync.WaitGroup
cancelMutex sync.Mutex
}
func NewService(db *sql.DB, eventFeed *event.Feed) *Service {
func NewService(db *sql.DB, tokenManager *token.Manager, eventFeed *event.Feed) *Service {
return &Service{
db: db,
eventFeed: eventFeed,
db: db,
tokenManager: tokenManager,
eventFeed: eventFeed,
}
}
@ -59,12 +62,13 @@ type FilterResponse struct {
// and it cancels the current one if a new one is started
// All calls will trigger an EventActivityFilteringDone event with the result of the filtering
func (s *Service) FilterActivityAsync(ctx context.Context, addresses []common.Address, chainIDs []w_common.ChainID, filter Filter, offset int, limit int) error {
s.mu.Lock()
defer s.mu.Unlock()
s.cancelMutex.Lock()
defer s.cancelMutex.Unlock()
// If a previous task is running, cancel it and wait to finish
if s.cancelFn != nil {
s.cancelFn()
s.cancelFn = nil
s.wg.Wait()
}
@ -73,16 +77,17 @@ func (s *Service) FilterActivityAsync(ctx context.Context, addresses []common.Ad
}
s.context, s.cancelFn = context.WithCancel(context.Background())
thisCancelFn := s.cancelFn
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer func() {
s.cancelFn = nil
}()
activities, err := getActivityEntries(s.context, s.getDeps(), addresses, chainIDs, filter, offset, limit)
// Don't lock s.cancelMutex, it might have been locked already
s.wg.Done()
activities, err := getActivityEntries(s.context, s.db, addresses, chainIDs, filter, offset, limit)
// Release context resources
thisCancelFn()
res := FilterResponse{
ErrorCode: ErrorCodeFilterFailed,
@ -104,8 +109,8 @@ func (s *Service) FilterActivityAsync(ctx context.Context, addresses []common.Ad
}
func (s *Service) Stop() {
s.mu.Lock()
defer s.mu.Unlock()
s.cancelMutex.Lock()
defer s.cancelMutex.Unlock()
// If a previous task is running, cancel it and wait to finish
if s.cancelFn != nil {
@ -115,12 +120,47 @@ func (s *Service) Stop() {
}
}
func (s *Service) getDeps() FilterDependencies {
return FilterDependencies{
db: s.db,
tokenSymbol: func(t Token) string {
info := s.tokenManager.LookupTokenIdentity(uint64(t.ChainID), t.Address, t.TokenType == Native)
if info == nil {
return ""
}
return info.Symbol
},
tokenFromSymbol: func(chainID *w_common.ChainID, symbol string) *Token {
var cID *uint64
if chainID != nil {
cID = new(uint64)
*cID = uint64(*chainID)
}
t, detectedNative := s.tokenManager.LookupToken(cID, symbol)
if t == nil {
return nil
}
tokenType := Native
if !detectedNative {
tokenType = Erc20
}
return &Token{
TokenType: tokenType,
ChainID: w_common.ChainID(t.ChainID),
Address: t.Address,
}
},
}
}
func (s *Service) sendResponseEvent(response FilterResponse) {
payload, err := json.Marshal(response)
if err != nil {
log.Error("Error marshaling response: %v", err)
}
log.Debug("wallet.api.FilterActivityAsync RESPONSE", "activities.len", len(response.Activities), "offset", response.Offset, "hasMore", response.HasMore, "error", response.ErrorCode)
s.eventFeed.Send(walletevent.Event{
Type: EventActivityFilteringDone,
Message: string(payload),

View File

@ -107,7 +107,7 @@ func (api *API) GetTransfersByAddressAndChainID(ctx context.Context, chainID uin
}
func (api *API) GetTransfersForIdentities(ctx context.Context, identities []transfer.TransactionIdentity) ([]transfer.View, error) {
log.Debug("[Wallet: GetTransfersForIdentities] count", len(identities))
log.Debug("wallet.api.GetTransfersForIdentities", "identities.len", len(identities))
return api.s.transferController.GetTransfersForIdentities(ctx, identities)
}
@ -520,7 +520,7 @@ func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionComm
}
func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []transfer.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) {
log.Debug("[WalletAPI:: GetMultiTransactions] for IDs", transactionIDs)
log.Debug("wallet.api.GetMultiTransactions", "IDs.len", len(transactionIDs))
return api.s.transactionManager.GetMultiTransactions(ctx, transactionIDs)
}
@ -535,7 +535,8 @@ func (api *API) FetchAllCurrencyFormats() (currency.FormatPerSymbol, error) {
}
func (api *API) FilterActivityAsync(ctx context.Context, addresses []common.Address, chainIDs []wcommon.ChainID, filter activity.Filter, offset int, limit int) error {
log.Debug("[WalletAPI:: FilterActivityAsync] addr.count", len(addresses), "chainIDs.count", len(chainIDs), "filter", filter, "offset", offset, "limit", limit)
log.Debug("wallet.api.FilterActivityAsync", "addr.count", len(addresses), "chainIDs.count", len(chainIDs), "offset", offset, "limit", limit)
return api.s.activity.FilterActivityAsync(ctx, addresses, chainIDs, filter, offset, limit)
}
@ -545,6 +546,7 @@ type GetAllRecipientsResponse struct {
}
func (api *API) GetAllRecipients(ctx context.Context, offset int, limit int) (result *GetAllRecipientsResponse, err error) {
log.Debug("wallet.api.GetAllRecipients", "offset", offset, "limit", limit)
result = &GetAllRecipientsResponse{}
result.Addresses, result.HasMore, err = activity.GetRecipients(ctx, api.s.db, offset, limit)
return result, err

View File

@ -95,7 +95,7 @@ func NewService(
reader := NewReader(rpcClient, tokenManager, marketManager, accountsDB, NewPersistence(db), walletFeed)
history := history.NewService(db, walletFeed, rpcClient, tokenManager, marketManager)
currency := currency.NewService(db, walletFeed, tokenManager, marketManager)
activity := activity.NewService(db, walletFeed)
activity := activity.NewService(db, tokenManager, walletFeed)
alchemyClient := alchemy.NewClient(config.WalletConfig.AlchemyAPIKeys)
infuraClient := infura.NewClient(config.WalletConfig.InfuraAPIKey, config.WalletConfig.InfuraAPIKeySecret)

View File

@ -6,6 +6,14 @@ const EthSymbol = "ETH"
const SntSymbol = "SNT"
const DaiSymbol = "DAI"
func SliceContains[T comparable](slice []T, value T) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
}
func StructExistsInSlice[T any](target T, slice []T) bool {
for _, item := range slice {
if reflect.DeepEqual(target, item) {

View File

@ -163,7 +163,38 @@ func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token
return tm.ToToken(network)
}
allTokens := tm.getFullTokenList(network.ChainID)
return tm.GetToken(network.ChainID, tokenSymbol)
}
func (tm *Manager) LookupToken(chainID *uint64, tokenSymbol string) (token *Token, isNative bool) {
if chainID == nil {
networks, err := tm.networkManager.Get(true)
if err != nil {
return nil, false
}
for _, network := range networks {
if tokenSymbol == network.NativeCurrencySymbol {
return tm.ToToken(network), true
}
token := tm.GetToken(network.ChainID, tokenSymbol)
if token != nil {
return token, false
}
}
} else {
network := tm.networkManager.Find(*chainID)
if tokenSymbol == network.NativeCurrencySymbol {
return tm.ToToken(network), true
}
return tm.GetToken(*chainID, tokenSymbol), false
}
return nil, false
}
// GetToken returns token by chainID and tokenSymbol. Use ToToken for native token
func (tm *Manager) GetToken(chainID uint64, tokenSymbol string) *Token {
allTokens := tm.getFullTokenList(chainID)
for _, token := range allTokens {
if token.Symbol == tokenSymbol {
return token
@ -172,6 +203,15 @@ func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token
return nil
}
func (tm *Manager) LookupTokenIdentity(chainID uint64, address common.Address, native bool) *Token {
network := tm.networkManager.Find(chainID)
if native {
return tm.ToToken(network)
}
return tm.FindTokenByAddress(chainID, address)
}
func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *Token {
allTokens := tm.getFullTokenList(chainID)
for _, token := range allTokens {

View File

@ -280,7 +280,6 @@ func (db *Database) GetTransfersForIdentities(ctx context.Context, identities []
query := newTransfersQuery()
for _, identity := range identities {
subQuery := newSubQuery()
// TODO optimization: consider using tuples in sqlite and IN operator
subQuery = subQuery.FilterNetwork(uint64(identity.ChainID)).FilterTransactionHash(identity.Hash).FilterAddress(identity.Address)
query.addSubQuery(subQuery, OrSeparator)
}
@ -450,12 +449,17 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
var txGasPrice, txGasTipCap, txGasFeeCap *int64
var txType *uint8
var txValue *string
var tokenAddress *common.Address
var dbAddress *string
var tokenID *big.Int
if t.Transaction != nil {
var value *big.Int
if t.Log != nil {
var tokenAddress *common.Address
_, tokenAddress, tokenID, value = w_common.ExtractTokenIdentity(t.Type, t.Log, t.Transaction)
if tokenAddress != nil {
dbAddress = new(string)
*dbAddress = tokenAddress.Hex()
}
} else {
value = new(big.Int).Set(t.Transaction.Value())
}
@ -478,7 +482,7 @@ func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers
_, err = insert.Exec(chainID, t.ID, t.BlockHash, (*bigint.SQLBigInt)(t.BlockNumber), t.Timestamp, t.Address, &JSONBlob{t.Transaction}, t.From, &JSONBlob{t.Receipt}, &JSONBlob{t.Log}, t.Type, t.BaseGasFees, t.MultiTransactionID,
receiptStatus, receiptType, txHash, logIndex, blockHash, cumulativeGasUsed, contractAddress, gasUsed, transactionIndex,
txType, txProtected, txGas, txGasPrice, txGasTipCap, txGasFeeCap, txValue, txNonce, txSize, &sqlite.JSONBlob{Data: tokenAddress}, (*bigint.SQLBigIntBytes)(tokenID))
txType, txProtected, txGas, txGasPrice, txGasTipCap, txGasFeeCap, txValue, txNonce, txSize, dbAddress, (*bigint.SQLBigIntBytes)(tokenID))
if err != nil {
log.Error("can't save transfer", "b-hash", t.BlockHash, "b-n", t.BlockNumber, "a", t.Address, "h", t.ID)
return err

View File

@ -4,13 +4,13 @@ import (
"database/sql"
"fmt"
"math/big"
"strings"
"testing"
eth_common "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"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/token"
"github.com/status-im/status-go/sqlite"
"github.com/stretchr/testify/require"
@ -29,8 +29,8 @@ type TestTransaction struct {
type TestTransfer struct {
TestTransaction
To eth_common.Address // [address]
Token string // used to detect type in transfers table
Value int64
Token *token.Token
}
type TestMultiTransaction struct {
@ -45,10 +45,26 @@ type TestMultiTransaction struct {
Timestamp int64
}
func SeedToToken(seed int) *token.Token {
tokenIndex := seed % len(TestTokens)
return TestTokens[tokenIndex]
}
func TestTrToToken(t *testing.T, tt *TestTransaction) (token *token.Token, isNative bool) {
// Sanity check that none of the markers changed and they should be equal to seed
require.Equal(t, tt.Timestamp, tt.BlkNumber)
tokenIndex := int(tt.Timestamp) % len(TestTokens)
isNative = testutils.SliceContains(NativeTokenIndices, tokenIndex)
return TestTokens[tokenIndex], isNative
}
func generateTestTransaction(seed int) TestTransaction {
token := SeedToToken(seed)
return TestTransaction{
Hash: eth_common.HexToHash(fmt.Sprintf("0x1%d", seed)),
ChainID: common.ChainID(seed),
ChainID: common.ChainID(token.ChainID),
From: eth_common.HexToAddress(fmt.Sprintf("0x2%d", seed)),
Timestamp: int64(seed),
BlkNumber: int64(seed),
@ -58,11 +74,13 @@ func generateTestTransaction(seed int) TestTransaction {
}
func generateTestTransfer(seed int) TestTransfer {
tokenIndex := seed % len(TestTokens)
token := TestTokens[tokenIndex]
return TestTransfer{
TestTransaction: generateTestTransaction(seed),
Token: "",
To: eth_common.HexToAddress(fmt.Sprintf("0x3%d", seed)),
Value: int64(seed),
Token: token,
}
}
@ -71,8 +89,8 @@ func GenerateTestSendMultiTransaction(tr TestTransfer) TestMultiTransaction {
MultiTransactionType: MultiTransactionSend,
FromAddress: tr.From,
ToAddress: tr.To,
FromToken: tr.Token,
ToToken: tr.Token,
FromToken: tr.Token.Symbol,
ToToken: tr.Token.Symbol,
FromAmount: tr.Value,
ToAmount: 0,
Timestamp: tr.Timestamp,
@ -84,7 +102,7 @@ func GenerateTestSwapMultiTransaction(tr TestTransfer, toToken string, toAmount
MultiTransactionType: MultiTransactionSwap,
FromAddress: tr.From,
ToAddress: tr.To,
FromToken: tr.Token,
FromToken: tr.Token.Symbol,
ToToken: toToken,
FromAmount: tr.Value,
ToAmount: toAmount,
@ -97,14 +115,16 @@ func GenerateTestBridgeMultiTransaction(fromTr, toTr TestTransfer) TestMultiTran
MultiTransactionType: MultiTransactionBridge,
FromAddress: fromTr.From,
ToAddress: toTr.To,
FromToken: fromTr.Token,
ToToken: toTr.Token,
FromToken: fromTr.Token.Symbol,
ToToken: toTr.Token.Symbol,
FromAmount: fromTr.Value,
ToAmount: toTr.Value,
Timestamp: fromTr.Timestamp,
}
}
// GenerateTestTransfers will generate transaction based on the TestTokens index and roll over if there are more than
// len(TestTokens) transactions
func GenerateTestTransfers(t *testing.T, db *sql.DB, firstStartIndex int, count int) (result []TestTransfer, fromAddresses, toAddresses []eth_common.Address) {
for i := firstStartIndex; i < (firstStartIndex + count); i++ {
tr := generateTestTransfer(i)
@ -115,12 +135,88 @@ func GenerateTestTransfers(t *testing.T, db *sql.DB, firstStartIndex int, count
return
}
var EthMainnet = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 1,
}
var EthGoerli = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 5,
}
var EthOptimism = token.Token{
Address: eth_common.HexToAddress("0x"),
Name: "Ether",
Symbol: "ETH",
ChainID: 10,
}
var UsdcMainnet = token.Token{
Address: eth_common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 1,
}
var UsdcGoerli = token.Token{
Address: eth_common.HexToAddress("0x98339d8c260052b7ad81c28c16c0b98420f2b46a"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 5,
}
var UsdcOptimism = token.Token{
Address: eth_common.HexToAddress("0x7f5c764cbc14f9669b88837ca1490cca17c31607"),
Name: "USD Coin",
Symbol: "USDC",
ChainID: 10,
}
var SntMainnet = token.Token{
Address: eth_common.HexToAddress("0x744d70fdbe2ba4cf95131626614a1763df805b9e"),
Name: "Status Network Token",
Symbol: "SNT",
ChainID: 1,
}
var DaiMainnet = token.Token{
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
Name: "DAI Stablecoin",
Symbol: "DAI",
ChainID: 5,
}
var DaiGoerli = token.Token{
Address: eth_common.HexToAddress("0xf2edF1c091f683E3fb452497d9a98A49cBA84666"),
Name: "DAI Stablecoin",
Symbol: "DAI",
ChainID: 5,
}
// TestTokens contains ETH/Mainnet, ETH/Goerli, ETH/Optimism, USDC/Mainnet, USDC/Goerli, USDC/Optimism, SNT/Mainnet, DAI/Mainnet, DAI/Goerli
var TestTokens = []*token.Token{
&EthMainnet, &EthGoerli, &EthOptimism, &UsdcMainnet, &UsdcGoerli, &UsdcOptimism, &SntMainnet, &DaiMainnet, &DaiGoerli,
}
var NativeTokenIndices = []int{0, 1, 2}
func InsertTestTransfer(t *testing.T, db *sql.DB, tr *TestTransfer) {
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
token := TestTokens[int(tr.Timestamp)%len(TestTokens)]
InsertTestTransferWithToken(t, db, tr, token.Address)
}
func InsertTestTransferWithToken(t *testing.T, db *sql.DB, tr *TestTransfer, tokenAddress eth_common.Address) {
tokenType := "eth"
if tr.Token != "" && strings.ToUpper(tr.Token) != testutils.EthSymbol {
if (tokenAddress != eth_common.Address{}) {
tokenType = "erc20"
}
// Respect `FOREIGN KEY(network_id,address,blk_hash)` of `transfers` table
blkHash := eth_common.HexToHash("4")
value := sqlite.Int64ToPadded128BitsStr(tr.Value)
@ -130,10 +226,10 @@ func InsertTestTransfer(t *testing.T, db *sql.DB, tr *TestTransfer) {
) VALUES (?, ?, ?, ?);
INSERT INTO transfers (network_id, hash, address, blk_hash, tx,
sender, receipt, log, type, blk_number, timestamp, loaded,
multi_transaction_id, base_gas_fee, status, amount_padded128hex
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, 0, ?, ?)`,
multi_transaction_id, base_gas_fee, status, amount_padded128hex, token_address
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, 0, ?, ?, ?)`,
tr.ChainID, tr.To, tr.BlkNumber, blkHash,
tr.ChainID, tr.Hash, tr.To, blkHash, &JSONBlob{}, tr.From, &JSONBlob{}, &JSONBlob{}, tokenType, tr.BlkNumber, tr.Timestamp, tr.MultiTransactionID, tr.Success, value)
tr.ChainID, tr.Hash, tr.To, blkHash, &JSONBlob{}, tr.From, &JSONBlob{}, &JSONBlob{}, tokenType, tr.BlkNumber, tr.Timestamp, tr.MultiTransactionID, tr.Success, value, tokenAddress.Hex())
require.NoError(t, err)
}