diff --git a/appdatabase/database.go b/appdatabase/database.go index f1b332579..b39ad63e9 100644 --- a/appdatabase/database.go +++ b/appdatabase/database.go @@ -4,17 +4,27 @@ import ( "database/sql" "encoding/json" "errors" + "math/big" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/appdatabase/migrations" migrationsprevnodecfg "github.com/status-im/status-go/appdatabase/migrationsprevnodecfg" "github.com/status-im/status-go/nodecfg" + "github.com/status-im/status-go/services/wallet/bigint" + w_common "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/sqlite" ) const nodeCfgMigrationDate = 1640111208 +var customSteps = []sqlite.PostStep{ + {Version: 1674136690, CustomMigration: migrateEnsUsernames}, + {Version: 1686048341, CustomMigration: migrateWalletJSONBlobs, RollBackVersion: 1686041510}, +} + // InitializeDB creates db file at a given path and applies migrations. func InitializeDB(path, password string, kdfIterationsNumber int) (*sql.DB, error) { db, err := sqlite.OpenDB(path, password, kdfIterationsNumber) @@ -22,23 +32,11 @@ func InitializeDB(path, password string, kdfIterationsNumber int) (*sql.DB, erro return nil, err } - // Check if the migration table exists - row := db.QueryRow("SELECT exists(SELECT name FROM sqlite_master WHERE type='table' AND name='status_go_schema_migrations')") - migrationTableExists := false - err = row.Scan(&migrationTableExists) - if err != nil && err != sql.ErrNoRows { + lastMigration, migrationTableExists, err := sqlite.GetLastMigrationVersion(db) + if err != nil { return nil, err } - var lastMigration uint64 = 0 - if migrationTableExists { - row = db.QueryRow("SELECT version FROM status_go_schema_migrations") - err = row.Scan(&lastMigration) - if err != nil && err != sql.ErrNoRows { - return nil, err - } - } - if !migrationTableExists || (lastMigration > 0 && lastMigration < nodeCfgMigrationDate) { // If it's the first time migration's being run, or latest migration happened before migrating the nodecfg table err = migrationsprevnodecfg.Migrate(db) @@ -53,13 +51,8 @@ func InitializeDB(path, password string, kdfIterationsNumber int) (*sql.DB, erro } } - err = migrations.Migrate(db) - if err != nil { - return nil, err - } - - // Migrate `settings.usernames` here, because current SQL implementation doesn't support `json_each` - err = MigrateEnsUsernames(db) + // Run all the new migrations + err = migrations.Migrate(db, customSteps) if err != nil { return nil, err } @@ -116,11 +109,11 @@ func GetDBFilename(db *sql.DB) (string, error) { return "", errors.New("no main database found") } -func MigrateEnsUsernames(db *sql.DB) error { +func migrateEnsUsernames(sqlTx *sql.Tx) error { // 1. Check if ens_usernames table already exist - // row := db.QueryRow("SELECT exists(SELECT name FROM sqlite_master WHERE type='table' AND name='ens_usernames')") + // row := sqlTx.QueryRow("SELECT exists(SELECT name FROM sqlite_master WHERE type='table' AND name='ens_usernames')") // tableExists := false // err := row.Scan(&tableExists) @@ -134,7 +127,7 @@ func MigrateEnsUsernames(db *sql.DB) error { // -- 1. Create new ens_usernames table - // _, err = db.Exec(`CREATE TABLE IF NOT EXISTS ens_usernames ( + // _, err = sqlTx.Exec(`CREATE TABLE IF NOT EXISTS ens_usernames ( // "username" TEXT NOT NULL, // "chain_id" UNSIGNED BIGINT DEFAULT 1);`) @@ -149,7 +142,7 @@ func MigrateEnsUsernames(db *sql.DB) error { SELECT json_each.value FROM settings, json_each(usernames); */ - rows, err := db.Query(`SELECT usernames FROM settings`) + rows, err := sqlTx.Query(`SELECT usernames FROM settings`) if err != nil { log.Error("Migrating ens usernames: failed to query 'settings.usernames'", "err", err.Error()) @@ -187,7 +180,7 @@ func MigrateEnsUsernames(db *sql.DB) error { var usernameAlreadyMigrated bool - row := db.QueryRow(`SELECT EXISTS(SELECT 1 FROM ens_usernames WHERE username=? AND chain_id=?)`, username, defaultChainID) + row := sqlTx.QueryRow(`SELECT EXISTS(SELECT 1 FROM ens_usernames WHERE username=? AND chain_id=?)`, username, defaultChainID) err := row.Scan(&usernameAlreadyMigrated) if err != nil { @@ -198,7 +191,7 @@ func MigrateEnsUsernames(db *sql.DB) error { continue } - _, err = db.Exec(`INSERT INTO ens_usernames (username, chain_id) VALUES (?, ?)`, username, defaultChainID) + _, err = sqlTx.Exec(`INSERT INTO ens_usernames (username, chain_id) VALUES (?, ?)`, username, defaultChainID) if err != nil { log.Error("Migrating ens usernames: failed to insert username into new database", "ensUsername", username, "err", err.Error()) } @@ -206,3 +199,173 @@ func MigrateEnsUsernames(db *sql.DB) error { return nil } + +const ( + batchSize = 1000 +) + +func migrateWalletJSONBlobs(sqlTx *sql.Tx) error { + var batchEntries [][]interface{} + + // Extract useful information from the receipt blob and store it as sql interpretable + // + // Added tx_hash because the hash column in the transfers table is not (always) the transaction hash. + // Each entry in that table could either be: A native token (ETH) transfer or ERC20/ERC721 token transfer + // Added block_hash because the block_hash we have is generated by us and used as block entry ID + // Added receipt_type, the type we have only indicates if chain or token + // Added log_index that the log data represents + // + // Dropped storing postState because it was replaced by the status after EIP 658 + // Dropped duplicating logs until we have a more structured way to store them. + // They can be extracted from the transfers.receipt still + // Dropped the bloom filter because in SQLite is not possible to use it in an + // efficient manner + // + // Extract useful information from the tx blob + // + // Added tx_type, which might be different than the receipt type + // + // Dropped access_list, need a separate table for it + // Already there chain_id + // Dropped v, r, s because I see no way to be useful as BLOBs + // Added BIGINT values as clamped 64 INT because we can't use 128 bits blobs/strings for int arithmetics + // _clamped64 prefix indicate clamped 64 bits INT values might be useful for queries (sorting, filtering ...) + // The amount is stored as a fixed length 128 bit hex string, in + // order to be able to sort and filter by it + newColumnsAndIndexSetup := ` + ALTER TABLE transfers ADD COLUMN status INT; + ALTER TABLE transfers ADD COLUMN receipt_type INT; + ALTER TABLE transfers ADD COLUMN tx_hash BLOB; + ALTER TABLE transfers ADD COLUMN log_index INT; + ALTER TABLE transfers ADD COLUMN block_hash BLOB; + ALTER TABLE transfers ADD COLUMN cumulative_gas_used INT; + ALTER TABLE transfers ADD COLUMN contract_address TEXT; + ALTER TABLE transfers ADD COLUMN gas_used INT; + ALTER TABLE transfers ADD COLUMN tx_index INT; + + ALTER TABLE transfers ADD COLUMN tx_type INT; + ALTER TABLE transfers ADD COLUMN protected BOOLEAN; + ALTER TABLE transfers ADD COLUMN gas_limit UNSIGNED INT; + ALTER TABLE transfers ADD COLUMN gas_price_clamped64 INT; + ALTER TABLE transfers ADD COLUMN gas_tip_cap_clamped64 INT; + ALTER TABLE transfers ADD COLUMN gas_fee_cap_clamped64 INT; + ALTER TABLE transfers ADD COLUMN amount_padded128hex CHAR(32); + ALTER TABLE transfers ADD COLUMN account_nonce INT; + ALTER TABLE transfers ADD COLUMN size INT; + ALTER TABLE transfers ADD COLUMN token_address BLOB; + ALTER TABLE transfers ADD COLUMN token_id BLOB; + + CREATE INDEX idx_transfers_filter ON transfers (status, token_address, token_id);` + + rowIndex := 0 + mightHaveRows := true + + _, err := sqlTx.Exec(newColumnsAndIndexSetup) + if err != nil { + return err + } + + for mightHaveRows { + var chainID uint64 + var hash common.Hash + var address common.Address + var entryType string + + rows, err := sqlTx.Query(`SELECT hash, address, network_id, tx, receipt, log, type FROM transfers WHERE tx IS NOT NULL OR receipt IS NOT NULL LIMIT ? OFFSET ?`, batchSize, rowIndex) + if err != nil { + return err + } + + curProcessed := 0 + for rows.Next() { + tx := &types.Transaction{} + r := &types.Receipt{} + l := &types.Log{} + + // Scan row data into the transaction and receipt objects + nullableTx := sqlite.JSONBlob{Data: tx} + nullableR := sqlite.JSONBlob{Data: r} + nullableL := sqlite.JSONBlob{Data: l} + err = rows.Scan(&hash, &address, &chainID, &nullableTx, &nullableR, &nullableL, &entryType) + if err != nil { + rows.Close() + return err + } + var logIndex *uint + if nullableL.Valid { + logIndex = new(uint) + *logIndex = l.Index + } + + var currentRow []interface{} + + // Check if the receipt is not null before transferring the receipt data + if nullableR.Valid { + currentRow = append(currentRow, r.Status, r.Type, r.TxHash, logIndex, r.BlockHash, r.CumulativeGasUsed, r.ContractAddress, r.GasUsed, r.TransactionIndex) + } else { + for i := 0; i < 9; i++ { + currentRow = append(currentRow, nil) + } + } + + 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()) + } + + 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) + } else { + for i := 0; i < 11; i++ { + currentRow = append(currentRow, nil) + } + currentRow = append(currentRow, w_common.EthTransfer) + } + currentRow = append(currentRow, hash, address, chainID) + batchEntries = append(batchEntries, currentRow) + + curProcessed++ + } + rowIndex += curProcessed + + // Check if there was an error in the last rows.Next() + rows.Close() + if err = rows.Err(); err != nil { + return err + } + mightHaveRows = (curProcessed == batchSize) + + // insert extracted data into the new columns + if len(batchEntries) > 0 { + var stmt *sql.Stmt + stmt, err = sqlTx.Prepare(`UPDATE transfers SET 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 = ? + WHERE hash = ? AND address = ? AND network_id = ?`) + if err != nil { + return err + } + + for _, dataEntry := range batchEntries { + _, err = stmt.Exec(dataEntry...) + if err != nil { + return err + } + } + + // Reset placeHolders and batchEntries for the next batch + batchEntries = [][]interface{}{} + } + } + + return nil +} diff --git a/appdatabase/database_test.go b/appdatabase/database_test.go index 8ca45e80b..a435e1d4e 100644 --- a/appdatabase/database_test.go +++ b/appdatabase/database_test.go @@ -1,10 +1,24 @@ package appdatabase import ( + "database/sql" + "encoding/json" + "errors" + "fmt" + "math/big" + "strconv" "testing" "github.com/stretchr/testify/require" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/status-im/status-go/appdatabase/migrations" + migrationsprevnodecfg "github.com/status-im/status-go/appdatabase/migrationsprevnodecfg" + "github.com/status-im/status-go/nodecfg" + "github.com/status-im/status-go/services/wallet/bigint" + w_common "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/sqlite" ) @@ -31,3 +45,420 @@ func Test_GetDBFilename(t *testing.T) { require.NoError(t, err) require.Equal(t, "", fn) } + +const ( + erc20ReceiptTestDataTemplate = `{"type":"0x2","root":"0x","status":"0x%d","cumulativeGasUsed":"0x10f8d2c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000001008000000000000000000000000000000000000002000000000020000000000000000000800000000000000000000000010000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000800000000000000000000","logs":[{"address":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a"],"data":"0x0000000000000000000000000000000000000000000000000000000000989680","blockNumber":"0x825527","transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","transactionIndex":"0x6c","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","logIndex":"0xcd","removed":false}],"transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","contractAddress":"0x0000000000000000000000000000000000000000","gasUsed":"0x8623","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","blockNumber":"0x825527","transactionIndex":"0x6c"}` + erc20TxTestData = `{"type":"0x2","nonce":"0x3d","gasPrice":"0x0","maxPriorityFeePerGas":"0x8c347c90","maxFeePerGas":"0x45964d43a4","gas":"0x8623","value":"0x0","input":"0x40c10f19000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a0000000000000000000000000000000000000000000000000000000000989680","v":"0x0","r":"0xbcac4bb290d48b467bb18ac67e98050b5f316d2c66b2f75dcc1d63a45c905d21","s":"0x10c15517ea9cabd7fe134b270daabf5d2e8335e935d3e021f54a4efaffb37cd2","to":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","chainId":"0x5","accessList":[],"hash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a"}` + + erc20LogTestData = `{"address":"0x98339d8c260052b7ad81c28c16c0b98420f2b46a","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000e2d622c817878da5143bbe06866ca8e35273ba8a"],"data":"0x0000000000000000000000000000000000000000000000000000000000989680","blockNumber":"0x825527","transactionHash":"0xdcaa0fc7fe2e0d1f1343d1f36807344bb4fd26cda62ad8f9d8700e2c458cc79a","transactionIndex":"0x6c","blockHash":"0x69e0f829a557052c134cd7e21c220507d91bc35c316d3c47217e9bd362270274","logIndex":"0xcd","removed":false}` + ethReceiptTestData = `{ + "type": "0x2", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x2b461", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": [], + "transactionHash": "0x4ac700ee2a1702f82b3cfdc88fd4d91f767b87fea9b929bd6223c6471a5e05b4", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x5208", + "blockHash": "0x25fe164361c1cb4ed1b46996f7b5236d3118144529b31fca037fcda1d8ee684d", + "blockNumber": "0x5e3294", + "transactionIndex": "0x3" + }` + ethTxTestData = `{ + "type": "0x2", + "nonce": "0x1", + "gasPrice": "0x0", + "maxPriorityFeePerGas": "0x33", + "maxFeePerGas": "0x3b9aca00", + "gas": "0x55f0", + "value": "0x%s", + "input": "0x", + "v": "0x0", + "r": "0xacc277ce156382d6f333cc8d75a56250778b17f1c6d1676af63cf68d53713986", + "s": "0x32417261484e9796390abb8db13f993965d917836be5cd96df25b9b581de91ec", + "to": "0xbd54a96c0ae19a220c8e1234f54c940dfab34639", + "chainId": "0x1a4", + "accessList": [], + "hash": "0x4ac700ee2a1702f82b3cfdc88fd4d91f767b87fea9b929bd6223c6471a5e05b4" + }` + + erc721TxTestData = `{"type":"0x2","nonce":"0x2f","gasPrice":"0x0","maxPriorityFeePerGas":"0x3b9aca00","maxFeePerGas":"0x2f691e609","gas":"0x1abc3","value":"0x0","input":"0x42842e0e000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c630000000000000000000000001c9751e0fbf5081849b56b522d50fb7f163b8080000000000000000000000000000000000000000000000000000000003ba7b95e360c6ebe","v":"0x1","r":"0xead469c32ffda3aa933f9aed814df411fb07893153c775b50596660036bbb5da","s":"0x73edadd4e4a7f0895f686b68e16101d195c0bb1b5f248f16b21557800b95bdf8","to":"0x85f0e02cb992aa1f9f47112f815f519ef1a59e2d","chainId":"0x1","accessList":[],"hash":"0x1dd936499e35ece8747bc481e476ac43eb4555a3a82e8cb93b7e429219bdd371"}` + erc721ReceiptTestData = `{"type":"0x2","root":"0x","status":"0x1","cumulativeGasUsed":"0x54cadb","logsBloom":"0x00000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200200000000000000000000000000008000000000000080000000000000000000000002000200000020000000200000000000800000000000000000000000010000000000000000000000040000000000000000000000000000000000000000000000000020000000000000000000040010000000000000000000000000000000800000000000002020000000000000000000000000000000000000000000000000020000010000000000000000000004000000000000000000000000000000000000000","logs":[{"address":"0x85f0e02cb992aa1f9f47112f815f519ef1a59e2d","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000000000000000000000000000003ba7b95e"],"data":"0x","blockNumber":"0xf57974","transactionHash":"0x1dd936499e35ece8747bc481e476ac43eb4555a3a82e8cb93b7e429219bdd371","transactionIndex":"0x44","blockHash":"0x9228724ff5c19f9b1586e19b13102f94798d1ee32b5f14d5cbcdf74cc32eb732","logIndex":"0x86","removed":false},{"address":"0x85f0e02cb992aa1f9f47112f815f519ef1a59e2d","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63","0x0000000000000000000000001c9751e0fbf5081849b56b522d50fb7f163b8080","0x000000000000000000000000000000000000000000000000000000003ba7b95e"],"data":"0x","blockNumber":"0xf57974","transactionHash":"0x1dd936499e35ece8747bc481e476ac43eb4555a3a82e8cb93b7e429219bdd371","transactionIndex":"0x44","blockHash":"0x9228724ff5c19f9b1586e19b13102f94798d1ee32b5f14d5cbcdf74cc32eb732","logIndex":"0x87","removed":false}],"transactionHash":"0x1dd936499e35ece8747bc481e476ac43eb4555a3a82e8cb93b7e429219bdd371","contractAddress":"0x0000000000000000000000000000000000000000","gasUsed":"0x18643","blockHash":"0x9228724ff5c19f9b1586e19b13102f94798d1ee32b5f14d5cbcdf74cc32eb732","blockNumber":"0xf57974","transactionIndex":"0x44"}` + erc721LogTestData = `{"address":"0x85f0e02cb992aa1f9f47112f815f519ef1a59e2d","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63","0x0000000000000000000000001c9751e0fbf5081849b56b522d50fb7f163b8080","0x000000000000000000000000000000000000000000000000000000003ba7b95e"],"data":"0x","blockNumber":"0xf57974","transactionHash":"0x1dd936499e35ece8747bc481e476ac43eb4555a3a82e8cb93b7e429219bdd371","transactionIndex":"0x44","blockHash":"0x9228724ff5c19f9b1586e19b13102f94798d1ee32b5f14d5cbcdf74cc32eb732","logIndex":"0x87","removed":false}` + + uniswapV2TxTestData = `{"type":"0x2","nonce":"0x42","gasPrice":"0x0","maxPriorityFeePerGas":"0x3b9aca00","maxFeePerGas":"0x13c6f691f2","gas":"0x2ed0d","value":"0xa688906bd8b0000","input":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006440875700000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000a688906bd8b0000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000a688906bd8b000000000000000000000000000000000000000000001188be846e642b0ae4ae055e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933","v":"0x1","r":"0xeb7b527c2bfd3d26ea8e21951f537f4603867a11532081ba77fde9465696c20a","s":"0x5c120e64973a3b83a80d8b045a2228b9d1421065c1d480d2c1e322dad3b76c0f","to":"0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","chainId":"0x1","accessList":[],"hash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa"}` + uniswapV2ReceiptTestData = `{"type":"0x2","root":"0x","status":"0x1","cumulativeGasUsed":"0x1ba15e","logsBloom":"0x00200000000000000000000080400000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000200000000000000080000000000000008000000200000000000000000000000008000000000000000000000000000000000000000000000000000000000000010000000000000000000008000000000000000040000000001000000080000004200000000000800000000000000000000008000000000000000000000000000000800000001000012000000000000000000000000400000000000001000000000000000000000200001000000020000000000000000000000000000400000000080000000","logs":[{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x0000000000000000000000000000000000000000000000000a688906bd8b0000","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2b","removed":false},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x0000000000000000000000000000000000000000000000000a688906bd8b0000","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2c","removed":false},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x000000000000000000000000a43fe16908251ee70ef74718545e4fe6c5ccec9f"],"data":"0x0000000000000000000000000000000000000000000000000a688906bd8b0000","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2d","removed":false},{"address":"0x6982508145454ce325ddbe47a25d4ec3d2311933","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a43fe16908251ee70ef74718545e4fe6c5ccec9f","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63"],"data":"0x000000000000000000000000000000000000000011b2e784030a3a65a3559087","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2e","removed":false},{"address":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","topics":["0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"],"data":"0x000000000000000000000000000000000000003bdd991fe0c766723fa956e323000000000000000000000000000000000000000000000023240d303bb8bbb575","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2f","removed":false},{"address":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","topics":["0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63"],"data":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a688906bd8b0000000000000000000000000000000000000000000011b2e784030a3a65a35590870000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x30","removed":false}],"transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","contractAddress":"0x0000000000000000000000000000000000000000","gasUsed":"0x1ec85","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","blockNumber":"0x104ae90","transactionIndex":"0x4"}` + uniswapV2LogTestData = `{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x0000000000000000000000000000000000000000000000000a688906bd8b0000","blockNumber":"0x104ae90","transactionHash":"0x6d70a0b14e2fe1ba28d6cb910ffc4aa787264dff6c273e20509136461ac587aa","transactionIndex":"0x4","blockHash":"0x49e3ef5a17eb5563b327fffdf315dd9269c5a5676eec1f5c15897c4ef61623df","logIndex":"0x2b","removed":false}` + + uniswapV3TxTestData = `{"type":"0x2","nonce":"0x41","gasPrice":"0x0","maxPriorityFeePerGas":"0x3b9aca00","maxFeePerGas":"0x92abb2610","gas":"0x34389","value":"0x1f161421c8e0000","input":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000643e278300000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000001f161421c8e00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000001f161421c8e000000000000000000000000000000000000000000000002488dd50cfbb0a2a15abb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20027105026f006b85729a8b14553fae6af249ad16c9aab000000000000000000000000000000000000000000","v":"0x1","r":"0x4fca68a439e7f841bdbe6d108bebd3d4c405f739cae203e61422152c4a0a057c","s":"0x597bd6d3848d31357207df1f92df77580310b7ad31f178575f0dae7f36934b39","to":"0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","chainId":"0x1","accessList":[],"hash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b"}` + uniswapV3ReceiptTestData = `{"type":"0x2","root":"0x","status":"0x1","cumulativeGasUsed":"0x6c1b8a","logsBloom":"0x00000000000000000000000000400000000000000000000200000000000000000000000000000000000000000000000002000000080020000000000200000000000004000000000800000028000000000000000000000000240000008000000000000000000000800000000000000000000000000000000000000010000800000000000000000000000000080000000000000001000000000000000000000000000800000000000000000000000000000000000000000000000000000800002000000002000000000000000000000000400000000000000000000000000000000000200000000000000000000000000000000400000000400000000080000000","logs":[{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x00000000000000000000000000000000000000000000000001f161421c8e0000","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xd8","removed":false},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x00000000000000000000000000000000000000000000000001f161421c8e0000","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xd9","removed":false},{"address":"0x5026f006b85729a8b14553fae6af249ad16c9aab","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007316f8dd242974f0fd7b16dbcc68920b96bc4db1","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63"],"data":"0x000000000000000000000000000000000000000000024cc783fc216d1e77f90d","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xda","removed":false},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x0000000000000000000000007316f8dd242974f0fd7b16dbcc68920b96bc4db1"],"data":"0x00000000000000000000000000000000000000000000000001f161421c8e0000","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xdb","removed":false},{"address":"0x7316f8dd242974f0fd7b16dbcc68920b96bc4db1","topics":["0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b","0x000000000000000000000000165eeecc32dcb623f51fc6c1ddd9e2aea1575c63"],"data":"0xfffffffffffffffffffffffffffffffffffffffffffdb3387c03de92e18806f300000000000000000000000000000000000000000000000001f161421c8e00000000000000000000000000000000000000000000000ea9ed3658a1ccb7e6d1cc000000000000000000000000000000000000000000001e5ab304463cab4cd155fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd6f54","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xdc","removed":false}],"transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","contractAddress":"0x0000000000000000000000000000000000000000","gasUsed":"0x22478","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","blockNumber":"0x1047cc4","transactionIndex":"0x4a"}` + uniswapV3LogTestData = `{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c","0x000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b"],"data":"0x00000000000000000000000000000000000000000000000001f161421c8e0000","blockNumber":"0x1047cc4","transactionHash":"0x5c5bca1291d1f09c07a9b66e56e78cc23da41b3e69e330dcd46a71ef6176df8b","transactionIndex":"0x4a","blockHash":"0x95c685d5165471e878aea2aaaa719bf4357cdbcd22722df4338e3e54f4e6c5d5","logIndex":"0xd8","removed":false}` +) + +func TestMigrateWalletJsonBlobs(t *testing.T) { + openDB := func() (*sql.DB, error) { + return sqlite.OpenDB(sqlite.InMemoryPath, "1234567890", sqlite.ReducedKDFIterationsNumber) + } + db, err := openDB() + require.NoError(t, err) + + // Execute the old migrations + err = migrationsprevnodecfg.Migrate(db) + require.NoError(t, err) + + err = nodecfg.MigrateNodeConfig(db) + require.NoError(t, err) + + // Migrate until 1682393575_sync_ens_name.up + err = migrations.MigrateTo(db, customSteps, 1682393575) + require.NoError(t, err) + + // Validate that transfers table has no status column + exists, err := ColumnExists(db, "transfers", "status") + require.NoError(t, err) + require.False(t, exists) + + exists, err = ColumnExists(db, "transfers", "status") + require.NoError(t, err) + require.False(t, exists) + + insertTestTransaction := func(index int, txBlob string, receiptBlob string, logBlob string, ethType bool) error { + indexStr := strconv.Itoa(index) + var txValue *string + if txBlob != "" { + txValue = &txBlob + } + var receiptValue *string + if receiptBlob != "" { + receiptValue = &receiptBlob + } + var logValue *string + if logBlob != "" { + logValue = &logBlob + } + entryType := "eth" + if !ethType { + entryType = "erc20" + } + _, err = db.Exec(`INSERT OR IGNORE INTO blocks(network_id, address, blk_number, blk_hash) VALUES (?, ?, ?, ?); + INSERT INTO transfers (hash, address, network_id, tx, receipt, log, blk_hash, type, blk_number, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + index, common.HexToAddress(indexStr), index, common.HexToHash(indexStr), + common.HexToHash(indexStr), common.HexToAddress(indexStr), index, txValue, receiptValue, logValue, common.HexToHash(indexStr), entryType, index, index) + return err + } + + // Empty transaction, found the usecase in the test DB + err = insertTestTransaction(1, "", "", "", true) + require.NoError(t, err) + + erc20FailReceiptJSON := fmt.Sprintf(erc20ReceiptTestDataTemplate, 0) + erc20SuccessReceiptJSON := fmt.Sprintf(erc20ReceiptTestDataTemplate, 1) + err = insertTestTransaction(2, erc20TxTestData, erc20FailReceiptJSON, erc20LogTestData, false) + require.NoError(t, err) + + err = insertTestTransaction(3, erc20TxTestData, erc20SuccessReceiptJSON, erc20LogTestData, false) + require.NoError(t, err) + + err = insertTestTransaction(4, erc721TxTestData, erc721ReceiptTestData, erc721LogTestData, false) + require.NoError(t, err) + + ethZeroValueTxTestData := fmt.Sprintf(ethTxTestData, "0") + ethVeryBigValueTxTestData := fmt.Sprintf(ethTxTestData, "12345678901234567890") + ethOriginalTxTestData := fmt.Sprintf(ethTxTestData, "2386f26fc10000") + + err = insertTestTransaction(5, ethZeroValueTxTestData, ethReceiptTestData, "", true) + require.NoError(t, err) + err = insertTestTransaction(6, ethVeryBigValueTxTestData, "", "", true) + require.NoError(t, err) + err = insertTestTransaction(7, ethOriginalTxTestData, ethReceiptTestData, "", true) + require.NoError(t, err) + + err = insertTestTransaction(8, uniswapV2TxTestData, uniswapV2ReceiptTestData, uniswapV2LogTestData, false) + require.NoError(t, err) + + err = insertTestTransaction(9, uniswapV3TxTestData, uniswapV3ReceiptTestData, uniswapV3LogTestData, false) + require.NoError(t, err) + + failMigrationSteps := []sqlite.PostStep{ + { + Version: customSteps[1].Version, + CustomMigration: func(sqlTx *sql.Tx) error { + return errors.New("failed to run custom migration") + }, + RollBackVersion: customSteps[1].RollBackVersion, + }, + } + + // Attempt to run test migration 1686048341 and fail in custom step + err = migrations.MigrateTo(db, failMigrationSteps, customSteps[1].Version) + require.Error(t, err) + + exists, err = ColumnExists(db, "transfers", "status") + require.NoError(t, err) + require.False(t, exists) + + // Run test migration 1686048341_transfers_receipt_json_blob_out..sql + err = migrations.MigrateTo(db, customSteps, customSteps[1].Version) + require.NoError(t, err) + + // Validate that the migration was run and transfers table has now status column + exists, err = ColumnExists(db, "transfers", "status") + require.NoError(t, err) + require.True(t, exists) + + var ( + status, receiptType, cumulativeGasUsed, gasUsed, txIndex sql.NullInt64 + gasLimit, gasPriceClamped64, gasTipCapClamped64 sql.NullInt64 + gasFeeCapClamped64, accountNonce, size, logIndex, txType sql.NullInt64 + + protected sql.NullBool + dbContractAddress, amount128Hex sql.NullString + contractAddress, tokenAddress common.Address + txHash, blockHash []byte + entryType string + isTokenIDNull bool + ) + dbTokenAddress := sqlite.JSONBlob{Data: &tokenAddress} + 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, + + CASE + WHEN token_id IS NULL THEN 1 + ELSE 0 + END as token_id_status + + FROM transfers ORDER BY timestamp ASC`) + require.NoError(t, err) + + scanNextData := func() error { + rows.Next() + if rows.Err() != nil { + return rows.Err() + } + err := rows.Scan(&status, &receiptType, &txHash, &logIndex, &blockHash, &cumulativeGasUsed, &dbContractAddress, &gasUsed, &txIndex, + &txType, &protected, &gasLimit, &gasPriceClamped64, &gasTipCapClamped64, &gasFeeCapClamped64, &amount128Hex, &accountNonce, &size, &dbTokenAddress, (*bigint.SQLBigIntBytes)(tokenID), &entryType, &isTokenIDNull) + if err != nil { + return err + } + if dbContractAddress.Valid { + contractAddress = common.HexToAddress(dbContractAddress.String) + } + return nil + } + + validateTransaction := func(tt *types.Transaction, expectedEntryType w_common.Type, tl *types.Log) { + if tt == nil { + require.False(t, txType.Valid) + require.False(t, protected.Valid) + require.False(t, gasLimit.Valid) + require.False(t, gasPriceClamped64.Valid) + require.False(t, gasTipCapClamped64.Valid) + require.False(t, gasFeeCapClamped64.Valid) + require.False(t, amount128Hex.Valid) + require.False(t, accountNonce.Valid) + require.False(t, size.Valid) + require.Equal(t, common.Address{}, tokenAddress) + require.True(t, isTokenIDNull) + require.Equal(t, string(w_common.EthTransfer), entryType) + } else { + require.True(t, txType.Valid) + require.Equal(t, tt.Type(), uint8(txType.Int64)) + require.True(t, protected.Valid) + require.Equal(t, tt.Protected(), protected.Bool) + require.True(t, gasLimit.Valid) + require.Equal(t, tt.Gas(), uint64(gasLimit.Int64)) + require.True(t, gasPriceClamped64.Valid) + require.Equal(t, *sqlite.BigIntToClampedInt64(tt.GasPrice()), gasPriceClamped64.Int64) + require.True(t, gasTipCapClamped64.Valid) + require.Equal(t, *sqlite.BigIntToClampedInt64(tt.GasTipCap()), gasTipCapClamped64.Int64) + require.True(t, gasFeeCapClamped64.Valid) + require.Equal(t, *sqlite.BigIntToClampedInt64(tt.GasFeeCap()), gasFeeCapClamped64.Int64) + require.True(t, accountNonce.Valid) + require.Equal(t, tt.Nonce(), uint64(accountNonce.Int64)) + require.True(t, size.Valid) + require.Equal(t, int64(tt.Size()), size.Int64) + + if expectedEntryType == w_common.EthTransfer { + require.True(t, amount128Hex.Valid) + require.Equal(t, *sqlite.BigIntToPadded128BitsStr(tt.Value()), amount128Hex.String) + require.False(t, dbTokenAddress.Valid) + require.True(t, isTokenIDNull) + } else { + actualEntryType, expectedTokenAddress, expectedTokenID, expectedValue := w_common.ExtractTokenIdentity(expectedEntryType, tl, tt) + if actualEntryType == w_common.Erc20Transfer { + require.True(t, amount128Hex.Valid) + require.Equal(t, *sqlite.BigIntToPadded128BitsStr(expectedValue), amount128Hex.String) + require.True(t, isTokenIDNull) + require.True(t, dbTokenAddress.Valid) + require.Equal(t, *expectedTokenAddress, tokenAddress) + } else if actualEntryType == w_common.Erc721Transfer { + require.False(t, amount128Hex.Valid) + require.False(t, isTokenIDNull) + require.Equal(t, expectedTokenID, expectedTokenID) + require.True(t, dbTokenAddress.Valid) + require.Equal(t, *expectedTokenAddress, tokenAddress) + } else { + require.False(t, amount128Hex.Valid) + require.True(t, isTokenIDNull) + require.False(t, dbTokenAddress.Valid) + } + + require.Equal(t, expectedEntryType, actualEntryType) + } + } + } + + validateReceipt := func(tr *types.Receipt, tl *types.Log) { + if tr == nil { + require.False(t, status.Valid) + require.False(t, receiptType.Valid) + require.Equal(t, []byte(nil), txHash) + require.Equal(t, []byte(nil), blockHash) + require.False(t, cumulativeGasUsed.Valid) + require.Equal(t, common.Address{}, contractAddress) + require.False(t, gasUsed.Valid) + require.False(t, txIndex.Valid) + } else { + require.True(t, status.Valid) + require.Equal(t, tr.Status, uint64(status.Int64)) + require.True(t, receiptType.Valid) + require.Equal(t, int64(tr.Type), receiptType.Int64) + require.Equal(t, tr.TxHash, common.BytesToHash(txHash)) + require.Equal(t, tr.BlockHash, common.BytesToHash(blockHash)) + require.True(t, cumulativeGasUsed.Valid) + require.Equal(t, int64(tr.CumulativeGasUsed), cumulativeGasUsed.Int64) + require.Equal(t, tr.ContractAddress, contractAddress) + require.True(t, gasUsed.Valid) + require.Equal(t, int64(tr.GasUsed), gasUsed.Int64) + require.True(t, txIndex.Valid) + require.Equal(t, int64(tr.TransactionIndex), txIndex.Int64) + } + if tl == nil { + require.False(t, logIndex.Valid) + } else { + require.True(t, logIndex.Valid) + require.Equal(t, uint(logIndex.Int64), tl.Index) + } + } + + err = scanNextData() + require.NoError(t, err) + validateTransaction(nil, w_common.EthTransfer, nil) + validateReceipt(nil, nil) + + var successReceipt types.Receipt + err = json.Unmarshal([]byte(erc20SuccessReceiptJSON), &successReceipt) + require.NoError(t, err) + + var failReceipt types.Receipt + err = json.Unmarshal([]byte(erc20FailReceiptJSON), &failReceipt) + require.NoError(t, err) + + var erc20Log types.Log + err = json.Unmarshal([]byte(erc20LogTestData), &erc20Log) + require.NoError(t, err) + + var erc20Tx types.Transaction + err = json.Unmarshal([]byte(erc20TxTestData), &erc20Tx) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&erc20Tx, w_common.Erc20Transfer, &erc20Log) + validateReceipt(&failReceipt, &erc20Log) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&erc20Tx, w_common.Erc20Transfer, &erc20Log) + validateReceipt(&successReceipt, &erc20Log) + + var erc721Receipt types.Receipt + err = json.Unmarshal([]byte(erc721ReceiptTestData), &erc721Receipt) + require.NoError(t, err) + + var erc721Log types.Log + err = json.Unmarshal([]byte(erc721LogTestData), &erc721Log) + require.NoError(t, err) + + var erc721Tx types.Transaction + err = json.Unmarshal([]byte(erc721TxTestData), &erc721Tx) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&erc721Tx, w_common.Erc721Transfer, &erc721Log) + validateReceipt(&erc721Receipt, &erc721Log) + + var zeroTestTx types.Transaction + err = json.Unmarshal([]byte(ethZeroValueTxTestData), &zeroTestTx) + require.NoError(t, err) + + var ethReceipt types.Receipt + err = json.Unmarshal([]byte(ethReceiptTestData), ðReceipt) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&zeroTestTx, w_common.EthTransfer, nil) + validateReceipt(ðReceipt, nil) + + var bigTestTx types.Transaction + err = json.Unmarshal([]byte(ethVeryBigValueTxTestData), &bigTestTx) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&bigTestTx, w_common.EthTransfer, nil) + validateReceipt(nil, nil) + + var ethOriginalTestTx types.Transaction + err = json.Unmarshal([]byte(ethOriginalTxTestData), ðOriginalTestTx) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(ðOriginalTestTx, w_common.EthTransfer, nil) + validateReceipt(ðReceipt, nil) + + var uniswapV2Receipt types.Receipt + err = json.Unmarshal([]byte(uniswapV2ReceiptTestData), &uniswapV2Receipt) + require.NoError(t, err) + + var uniswapV2Log types.Log + err = json.Unmarshal([]byte(uniswapV2LogTestData), &uniswapV2Log) + require.NoError(t, err) + + var uniswapV2Tx types.Transaction + err = json.Unmarshal([]byte(uniswapV2TxTestData), &uniswapV2Tx) + require.NoError(t, err) + + var uniswapV3Receipt types.Receipt + err = json.Unmarshal([]byte(uniswapV3ReceiptTestData), &uniswapV3Receipt) + require.NoError(t, err) + + var uniswapV3Log types.Log + err = json.Unmarshal([]byte(uniswapV3LogTestData), &uniswapV3Log) + require.NoError(t, err) + + var uniswapV3Tx types.Transaction + err = json.Unmarshal([]byte(uniswapV3TxTestData), &uniswapV3Tx) + require.NoError(t, err) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&uniswapV2Tx, w_common.UniswapV2Swap, &uniswapV2Log) + validateReceipt(&uniswapV2Receipt, &uniswapV2Log) + + err = scanNextData() + require.NoError(t, err) + validateTransaction(&uniswapV3Tx, w_common.UniswapV3Swap, &uniswapV3Log) + validateReceipt(&uniswapV3Receipt, &uniswapV3Log) + + err = scanNextData() + // Validate that we processed all data (no more rows expected) + require.Error(t, err) + + db.Close() +} diff --git a/appdatabase/migrations/bindata.go b/appdatabase/migrations/bindata.go index 2fb891a73..a9139344d 100644 --- a/appdatabase/migrations/bindata.go +++ b/appdatabase/migrations/bindata.go @@ -67,6 +67,8 @@ // 1685463947_add_to_asset_to_multitransaction.up.sql (61B) // 1685880973_add_profile_links_settings_table.up.sql (1.656kB) // 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) // doc.go (74B) package migrations @@ -151,7 +153,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(1685543739, 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 } @@ -171,7 +173,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(1685543739, 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 } @@ -191,7 +193,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(1685543739, 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 } @@ -211,7 +213,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(1685543739, 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 } @@ -231,7 +233,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(1685543739, 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 } @@ -251,7 +253,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(1685543739, 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 } @@ -271,7 +273,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(1685543739, 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 } @@ -291,7 +293,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(1685543739, 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 } @@ -311,7 +313,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(1685543739, 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 } @@ -331,7 +333,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(1685543739, 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 } @@ -351,7 +353,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(1685543739, 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 } @@ -371,7 +373,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(1685543739, 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 } @@ -391,7 +393,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(1685543739, 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 } @@ -411,7 +413,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(1685543739, 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 } @@ -431,7 +433,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(1685543739, 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 } @@ -451,7 +453,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(1685543739, 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 } @@ -471,7 +473,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(1685543739, 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 } @@ -491,7 +493,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(1685543739, 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 } @@ -511,7 +513,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(1685543739, 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 } @@ -531,7 +533,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(1685543739, 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 } @@ -551,7 +553,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(1685543739, 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 } @@ -571,7 +573,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(1685543739, 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 } @@ -591,7 +593,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(1685543739, 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 } @@ -611,7 +613,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(1685543739, 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 } @@ -631,7 +633,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(1685543739, 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 } @@ -651,7 +653,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(1685543739, 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 } @@ -671,7 +673,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(1685543739, 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 } @@ -691,7 +693,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(1685543739, 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 } @@ -711,7 +713,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(1685543739, 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 } @@ -731,7 +733,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(1685543739, 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 } @@ -751,7 +753,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(1685543739, 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 } @@ -771,7 +773,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(1685543739, 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 } @@ -791,7 +793,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(1685543739, 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 } @@ -811,7 +813,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(1685543739, 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 } @@ -831,7 +833,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(1685543739, 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 } @@ -851,7 +853,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(1685543739, 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 } @@ -871,7 +873,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(1685543739, 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 } @@ -891,7 +893,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(1685543739, 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 } @@ -911,7 +913,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(1685543739, 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 } @@ -931,7 +933,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(1685543739, 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 } @@ -951,7 +953,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(1685543739, 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 } @@ -971,7 +973,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(1685543739, 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 } @@ -991,7 +993,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(1685543739, 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 } @@ -1011,7 +1013,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(1685543739, 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 } @@ -1031,7 +1033,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(1685543739, 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 } @@ -1051,7 +1053,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(1685543739, 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 } @@ -1071,7 +1073,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(1685543739, 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 } @@ -1091,7 +1093,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(1685543739, 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 } @@ -1111,7 +1113,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(1685543739, 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 } @@ -1131,7 +1133,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(1685543739, 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 } @@ -1151,7 +1153,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(1685543739, 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 } @@ -1171,7 +1173,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(1685543739, 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 } @@ -1191,7 +1193,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(1685543739, 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 } @@ -1211,7 +1213,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(1685543739, 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 } @@ -1231,7 +1233,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(1685543739, 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 } @@ -1251,7 +1253,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(1685543739, 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 } @@ -1271,7 +1273,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(1685543739, 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 } @@ -1291,7 +1293,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(1685543739, 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 } @@ -1311,7 +1313,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(1685543739, 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 } @@ -1331,7 +1333,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(1685543739, 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 } @@ -1351,7 +1353,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(1685543739, 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 } @@ -1371,7 +1373,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(1685543739, 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 } @@ -1391,7 +1393,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(1685543739, 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 } @@ -1411,7 +1413,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(1686041617, 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 } @@ -1431,7 +1433,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(1686041617, 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 } @@ -1451,7 +1453,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(1686041617, 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 } @@ -1471,11 +1473,51 @@ 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(1686041617, 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 } +var __1686048341_transfers_receipt_json_blob_outUpSqlDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xcb\xc1\x0d\xc2\x30\x0c\x46\xe1\x55\xfe\x05\xc2\x1a\x88\x13\x17\x16\x30\xc1\x21\x16\xc1\xae\x6c\x37\x52\xb7\xaf\x72\xea\xed\x49\x4f\x5f\x29\x78\x75\x09\xfc\xe5\xeb\x94\x62\x0a\x09\x88\xe2\xfe\x44\xb5\x0f\xdf\xf0\x68\xab\xaf\xdd\x48\x46\x20\x97\x61\x4d\x3f\x10\xec\x93\x03\x14\x70\x1b\xe3\x4d\xf5\x87\x34\x64\x67\x6c\xce\x53\x6c\x0f\x98\xf2\x19\x00\x00\xff\xff\x4e\x62\x72\x83\x68\x00\x00\x00") + +func _1686048341_transfers_receipt_json_blob_outUpSqlDownSqlBytes() ([]byte, error) { + return bindataRead( + __1686048341_transfers_receipt_json_blob_outUpSqlDownSql, + "1686048341_transfers_receipt_json_blob_out.up.sql.down.sql", + ) +} + +func _1686048341_transfers_receipt_json_blob_outUpSqlDownSql() (*asset, error) { + bytes, err := _1686048341_transfers_receipt_json_blob_outUpSqlDownSqlBytes() + if err != nil { + 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(1686147405, 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 +} + +var __1686048341_transfers_receipt_json_blob_outUpSqlUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x94\x41\x6f\x9b\x4e\x10\xc5\xef\xf9\x14\x73\x4c\xa4\x24\x7f\xfd\xd3\xaa\xaa\x9a\x13\x8e\x51\xea\xca\x05\xc9\x25\x6a\x6e\x74\xbc\x3b\x98\xad\x97\x1d\xb4\x33\x24\xa4\x9f\xbe\x32\xb6\x53\xb7\x27\xc8\x0d\x2d\xf3\x7b\x6f\x61\xf7\xbd\xab\x2b\x28\x6a\x27\xd0\xb8\x4d\x44\x75\x1c\xc0\x09\x58\x0e\x04\x2e\xc0\x7d\x0e\x86\x2d\x01\x0a\x20\x98\x4e\x94\x1b\x10\xa5\xf6\xfa\xec\x88\x55\xce\x13\x08\xc5\x27\x92\x61\x2a\x00\x06\x53\x73\x84\x8a\x23\x68\x4d\x27\xba\xf2\x22\x4a\xcd\x8e\xbc\xab\xc9\x6c\x0f\x6f\xe8\x3b\x7a\x4f\xfa\x45\x38\xcc\x3c\xaf\x05\xaa\xc8\x0d\x60\xdb\x5a\x54\x5c\xa3\xd0\x7f\xc7\x87\xeb\x0d\x9f\xed\x7d\x09\x2a\xf6\x9e\x9f\x5d\xd8\x0c\xdb\x11\xc0\x48\xff\x6e\xfa\xd3\x30\x9c\x2c\x8b\x74\x05\x45\x32\x5b\xa6\xa0\x11\x83\x54\x14\x05\x92\xf9\x1c\xee\xf2\xe5\xc3\xd7\x0c\x44\x51\x3b\x81\x45\x56\xdc\x8e\x9a\x8f\x64\xc8\xb5\x5a\xea\x4b\x4b\xe3\x29\xed\xcb\x1a\xa5\x86\xd9\x32\x9f\x8d\x23\x3c\x6f\x4a\x17\x2c\xf5\xe3\x4d\xd6\x9e\xcd\x76\xaa\x8f\xe9\x9a\xce\xa3\xba\x27\x2a\x37\x28\x65\x27\x64\xc7\x3b\x1a\x0e\x1a\xd1\x68\x89\xd6\x46\x12\x81\x22\x7d\x1c\x89\x4e\x37\xd3\xfe\xf4\x87\x8c\x45\xa6\x9d\x53\x1b\x59\xc9\x28\x59\x98\xe5\xf9\x32\x4d\xb2\xf1\x1f\xe3\x5d\xe3\x14\x1e\xb2\x6f\x8b\xfb\x2c\x9d\x8f\xb7\xdc\xb1\x6d\x74\x86\x4a\xe3\xb1\x69\xc9\x7e\x78\x3f\x0d\x56\xd7\x96\x06\xdb\xb7\xe2\x15\xd1\x5b\x71\x6c\xb8\x0b\x5a\xb6\x68\x2d\xd9\xff\x6f\x3e\xd6\xd4\xc3\xdd\xe7\x64\x75\xfe\xee\xe6\x62\xa4\x82\x31\x83\x44\xe0\x60\x26\x1c\x93\xb8\x5f\x53\xc2\xc7\x5b\x0a\xaf\x57\x74\x7c\x34\xf6\x9c\xb3\x07\x64\x68\xae\x55\x9a\x14\x29\x2c\xb2\x79\xfa\xf8\x07\x2a\x2b\xe7\x95\x22\xe4\xd9\x89\xd0\xf9\xbe\x59\x2e\xff\xb6\xbf\x7c\x55\xbd\xb8\xfd\x31\x48\xa6\xfd\x10\x21\xd0\x1e\x30\xd8\x63\xbf\xc0\xae\xf5\xf6\x5d\xb8\x6b\xd1\x9f\xc2\x61\x97\xef\xf5\x30\x83\xd6\x0e\xab\x2e\x54\x1c\x9b\x43\x6b\x07\xe5\x61\x31\xd0\x33\x18\xf6\x5d\x13\xe4\x77\x00\x00\x00\xff\xff\x39\x7c\x13\x6d\xdc\x05\x00\x00") + +func _1686048341_transfers_receipt_json_blob_outUpSqlUpSqlBytes() ([]byte, error) { + return bindataRead( + __1686048341_transfers_receipt_json_blob_outUpSqlUpSql, + "1686048341_transfers_receipt_json_blob_out.up.sql.up.sql", + ) +} + +func _1686048341_transfers_receipt_json_blob_outUpSqlUpSql() (*asset, error) { + bytes, err := _1686048341_transfers_receipt_json_blob_outUpSqlUpSqlBytes() + if err != nil { + 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(1686147405, 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 _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) { @@ -1491,7 +1533,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1685543739, 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 } @@ -1721,6 +1763,10 @@ var _bindata = map[string]func() (*asset, error){ "1686041510_add_idx_transfers_blkno_loaded.up.sql": _1686041510_add_idx_transfers_blkno_loadedUpSql, + "1686048341_transfers_receipt_json_blob_out.up.sql.down.sql": _1686048341_transfers_receipt_json_blob_outUpSqlDownSql, + + "1686048341_transfers_receipt_json_blob_out.up.sql.up.sql": _1686048341_transfers_receipt_json_blob_outUpSqlUpSql, + "doc.go": docGo, } @@ -1832,6 +1878,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "1685463947_add_to_asset_to_multitransaction.up.sql": &bintree{_1685463947_add_to_asset_to_multitransactionUpSql, map[string]*bintree{}}, "1685880973_add_profile_links_settings_table.up.sql": &bintree{_1685880973_add_profile_links_settings_tableUpSql, 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{}}, "doc.go": &bintree{docGo, map[string]*bintree{}}, }} diff --git a/appdatabase/migrations/migrate.go b/appdatabase/migrations/migrate.go index 8757b0f1c..bb6b130b5 100644 --- a/appdatabase/migrations/migrate.go +++ b/appdatabase/migrations/migrate.go @@ -9,11 +9,22 @@ import ( ) // Migrate applies migrations. -func Migrate(db *sql.DB) error { +// see Migrate in vendor/status-go/sqlite/migrate.go +func Migrate(db *sql.DB, customSteps []sqlite.PostStep) error { return sqlite.Migrate(db, bindata.Resource( AssetNames(), func(name string) ([]byte, error) { return Asset(name) }, - )) + ), customSteps, nil) +} + +// MigrateTo is used for testing purposes +func MigrateTo(db *sql.DB, customSteps []sqlite.PostStep, untilVersion uint) error { + return sqlite.Migrate(db, bindata.Resource( + AssetNames(), + func(name string) ([]byte, error) { + return Asset(name) + }, + ), customSteps, &untilVersion) } diff --git a/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.down.sql b/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.down.sql new file mode 100644 index 000000000..967192b87 --- /dev/null +++ b/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.down.sql @@ -0,0 +1 @@ +-- This migration is in GO code. If GO migration fails this entry serves as rollback to the previous one \ No newline at end of file diff --git a/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.up.sql b/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.up.sql new file mode 100644 index 000000000..dd5347c4d --- /dev/null +++ b/appdatabase/migrations/sql/1686048341_transfers_receipt_json_blob_out.up.sql.up.sql @@ -0,0 +1,31 @@ +-- This migration is done in GO code as a custom step. +-- This file serves as an anchor for the migration system +-- Check migrateWalletJsonBlobs from appdatabase/database.go + +-- The following steps are done in GO code: + +-- ALTER TABLE transfers ADD COLUMN status INT; +-- ALTER TABLE transfers ADD COLUMN receipt_type INT; +-- ALTER TABLE transfers ADD COLUMN tx_hash BLOB; +-- ALTER TABLE transfers ADD COLUMN log_index INT; +-- ALTER TABLE transfers ADD COLUMN block_hash BLOB; +-- ALTER TABLE transfers ADD COLUMN cumulative_gas_used INT; +-- ALTER TABLE transfers ADD COLUMN contract_address TEXT; +-- ALTER TABLE transfers ADD COLUMN gas_used INT; +-- ALTER TABLE transfers ADD COLUMN tx_index INT; + +-- ALTER TABLE transfers ADD COLUMN tx_type INT; +-- ALTER TABLE transfers ADD COLUMN protected BOOLEAN; +-- ALTER TABLE transfers ADD COLUMN gas_limit UNSIGNED INT; +-- ALTER TABLE transfers ADD COLUMN gas_price_clamped64 INT; +-- ALTER TABLE transfers ADD COLUMN gas_tip_cap_clamped64 INT; +-- ALTER TABLE transfers ADD COLUMN gas_fee_cap_clamped64 INT; +-- ALTER TABLE transfers ADD COLUMN amount_padded128hex CHAR(32); +-- ALTER TABLE transfers ADD COLUMN account_nonce INT; +-- ALTER TABLE transfers ADD COLUMN size INT; +-- ALTER TABLE transfers ADD COLUMN token_address BLOB; +-- ALTER TABLE transfers ADD COLUMN token_id BLOB; + +-- CREATE INDEX transfers_filter ON transfers (status, token_address, token_id);` + +-- Extract tx and receipt data from the json blob and add the information into the new columns \ No newline at end of file diff --git a/appdatabase/migrationsprevnodecfg/migrate.go b/appdatabase/migrationsprevnodecfg/migrate.go index c53577812..ad246a453 100644 --- a/appdatabase/migrationsprevnodecfg/migrate.go +++ b/appdatabase/migrationsprevnodecfg/migrate.go @@ -15,5 +15,5 @@ func Migrate(db *sql.DB) error { func(name string) ([]byte, error) { return Asset(name) }, - )) + ), nil, nil) } diff --git a/appdatabase/test_helpers.go b/appdatabase/test_helpers.go index 57af16f66..71df779c1 100644 --- a/appdatabase/test_helpers.go +++ b/appdatabase/test_helpers.go @@ -38,3 +38,34 @@ func SetupTestMemorySQLDB(prefix string) (*sql.DB, error) { return db, nil } + +func ColumnExists(db *sql.DB, tableName string, columnName string) (bool, error) { + rows, err := db.Query("PRAGMA table_info(" + tableName + ")") + if err != nil { + return false, err + } + defer rows.Close() + + var cid int + var name string + var dataType string + var notNull bool + var dFLTValue sql.NullString + var pk int + + for rows.Next() { + err := rows.Scan(&cid, &name, &dataType, ¬Null, &dFLTValue, &pk) + if err != nil { + return false, err + } + if name == columnName { + return true, nil + } + } + + if rows.Err() != nil { + return false, rows.Err() + } + + return false, nil +} diff --git a/multiaccounts/database.go b/multiaccounts/database.go index 7fb258189..a83170768 100644 --- a/multiaccounts/database.go +++ b/multiaccounts/database.go @@ -102,7 +102,7 @@ func InitializeDB(path string) (*Database, error) { if err != nil { return nil, err } - err = migrations.Migrate(db) + err = migrations.Migrate(db, nil) if err != nil { return nil, err } diff --git a/multiaccounts/migrations/migrate.go b/multiaccounts/migrations/migrate.go index 8757b0f1c..9e0352815 100644 --- a/multiaccounts/migrations/migrate.go +++ b/multiaccounts/migrations/migrate.go @@ -9,11 +9,12 @@ import ( ) // Migrate applies migrations. -func Migrate(db *sql.DB) error { +// see Migrate in vendor/status-go/sqlite/migrate.go +func Migrate(db *sql.DB, customSteps []sqlite.PostStep) error { return sqlite.Migrate(db, bindata.Resource( AssetNames(), func(name string) ([]byte, error) { return Asset(name) }, - )) + ), customSteps, nil) } diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index f4b830503..59ba2aa40 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -152,7 +152,7 @@ func WithDatabase(db *sql.DB) Option { func WithToplevelDatabaseMigrations() Option { return func(c *config) error { c.afterDbCreatedHooks = append(c.afterDbCreatedHooks, func(c *config) error { - return migrations.Migrate(c.db) + return migrations.Migrate(c.db, nil) }) return nil } diff --git a/services/ext/service.go b/services/ext/service.go index 15c635dfb..0ed8f4980 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -48,8 +48,8 @@ import ( localnotifications "github.com/status-im/status-go/services/local-notifications" mailserversDB "github.com/status-im/status-go/services/mailservers" "github.com/status-im/status-go/services/wallet" + w_common "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/thirdparty" - "github.com/status-im/status-go/services/wallet/transfer" ) // EnvelopeEventsHandler used for two different event types. @@ -296,7 +296,7 @@ func (c *verifyTransactionClient) TransactionByHash(ctx context.Context, hash ty // Token transfer, check the logs if len(coremessage.Data()) != 0 { - if transfer.IsTokenTransfer(receipt.Logs) { + if w_common.IsTokenTransfer(receipt.Logs) { return coremessage, coretypes.TransactionStatus(receipt.Status), nil } return coremessage, coretypes.TransactionStatusFailed, nil diff --git a/services/local-notifications/core_test.go b/services/local-notifications/core_test.go index 7112ef1fe..81bddcc89 100644 --- a/services/local-notifications/core_test.go +++ b/services/local-notifications/core_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" + w_common "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/transfer" "github.com/status-im/status-go/services/wallet/walletevent" "github.com/status-im/status-go/signal" @@ -99,7 +100,7 @@ func TestTransactionNotification(t *testing.T) { transfers := []transfer.Transfer{ { ID: common.Hash{1}, - Type: transfer.Type("eth"), + Type: w_common.Type("eth"), BlockHash: header.Hash, BlockNumber: header.Number, Transaction: tx, diff --git a/services/wallet/activity/TODO.md b/services/wallet/activity/TODO.md new file mode 100644 index 000000000..019069638 --- /dev/null +++ b/services/wallet/activity/TODO.md @@ -0,0 +1,157 @@ +# DB extension and refactoring for activity + +## Work in progress + +| | Buy | Swap | Bridge | Send/Receive | +| ------------- | -------------- | -------------- | ------------- | ------------- | +| Activity data | ~~API~~ ~~DB~~ | ~~API~~ ~~DB~~ | _API_ _DB_ | _API_ _DB_ | +| Raw data | ~~API~~ ~~DB~~ | ~~API~~ ~~DB~~ | _API_ _DB_ | _API_ _DB_ | +| Pending data | ~~API~~ ~~DB~~ | ~~API~~ ~~DB~~ | _API_ _DB_ | _API_ _DB_ | + +Legend: + +- ~~API~~ - not much or at all provided +- _API_ - partially provided +- API - complete + +### Summary + +Improve on the identified limitations + +- [ ] Missing filtering data +- [x] Missing cached (not extracted as a column) + - Extracting the data from the raw data is expensive but might be negligible given that usually we should not expect more than 20 entries per second in the worst case scenario. +- [x] Table extensions + - ~~Activity specific info in activity data store (multi_transaction table)~~ + - Activity specific info in in the transactions data store (transfers table) + +### Missing data + +Filter requirements + +- [ ] Activity operation status + - [ ] `pending`: have to aggregate for `Buy`, `Swap`, `Bridge` + - already there for `Send`, `Receive` + - [ ] `complete`: only extract and check for `status` in the `receipt` for `Send`, `Receive` + - For complex operations aggregate the `complete` status `Buy`, `Swap`, `Bridge` + - [ ] `finalized`: similar to `complete` for `Send`, `Receive` + - all sub-transactions are `complete` for `Buy`, `Swap`, `Bridge` + - [ ] `failed`: extract from `status` for all sub-transactions +- [ ] `chainID`: aggregate data for activity entries `Bridge`, `Buy`, `Swap` +- [ ] `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`) + +UX requirements + +- [ ] `status`: for status icon and label +- [ ] `chainIDs`: for chain icons + - Missing for `Bridge`, `Buy`, `Swap` +- [ ] `amount`s: add to the activity.Entry + - already in DB +- [ ] `tokenCode`s: add to the activity.Entry + - already in DB +- [ ] `to`/`from`/`owner`: add to the activity.Entry + - already in DB, coming soon +- [ ] `tokenIdentity`: collectible is missing (`chainId`, `address`, `tokenId`) + - `tokenCode` should be covering fungible operations +- [x] `identity`: for all the sources +- [x] `type`: for the main icon and label +- [x] `time`: timestamp + +### Refactoring + +## Current state + +### Transfers Table + +The `transfers` transactions raw data + +- Transaction identity: `network_id`, `hash`, `address` + - Implementation by `sqlite_autoindex_transfers_1` unique index +- `multi_transaction_id`: `multi_transaction` entries to `transfers` entries mapping (one to many) +- Raw data: + - `tx` transaction + - `receipt`: transfer receipt + +### Multi-Transaction Table + +Represented by `multi_transaction` + +Responsibilities + +- UX metadata for transactions originating from our APP. + - `from_address`, `to_address` + - `from_asset`, `to_asset` + - `from_amount`, `to_amount` (token codes) + - `type` identifies the type (initially only Send and Bridge) + - `timestamp` the timestamp of the execution +- Multi-transaction to sub-transaction mapping + - The `multi_transaction_id` in the `transfers` and `pending_transaction` table corresponds to the `ROWID` in the `multi_transactions`. + +### Pending Transactions Table + +The `pending_transactions` table represents transactions initiated from the app + +- Transaction identity + - `network_id`, `hash` + - implemented by the `sqlite_autoindex_pending_transactions_1` index + - Note how this is different from the `transfers` table, where the `address` is also part of the identity. +- `timestamp`: The timestamp of the pending transaction. +- `multi_transaction_id`: `multi_transaction` entries to `pending_transactions` entries mapping (one to many) + +### Schema + +Relationships between the tables + +```mermaid +erDiagram + multi_transaction ||--o{ transfers : has + multi_transaction ||--o{ pending_transactions : has + transfers { + network_id BIGINT + hash VARCHAR + timestamp BIGINT + multi_transaction_id INT + tx BLOB + receipt BLOB + log BLOB + } + multi_transaction { + ROWID INT + type VARCHAR + } + pending_transactions { + network_id BIGINT + hash VARCHAR + timestamp INT + multi_transaction_id INT + } +``` + +### Dropped tasks + +Dropped the DB refactoring and improvements after further discussion and concerns + +- [x] Terminology proposal + - [x] using `transactions` instead of `transfers` for the raw data + - [x] using `activity` instead of `multi-transaction` to better match the new requirements + - [x] Convert JSON blobs into structured data + +Dropped benchmark performance and move on using theoretical knowledge by adding indexes for what we know only +Will leave the performance concerns for the next milestone + +- [ ] Joining DBs +- [ ] One activity DB for all require metadata + - Pros: + - Faster to query (don't know the numbers) + - Simpler query will decrease maintenance + - Cons: + - have to migrate all data, extract and fill the activity on every download of updates for all activities +- [ ] Keep only filter specific metadata in the Activity DB + - Pros: + - Less changes to migrate existing data. Still have to maintain activity filtering specific data + - Cons: + - Slower to query (don't know how much yet) + - Complex query increases maintenance + diff --git a/services/wallet/activity/activity.go b/services/wallet/activity/activity.go index b664eff94..4ad627072 100644 --- a/services/wallet/activity/activity.go +++ b/services/wallet/activity/activity.go @@ -19,9 +19,7 @@ import ( type PayloadType = int -// Beware if adding/removing please check if affected and update the functions below -// - NewActivityEntryWithTransaction -// - multiTransactionTypeToActivityType +// Beware: pleas update multiTransactionTypeToActivityType if changing this enum const ( MultiTransactionPT PayloadType = iota + 1 SimpleTransactionPT @@ -75,9 +73,18 @@ func (e *Entry) UnmarshalJSON(data []byte) error { return nil } -func NewActivityEntryWithTransaction(payloadType PayloadType, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status) Entry { - if payloadType != SimpleTransactionPT && payloadType != PendingTransactionPT { - panic("invalid transaction type") +func newActivityEntryWithPendingTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status) Entry { + return newActivityEntryWithTransaction(true, transaction, timestamp, activityType, activityStatus) +} + +func newActivityEntryWithSimpleTransaction(transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status) Entry { + return newActivityEntryWithTransaction(false, transaction, timestamp, activityType, activityStatus) +} + +func newActivityEntryWithTransaction(pending bool, transaction *transfer.TransactionIdentity, timestamp int64, activityType Type, activityStatus Status) Entry { + payloadType := SimpleTransactionPT + if pending { + payloadType = PendingTransactionPT } return Entry{ @@ -442,14 +449,13 @@ func GetActivityEntries(db *sql.DB, addresses []eth.Address, chainIDs []common.C // TODO: extend DB with status in order to filter by status. The status has to be extracted from the receipt upon downloading activityStatus := FinalizedAS activityType, filteredAddress := getActivityType(dbTrType) - entry = NewActivityEntryWithTransaction(SimpleTransactionPT, + entry = newActivityEntryWithSimpleTransaction( &transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(transferHash), Address: filteredAddress}, timestamp, activityType, activityStatus) } else if pendingHash != nil && chainID.Valid { activityStatus := PendingAS activityType, _ := getActivityType(dbTrType) - entry = NewActivityEntryWithTransaction(PendingTransactionPT, - &transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(pendingHash)}, + entry = newActivityEntryWithPendingTransaction(&transfer.TransactionIdentity{ChainID: common.ChainID(chainID.Int64), Hash: eth.BytesToHash(pendingHash)}, timestamp, activityType, activityStatus) } else if multiTxID.Valid { activityType := multiTransactionTypeToActivityType(transfer.MultiTransactionType(dbMtType.Byte)) diff --git a/services/wallet/transfer/log_parser.go b/services/wallet/common/log_parser.go similarity index 67% rename from services/wallet/transfer/log_parser.go rename to services/wallet/common/log_parser.go index 965ea55fb..f237eaeed 100644 --- a/services/wallet/transfer/log_parser.go +++ b/services/wallet/common/log_parser.go @@ -1,4 +1,6 @@ -package transfer +// Moved here because transactions package depends on accounts package which +// depends on appdatabase where this functionality is needed +package common import ( "fmt" @@ -6,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" ) @@ -17,21 +20,21 @@ type EventType string const ( // Transaction types - ethTransfer Type = "eth" - erc20Transfer Type = "erc20" - erc721Transfer Type = "erc721" - uniswapV2Swap Type = "uniswapV2Swap" - uniswapV3Swap Type = "uniswapV3Swap" + EthTransfer Type = "eth" + Erc20Transfer Type = "erc20" + Erc721Transfer Type = "erc721" + UniswapV2Swap Type = "uniswapV2Swap" + UniswapV3Swap Type = "uniswapV3Swap" unknownTransaction Type = "unknown" // Event types - erc20TransferEventType EventType = "erc20Event" - erc721TransferEventType EventType = "erc721Event" - uniswapV2SwapEventType EventType = "uniswapV2SwapEvent" - uniswapV3SwapEventType EventType = "uniswapV3SwapEvent" - unknownEventType EventType = "unknownEvent" + Erc20TransferEventType EventType = "erc20Event" + Erc721TransferEventType EventType = "erc721Event" + UniswapV2SwapEventType EventType = "uniswapV2SwapEvent" + UniswapV3SwapEventType EventType = "uniswapV3SwapEvent" + UnknownEventType EventType = "unknownEvent" - erc20_721TransferEventSignature = "Transfer(address,address,uint256)" + Erc20_721TransferEventSignature = "Transfer(address,address,uint256)" erc20TransferEventIndexedParameters = 3 // signature, from, to erc721TransferEventIndexedParameters = 4 // signature, from, to, tokenId @@ -47,39 +50,39 @@ var ( // Detect event type for a cetain item from the Events Log func GetEventType(log *types.Log) EventType { - erc20_721TransferEventSignatureHash := getEventSignatureHash(erc20_721TransferEventSignature) - uniswapV2SwapEventSignatureHash := getEventSignatureHash(uniswapV2SwapEventSignature) - uniswapV3SwapEventSignatureHash := getEventSignatureHash(uniswapV3SwapEventSignature) + erc20_721TransferEventSignatureHash := GetEventSignatureHash(Erc20_721TransferEventSignature) + uniswapV2SwapEventSignatureHash := GetEventSignatureHash(uniswapV2SwapEventSignature) + uniswapV3SwapEventSignatureHash := GetEventSignatureHash(uniswapV3SwapEventSignature) if len(log.Topics) > 0 { switch log.Topics[0] { case erc20_721TransferEventSignatureHash: switch len(log.Topics) { case erc20TransferEventIndexedParameters: - return erc20TransferEventType + return Erc20TransferEventType case erc721TransferEventIndexedParameters: - return erc721TransferEventType + return Erc721TransferEventType } case uniswapV2SwapEventSignatureHash: - return uniswapV2SwapEventType + return UniswapV2SwapEventType case uniswapV3SwapEventSignatureHash: - return uniswapV3SwapEventType + return UniswapV3SwapEventType } } - return unknownEventType + return UnknownEventType } func EventTypeToSubtransactionType(eventType EventType) Type { switch eventType { - case erc20TransferEventType: - return erc20Transfer - case erc721TransferEventType: - return erc721Transfer - case uniswapV2SwapEventType: - return uniswapV2Swap - case uniswapV3SwapEventType: - return uniswapV3Swap + case Erc20TransferEventType: + return Erc20Transfer + case Erc721TransferEventType: + return Erc721Transfer + case UniswapV2SwapEventType: + return UniswapV2Swap + case UniswapV3SwapEventType: + return UniswapV3Swap } return unknownTransaction @@ -88,20 +91,20 @@ func EventTypeToSubtransactionType(eventType EventType) Type { func GetFirstEvent(logs []*types.Log) (EventType, *types.Log) { for _, log := range logs { eventType := GetEventType(log) - if eventType != unknownEventType { + if eventType != UnknownEventType { return eventType, log } } - return unknownEventType, nil + return UnknownEventType, nil } func IsTokenTransfer(logs []*types.Log) bool { eventType, _ := GetFirstEvent(logs) - return eventType == erc20TransferEventType + return eventType == Erc20TransferEventType } -func parseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) { +func ParseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) { amount = new(big.Int) if len(ethlog.Topics) < 3 { log.Warn("not enough topics for erc20 transfer", "topics", ethlog.Topics) @@ -126,7 +129,7 @@ func parseErc20TransferLog(ethlog *types.Log) (from, to common.Address, amount * return } -func parseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID *big.Int) { +func ParseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID *big.Int) { tokenID = new(big.Int) if len(ethlog.Topics) < 4 { log.Warn("not enough topics for erc721 transfer", "topics", ethlog.Topics) @@ -151,7 +154,7 @@ func parseErc721TransferLog(ethlog *types.Log) (from, to common.Address, tokenID return } -func parseUniswapV2Log(ethlog *types.Log) (pairAddress common.Address, from common.Address, to common.Address, amount0In *big.Int, amount1In *big.Int, amount0Out *big.Int, amount1Out *big.Int, err error) { +func ParseUniswapV2Log(ethlog *types.Log) (pairAddress common.Address, from common.Address, to common.Address, amount0In *big.Int, amount1In *big.Int, amount0Out *big.Int, amount1Out *big.Int, err error) { amount0In = new(big.Int) amount1In = new(big.Int) amount0Out = new(big.Int) @@ -199,7 +202,7 @@ func readInt256(b []byte) *big.Int { return ret } -func parseUniswapV3Log(ethlog *types.Log) (poolAddress common.Address, sender common.Address, recipient common.Address, amount0 *big.Int, amount1 *big.Int, err error) { +func ParseUniswapV3Log(ethlog *types.Log) (poolAddress common.Address, sender common.Address, recipient common.Address, amount0 *big.Int, amount1 *big.Int, err error) { amount0 = new(big.Int) amount1 = new(big.Int) @@ -229,3 +232,35 @@ func parseUniswapV3Log(ethlog *types.Log) (poolAddress common.Address, sender co return } + +func GetEventSignatureHash(signature string) common.Hash { + return crypto.Keccak256Hash([]byte(signature)) +} + +func ExtractTokenIdentity(dbEntryType Type, log *types.Log, tx *types.Transaction) (correctType Type, tokenAddress *common.Address, tokenID *big.Int, value *big.Int) { + // erc721 transfers share signature with erc20 ones, so they both used to be categorized as erc20 + // by the Downloader. We fix this here since they might be mis-categorized in the db. + if dbEntryType == Erc20Transfer { + eventType := GetEventType(log) + correctType = EventTypeToSubtransactionType(eventType) + } else { + correctType = dbEntryType + } + + switch correctType { + case EthTransfer: + if tx != nil { + value = new(big.Int).Set(tx.Value()) + } + case Erc20Transfer: + tokenAddress = new(common.Address) + *tokenAddress = log.Address + _, _, value = ParseErc20TransferLog(log) + case Erc721Transfer: + tokenAddress = new(common.Address) + *tokenAddress = log.Address + _, _, tokenID = ParseErc721TransferLog(log) + } + + return +} diff --git a/services/wallet/transfer/commands.go b/services/wallet/transfer/commands.go index 8b71111a7..35c4ce4d1 100644 --- a/services/wallet/transfer/commands.go +++ b/services/wallet/transfer/commands.go @@ -11,8 +11,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/status-im/status-go/rpc/chain" "github.com/status-im/status-go/services/wallet/async" + 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" ) @@ -450,7 +452,7 @@ func (c *transfersCommand) checkAndProcessPendingMultiTx(subTx *Transfer) (Multi func (c *transfersCommand) checkAndProcessSwapMultiTx(ctx context.Context, subTx *Transfer) (MultiTransactionIDType, error) { switch subTx.Type { // If the Tx contains any uniswapV2Swap/uniswapV3Swap subTx, generate a Swap multiTx - case uniswapV2Swap, uniswapV3Swap: + case w_common.UniswapV2Swap, w_common.UniswapV3Swap: multiTransaction, err := buildUniswapSwapMultitransaction(ctx, c.chainClient, c.tokenManager, subTx) if err != nil { return NoMultiTransactionID, err diff --git a/services/wallet/transfer/database.go b/services/wallet/transfer/database.go index c36da5ffd..0a56c2dc3 100644 --- a/services/wallet/transfer/database.go +++ b/services/wallet/transfer/database.go @@ -12,7 +12,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/status-im/status-go/services/wallet/bigint" + w_common "github.com/status-im/status-go/services/wallet/common" + "github.com/status-im/status-go/sqlite" ) // DBHeader fields from header that are stored in database. @@ -25,7 +28,7 @@ type DBHeader struct { Address common.Address // Head is true if the block was a head at the time it was pulled from chain. Head bool - // Loaded is true if trasfers from this block has been already fetched + // Loaded is true if transfers from this block have been already fetched Loaded bool } @@ -364,15 +367,15 @@ func insertBlocksWithTransactions(chainID uint64, creator statementCreator, acco return err } updateTx, err := creator.Prepare(`UPDATE transfers - SET log = ? + SET log = ?, log_index = ? WHERE network_id = ? AND address = ? AND hash = ?`) if err != nil { return err } insertTx, err := creator.Prepare(`INSERT OR IGNORE - INTO transfers (network_id, address, sender, hash, blk_number, blk_hash, type, timestamp, log, loaded) - VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, 0)`) + INTO transfers (network_id, address, sender, hash, blk_number, blk_hash, type, timestamp, log, loaded, log_index) + VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, 0, ?)`) if err != nil { return err } @@ -383,7 +386,12 @@ func insertBlocksWithTransactions(chainID uint64, creator statementCreator, acco return err } for _, transaction := range header.PreloadedTransactions { - res, err := updateTx.Exec(&JSONBlob{transaction.Log}, chainID, account, transaction.ID) + var logIndex *uint + if transaction.Log != nil { + logIndex = new(uint) + *logIndex = transaction.Log.Index + } + res, err := updateTx.Exec(&JSONBlob{transaction.Log}, logIndex, chainID, account, transaction.ID) if err != nil { return err } @@ -395,9 +403,9 @@ func insertBlocksWithTransactions(chainID uint64, creator statementCreator, acco continue } - _, err = insertTx.Exec(chainID, account, account, transaction.ID, (*bigint.SQLBigInt)(header.Number), header.Hash, erc20Transfer, &JSONBlob{transaction.Log}) + _, err = insertTx.Exec(chainID, account, account, transaction.ID, (*bigint.SQLBigInt)(header.Number), header.Hash, w_common.Erc20Transfer, &JSONBlob{transaction.Log}, logIndex) if err != nil { - log.Error("error saving erc20transfer", "err", err) + log.Error("error saving Erc20transfer", "err", err) return err } } @@ -407,14 +415,70 @@ func insertBlocksWithTransactions(chainID uint64, creator statementCreator, acco func updateOrInsertTransfers(chainID uint64, creator statementCreator, transfers []Transfer) error { insert, err := creator.Prepare(`INSERT OR REPLACE INTO transfers - (network_id, hash, blk_hash, blk_number, timestamp, address, tx, sender, receipt, log, type, loaded, base_gas_fee, multi_transaction_id) + (network_id, hash, blk_hash, blk_number, timestamp, address, tx, sender, receipt, log, type, loaded, base_gas_fee, multi_transaction_id, + 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) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`) + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { return err } for _, t := range 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) + var receiptType *uint8 + var txHash, blockHash *common.Hash + var receiptStatus, cumulativeGasUsed, gasUsed *uint64 + var contractAddress *common.Address + var transactionIndex, logIndex *uint + + if t.Receipt != nil { + receiptType = &t.Receipt.Type + receiptStatus = &t.Receipt.Status + txHash = &t.Receipt.TxHash + if t.Log != nil { + logIndex = new(uint) + *logIndex = t.Log.Index + } + blockHash = &t.Receipt.BlockHash + cumulativeGasUsed = &t.Receipt.CumulativeGasUsed + contractAddress = &t.Receipt.ContractAddress + gasUsed = &t.Receipt.GasUsed + transactionIndex = &t.Receipt.TransactionIndex + } + + var txProtected *bool + var txGas, txNonce, txSize *uint64 + var txGasPrice, txGasTipCap, txGasFeeCap *int64 + var txType *uint8 + var txValue *string + var tokenAddress *common.Address + var tokenID *big.Int + if t.Transaction != nil { + var value *big.Int + if t.Log != nil { + _, tokenAddress, tokenID, value = w_common.ExtractTokenIdentity(t.Type, t.Log, t.Transaction) + } else { + value = new(big.Int).Set(t.Transaction.Value()) + } + + txType = new(uint8) + *txType = t.Transaction.Type() + txProtected = new(bool) + *txProtected = t.Transaction.Protected() + txGas = new(uint64) + *txGas = t.Transaction.Gas() + txGasPrice = sqlite.BigIntToClampedInt64(t.Transaction.GasPrice()) + txGasTipCap = sqlite.BigIntToClampedInt64(t.Transaction.GasTipCap()) + txGasFeeCap = sqlite.BigIntToClampedInt64(t.Transaction.GasFeeCap()) + txValue = sqlite.BigIntToPadded128BitsStr(value) + txNonce = new(uint64) + *txNonce = t.Transaction.Nonce() + txSize = new(uint64) + *txSize = uint64(t.Transaction.Size()) + } + + _, 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)) 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 diff --git a/services/wallet/transfer/database_test.go b/services/wallet/transfer/database_test.go index a4fe302c9..fc4475c2e 100644 --- a/services/wallet/transfer/database_test.go +++ b/services/wallet/transfer/database_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/status-im/status-go/appdatabase" + w_common "github.com/status-im/status-go/services/wallet/common" ) func setupTestDB(t *testing.T) (*Database, *BlockDAO, func()) { @@ -28,11 +29,11 @@ func TestDBProcessBlocks(t *testing.T) { from := big.NewInt(0) to := big.NewInt(10) blocks := []*DBHeader{ - &DBHeader{ + { Number: big.NewInt(1), Hash: common.Hash{1}, }, - &DBHeader{ + { Number: big.NewInt(2), Hash: common.Hash{2}, }} @@ -48,7 +49,7 @@ func TestDBProcessBlocks(t *testing.T) { transfers := []Transfer{ { ID: common.Hash{1}, - Type: ethTransfer, + Type: w_common.EthTransfer, BlockHash: common.Hash{2}, BlockNumber: big.NewInt(1), Address: common.Address{1}, @@ -71,7 +72,7 @@ func TestDBProcessTransfer(t *testing.T) { transfers := []Transfer{ { ID: common.Hash{1}, - Type: ethTransfer, + Type: w_common.EthTransfer, BlockHash: header.Hash, BlockNumber: header.Number, Transaction: tx, @@ -115,7 +116,7 @@ func TestDBReorgTransfers(t *testing.T) { } require.NoError(t, db.ProcessBlocks(777, original.Address, original.Number, lastBlock, []*DBHeader{original})) require.NoError(t, db.ProcessTransfers(777, []Transfer{ - {ethTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, true, 1777, common.Address{1}, rcpt, nil, "2100", NoMultiTransactionID}, + {w_common.EthTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, true, 1777, common.Address{1}, rcpt, nil, "2100", NoMultiTransactionID}, }, []*DBHeader{})) nonce = int64(0) lastBlock = &Block{ @@ -125,7 +126,7 @@ func TestDBReorgTransfers(t *testing.T) { } require.NoError(t, db.ProcessBlocks(777, replaced.Address, replaced.Number, lastBlock, []*DBHeader{replaced})) require.NoError(t, db.ProcessTransfers(777, []Transfer{ - {ethTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, true, 1777, common.Address{1}, rcpt, nil, "2100", NoMultiTransactionID}, + {w_common.EthTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, true, 1777, common.Address{1}, rcpt, nil, "2100", NoMultiTransactionID}, }, []*DBHeader{original})) all, err := db.GetTransfers(777, big.NewInt(0), nil) @@ -151,7 +152,7 @@ func TestDBGetTransfersFromBlock(t *testing.T) { receipt.Logs = []*types.Log{} transfer := Transfer{ ID: tx.Hash(), - Type: ethTransfer, + Type: w_common.EthTransfer, BlockNumber: header.Number, BlockHash: header.Hash, Transaction: tx, diff --git a/services/wallet/transfer/downloader.go b/services/wallet/transfer/downloader.go index d56bc096a..b74824bf7 100644 --- a/services/wallet/transfer/downloader.go +++ b/services/wallet/transfer/downloader.go @@ -13,7 +13,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/status-im/status-go/rpc/chain" + w_common "github.com/status-im/status-go/services/wallet/common" ) type MultiTransactionIDType int64 @@ -22,10 +24,6 @@ const ( NoMultiTransactionID = MultiTransactionIDType(0) ) -func getEventSignatureHash(signature string) common.Hash { - return crypto.Keccak256Hash([]byte(signature)) -} - func getLogSubTxID(log types.Log) common.Hash { // Get unique ID by using TxHash and log index index := [4]byte{} @@ -45,7 +43,7 @@ var ( // To be converted into one or many Transfer objects post-indexing. type PreloadedTransaction struct { NetworkID uint64 - Type Type `json:"type"` + Type w_common.Type `json:"type"` ID common.Hash `json:"-"` Address common.Address `json:"address"` BlockNumber *big.Int `json:"blockNumber"` @@ -61,7 +59,7 @@ type PreloadedTransaction struct { // Transfer stores information about transfer. // A Transfer represents a plain ETH transfer or some token activity inside a Transaction type Transfer struct { - Type Type `json:"type"` + Type w_common.Type `json:"type"` ID common.Hash `json:"-"` Address common.Address `json:"address"` BlockNumber *big.Int `json:"blockNumber"` @@ -114,8 +112,8 @@ func getTransferByHash(ctx context.Context, client *chain.ClientWithFallback, si return nil, err } - eventType, transactionLog := GetFirstEvent(receipt.Logs) - transactionType := EventTypeToSubtransactionType(eventType) + eventType, transactionLog := w_common.GetFirstEvent(receipt.Logs) + transactionType := w_common.EventTypeToSubtransactionType(eventType) from, err := types.Sender(signer, transaction) @@ -184,7 +182,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc return nil, err } - eventType, _ := GetFirstEvent(receipt.Logs) + eventType, _ := w_common.GetFirstEvent(receipt.Logs) baseGasFee, err := d.chainClient.GetBaseFeeFromBlock(blk.Number()) if err != nil { @@ -193,9 +191,9 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc // If the transaction is not already some known transfer type, add it // to the list as a plain eth transfer - if eventType == unknownEventType { + if eventType == w_common.UnknownEventType { rst = append(rst, Transfer{ - Type: ethTransfer, + Type: w_common.EthTransfer, ID: tx.Hash(), Address: address, BlockNumber: blk.Number(), @@ -218,7 +216,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc // NewERC20TransfersDownloader returns new instance. func NewERC20TransfersDownloader(client *chain.ClientWithFallback, accounts []common.Address, signer types.Signer) *ERC20TransfersDownloader { - signature := getEventSignatureHash(erc20_721TransferEventSignature) + signature := w_common.GetEventSignatureHash(w_common.Erc20_721TransferEventSignature) return &ERC20TransfersDownloader{ client: client, @@ -230,7 +228,7 @@ func NewERC20TransfersDownloader(client *chain.ClientWithFallback, accounts []co // ERC20TransfersDownloader is a downloader for erc20 and erc721 tokens transfers. // Since both transaction types share the same signature, both will be assigned -// type erc20Transfer. Until the downloader gets refactored and a migration of the +// type Erc20Transfer. Until the downloader gets refactored and a migration of the // database gets implemented, differentiation between erc20 and erc721 will handled // in the controller. type ERC20TransfersDownloader struct { @@ -291,28 +289,28 @@ func (d *ETHDownloader) subTransactionsFromTransactionHash(parent context.Contex rst := make([]Transfer, 0, len(receipt.Logs)) for _, log := range receipt.Logs { - eventType := GetEventType(log) + eventType := w_common.GetEventType(log) // Only add ERC20/ERC721 transfers from/to the given account // Other types of events get always added mustAppend := false switch eventType { - case erc20TransferEventType: - from, to, _ := parseErc20TransferLog(log) + case w_common.Erc20TransferEventType: + from, to, _ := w_common.ParseErc20TransferLog(log) if from == address || to == address { mustAppend = true } - case erc721TransferEventType: - from, to, _ := parseErc721TransferLog(log) + case w_common.Erc721TransferEventType: + from, to, _ := w_common.ParseErc721TransferLog(log) if from == address || to == address { mustAppend = true } - case uniswapV2SwapEventType, uniswapV3SwapEventType: + case w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType: mustAppend = true } if mustAppend { transfer := Transfer{ - Type: EventTypeToSubtransactionType(eventType), + Type: w_common.EventTypeToSubtransactionType(eventType), ID: getLogSubTxID(*log), Address: address, BlockNumber: new(big.Int).SetUint64(log.BlockNumber), @@ -359,7 +357,7 @@ func (d *ERC20TransfersDownloader) blocksFromLogs(parent context.Context, logs [ ID: id, From: address, Loaded: false, - Type: erc20Transfer, + Type: w_common.Erc20Transfer, Log: &l, BaseGasFees: baseGasFee, }}, diff --git a/services/wallet/transfer/query.go b/services/wallet/transfer/query.go index 2b3bfe512..8cf425ae1 100644 --- a/services/wallet/transfer/query.go +++ b/services/wallet/transfer/query.go @@ -53,7 +53,7 @@ func (q *transfersQuery) addWhereSeparator(separator SeparatorType) { type SeparatorType int -// Beware if changing this enum please update addWhereSeparator as well +// Beware: please update addWhereSeparator if changing this enum const ( NoSeparator SeparatorType = iota + 1 OrSeparator diff --git a/services/wallet/transfer/swap_identifier.go b/services/wallet/transfer/swap_identifier.go index 70720caff..a29b44dc9 100644 --- a/services/wallet/transfer/swap_identifier.go +++ b/services/wallet/transfer/swap_identifier.go @@ -9,9 +9,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + uniswapv2 "github.com/status-im/status-go/contracts/uniswapV2" uniswapv3 "github.com/status-im/status-go/contracts/uniswapV3" "github.com/status-im/status-go/rpc/chain" + w_common "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/token" ) @@ -86,7 +88,7 @@ func identifyUniswapV2Asset(tokenManager *token.Manager, chainID uint64, amount0 } func fetchUniswapV2Info(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) { - pairAddress, _, _, amount0In, amount1In, amount0Out, amount1Out, err := parseUniswapV2Log(log) + pairAddress, _, _, amount0In, amount1In, amount0Out, amount1Out, err := w_common.ParseUniswapV2Log(log) if err != nil { return } @@ -155,7 +157,7 @@ func identifyUniswapV3Assets(tokenManager *token.Manager, chainID uint64, amount } func fetchUniswapV3Info(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) { - poolAddress, _, _, amount0, amount1, err := parseUniswapV3Log(log) + poolAddress, _, _, amount0, amount1, err := w_common.ParseUniswapV3Log(log) if err != nil { return } @@ -183,11 +185,11 @@ func fetchUniswapV3Info(ctx context.Context, client *chain.ClientWithFallback, t return } -func fetchUniswapInfo(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log, logType EventType) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) { +func fetchUniswapInfo(ctx context.Context, client *chain.ClientWithFallback, tokenManager *token.Manager, log *types.Log, logType w_common.EventType) (fromAsset string, fromAmount *hexutil.Big, toAsset string, toAmount *hexutil.Big, err error) { switch logType { - case uniswapV2SwapEventType: + case w_common.UniswapV2SwapEventType: return fetchUniswapV2Info(ctx, client, tokenManager, log) - case uniswapV3SwapEventType: + case w_common.UniswapV3SwapEventType: return fetchUniswapV3Info(ctx, client, tokenManager, log) } err = fmt.Errorf("wrong log type %s", logType) @@ -204,12 +206,12 @@ func buildUniswapSwapMultitransaction(ctx context.Context, client *chain.ClientW } var firstSwapLog, lastSwapLog *types.Log - var firstSwapLogType, lastSwapLogType EventType + var firstSwapLogType, lastSwapLogType w_common.EventType for _, ethlog := range transfer.Receipt.Logs { - logType := GetEventType(ethlog) + logType := w_common.GetEventType(ethlog) switch logType { - case uniswapV2SwapEventType, uniswapV3SwapEventType: + case w_common.UniswapV2SwapEventType, w_common.UniswapV3SwapEventType: if firstSwapLog == nil { firstSwapLog = ethlog firstSwapLogType = logType diff --git a/services/wallet/transfer/view.go b/services/wallet/transfer/view.go index 9b4b3408e..f6419b5b7 100644 --- a/services/wallet/transfer/view.go +++ b/services/wallet/transfer/view.go @@ -5,13 +5,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + w_common "github.com/status-im/status-go/services/wallet/common" ) // View stores only fields used by a client and ensures that all relevant fields are // encoded in hex. type View struct { ID common.Hash `json:"id"` - Type Type `json:"type"` + Type w_common.Type `json:"type"` Address common.Address `json:"address"` BlockNumber *hexutil.Big `json:"blockNumber"` BlockHash common.Hash `json:"blockhash"` @@ -27,8 +28,8 @@ type View struct { TxStatus hexutil.Uint64 `json:"txStatus"` Input hexutil.Bytes `json:"input"` TxHash common.Hash `json:"txHash"` - Value *hexutil.Big `json:"value"` // Only used for Type ethTransfer and erc20Transfer - TokenID *hexutil.Big `json:"tokenId"` // Only used for Type erc721Transfer + Value *hexutil.Big `json:"value"` // Only used for Type EthTransfer and Erc20Transfer + TokenID *hexutil.Big `json:"tokenId"` // Only used for Type Erc721Transfer From common.Address `json:"from"` To common.Address `json:"to"` Contract common.Address `json:"contract"` @@ -41,7 +42,7 @@ func castToTransferViews(transfers []Transfer) []View { views := make([]View, 0, len(transfers)) for _, tx := range transfers { switch tx.Type { - case ethTransfer, erc20Transfer, erc721Transfer: + case w_common.EthTransfer, w_common.Erc20Transfer, w_common.Erc721Transfer: view := CastToTransferView(tx) views = append(views, view) } @@ -82,20 +83,20 @@ func CastToTransferView(t Transfer) View { tokenID := new(hexutil.Big) switch view.Type { - case ethTransfer: + case w_common.EthTransfer: view.From = t.From if t.Transaction.To() != nil { view.To = *t.Transaction.To() } value = (*hexutil.Big)(t.Transaction.Value()) view.Contract = t.Receipt.ContractAddress - case erc20Transfer: + case w_common.Erc20Transfer: view.Contract = t.Log.Address - from, to, valueInt := parseErc20TransferLog(t.Log) + from, to, valueInt := w_common.ParseErc20TransferLog(t.Log) view.From, view.To, value = from, to, (*hexutil.Big)(valueInt) - case erc721Transfer: + case w_common.Erc721Transfer: view.Contract = t.Log.Address - from, to, tokenIDInt := parseErc721TransferLog(t.Log) + from, to, tokenIDInt := w_common.ParseErc721TransferLog(t.Log) view.From, view.To, tokenID = from, to, (*hexutil.Big)(tokenIDInt) } @@ -106,12 +107,12 @@ func CastToTransferView(t Transfer) View { return view } -func getFixedTransferType(tx Transfer) Type { +func getFixedTransferType(tx Transfer) w_common.Type { // erc721 transfers share signature with erc20 ones, so they both used to be categorized as erc20 // by the Downloader. We fix this here since they might be mis-categorized in the db. - if tx.Type == erc20Transfer { - eventType := GetEventType(tx.Log) - return EventTypeToSubtransactionType(eventType) + if tx.Type == w_common.Erc20Transfer { + eventType := w_common.GetEventType(tx.Log) + return w_common.EventTypeToSubtransactionType(eventType) } return tx.Type } diff --git a/sqlite/fields.go b/sqlite/fields.go index 59413b903..19ddb20c2 100644 --- a/sqlite/fields.go +++ b/sqlite/fields.go @@ -4,28 +4,44 @@ import ( "database/sql/driver" "encoding/json" "errors" + "fmt" + "math" + "math/big" "reflect" ) // JSONBlob type for marshaling/unmarshaling inner type to json. type JSONBlob struct { - Data interface{} + Data interface{} + Valid bool } // Scan implements interface. func (blob *JSONBlob) Scan(value interface{}) error { dataVal := reflect.ValueOf(blob.Data) + blob.Valid = false if value == nil || dataVal.Kind() == reflect.Ptr && dataVal.IsNil() { return nil } - bytes, ok := value.([]byte) + + var bytes []byte + ok := true + switch v := value.(type) { + case []byte: + bytes, ok = value.([]byte) + case string: + bytes = []byte(v) + default: + ok = false + } if !ok { - return errors.New("not a byte slice") + return errors.New("not a byte slice or string") } if len(bytes) == 0 { return nil } err := json.Unmarshal(bytes, blob.Data) + blob.Valid = err == nil return err } @@ -45,3 +61,29 @@ func (blob *JSONBlob) Value() (driver.Value, error) { return json.Marshal(blob.Data) } + +func BigIntToClampedInt64(val *big.Int) *int64 { + if val == nil { + return nil + } + var v int64 + if val.IsInt64() { + v = val.Int64() + } else { + v = math.MaxInt64 + } + return &v +} + +// BigIntToPadded128BitsStr converts a big.Int to a string, padding it with 0 to account for 128 bits size +// Returns nil if input val is nil +// This should work to sort and compare big.Ints values in SQLite +func BigIntToPadded128BitsStr(val *big.Int) *string { + if val == nil { + return nil + } + hexStr := val.Text(16) + res := new(string) + *res = fmt.Sprintf("%032s", hexStr) + return res +} diff --git a/sqlite/fields_test.go b/sqlite/fields_test.go new file mode 100644 index 000000000..5da203ce1 --- /dev/null +++ b/sqlite/fields_test.go @@ -0,0 +1,59 @@ +package sqlite + +import ( + "math/big" + "testing" +) + +func strToPtr(s string) *string { + res := new(string) + *res = s + return res +} + +func TestBigIntToPadded128BitsStr(t *testing.T) { + testCases := []struct { + name string + input *big.Int + expected *string + }{ + { + name: "case small", + input: big.NewInt(123456), + expected: strToPtr("0000000000000000000000000001e240"), + }, + { + name: "case zero", + input: big.NewInt(0), + expected: strToPtr("00000000000000000000000000000000"), + }, + { + name: "case very large", + input: new(big.Int).Exp(big.NewInt(10), big.NewInt(26), nil), + expected: strToPtr("000000000052b7d2dcc80cd2e4000000"), + }, + { + name: "case max", + input: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1)), + expected: strToPtr("ffffffffffffffffffffffffffffffff"), + }, + { + name: "case 3", + input: nil, + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := BigIntToPadded128BitsStr(tc.input) + if result != nil && tc.expected != nil { + if *result != *tc.expected { + t.Errorf("expected %s, got %s", *tc.expected, *result) + } + } else if result != nil || tc.expected != nil { + t.Errorf("expected %v, got %v", tc.expected, result) + } + }) + } +} diff --git a/sqlite/migrate.go b/sqlite/migrate.go index 29b6ff960..2fba2b49d 100644 --- a/sqlite/migrate.go +++ b/sqlite/migrate.go @@ -2,37 +2,178 @@ package sqlite import ( "database/sql" + "fmt" + "sort" "github.com/status-im/migrate/v4" "github.com/status-im/migrate/v4/database/sqlcipher" bindata "github.com/status-im/migrate/v4/source/go_bindata" ) -// Migrate database using provided resources. -func Migrate(db *sql.DB, resources *bindata.AssetSource) error { +type PostStep struct { + Version uint + CustomMigration func(tx *sql.Tx) error + RollBackVersion uint +} + +var migrationTable = "status_go_" + sqlcipher.DefaultMigrationsTable + +// Migrate database with option to augment the migration steps with additional processing using the customSteps +// parameter. For each PostStep entry in customSteps the CustomMigration will be called after the migration step +// with the matching Version number has been executed. If the CustomMigration returns an error, the migration process +// is aborted. In case the custom step failures the migrations are run down to RollBackVersion if > 0. +// +// The recommended way to create a custom migration is by providing empty and versioned run/down sql files as markers. +// Then running all the SQL code inside the same transaction to transform and commit provides the possibility +// to completely rollback the migration in case of failure, avoiding to leave the DB in an inconsistent state. +// +// Marker migrations can be created by using PostStep structs with specific Version numbers and a callback function, +// even when no accompanying SQL migration is needed. This can be used to trigger Go code at specific points +// during the migration process. +// +// Caution: This mechanism should be used as a last resort. Prefer data migration using SQL migration files +// whenever possible to ensure consistency and compatibility with standard migration tools. +// +// untilVersion, for testing purposes optional parameter, can be used to limit the migration to a specific version. +// Pass nil to migrate to the latest available version. +func Migrate(db *sql.DB, resources *bindata.AssetSource, customSteps []PostStep, untilVersion *uint) error { source, err := bindata.WithInstance(resources) if err != nil { - return err + return fmt.Errorf("failed to create bindata migration source: %w", err) } driver, err := sqlcipher.WithInstance(db, &sqlcipher.Config{ - MigrationsTable: "status_go_" + sqlcipher.DefaultMigrationsTable, + MigrationsTable: migrationTable, }) if err != nil { - return err + return fmt.Errorf("failed to create sqlcipher driver: %w", err) } - m, err := migrate.NewWithInstance( - "go-bindata", - source, - "sqlcipher", - driver) + m, err := migrate.NewWithInstance("go-bindata", source, "sqlcipher", driver) + if err != nil { + return fmt.Errorf("failed to create migration instance: %w", err) + } + + if len(customSteps) == 0 { + return runRemainingMigrations(m, untilVersion) + } + + sort.Slice(customSteps, func(i, j int) bool { + return customSteps[i].Version < customSteps[j].Version + }) + + lastVersion, err := getCurrentVersion(m, db) if err != nil { return err } - if err = m.Up(); err != migrate.ErrNoChange { + customIndex := 0 + // ignore processed versions + for customIndex < len(customSteps) && customSteps[customIndex].Version <= lastVersion { + customIndex++ + } + + if err := runCustomMigrations(m, db, customSteps, customIndex, untilVersion); err != nil { return err } + + return runRemainingMigrations(m, untilVersion) +} + +// runCustomMigrations performs source migrations from current to each custom steps, then runs custom migration callback +// until it executes all custom migrations or an error occurs and it tries to rollback to RollBackVersion if > 0. +func runCustomMigrations(m *migrate.Migrate, db *sql.DB, customSteps []PostStep, customIndex int, untilVersion *uint) error { + for customIndex < len(customSteps) && (untilVersion == nil || customSteps[customIndex].Version <= *untilVersion) { + customStep := customSteps[customIndex] + + if err := m.Migrate(customStep.Version); err != nil && err != migrate.ErrNoChange { + return fmt.Errorf("failed to migrate to version %d: %w", customStep.Version, err) + } + + if err := runCustomMigrationStep(db, customStep, m); err != nil { + return err + } + + customIndex++ + } return nil } + +func runCustomMigrationStep(db *sql.DB, customStep PostStep, m *migrate.Migrate) error { + sqlTx, err := db.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + + if err := customStep.CustomMigration(sqlTx); err != nil { + _ = sqlTx.Rollback() + return rollbackCustomMigration(m, customStep, err) + } + + if err := sqlTx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + return nil +} + +func rollbackCustomMigration(m *migrate.Migrate, customStep PostStep, customErr error) error { + if customStep.RollBackVersion > 0 { + err := m.Migrate(customStep.RollBackVersion) + newV, _, _ := m.Version() + if err != nil { + return fmt.Errorf("failed to rollback migration to version %d: %w", customStep.RollBackVersion, err) + } + return fmt.Errorf("custom migration step failed for version %d. Successfully rolled back migration to version %d: %w", customStep.Version, newV, customErr) + } + return fmt.Errorf("custom migration step failed for version %d: %w", customStep.Version, customErr) +} + +func runRemainingMigrations(m *migrate.Migrate, untilVersion *uint) error { + if untilVersion != nil { + if err := m.Migrate(*untilVersion); err != nil && err != migrate.ErrNoChange { + return fmt.Errorf("failed to migrate to version %d: %w", *untilVersion, err) + } + } else { + if err := m.Up(); err != nil && err != migrate.ErrNoChange { + return fmt.Errorf("failed to migrate up: %w", err) + } + } + return nil +} + +func getCurrentVersion(m *migrate.Migrate, db *sql.DB) (uint, error) { + lastVersion, dirty, err := m.Version() + if err != nil && err != migrate.ErrNilVersion { + return 0, fmt.Errorf("failed to get migration version: %w", err) + } + if dirty { + return 0, fmt.Errorf("DB is dirty after migration version %d", lastVersion) + } + if err == migrate.ErrNilVersion { + lastVersion, _, err = GetLastMigrationVersion(db) + return lastVersion, err + } + return lastVersion, nil +} + +// GetLastMigrationVersion returns the last migration version stored in the migration table. +// Returns 0 for version in case migrationTableExists is true +func GetLastMigrationVersion(db *sql.DB) (version uint, migrationTableExists bool, err error) { + // Check if the migration table exists + row := db.QueryRow("SELECT exists(SELECT name FROM sqlite_master WHERE type='table' AND name=?)", migrationTable) + migrationTableExists = false + err = row.Scan(&migrationTableExists) + if err != nil && err != sql.ErrNoRows { + return 0, false, err + } + + var lastMigration uint64 = 0 + if migrationTableExists { + row = db.QueryRow("SELECT version FROM status_go_schema_migrations") + err = row.Scan(&lastMigration) + if err != nil && err != sql.ErrNoRows { + return 0, true, err + } + } + return uint(lastMigration), migrationTableExists, nil +}