From 9f6396f5d8c83f0a6a97ada56eb068ea41ebd5b8 Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 6 May 2026 23:02:04 +0100 Subject: [PATCH] feat: migrate Trade data to SQLite with TradeLegacy for backward compatibility (#3106) * feat: migrate Trade data to SQLite with TradeLegacy for backward compatibility * feat: persist full currency snapshots in sql storage, not raw-only [WIP] * feat: persist full trade currency snapshots in sql storage * refactor trade and currency handling for migration * Minor fixes --------- Co-authored-by: Omar Hatem --- cw_core/lib/db/sqlite.dart | 68 ++- cw_core/lib/wallet_info_legacy.dart | 62 ++- .../robots/transactions_page_robot.dart | 4 +- lib/core/backup_service.dart | 2 + lib/core/trade_monitor.dart | 14 +- lib/di.dart | 15 +- lib/entities/default_settings_migration.dart | 4 +- lib/entities/fs_migration.dart | 21 +- .../provider/chainflip_exchange_provider.dart | 74 +-- .../provider/changenow_exchange_provider.dart | 6 - lib/exchange/provider/exchange_provider.dart | 14 +- .../provider/exolix_exchange_provider.dart | 24 +- .../provider/jupiter_exchange_provider.dart | 2 - .../letsexchange_exchange_provider.dart | 34 +- .../near_Intents_exchange_provider.dart | 7 - .../provider/sideshift_exchange_provider.dart | 33 +- .../simpleswap_exchange_provider.dart | 12 +- .../stealth_ex_exchange_provider.dart | 6 - .../provider/swapsxyz_exchange_provider.dart | 6 - .../provider/swaptrade_exchange_provider.dart | 4 - .../provider/thorchain_exchange.provider.dart | 10 +- .../provider/trocador_exchange_provider.dart | 5 - .../provider/xoswap_exchange_provider.dart | 12 - lib/exchange/trade.dart | 506 +++++++----------- lib/exchange/trade_currency_snapshot.dart | 45 ++ lib/exchange/trade_legacy.dart | 168 ++++++ lib/exchange/trade_legacy.part.dart | 146 +++++ lib/main.dart | 31 +- .../assets_history/history_section.dart | 261 ++++----- .../assets_history/history_trade_tile.dart | 60 +-- .../dashboard/pages/transactions_page.dart | 4 +- .../exchange_trade/exchange_trade_page.dart | 2 +- .../swap_details_bottom_sheet.dart | 4 +- lib/store/dashboard/trades_store.dart | 30 +- lib/view_model/dashboard/trade_list_item.dart | 25 +- .../exchange/exchange_trade_view_model.dart | 59 +- .../exchange/exchange_view_model.dart | 9 +- lib/view_model/send/send_view_model.dart | 4 +- lib/view_model/trade_details_view_model.dart | 51 +- 39 files changed, 1011 insertions(+), 833 deletions(-) create mode 100644 lib/exchange/trade_currency_snapshot.dart create mode 100644 lib/exchange/trade_legacy.dart create mode 100644 lib/exchange/trade_legacy.part.dart diff --git a/cw_core/lib/db/sqlite.dart b/cw_core/lib/db/sqlite.dart index 8d716c449..311344a96 100644 --- a/cw_core/lib/db/sqlite.dart +++ b/cw_core/lib/db/sqlite.dart @@ -41,7 +41,7 @@ Future initDb({String? pathOverride}) async { } } await db?.close(); - db = await openDatabase(dbFile.path, version: 5, + db = await openDatabase(dbFile.path, version: 6, onUpgrade: (Database db, int oldVersion, int newVersion) async { printV("migrating: $oldVersion, $newVersion"); if (oldVersion <= 1) { @@ -93,6 +93,9 @@ CREATE TABLE IF NOT EXISTS BalanceCardStyleSettings ( if (oldVersion <= 4) { await _createBridgeTransferTable(db); } + if (oldVersion <= 5) { + await _createTradeTable(db); + } }, onCreate: (Database db, int version) async { await db.execute( @@ -191,10 +194,73 @@ CREATE TABLE BalanceCardStyleSettings ( ); '''); await _createBridgeTransferTable(db); + await _createTradeTable(db); } ); } +Future _createTradeTable(Database db) async { + await db.execute(''' +CREATE TABLE IF NOT EXISTS Trade ( + tradeId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + id TEXT NOT NULL, + providerRaw INTEGER NOT NULL DEFAULT 0, + fromTitle TEXT, + fromName TEXT, + fromTag TEXT, + fromFullName TEXT, + fromDecimals INTEGER, + fromRaw INTEGER, + fromIconPath TEXT, + fromFlatIconPath TEXT, + fromChainIconPath TEXT, + toTitle TEXT, + toName TEXT, + toTag TEXT, + toFullName TEXT, + toDecimals INTEGER, + toRaw INTEGER, + toIconPath TEXT, + toFlatIconPath TEXT, + toChainIconPath TEXT, + stateRaw TEXT NOT NULL DEFAULT '', + createdAt INTEGER, + expiredAt INTEGER, + amount TEXT NOT NULL DEFAULT '', + receiveAmount TEXT, + inputAddress TEXT, + extraId TEXT, + outputTransaction TEXT, + refundAddress TEXT, + walletId TEXT, + payoutAddress TEXT, + password TEXT, + providerId TEXT, + providerName TEXT, + fromWalletAddress TEXT, + memo TEXT, + txId TEXT, + isRefund INTEGER DEFAULT 0, + isSendAll INTEGER DEFAULT 0, + router TEXT, + needToRegisterInSwapXyz INTEGER DEFAULT 0, + sourceTokenAddress TEXT, + sourceTokenDecimals INTEGER, + routerData TEXT, + routerValue TEXT, + routerChainId INTEGER, + sourceTokenAmountRaw TEXT, + requiresTokenApproval INTEGER DEFAULT 0, + chainId INTEGER, + fee REAL +); +'''); + await db.execute(''' +CREATE UNIQUE INDEX IF NOT EXISTS idx_trade_id_unique +ON Trade (id); +'''); +} + Future> dumpDb() async { try { return await _dumpDb(); diff --git a/cw_core/lib/wallet_info_legacy.dart b/cw_core/lib/wallet_info_legacy.dart index a7e6bc31f..4c665124f 100644 --- a/cw_core/lib/wallet_info_legacy.dart +++ b/cw_core/lib/wallet_info_legacy.dart @@ -246,35 +246,35 @@ class WalletInfo extends HiveObject { Future migrateToSqlite() async { final di = newWi.DerivationInfo( id: 0, - derivationType: derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown, + derivationType: + derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown, derivationPath: derivationInfo?.derivationPath ?? derivationPath ?? '', ); final derivationInfoId = await di.save(); final walletInfo = newWi.WalletInfo( - 0, - id, - name, - type, - isRecovery, - restoreHeight, - timestamp, - dirPath, - path, - address, - yatEid, - yatLastUsedAddressRaw, - showIntroCakePayCard, - derivationInfoId, - hardwareWalletType, - parentAddress, - hashedWalletIdentifier, - isNonSeedWallet, - 0, - addressPageType, - false, - true, - null - ); + 0, + id, + name, + type, + isRecovery, + restoreHeight, + timestamp, + dirPath, + path, + address, + yatEid, + yatLastUsedAddressRaw, + showIntroCakePayCard, + derivationInfoId, + hardwareWalletType, + parentAddress, + hashedWalletIdentifier, + isNonSeedWallet, + 0, + addressPageType, + false, + true, + null); final wiId = await walletInfo.save(); for (final address in usedAddresses ?? []) { await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.used, address); @@ -289,8 +289,8 @@ class WalletInfo extends HiveObject { for (final address in addressInfos![i] ?? []) { await newWi.WalletInfoAddressInfo.insert( walletInfoId: wiId, - mapKey: i, - accountIndex: address.accountIndex??0, + mapKey: i, + accountIndex: address.accountIndex ?? 0, address: address.address, label: address.label, ); @@ -304,8 +304,12 @@ class WalletInfo extends HiveObject { final sw = Stopwatch()..start(); final list = box.values.toList(); for (final wi in list) { - await wi.migrateToSqlite(); - await wi.delete(); + try { + await wi.migrateToSqlite(); + await wi.delete(); + } catch (e) { + printV('Error migrating WalletInfo ${wi.id}: $e'); + } } printV('Migrating WalletInfo to SQLite: end (${sw.elapsedMilliseconds}ms)'); } diff --git a/integration_test/robots/transactions_page_robot.dart b/integration_test/robots/transactions_page_robot.dart index e659626ea..1365c54c2 100644 --- a/integration_test/robots/transactions_page_robot.dart +++ b/integration_test/robots/transactions_page_robot.dart @@ -385,8 +385,8 @@ class TransactionsPageRobot { Future _verifyTradeListItemDisplay(TradeListItem item) async { final keyId = 'trade_list_item_${item.trade.id}_key'; - final from = item.trade.from?.toString() ?? item.trade.userCurrencyFrom.toString(); - final to = item.trade.to?.toString() ?? item.trade.userCurrencyTo.toString(); + final from = item.trade.from?.toString() ?? ''; + final to = item.trade.to?.toString() ?? ''; //* ==============Confirm it has the right key for this item ======== commonTestCases.hasValueKey(keyId); diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 2db105c13..5daab7d45 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -19,6 +19,7 @@ import 'package:cake_wallet/entities/encrypt.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cake_wallet/exchange/trade_legacy.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_backup/backup.dart' as cake_backup; @@ -110,6 +111,7 @@ class $BackupService { Future verifyWallets() async { await performHiveMigration(); // for backups made before sqlite migration + await performTradeHiveMigration(_secureStorage); correctWallets = (await WalletInfo.getAll()).where((info) => availableWalletTypes.contains(info.type)).toList(); if (correctWallets.isEmpty) { diff --git a/lib/core/trade_monitor.dart b/lib/core/trade_monitor.dart index 07cd3eeca..593275709 100644 --- a/lib/core/trade_monitor.dart +++ b/lib/core/trade_monitor.dart @@ -20,7 +20,6 @@ import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart'; import 'package:cw_core/utils/print_verbose.dart'; -import 'package:hive/hive.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -30,13 +29,11 @@ class TradeMonitor { TradeMonitor({ required this.tradesStore, - required this.trades, required this.appStore, required this.preferences, }); final TradesStore tradesStore; - final Box trades; final AppStore appStore; final Map _tradeTimers = {}; final SharedPreferences preferences; @@ -54,7 +51,7 @@ class TradeMonitor { case ExchangeProviderDescription.exolix: return ExolixExchangeProvider(); case ExchangeProviderDescription.thorChain: - return ThorChainExchangeProvider(tradesStore: trades); + return ThorChainExchangeProvider(); case ExchangeProviderDescription.swapTrade: return SwapTradeExchangeProvider(); case ExchangeProviderDescription.letsExchange: @@ -62,7 +59,7 @@ class TradeMonitor { case ExchangeProviderDescription.stealthEx: return StealthExExchangeProvider(); case ExchangeProviderDescription.chainflip: - return ChainflipExchangeProvider(tradesStore: trades); + return ChainflipExchangeProvider(); case ExchangeProviderDescription.xoSwap: return XOSwapExchangeProvider(); case ExchangeProviderDescription.swapsXyz: @@ -187,12 +184,7 @@ class TradeMonitor { try { final updated = await provider.findTradeById(id: trade.id); - trade - ..stateRaw = updated.state.raw - ..receiveAmount = updated.receiveAmount ?? trade.receiveAmount - ..outputTransaction = updated.outputTransaction ?? trade.outputTransaction - ..userCurrencyToRaw = updated.userCurrencyToRaw - ..userCurrencyFromRaw = updated.userCurrencyFromRaw; + trade.mergeFindTradeByIdResult(updated); printV('Trade ${trade.id} updated: ${trade.state}'); await trade.save(); diff --git a/lib/di.dart b/lib/di.dart index a4b3c0fd1..50a7cefcf 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -329,7 +329,6 @@ var _isSetupFinished = false; late Box _nodeSource; late Box _powNodeSource; late Box _contactSource; -late Box _tradesSource; late Box