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 <omarh.ismail1@gmail.com>

David Adegoke committed May 6, 2026 at 23:02 UTC 9f6396f5d8c83f0a6a97ada56eb068ea41ebd5b8
39 files changed +1003 -825
cw_core/lib/db/sqlite.dart
+67 -1
@@ -41,7 +41,7 @@ Future<void> initDb({String? pathOverride}) async {
41 }
42 }
43 await db?.close();
44 - db = await openDatabase(dbFile.path, version: 5,
44 + db = await openDatabase(dbFile.path, version: 6,
45 onUpgrade: (Database db, int oldVersion, int newVersion) async {
46 printV("migrating: $oldVersion, $newVersion");
47 if (oldVersion <= 1) {
@@ -93,6 +93,9 @@ CREATE TABLE IF NOT EXISTS BalanceCardStyleSettings (
93 if (oldVersion <= 4) {
94 await _createBridgeTransferTable(db);
95 }
96 + if (oldVersion <= 5) {
97 + await _createTradeTable(db);
98 + }
99 },
100 onCreate: (Database db, int version) async {
101 await db.execute(
@@ -191,10 +194,73 @@ CREATE TABLE BalanceCardStyleSettings (
194 );
195 ''');
196 await _createBridgeTransferTable(db);
197 + await _createTradeTable(db);
198 }
199 );
200 }
201
202 +Future<void> _createTradeTable(Database db) async {
203 + await db.execute('''
204 +CREATE TABLE IF NOT EXISTS Trade (
205 + tradeId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
206 + id TEXT NOT NULL,
207 + providerRaw INTEGER NOT NULL DEFAULT 0,
208 + fromTitle TEXT,
209 + fromName TEXT,
210 + fromTag TEXT,
211 + fromFullName TEXT,
212 + fromDecimals INTEGER,
213 + fromRaw INTEGER,
214 + fromIconPath TEXT,
215 + fromFlatIconPath TEXT,
216 + fromChainIconPath TEXT,
217 + toTitle TEXT,
218 + toName TEXT,
219 + toTag TEXT,
220 + toFullName TEXT,
221 + toDecimals INTEGER,
222 + toRaw INTEGER,
223 + toIconPath TEXT,
224 + toFlatIconPath TEXT,
225 + toChainIconPath TEXT,
226 + stateRaw TEXT NOT NULL DEFAULT '',
227 + createdAt INTEGER,
228 + expiredAt INTEGER,
229 + amount TEXT NOT NULL DEFAULT '',
230 + receiveAmount TEXT,
231 + inputAddress TEXT,
232 + extraId TEXT,
233 + outputTransaction TEXT,
234 + refundAddress TEXT,
235 + walletId TEXT,
236 + payoutAddress TEXT,
237 + password TEXT,
238 + providerId TEXT,
239 + providerName TEXT,
240 + fromWalletAddress TEXT,
241 + memo TEXT,
242 + txId TEXT,
243 + isRefund INTEGER DEFAULT 0,
244 + isSendAll INTEGER DEFAULT 0,
245 + router TEXT,
246 + needToRegisterInSwapXyz INTEGER DEFAULT 0,
247 + sourceTokenAddress TEXT,
248 + sourceTokenDecimals INTEGER,
249 + routerData TEXT,
250 + routerValue TEXT,
251 + routerChainId INTEGER,
252 + sourceTokenAmountRaw TEXT,
253 + requiresTokenApproval INTEGER DEFAULT 0,
254 + chainId INTEGER,
255 + fee REAL
256 +);
257 +''');
258 + await db.execute('''
259 +CREATE UNIQUE INDEX IF NOT EXISTS idx_trade_id_unique
260 +ON Trade (id);
261 +''');
262 +}
263 +
264 Future<Map<String, dynamic>> dumpDb() async {
265 try {
266 return await _dumpDb();
cw_core/lib/wallet_info_legacy.dart
+33 -29
@@ -246,35 +246,35 @@ class WalletInfo extends HiveObject {
246 Future<void> migrateToSqlite() async {
247 final di = newWi.DerivationInfo(
248 id: 0,
249 - derivationType: derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown,
249 + derivationType:
250 + derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown,
251 derivationPath: derivationInfo?.derivationPath ?? derivationPath ?? '',
252 );
253 final derivationInfoId = await di.save();
254 final walletInfo = newWi.WalletInfo(
254 - 0,
255 - id,
256 - name,
257 - type,
258 - isRecovery,
259 - restoreHeight,
260 - timestamp,
261 - dirPath,
262 - path,
263 - address,
264 - yatEid,
265 - yatLastUsedAddressRaw,
266 - showIntroCakePayCard,
267 - derivationInfoId,
268 - hardwareWalletType,
269 - parentAddress,
270 - hashedWalletIdentifier,
271 - isNonSeedWallet,
272 - 0,
273 - addressPageType,
274 - false,
275 - true,
276 - null
277 - );
255 + 0,
256 + id,
257 + name,
258 + type,
259 + isRecovery,
260 + restoreHeight,
261 + timestamp,
262 + dirPath,
263 + path,
264 + address,
265 + yatEid,
266 + yatLastUsedAddressRaw,
267 + showIntroCakePayCard,
268 + derivationInfoId,
269 + hardwareWalletType,
270 + parentAddress,
271 + hashedWalletIdentifier,
272 + isNonSeedWallet,
273 + 0,
274 + addressPageType,
275 + false,
276 + true,
277 + null);
278 final wiId = await walletInfo.save();
279 for (final address in usedAddresses ?? <String>[]) {
280 await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.used, address);
@@ -289,8 +289,8 @@ class WalletInfo extends HiveObject {
289 for (final address in addressInfos![i] ?? <AddressInfo>[]) {
290 await newWi.WalletInfoAddressInfo.insert(
291 walletInfoId: wiId,
292 - mapKey: i,
293 - accountIndex: address.accountIndex??0,
292 + mapKey: i,
293 + accountIndex: address.accountIndex ?? 0,
294 address: address.address,
295 label: address.label,
296 );
@@ -304,8 +304,12 @@ class WalletInfo extends HiveObject {
304 final sw = Stopwatch()..start();
305 final list = box.values.toList();
306 for (final wi in list) {
307 - await wi.migrateToSqlite();
308 - await wi.delete();
307 + try {
308 + await wi.migrateToSqlite();
309 + await wi.delete();
310 + } catch (e) {
311 + printV('Error migrating WalletInfo ${wi.id}: $e');
312 + }
313 }
314 printV('Migrating WalletInfo to SQLite: end (${sw.elapsedMilliseconds}ms)');
315 }
integration_test/robots/transactions_page_robot.dart
+2 -2
@@ -385,8 +385,8 @@ class TransactionsPageRobot {
385
386 Future<void> _verifyTradeListItemDisplay(TradeListItem item) async {
387 final keyId = 'trade_list_item_${item.trade.id}_key';
388 - final from = item.trade.from?.toString() ?? item.trade.userCurrencyFrom.toString();
389 - final to = item.trade.to?.toString() ?? item.trade.userCurrencyTo.toString();
388 + final from = item.trade.from?.toString() ?? '';
389 + final to = item.trade.to?.toString() ?? '';
390
391 //* ==============Confirm it has the right key for this item ========
392 commonTestCases.hasValueKey(keyId);
lib/core/backup_service.dart
+2
@@ -19,6 +19,7 @@ import 'package:cake_wallet/entities/encrypt.dart';
19 import 'package:cake_wallet/entities/preferences_key.dart';
20 import 'package:cake_wallet/entities/secret_store_key.dart';
21 import 'package:cw_core/wallet_info.dart';
22 +import 'package:cake_wallet/exchange/trade_legacy.dart';
23 import 'package:cake_wallet/.secrets.g.dart' as secrets;
24 import 'package:cake_wallet/wallet_types.g.dart';
25 import 'package:cake_backup/backup.dart' as cake_backup;
@@ -110,6 +111,7 @@ class $BackupService {
111
112 Future<void> verifyWallets() async {
113 await performHiveMigration(); // for backups made before sqlite migration
114 + await performTradeHiveMigration(_secureStorage);
115 correctWallets = (await WalletInfo.getAll()).where((info) => availableWalletTypes.contains(info.type)).toList();
116
117 if (correctWallets.isEmpty) {
lib/core/trade_monitor.dart
+3 -11
@@ -20,7 +20,6 @@ import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
20 import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
21 import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
22 import 'package:cw_core/utils/print_verbose.dart';
23 -import 'package:hive/hive.dart';
23 import 'package:cake_wallet/store/app_store.dart';
24 import 'package:shared_preferences/shared_preferences.dart';
25
@@ -30,13 +29,11 @@ class TradeMonitor {
29
30 TradeMonitor({
31 required this.tradesStore,
33 - required this.trades,
32 required this.appStore,
33 required this.preferences,
34 });
35
36 final TradesStore tradesStore;
39 - final Box<Trade> trades;
37 final AppStore appStore;
38 final Map<String, Timer> _tradeTimers = {};
39 final SharedPreferences preferences;
@@ -54,7 +51,7 @@ class TradeMonitor {
51 case ExchangeProviderDescription.exolix:
52 return ExolixExchangeProvider();
53 case ExchangeProviderDescription.thorChain:
57 - return ThorChainExchangeProvider(tradesStore: trades);
54 + return ThorChainExchangeProvider();
55 case ExchangeProviderDescription.swapTrade:
56 return SwapTradeExchangeProvider();
57 case ExchangeProviderDescription.letsExchange:
@@ -62,7 +59,7 @@ class TradeMonitor {
59 case ExchangeProviderDescription.stealthEx:
60 return StealthExExchangeProvider();
61 case ExchangeProviderDescription.chainflip:
65 - return ChainflipExchangeProvider(tradesStore: trades);
62 + return ChainflipExchangeProvider();
63 case ExchangeProviderDescription.xoSwap:
64 return XOSwapExchangeProvider();
65 case ExchangeProviderDescription.swapsXyz:
@@ -187,12 +184,7 @@ class TradeMonitor {
184
185 try {
186 final updated = await provider.findTradeById(id: trade.id);
190 - trade
191 - ..stateRaw = updated.state.raw
192 - ..receiveAmount = updated.receiveAmount ?? trade.receiveAmount
193 - ..outputTransaction = updated.outputTransaction ?? trade.outputTransaction
194 - ..userCurrencyToRaw = updated.userCurrencyToRaw
195 - ..userCurrencyFromRaw = updated.userCurrencyFromRaw;
187 + trade.mergeFindTradeByIdResult(updated);
188 printV('Trade ${trade.id} updated: ${trade.state}');
189 await trade.save();
190
lib/di.dart
+3 -12
@@ -329,7 +329,6 @@ var _isSetupFinished = false;
329 late Box<Node> _nodeSource;
330 late Box<Node> _powNodeSource;
331 late Box<Contact> _contactSource;
332 -late Box<Trade> _tradesSource;
332 late Box<Template> _templates;
333 late Box<ExchangeTemplate> _exchangeTemplates;
334 late Box<TransactionDescription> _transactionDescriptionBox;
@@ -341,7 +340,6 @@ Future<void> setup({
340 required Box<Node> nodeSource,
341 required Box<Node> powNodeSource,
342 required Box<Contact> contactSource,
344 - required Box<Trade> tradesSource,
343 required Box<Template> templates,
344 required Box<ExchangeTemplate> exchangeTemplates,
345 required Box<TransactionDescription> transactionDescriptionBox,
@@ -355,7 +353,6 @@ Future<void> setup({
353 _nodeSource = nodeSource;
354 _powNodeSource = powNodeSource;
355 _contactSource = contactSource;
358 - _tradesSource = tradesSource;
356 _templates = templates;
357 _exchangeTemplates = exchangeTemplates;
358 _transactionDescriptionBox = transactionDescriptionBox;
@@ -402,7 +399,7 @@ Future<void> setup({
399 nodeListStore: getIt.get<NodeListStore>(),
400 themeStore: getIt.get<ThemeStore>()));
401 getIt.registerSingleton<TradesStore>(
405 - TradesStore(tradesSource: _tradesSource, appStore: getIt.get<AppStore>()));
402 + TradesStore(appStore: getIt.get<AppStore>()));
403 getIt.registerSingleton<OrdersStore>(
404 OrdersStore(ordersSource: _ordersSource, settingsStore: getIt.get<SettingsStore>()));
405 getIt.registerSingleton<BridgeTransfersStore>(BridgeTransfersStore());
@@ -569,7 +566,6 @@ Future<void> setup({
566 getIt.registerFactory(
567 () => ExchangeViewModel(
568 getIt.get<AppStore>(),
572 - _tradesSource,
569 getIt.get<ExchangeTemplateStore>(),
570 getIt.get<TradesStore>(),
571 getIt.get<SharedPreferences>(),
@@ -583,7 +579,6 @@ Future<void> setup({
579 getIt.registerSingleton(
580 TradeMonitor(
581 tradesStore: getIt.get<TradesStore>(),
586 - trades: _tradesSource,
582 appStore: getIt.get<AppStore>(),
583 preferences: getIt.get<SharedPreferences>(),
584 ),
@@ -1258,7 +1253,6 @@ Future<void> setup({
1253 getIt.registerFactory(
1254 () => ExchangeTradeViewModel(
1255 wallet: getIt.get<AppStore>().wallet!,
1261 - trades: _tradesSource,
1256 tradesStore: getIt.get<TradesStore>(),
1257 sendViewModel: getIt.get<SendViewModel>(),
1258 feesViewModel: getIt.get<FeesViewModel>(),
@@ -1471,11 +1465,8 @@ Future<void> setup({
1465 getIt.registerFactoryParam<TransactionSuccessPage, String, void>(
1466 (content, _) => TransactionSuccessPage(content: content));
1467
1474 - getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
1475 - TradeDetailsViewModel(
1476 - tradeForDetails: trade,
1477 - trades: _tradesSource,
1478 - appStore: getIt.get<AppStore>()));
1468 + getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>(
1469 + (trade, _) => TradeDetailsViewModel(tradeForDetails: trade, appStore: getIt.get<AppStore>()));
1470
1471 getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<CakePayService>()));
1472
lib/entities/default_settings_migration.dart
+1 -3
@@ -13,7 +13,6 @@ import 'package:cake_wallet/entities/haven_seed_store.dart';
13 import 'package:cake_wallet/entities/node_list.dart';
14 import 'package:cake_wallet/entities/preferences_key.dart';
15 import 'package:cake_wallet/entities/secret_store_key.dart';
16 -import 'package:cake_wallet/exchange/trade.dart';
16 import 'package:cake_wallet/monero/monero.dart';
17 import 'package:cake_wallet/wownero/wownero.dart';
18 import 'package:collection/collection.dart';
@@ -64,11 +63,10 @@ Future<void> defaultSettingsMigration(
63 required SecureStorage secureStorage,
64 required Box<Node> nodes,
65 required Box<Node> powNodes,
67 - required Box<Trade> tradeSource,
66 required Box<Contact> contactSource,
67 required Box<HavenSeedStore> havenSeedStore}) async {
68 if (Platform.isIOS) {
71 - await ios_migrate_v1(tradeSource, contactSource);
69 + await ios_migrate_v1(contactSource);
70 }
71
72 // check current nodes for nullability regardless of the version
lib/entities/fs_migration.dart
+12 -9
@@ -29,7 +29,7 @@ Future<void> migrate_android_v1() async {
29 await android_migrate_wallets(appDocDir: appDocDir);
30 }
31
32 -Future<void> ios_migrate_v1(Box<Trade> tradeSource, Box<Contact> contactSource) async {
32 +Future<void> ios_migrate_v1(Box<Contact> contactSource) async {
33 final prefs = await SharedPreferences.getInstance();
34
35 if (prefs.getBool('ios_migration_v1_completed') ?? false) {
@@ -40,7 +40,7 @@ Future<void> ios_migrate_v1(Box<Trade> tradeSource, Box<Contact> contactSource)
40 await ios_migrate_pin();
41 await ios_migrate_wallet_passwords();
42 await ios_migrate_wallet_info();
43 - await ios_migrate_trades_list(tradeSource);
43 + await ios_migrate_trades_list();
44 await ios_migrate_address_book(contactSource);
45
46 await prefs.setBool('ios_migration_v1_completed', true);
@@ -343,7 +343,7 @@ Future<void> ios_migrate_wallet_info() async {
343 }
344 }
345
346 -Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
346 +Future<void> ios_migrate_trades_list() async {
347 final prefs = await SharedPreferences.getInstance();
348
349 if (prefs.getBool('ios_migration_trade_list_completed') ?? false) {
@@ -366,7 +366,8 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
366 final key = masterPassword!.replaceAll('-', '');
367 final decoded = await ios_legacy_helper.decrypt(content, key: key, salt: secrets.salt);
368 final decodedJson = json.decode(decoded) as List<dynamic>;
369 - final trades = decodedJson.map((dynamic el) {
369 +
370 + for (final dynamic el in decodedJson) {
371 final elAsMap = el as Map<String, dynamic>;
372 final providerAsString = elAsMap['provider'] as String;
373 final fromAsString = elAsMap['from'] as String;
@@ -393,17 +394,19 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
394 break;
395 }
396
396 - return Trade(
397 + if (provider == null) continue;
398 +
399 + await Trade(
400 id: tradeId,
398 - provider: provider!,
401 + provider: provider,
402 from: from,
403 to: to,
404 createdAt: date,
405 amount: '',
406 receiveAmount: '',
404 - );
405 - });
406 - await tradeSource.addAll(trades);
407 + ).save();
408 + }
409 +
410 await prefs.setBool('ios_migration_trade_list_completed', true);
411 } catch (e) {
412 printV(e.toString());
lib/exchange/provider/chainflip_exchange_provider.dart
+24 -50
@@ -8,15 +8,13 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
8 import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/trade_request.dart';
10 import 'package:cake_wallet/exchange/trade_state.dart';
11 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
11 import 'package:cw_core/crypto_currency.dart';
12 import 'package:cw_core/utils/print_verbose.dart';
14 -import 'package:hive/hive.dart';
13 import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cake_wallet/utils/exchange_provider_logger.dart';
15
16 class ChainflipExchangeProvider extends ExchangeProvider {
19 - ChainflipExchangeProvider({required this.tradesStore});
17 + ChainflipExchangeProvider();
18
19 static final List<CryptoCurrency> _supported = [
20 CryptoCurrency.btc,
@@ -41,8 +39,6 @@ class ChainflipExchangeProvider extends ExchangeProvider {
39 static const _affiliateBps = secrets.chainflipAffiliateFee;
40 static const _affiliateKey = secrets.chainflipApiKey;
41
44 - final Box<Trade> tradesStore;
45 -
42 @override
43 String get title => 'Chainflip';
44
@@ -229,19 +225,18 @@ class ChainflipExchangeProvider extends ExchangeProvider {
225 );
226
227 return Trade(
232 - id: id,
233 - from: request.fromCurrency,
234 - to: request.toCurrency,
235 - provider: description,
236 - inputAddress: swapResponse['address'].toString(),
237 - createdAt: DateTime.now(),
238 - amount: request.fromAmount,
239 - receiveAmount: request.toAmount,
240 - state: TradeState.waiting,
241 - payoutAddress: request.toAddress,
242 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
243 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
244 - isSendAll: isSendAll);
228 + id: id,
229 + from: request.fromCurrency,
230 + to: request.toCurrency,
231 + provider: description,
232 + inputAddress: swapResponse['address'].toString(),
233 + createdAt: DateTime.now(),
234 + amount: request.fromAmount,
235 + receiveAmount: request.toAmount,
236 + state: TradeState.waiting,
237 + payoutAddress: request.toAddress,
238 + isSendAll: isSendAll,
239 + );
240 } catch (e, s) {
241 ExchangeProviderLogger.logError(
242 provider: description,
@@ -296,29 +291,19 @@ class ChainflipExchangeProvider extends ExchangeProvider {
291 final to = status['destinationAsset'].toString();
292
293 final newTrade = Trade(
299 - id: id,
300 - from: _toCurrency(status['sourceAsset'].toString()),
301 - to: _toCurrency(status['destinationAsset'].toString()),
302 - provider: description,
303 - amount: depositAmount,
304 - receiveAmount: amount,
305 - state: currentState,
306 - payoutAddress: status['destinationAddress'].toString(),
307 - outputTransaction: status['swapEgress']?['transactionReference']?.toString(),
308 - isRefund: isRefund,
309 - userCurrencyFromRaw: '${from.toUpperCase()}' + '_',
310 - userCurrencyToRaw: '${to.toUpperCase()}' + '_',
294 + id: id,
295 + from: _toCurrency(from),
296 + to: _toCurrency(to),
297 + provider: description,
298 + amount: depositAmount,
299 + receiveAmount: amount,
300 + state: currentState,
301 + payoutAddress: status['destinationAddress'].toString(),
302 + outputTransaction:
303 + status['swapEgress']?['transactionReference']?.toString(),
304 + isRefund: isRefund,
305 );
306
313 - // Find trade and update receiveAmount with the real value received
314 - final storedTrade = _getStoredTrade(id);
315 -
316 - if (storedTrade != null) {
317 - storedTrade.$2.receiveAmount = newTrade.receiveAmount;
318 - storedTrade.$2.outputTransaction = newTrade.outputTransaction;
319 - tradesStore.put(storedTrade.$1, storedTrade.$2);
320 - }
321 -
307 return newTrade;
308 } catch (e) {
309 printV(e.toString());
@@ -368,17 +353,6 @@ class ChainflipExchangeProvider extends ExchangeProvider {
353 return currency;
354 }
355
371 - (dynamic, Trade)? _getStoredTrade(String id) {
372 - for (var i = tradesStore.length -1; i >= 0; i--) {
373 - Trade? t = tradesStore.getAt(i);
374 -
375 - if (t != null && t.id == id)
376 - return (i, t);
377 - }
378 -
379 - return null;
380 - }
381 -
356 String _amountToNative(double amount, CryptoCurrency currency) =>
357 (amount * pow(10, currency.decimals)).toInt().toString();
358
lib/exchange/provider/changenow_exchange_provider.dart
-6
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cake_wallet/store/settings_store.dart';
13 import 'package:cake_wallet/utils/distribution_info.dart';
14 import 'package:cw_core/utils/proxy_wrapper.dart';
@@ -248,8 +247,6 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
247 receiveAmount: toAmount ?? request.toAmount,
248 state: TradeState.created,
249 payoutAddress: payoutAddress,
251 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
252 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
250 isSendAll: isSendAll,
251 );
252 }
@@ -315,9 +312,6 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
312 expiredAt: expiredAt,
313 outputTransaction: outputTransaction,
314 payoutAddress: payoutAddress,
318 - userCurrencyFromRaw:
319 - '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
320 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
315 );
316 }
317
lib/exchange/provider/exchange_provider.dart
+6 -8
@@ -1,4 +1,3 @@
1 -import 'package:cake_wallet/exchange/exchange_pair.dart';
1 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
2 import 'package:cake_wallet/exchange/limits.dart';
3 import 'package:cake_wallet/exchange/trade.dart';
@@ -31,13 +30,12 @@ abstract class ExchangeProvider {
30
31 Future<Trade> findTradeById({required String id});
32
34 - Future<double> fetchRate({
35 - required CryptoCurrency from,
36 - required CryptoCurrency to,
37 - required double amount,
38 - required bool isFixedRateMode,
39 - required bool isReceiveAmount
40 - });
33 + Future<double> fetchRate(
34 + {required CryptoCurrency from,
35 + required CryptoCurrency to,
36 + required double amount,
37 + required bool isFixedRateMode,
38 + required bool isReceiveAmount});
39
40 Future<bool> checkIsAvailable();
41 }
lib/exchange/provider/exolix_exchange_provider.dart
+10 -14
@@ -314,8 +314,6 @@ class ExolixExchangeProvider extends ExchangeProvider {
314 receiveAmount: receiveAmount ?? request.toAmount,
315 state: TradeState.created,
316 payoutAddress: payoutAddress,
317 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
318 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
317 isSendAll: isSendAll,
318 );
319 }
@@ -363,18 +361,16 @@ class ExolixExchangeProvider extends ExchangeProvider {
361 final payoutAddress = responseJSON['withdrawalAddress'] as String;
362
363 return Trade(
366 - id: id,
367 - from: from,
368 - to: to,
369 - provider: description,
370 - inputAddress: inputAddress,
371 - amount: amount,
372 - state: TradeState.deserialize(raw: _prepareStatus(status)),
373 - extraId: extraId,
374 - outputTransaction: outputTransaction,
375 - payoutAddress: payoutAddress,
376 - userCurrencyFromRaw: '${coinFrom.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
377 - userCurrencyToRaw: '${coinTo.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
364 + id: id,
365 + from: from,
366 + to: to,
367 + provider: description,
368 + inputAddress: inputAddress,
369 + amount: amount,
370 + state: TradeState.deserialize(raw: _prepareStatus(status)),
371 + extraId: extraId,
372 + outputTransaction: outputTransaction,
373 + payoutAddress: payoutAddress,
374 );
375 }
376
lib/exchange/provider/jupiter_exchange_provider.dart
-2
@@ -367,8 +367,6 @@ class JupiterExchangeProvider extends ExchangeProvider {
367 receiveAmount: receiveAmount,
368 payoutAddress: request.toAddress,
369 isSendAll: isSendAll,
370 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? 'SOL'}',
371 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? 'SOL'}',
370 routerData: transaction,
371 routerValue: requestId,
372 fee: totalFeeInSol,
lib/exchange/provider/letsexchange_exchange_provider.dart
+9 -25
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cw_core/utils/print_verbose.dart';
@@ -76,13 +75,12 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
75 }
76
77 @override
79 - Future<double> fetchRate({
80 - required CryptoCurrency from,
81 - required CryptoCurrency to,
82 - required double amount,
83 - required bool isFixedRateMode,
84 - required bool isReceiveAmount
85 - }) async {
78 + Future<double> fetchRate(
79 + {required CryptoCurrency from,
80 + required CryptoCurrency to,
81 + required double amount,
82 + required bool isFixedRateMode,
83 + required bool isReceiveAmount}) async {
84 final networkFrom = _getNetworkType(from);
85 final networkTo = _getNetworkType(to);
86 try {
@@ -191,14 +189,13 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
189 'Authorization': apiKey
190 };
191
194 - final uri = Uri.https(_baseUrl,
195 - isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath);
192 + final uri = Uri.https(
193 + _baseUrl, isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath);
194 final response = await ProxyWrapper().post(
195 clearnetUri: uri,
196 headers: headers,
197 body: json.encode(tradeParams),
198 );
201 -
199
200 if (response.statusCode != 200) {
201 ExchangeProviderLogger.logError(
@@ -304,8 +301,6 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
301 createdAt: createdAt,
302 expiredAt: expiredAt,
303 extraId: extraId,
307 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
308 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
304 isSendAll: isSendAll,
305 );
306 } catch (e, s) {
@@ -342,7 +337,6 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
337
338 final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
339 final response = await ProxyWrapper().get(clearnetUri: url, headers: headers);
345 -
340
341 if (response.statusCode != 200) {
342 throw Exception('LetsExchange fetch trade failed: ${response.body}');
@@ -363,7 +357,6 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
357 final toTag = toCurrency == normalizedToNetwork ? null : normalizedToNetwork;
358 final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency, tag: toTag);
359
366 -
360 final payoutAddress = responseJSON['withdrawal'] as String;
361 final depositAddress = responseJSON['deposit'] as String;
362 final refundAddress = responseJSON['return'] as String;
@@ -371,15 +364,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
364 final receiveAmount = responseJSON['withdrawal_amount'] as String;
365 final status = responseJSON['status'] as String;
366
374 - // We ignore the created_at from response and use DateTime.now() instead
375 - final createdAtString = responseJSON['created_at'] as String;
376 - final expiredAtTimestamp = responseJSON['expired_at'] as int;
377 -
367 final extraId = responseJSON['deposit_extra_id'] as String?;
368
380 - final createdAt = DateTime.parse(createdAtString).toLocal();
381 - final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000).toLocal();
382 -
369 return Trade(
370 id: id,
371 from: from,
@@ -393,8 +379,6 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
379 state: TradeState.deserialize(raw: status),
380 isRefund: status == 'refund',
381 extraId: extraId,
396 - userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
397 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
382 );
383 }
384
@@ -412,7 +396,7 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
396 headers: headers,
397 body: json.encode(params),
398 );
415 -
399 +
400 if (response.statusCode != 200) {
401 throw Exception('LetsExchange fetch info failed: ${response.body}');
402 }
lib/exchange/provider/near_Intents_exchange_provider.dart
-7
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cw_core/amount_converter.dart';
13 import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
@@ -286,10 +285,6 @@ class NearIntentsExchangeProvider extends ExchangeProvider {
285 receiveAmount: quoteObj['amountOutFormatted']?.toString(),
286 memo: depositMemo,
287 isSendAll: isSendAll,
289 - userCurrencyFromRaw:
290 - '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
291 - userCurrencyToRaw:
292 - '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
288 );
289
290 ExchangeProviderLogger.logSuccess(
@@ -415,8 +410,6 @@ class NearIntentsExchangeProvider extends ExchangeProvider {
410 txId: originTxHash,
411 extraId: depositMemo,
412 isRefund: statusRaw == 'REFUNDED',
418 - userCurrencyFromRaw: '${from?.$1.toUpperCase()}' + '_' + '${from?.$2?.toUpperCase() ?? ''}',
419 - userCurrencyToRaw: '${to?.$1.toUpperCase()}' + '_' + '${to?.$2?.toUpperCase() ?? ''}',
413 );
414 }
415
lib/exchange/provider/sideshift_exchange_provider.dart
+18 -15
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
9 import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cw_core/utils/print_verbose.dart';
@@ -337,8 +336,6 @@ class SideShiftExchangeProvider extends ExchangeProvider {
336 createdAt: DateTime.now(),
337 isSendAll: isSendAll,
338 extraId: depositMemo,
340 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
341 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
339 );
340 }
341
@@ -377,19 +374,25 @@ class SideShiftExchangeProvider extends ExchangeProvider {
374 final expiredAt = isVariable ? null : DateTime.tryParse(expiredAtRaw)?.toLocal();
375 final depositMemo = responseJSON['depositMemo'] as String?;
376
377 + final fromParsed = CryptoCurrency.safeParseCurrencyFromString(
378 + fromCurrency,
379 + tag: fromNetwork,
380 + );
381 + final toParsed = CryptoCurrency.safeParseCurrencyFromString(
382 + toCurrency,
383 + tag: toNetwork,
384 + );
385 return Trade(
381 - id: id,
382 - from: CryptoCurrency.safeParseCurrencyFromString(fromCurrency),
383 - to: CryptoCurrency.safeParseCurrencyFromString(toCurrency),
384 - provider: description,
385 - inputAddress: inputAddress,
386 - amount: expectedSendAmount ?? '',
387 - state: TradeState.deserialize(raw: status ?? 'created'),
388 - expiredAt: expiredAt,
389 - payoutAddress: settleAddress,
390 - extraId: depositMemo,
391 - userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + _normalizeNetworkType(fromNetwork ?? ''),
392 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + _normalizeNetworkType(toNetwork ?? ''),
386 + id: id,
387 + from: fromParsed,
388 + to: toParsed,
389 + provider: description,
390 + inputAddress: inputAddress,
391 + amount: expectedSendAmount ?? '',
392 + state: TradeState.deserialize(raw: status ?? 'created'),
393 + expiredAt: expiredAt,
394 + payoutAddress: settleAddress,
395 + extraId: depositMemo,
396 );
397 }
398
lib/exchange/provider/simpleswap_exchange_provider.dart
+5 -7
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
9 import 'package:cake_wallet/exchange/trade_not_found_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cake_wallet/utils/device_info.dart';
13 import 'package:cw_core/utils/proxy_wrapper.dart';
14 import 'package:cw_core/crypto_currency.dart';
@@ -290,8 +289,6 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
289 payoutAddress: payoutAddress,
290 createdAt: DateTime.now(),
291 isSendAll: isSendAll,
293 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
294 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
292 );
293 }
294
@@ -326,18 +323,19 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
323 final status = responseJSON['status'] as String;
324 final payoutAddress = responseJSON['address_to'] as String;
325
326 + final fromParsed =
327 + CryptoCurrency.safeParseCurrencyFromString(fromCurrency);
328 + final toParsed = CryptoCurrency.safeParseCurrencyFromString(toCurrency);
329 return Trade(
330 id: id,
331 - from: CryptoCurrency.safeParseCurrencyFromString(fromCurrency),
332 - to: CryptoCurrency.safeParseCurrencyFromString(toCurrency),
331 + from: fromParsed,
332 + to: toParsed,
333 extraId: extraId,
334 provider: description,
335 inputAddress: inputAddress,
336 amount: expectedSendAmount,
337 state: TradeState.deserialize(raw: status),
338 payoutAddress: payoutAddress,
339 - userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
340 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
339 );
340 }
341
lib/exchange/provider/stealth_ex_exchange_provider.dart
-6
@@ -9,7 +9,6 @@ import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
10 import 'package:cake_wallet/exchange/trade_request.dart';
11 import 'package:cake_wallet/exchange/trade_state.dart';
12 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
12 import 'package:cw_core/utils/proxy_wrapper.dart';
13 import 'package:cw_core/crypto_currency.dart';
14 import 'package:cake_wallet/utils/exchange_provider_logger.dart';
@@ -299,8 +298,6 @@ class StealthExExchangeProvider extends ExchangeProvider {
298 createdAt: createdAt,
299 expiredAt: expiredAt,
300 extraId: extraId,
302 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
303 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
301 isSendAll: isSendAll,
302 );
303 } catch (e, s) {
@@ -377,9 +374,6 @@ class StealthExExchangeProvider extends ExchangeProvider {
374 createdAt: createdAt,
375 isRefund: status == 'refunded',
376 extraId: extraId,
380 - userCurrencyFromRaw:
381 - '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
382 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
377 );
378 }
379
lib/exchange/provider/swapsxyz_exchange_provider.dart
-6
@@ -407,10 +407,6 @@ class SwapsXyzExchangeProvider extends ExchangeProvider {
407 requiresTokenApproval: requiresTokenApproval,
408 routerData: routerData,
409 routerValue: txValue,
410 - userCurrencyFromRaw:
411 - '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
412 - userCurrencyToRaw:
413 - '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
410 );
411
412 return trade;
@@ -554,8 +550,6 @@ class SwapsXyzExchangeProvider extends ExchangeProvider {
550 state: state,
551 createdAt: createdAt,
552 refundAddress: refundAddress,
557 - userCurrencyFromRaw: '${fromSymbol.toUpperCase()}' + '_',
558 - userCurrencyToRaw: '${toSymbol.toUpperCase()}' + '_',
553 );
554 }
555
lib/exchange/provider/swaptrade_exchange_provider.dart
-4
@@ -297,8 +297,6 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
297 state: TradeState.created,
298 payoutAddress: request.toAddress,
299 isSendAll: isSendAll,
300 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
301 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
300 );
301 } catch (e, s) {
302 ExchangeProviderLogger.logError(
@@ -374,8 +372,6 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
372 receiveAmount: expectedReceiveAmount,
373 memo: memo,
374 createdAt: DateTime.tryParse(createdAt ?? ''),
377 - userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
378 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
375 );
376 } catch (e) {
377 printV("error getting trade: ${e.toString()}");
lib/exchange/provider/thorchain_exchange.provider.dart
+1 -9
@@ -6,15 +6,13 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
6 import 'package:cake_wallet/exchange/trade.dart';
7 import 'package:cake_wallet/exchange/trade_request.dart';
8 import 'package:cake_wallet/exchange/trade_state.dart';
9 -import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
9 import 'package:cw_core/utils/proxy_wrapper.dart';
10 import 'package:cw_core/crypto_currency.dart';
11 import 'package:cw_core/utils/print_verbose.dart';
13 -import 'package:hive/hive.dart';
12 import 'package:cake_wallet/utils/exchange_provider_logger.dart';
13
14 class ThorChainExchangeProvider extends ExchangeProvider {
17 - ThorChainExchangeProvider({required this.tradesStore});
15 + ThorChainExchangeProvider();
16
17 static final isRefundAddressSupported = [CryptoCurrency.eth];
18
@@ -26,8 +24,6 @@ class ThorChainExchangeProvider extends ExchangeProvider {
24 static const _affiliateBps = '175';
25 static const _nameLookUpPath = 'v2/thorname/lookup/';
26
29 - final Box<Trade> tradesStore;
30 -
27 @override
28 String get title => 'THORChain';
29
@@ -195,8 +191,6 @@ class ThorChainExchangeProvider extends ExchangeProvider {
191 payoutAddress: request.toAddress,
192 memo: memo,
193 isSendAll: isSendAll,
198 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
199 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
194 );
195 }
196
@@ -254,8 +248,6 @@ class ThorChainExchangeProvider extends ExchangeProvider {
248 state: currentState,
249 memo: memo,
250 isRefund: isRefund,
257 - userCurrencyFromRaw: '${tx['chain'] as String? ?? ''}' + '_',
258 - userCurrencyToRaw: '$toAsset' + '_',
251 );
252 }
253
lib/exchange/provider/trocador_exchange_provider.dart
-5
@@ -392,8 +392,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
392 payoutAddress: payoutAddress,
393 isSendAll: isSendAll,
394 extraId: addressProviderMemo,
395 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
396 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
395 );
396 }
397
@@ -456,9 +454,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
454 providerId: providerId,
455 providerName: providerName,
456 extraId: addressProviderMemo,
459 - userCurrencyFromRaw:
460 - '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
461 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
457 );
458 });
459 }
lib/exchange/provider/xoswap_exchange_provider.dart
-12
@@ -423,8 +423,6 @@ class XOSwapExchangeProvider extends ExchangeProvider {
423 receiveAmount: receiveAmount.toString(),
424 payoutAddress: payoutAddress,
425 extraId: extraId,
426 - userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
427 - userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
426 isSendAll: isSendAll,
427 );
428 } catch (e, s) {
@@ -529,14 +527,6 @@ class XOSwapExchangeProvider extends ExchangeProvider {
527 final createdAt = DateTime.parse(createdAtString).toLocal();
528 final extraId = responseJSON['payInAddressTag'] as String?;
529
532 - final userCurrencyFromRaw = fromCurrency != null
533 - ? '${fromCurrency.title}' + '_' + '${fromCurrency.tag ?? ''}'
534 - : '${fromAssetBase}' + '_' + '${fromAssetTag ?? ''}';
535 -
536 - final userCurrencyToRaw = toCurrency != null
537 - ? '${toCurrency.title}' + '_' + '${toCurrency.tag ?? ''}'
538 - : '${toAssetBase}' + '_' + '${toAssetTag ?? ''}';
539 -
530 return Trade(
531 id: orderId,
532 from: fromCurrency,
@@ -550,8 +540,6 @@ class XOSwapExchangeProvider extends ExchangeProvider {
540 receiveAmount: receiveAmount,
541 payoutAddress: payoutAddress,
542 extraId: extraId,
553 - userCurrencyFromRaw: userCurrencyFromRaw,
554 - userCurrencyToRaw: userCurrencyToRaw,
543 );
544 } catch (e) {
545 printV(e.toString());
lib/exchange/trade.dart
+199 -291
@@ -1,19 +1,22 @@
1 +import 'dart:async';
2 +
3 import 'package:cake_wallet/evm/evm.dart';
4 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
5 import 'package:cake_wallet/exchange/trade_state.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 +import 'package:cw_core/db/sqlite.dart';
8 import 'package:cw_core/format_amount.dart';
9 import 'package:cw_core/generate_name.dart';
7 -import 'package:cw_core/hive_type_ids.dart';
8 -import 'package:hive/hive.dart';
10 +import 'package:sqflite/sqflite.dart';
11
10 -class Trade extends HiveObject {
12 +class Trade {
13 Trade({
14 + this.internalId = 0,
15 required this.id,
16 required this.amount,
17 ExchangeProviderDescription? provider,
15 - CryptoCurrency? from,
16 - CryptoCurrency? to,
18 + this.from,
19 + this.to,
20 TradeState? state,
21 this.receiveAmount,
22 this.createdAt,
@@ -34,8 +37,6 @@ class Trade extends HiveObject {
37 this.isRefund,
38 this.isSendAll,
39 this.router,
37 - this.userCurrencyFromRaw,
38 - this.userCurrencyToRaw,
40 // The following fields are used for SwapXyz trades only
41 this.needToRegisterInSwapXyz,
42 this.sourceTokenAddress,
@@ -48,364 +49,271 @@ class Trade extends HiveObject {
49 this.chainId,
50 }) {
51 if (provider != null) providerRaw = provider.raw;
51 -
52 - fromRaw = from?.raw ?? -1;
53 - toRaw = to?.raw ?? -1;
54 -
52 if (state != null) stateRaw = state.raw;
53 }
54
58 - static const typeId = TRADE_TYPE_ID;
55 + static const tableName = 'Trade';
56 + static const selfIdColumn = 'tradeId';
57 +
58 static const boxName = 'Trades';
59 static const boxKey = 'tradesBoxKey';
60
62 - @HiveField(0, defaultValue: '')
61 + static final StreamController<void> onChanged = StreamController<void>.broadcast();
62 +
63 + int internalId;
64 +
65 String id;
66
65 - @HiveField(1, defaultValue: 0)
66 - late int providerRaw;
67 + int providerRaw = 0;
68
69 ExchangeProviderDescription get provider =>
70 ExchangeProviderDescription.deserialize(raw: providerRaw);
71
71 - @HiveField(2, defaultValue: -1)
72 - int fromRaw = -1;
73 -
74 - CryptoCurrency? get from => CryptoCurrency.safeDeserialize(raw: fromRaw);
75 -
76 - @HiveField(3, defaultValue: -1)
77 - int toRaw = -1;
72 + CryptoCurrency? from;
73 + CryptoCurrency? to;
74
79 - CryptoCurrency? get to => CryptoCurrency.safeDeserialize(raw: toRaw);
80 -
81 - @HiveField(4, defaultValue: '')
82 - late String stateRaw;
75 + String stateRaw = '';
76
77 TradeState get state => TradeState.deserialize(raw: stateRaw);
78
86 - @HiveField(5)
79 DateTime? createdAt;
88 -
89 - @HiveField(6)
80 DateTime? expiredAt;
91 -
92 - @HiveField(7, defaultValue: '')
81 String amount;
94 -
95 - @HiveField(8)
82 + String? receiveAmount;
83 String? inputAddress;
97 -
98 - @HiveField(9)
84 String? extraId;
100 -
101 - @HiveField(10)
85 String? outputTransaction;
103 -
104 - @HiveField(11)
86 String? refundAddress;
106 -
107 - @HiveField(12)
87 String? walletId;
109 -
110 - @HiveField(13)
88 String? payoutAddress;
112 -
113 - @HiveField(14)
89 String? password;
115 -
116 - @HiveField(15)
90 String? providerId;
118 -
119 - @HiveField(16)
91 String? providerName;
121 -
122 - @HiveField(17)
92 String? fromWalletAddress;
124 -
125 - @HiveField(18)
93 String? memo;
127 -
128 - @HiveField(19)
94 String? txId;
130 -
131 - @HiveField(20)
95 bool? isRefund;
133 -
134 - @HiveField(21)
96 bool? isSendAll;
136 -
137 - /// Must be set on createTrade;
138 -
139 - @HiveField(22)
97 String? router;
98
142 - @HiveField(23, defaultValue: '')
143 - String? receiveAmount;
144 -
145 - @HiveField(24, defaultValue: '')
146 - String? userCurrencyFromRaw;
147 -
148 - @HiveField(25, defaultValue: '')
149 - String? userCurrencyToRaw;
150 -
99 // The following fields are used for SwapXyz trades only
152 - @HiveField(26)
100 bool? needToRegisterInSwapXyz;
154 -
155 - @HiveField(27)
101 String? sourceTokenAddress;
157 -
158 - @HiveField(28)
102 int? sourceTokenDecimals;
160 -
161 - @HiveField(29)
103 String? routerData;
163 -
164 - @HiveField(30)
104 String? routerValue;
166 -
167 - @HiveField(31)
105 int? routerChainId;
169 -
170 - @HiveField(32)
106 String? sourceTokenAmountRaw;
172 -
173 - @HiveField(33, defaultValue: false)
107 bool? requiresTokenApproval;
108
176 - @HiveField(34)
109 int? chainId;
178 -
179 - @HiveField(35)
110 double? fee;
111
182 - CryptoCurrency? get userCurrencyFrom {
183 - if (userCurrencyFromRaw == null || userCurrencyFromRaw!.isEmpty) {
184 - return null;
185 - }
186 - final underscoreIndex = userCurrencyFromRaw!.indexOf('_');
187 - final title = userCurrencyFromRaw!.substring(0, underscoreIndex);
188 - String tag = userCurrencyFromRaw!.substring(underscoreIndex + 1);
189 -
190 - if (tag.contains('ARB')) tag = 'ARB';
112 + String get chainName {
113 + if (chainId == null) return '';
114
192 - return CryptoCurrency(
193 - title: title,
194 - tag: tag.isNotEmpty ? tag : null,
195 - name: '',
196 - raw: -1,
197 - decimals: 1,
198 - );
115 + return evm!.getChainNameByChainId(chainId!).capitalized();
116 }
117
201 - CryptoCurrency? get userCurrencyTo {
202 - if (userCurrencyToRaw == null || userCurrencyToRaw!.isEmpty) {
203 - return null;
204 - }
205 - final underscoreIndex = userCurrencyToRaw!.indexOf('_');
206 - final title = userCurrencyToRaw!.substring(0, underscoreIndex);
207 - final tag = userCurrencyToRaw!.substring(underscoreIndex + 1);
118 + // ── SQLite CRUD ──────────────────────────────────────
119
209 - return CryptoCurrency(
210 - title: title,
211 - tag: tag.isNotEmpty ? tag : null,
212 - name: '',
213 - raw: -1,
214 - decimals: 1,
120 + Future<int> save() async {
121 + final json = toSqliteMap();
122 + if (json[selfIdColumn] == 0) {
123 + json[selfIdColumn] = null;
124 + }
125 + internalId = await db!.insert(
126 + tableName,
127 + json,
128 + conflictAlgorithm: ConflictAlgorithm.replace,
129 );
130 + onChanged.add(null);
131 + return internalId;
132 }
133
218 - String get chainName {
219 - if (chainId == null) return '';
134 + static Future<List<Trade>> getAll({String? orderBy}) async {
135 + final list = await db!.query(
136 + tableName,
137 + orderBy: orderBy ?? 'createdAt DESC',
138 + );
139 + return List.generate(
140 + list.length,
141 + (i) => Trade.fromSqliteRow(list[i]),
142 + );
143 + }
144
221 - return evm!.getChainNameByChainId(chainId!).capitalized();
145 + static Future<Trade?> getByTradeId(String id) async {
146 + final list = await db!.query(
147 + tableName,
148 + where: 'id = ?',
149 + whereArgs: [id],
150 + limit: 1,
151 + );
152 + if (list.isEmpty) return null;
153 + return Trade.fromSqliteRow(list.first);
154 }
155
224 - static Trade fromMap(Map<String, Object?> map) {
225 - return Trade(
226 - id: map['id'] as String,
227 - provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int),
228 - from: CryptoCurrency.deserialize(raw: map['input'] as int),
229 - to: CryptoCurrency.deserialize(raw: map['output'] as int),
230 - createdAt:
231 - map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null,
232 - amount: map['amount'] as String,
233 - receiveAmount: map['receive_amount'] as String?,
234 - walletId: map['wallet_id'] as String,
235 - fromWalletAddress: map['from_wallet_address'] as String?,
236 - memo: map['memo'] as String?,
237 - txId: map['tx_id'] as String?,
238 - isRefund: map['isRefund'] as bool?,
239 - isSendAll: map['isSendAll'] as bool?,
240 - router: map['router'] as String?,
241 - extraId: map['extra_id'] as String?,
242 - chainId: map['chain_id'] as int?,
156 + static Future<int> deleteTrade(Trade trade) async {
157 + final rows = await db!.delete(
158 + tableName,
159 + where: '$selfIdColumn = ?',
160 + whereArgs: [trade.internalId],
161 );
162 + onChanged.add(null);
163 + return rows;
164 + }
165 +
166 + // ── SQLite serialization ─────────────────────────────
167 + void mergeFindTradeByIdResult(Trade updated) {
168 + if (updated.stateRaw.isNotEmpty) stateRaw = updated.stateRaw;
169 + if (createdAt == null && updated.createdAt != null) {
170 + createdAt = updated.createdAt;
171 + }
172 + if (updated.expiredAt != null) expiredAt = updated.expiredAt;
173 + if (updated.isRefund != null) isRefund = updated.isRefund;
174 +
175 + if (updated.receiveAmount != null) receiveAmount = updated.receiveAmount;
176 + if (updated.inputAddress != null) inputAddress = updated.inputAddress;
177 + if (updated.extraId != null) extraId = updated.extraId;
178 + if (updated.outputTransaction != null) {
179 + outputTransaction = updated.outputTransaction;
180 + }
181 + if (updated.refundAddress != null) refundAddress = updated.refundAddress;
182 + if (updated.payoutAddress != null) payoutAddress = updated.payoutAddress;
183 + if (updated.password != null) password = updated.password;
184 + if (updated.providerId != null) providerId = updated.providerId;
185 + if (updated.providerName != null) providerName = updated.providerName;
186 + if (updated.memo != null) memo = updated.memo;
187 + if (updated.txId != null) txId = updated.txId;
188 }
189
246 - Map<String, dynamic> toMap() {
190 + Map<String, dynamic> toSqliteMap() {
191 return <String, dynamic>{
192 + selfIdColumn: internalId,
193 'id': id,
249 - 'provider': provider.serialize(),
250 - 'input': fromRaw,
251 - 'output': toRaw,
252 - 'date': createdAt != null ? createdAt!.millisecondsSinceEpoch : null,
194 + 'providerRaw': providerRaw,
195 + 'fromTitle': from?.title,
196 + 'fromName': from?.name,
197 + 'fromTag': from?.tag,
198 + 'fromFullName': from?.fullName,
199 + 'fromDecimals': from?.decimals,
200 + 'fromRaw': from?.raw,
201 + 'fromIconPath': from?.iconPath,
202 + 'fromFlatIconPath': from?.flatIconPath,
203 + 'fromChainIconPath': from?.chainIconPath,
204 + 'toTitle': to?.title,
205 + 'toName': to?.name,
206 + 'toTag': to?.tag,
207 + 'toFullName': to?.fullName,
208 + 'toDecimals': to?.decimals,
209 + 'toRaw': to?.raw,
210 + 'toIconPath': to?.iconPath,
211 + 'toFlatIconPath': to?.flatIconPath,
212 + 'toChainIconPath': to?.chainIconPath,
213 + 'stateRaw': stateRaw,
214 + 'createdAt': createdAt?.millisecondsSinceEpoch,
215 + 'expiredAt': expiredAt?.millisecondsSinceEpoch,
216 'amount': amount,
254 - 'receive_amount': receiveAmount,
255 - 'wallet_id': walletId,
256 - 'from_wallet_address': fromWalletAddress,
217 + 'receiveAmount': receiveAmount,
218 + 'inputAddress': inputAddress,
219 + 'extraId': extraId,
220 + 'outputTransaction': outputTransaction,
221 + 'refundAddress': refundAddress,
222 + 'walletId': walletId,
223 + 'payoutAddress': payoutAddress,
224 + 'password': password,
225 + 'providerId': providerId,
226 + 'providerName': providerName,
227 + 'fromWalletAddress': fromWalletAddress,
228 'memo': memo,
258 - 'tx_id': txId,
259 - 'isRefund': isRefund,
260 - 'isSendAll': isSendAll,
229 + 'txId': txId,
230 + 'isRefund': isRefund == true ? 1 : 0,
231 + 'isSendAll': isSendAll == true ? 1 : 0,
232 'router': router,
262 - 'extra_id': extraId,
263 - 'chain_id': chainId,
233 + 'needToRegisterInSwapXyz': needToRegisterInSwapXyz == true ? 1 : 0,
234 + 'sourceTokenAddress': sourceTokenAddress,
235 + 'sourceTokenDecimals': sourceTokenDecimals,
236 + 'routerData': routerData,
237 + 'routerValue': routerValue,
238 + 'routerChainId': routerChainId,
239 + 'sourceTokenAmountRaw': sourceTokenAmountRaw,
240 + 'requiresTokenApproval': requiresTokenApproval == true ? 1 : 0,
241 + 'chainId': chainId,
242 'fee': fee,
243 };
244 }
245
268 - String amountFormatted() => formatAmount(amount);
269 - String receiveAmountFormatted() => formatAmount(receiveAmount ?? '');
270 -}
246 + factory Trade.fromSqliteRow(Map<String, dynamic> row) {
247 + final trade = Trade(
248 + id: row['id'] as String? ?? '',
249 + amount: row['amount'] as String? ?? '',
250 + receiveAmount: row['receiveAmount'] as String?,
251 + createdAt: row['createdAt'] != null
252 + ? DateTime.fromMillisecondsSinceEpoch(
253 + row['createdAt'] as int,
254 + )
255 + : null,
256 + expiredAt: row['expiredAt'] != null
257 + ? DateTime.fromMillisecondsSinceEpoch(
258 + row['expiredAt'] as int,
259 + )
260 + : null,
261 + inputAddress: row['inputAddress'] as String?,
262 + extraId: row['extraId'] as String?,
263 + outputTransaction: row['outputTransaction'] as String?,
264 + refundAddress: row['refundAddress'] as String?,
265 + walletId: row['walletId'] as String?,
266 + payoutAddress: row['payoutAddress'] as String?,
267 + password: row['password'] as String?,
268 + providerId: row['providerId'] as String?,
269 + providerName: row['providerName'] as String?,
270 + fromWalletAddress: row['fromWalletAddress'] as String?,
271 + memo: row['memo'] as String?,
272 + fee: row['fee'] as double?,
273 + txId: row['txId'] as String?,
274 + isRefund: (row['isRefund'] as int?) == 1,
275 + isSendAll: (row['isSendAll'] as int?) == 1,
276 + router: row['router'] as String?,
277 + from: _currencyFromRow(row, 'from'),
278 + to: _currencyFromRow(row, 'to'),
279 + needToRegisterInSwapXyz: (row['needToRegisterInSwapXyz'] as int?) == 1,
280 + sourceTokenAddress: row['sourceTokenAddress'] as String?,
281 + sourceTokenDecimals: row['sourceTokenDecimals'] as int?,
282 + routerData: row['routerData'] as String?,
283 + routerValue: row['routerValue'] as String?,
284 + routerChainId: row['routerChainId'] as int?,
285 + sourceTokenAmountRaw: row['sourceTokenAmountRaw'] as String?,
286 + requiresTokenApproval: (row['requiresTokenApproval'] as int?) == 1,
287 + chainId: row['chainId'] as int?,
288 + );
289 + trade.internalId = row[selfIdColumn] as int? ?? 0;
290 + trade.providerRaw = row['providerRaw'] as int? ?? 0;
291 + trade.stateRaw = row['stateRaw'] as String? ?? '';
292 + return trade;
293 + }
294
272 -class TradeAdapter extends TypeAdapter<Trade> {
273 - @override
274 - final int typeId = Trade.typeId;
295 + static CryptoCurrency? _currencyFromRow(Map<String, dynamic> row, String prefix) {
296 + final title = row['${prefix}Title'] as String?;
297 + if (title == null || title.isEmpty) return null;
298
276 - @override
277 - Trade read(BinaryReader reader) {
278 - final numOfFields = reader.readByte();
279 - final fields = <int, dynamic>{};
280 - for (int i = 0; i < numOfFields; i++) {
281 - try {
282 - fields[reader.readByte()] = reader.read();
283 - } catch (_) {}
284 - }
299 + final tag = row['${prefix}Tag'] as String?;
300
286 - return Trade(
287 - id: fields[0] == null ? '' : fields[0] as String,
288 - amount: fields[7] == null ? '' : fields[7] as String,
289 - receiveAmount: fields[23] as String?,
290 - createdAt: fields[5] as DateTime?,
291 - expiredAt: fields[6] as DateTime?,
292 - inputAddress: fields[8] as String?,
293 - extraId: fields[9] as String?,
294 - outputTransaction: fields[10] as String?,
295 - refundAddress: fields[11] as String?,
296 - walletId: fields[12] as String?,
297 - payoutAddress: fields[13] as String?,
298 - password: fields[14] as String?,
299 - providerId: fields[15] as String?,
300 - providerName: fields[16] as String?,
301 - fromWalletAddress: fields[17] as String?,
302 - memo: fields[18] as String?,
303 - txId: fields[19] as String?,
304 - isRefund: fields[20] as bool?,
305 - isSendAll: fields[21] as bool?,
306 - router: fields[22] as String?,
307 - userCurrencyFromRaw: fields[24] as String?,
308 - userCurrencyToRaw: fields[25] as String?,
309 - needToRegisterInSwapXyz: fields[26] as bool?,
310 - sourceTokenAddress: fields[27] as String?,
311 - sourceTokenDecimals: fields[28] as int?,
312 - routerData: fields[29] as String?,
313 - routerValue: fields[30] as String?,
314 - routerChainId: fields[31] as int?,
315 - sourceTokenAmountRaw: fields[32] as String?,
316 - requiresTokenApproval: fields[33] as bool?,
317 - chainId: fields[34] as int?,
318 - fee: fields[35] as double?,
319 - )
320 - ..providerRaw = fields[1] == null ? 0 : fields[1] as int
321 - ..fromRaw = (fields[2] as int?) ?? -1
322 - ..toRaw = (fields[3] as int?) ?? -1
323 - ..stateRaw = fields[4] == null ? '' : fields[4] as String;
324 - }
301 + final live = CryptoCurrency.safeParseCurrencyFromString(title, tag: tag);
302 + if (live != null) return live;
303
326 - @override
327 - void write(BinaryWriter writer, Trade obj) {
328 - writer
329 - ..writeByte(26)
330 - ..writeByte(0)
331 - ..write(obj.id)
332 - ..writeByte(1)
333 - ..write(obj.providerRaw)
334 - ..writeByte(2)
335 - ..write(obj.fromRaw)
336 - ..writeByte(3)
337 - ..write(obj.toRaw)
338 - ..writeByte(4)
339 - ..write(obj.stateRaw)
340 - ..writeByte(5)
341 - ..write(obj.createdAt)
342 - ..writeByte(6)
343 - ..write(obj.expiredAt)
344 - ..writeByte(7)
345 - ..write(obj.amount)
346 - ..writeByte(8)
347 - ..write(obj.inputAddress)
348 - ..writeByte(9)
349 - ..write(obj.extraId)
350 - ..writeByte(10)
351 - ..write(obj.outputTransaction)
352 - ..writeByte(11)
353 - ..write(obj.refundAddress)
354 - ..writeByte(12)
355 - ..write(obj.walletId)
356 - ..writeByte(13)
357 - ..write(obj.payoutAddress)
358 - ..writeByte(14)
359 - ..write(obj.password)
360 - ..writeByte(15)
361 - ..write(obj.providerId)
362 - ..writeByte(16)
363 - ..write(obj.providerName)
364 - ..writeByte(17)
365 - ..write(obj.fromWalletAddress)
366 - ..writeByte(18)
367 - ..write(obj.memo)
368 - ..writeByte(19)
369 - ..write(obj.txId)
370 - ..writeByte(20)
371 - ..write(obj.isRefund)
372 - ..writeByte(21)
373 - ..write(obj.isSendAll)
374 - ..writeByte(22)
375 - ..write(obj.router)
376 - ..writeByte(23)
377 - ..write(obj.receiveAmount)
378 - ..writeByte(24)
379 - ..write(obj.userCurrencyFromRaw)
380 - ..writeByte(25)
381 - ..write(obj.userCurrencyToRaw)
382 - ..writeByte(26)
383 - ..write(obj.needToRegisterInSwapXyz)
384 - ..writeByte(27)
385 - ..write(obj.sourceTokenAddress)
386 - ..writeByte(28)
387 - ..write(obj.sourceTokenDecimals)
388 - ..writeByte(29)
389 - ..write(obj.routerData)
390 - ..writeByte(30)
391 - ..write(obj.routerValue)
392 - ..writeByte(31)
393 - ..write(obj.routerChainId)
394 - ..writeByte(32)
395 - ..write(obj.sourceTokenAmountRaw)
396 - ..writeByte(33)
397 - ..write(obj.requiresTokenApproval)
398 - ..writeByte(34)
399 - ..write(obj.chainId)
400 - ..writeByte(35)
401 - ..write(obj.fee);
304 + return CryptoCurrency(
305 + title: title,
306 + name: row['${prefix}Name'] as String? ?? '',
307 + tag: tag,
308 + fullName: row['${prefix}FullName'] as String?,
309 + decimals: row['${prefix}Decimals'] as int? ?? 1,
310 + raw: row['${prefix}Raw'] as int? ?? -1,
311 + iconPath: row['${prefix}IconPath'] as String?,
312 + flatIconPath: row['${prefix}FlatIconPath'] as String?,
313 + chainIconPath: row['${prefix}ChainIconPath'] as String?,
314 + );
315 }
316
404 - @override
405 - int get hashCode => typeId.hashCode;
406 -
407 - @override
408 - bool operator ==(Object other) =>
409 - identical(this, other) ||
410 - other is TradeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
317 + String amountFormatted() => formatAmount(amount);
318 + String receiveAmountFormatted() => formatAmount(receiveAmount ?? '');
319 }
lib/exchange/trade_currency_snapshot.dart new
+45
@@ -0,0 +1,45 @@
1 +import 'package:cw_core/crypto_currency.dart';
2 +
3 +class TradeCurrencySnapshot {
4 + TradeCurrencySnapshot._();
5 +
6 + static CryptoCurrency? fromLegacyHive({
7 + required int raw,
8 + String? displayTitleTag,
9 + }) {
10 + if (raw >= 0) {
11 + final curr = CryptoCurrency.safeDeserialize(raw: raw);
12 + if (curr != null) return curr;
13 + }
14 +
15 + return _parseLegacyTitleTag(displayTitleTag);
16 + }
17 +
18 + static CryptoCurrency? _parseLegacyTitleTag(String? titleTag) {
19 + if (titleTag == null || titleTag.isEmpty) return null;
20 +
21 + final idx = titleTag.indexOf('_');
22 + if (idx < 0) {
23 + return CryptoCurrency(
24 + title: titleTag,
25 + name: '',
26 + raw: -1,
27 + decimals: 1,
28 + );
29 + }
30 +
31 + final title = titleTag.substring(0, idx);
32 +
33 + var tag = titleTag.substring(idx + 1);
34 +
35 + if (tag.contains('ARB')) tag = 'ARB';
36 +
37 + return CryptoCurrency(
38 + title: title,
39 + tag: tag.isNotEmpty ? tag : null,
40 + name: '',
41 + raw: -1,
42 + decimals: 1,
43 + );
44 + }
45 +}
lib/exchange/trade_legacy.dart new
+168
@@ -0,0 +1,168 @@
1 +import 'package:cake_wallet/core/secure_storage.dart';
2 +import 'package:cake_wallet/entities/get_encryption_key.dart';
3 +import 'package:cake_wallet/exchange/exchange_provider_description.dart';
4 +import 'package:cake_wallet/exchange/trade.dart';
5 +import 'package:cake_wallet/exchange/trade_currency_snapshot.dart';
6 +import 'package:cake_wallet/exchange/trade_state.dart';
7 +import 'package:cw_core/cake_hive.dart';
8 +import 'package:cw_core/crypto_currency.dart';
9 +import 'package:cw_core/hive_type_ids.dart';
10 +import 'package:cw_core/utils/print_verbose.dart';
11 +import 'package:hive/hive.dart';
12 +
13 +part 'trade_legacy.part.dart';
14 +
15 +Future<void> performTradeHiveMigration(SecureStorage secureStorage) async {
16 + try {
17 + if (!CakeHive.isAdapterRegistered(TradeLegacy.typeId)) {
18 + CakeHive.registerAdapter(TradeLegacyAdapter());
19 + }
20 + final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey);
21 + final box = await CakeHive.openBox<TradeLegacy>(Trade.boxName, encryptionKey: tradesBoxKey);
22 + await TradeLegacy.migrateAllToSqlite(box);
23 + } catch (e) {
24 + printV('Trade Hive migration error: $e');
25 + }
26 +}
27 +
28 +class TradeLegacy extends HiveObject {
29 + TradeLegacy({
30 + required this.id,
31 + required this.amount,
32 + ExchangeProviderDescription? provider,
33 + CryptoCurrency? from,
34 + CryptoCurrency? to,
35 + TradeState? state,
36 + this.receiveAmount,
37 + this.createdAt,
38 + this.expiredAt,
39 + this.inputAddress,
40 + this.extraId,
41 + this.outputTransaction,
42 + this.refundAddress,
43 + this.walletId,
44 + this.payoutAddress,
45 + this.password,
46 + this.providerId,
47 + this.providerName,
48 + this.fromWalletAddress,
49 + this.memo,
50 + this.fee,
51 + this.txId,
52 + this.isRefund,
53 + this.isSendAll,
54 + this.router,
55 + this.userCurrencyFromRaw,
56 + this.userCurrencyToRaw,
57 + this.needToRegisterInSwapXyz,
58 + this.sourceTokenAddress,
59 + this.sourceTokenDecimals,
60 + this.routerData,
61 + this.routerValue,
62 + this.routerChainId,
63 + this.sourceTokenAmountRaw,
64 + this.requiresTokenApproval,
65 + this.chainId,
66 + }) {
67 + if (provider != null) providerRaw = provider.raw;
68 + fromRaw = from?.raw ?? -1;
69 + toRaw = to?.raw ?? -1;
70 + if (state != null) stateRaw = state.raw;
71 + }
72 +
73 + static const typeId = TRADE_TYPE_ID;
74 +
75 + String id;
76 + late int providerRaw;
77 + int fromRaw = -1;
78 + int toRaw = -1;
79 + late String stateRaw;
80 + DateTime? createdAt;
81 + DateTime? expiredAt;
82 + String amount;
83 + String? receiveAmount;
84 + String? inputAddress;
85 + String? extraId;
86 + String? outputTransaction;
87 + String? refundAddress;
88 + String? walletId;
89 + String? payoutAddress;
90 + String? password;
91 + String? providerId;
92 + String? providerName;
93 + String? fromWalletAddress;
94 + String? memo;
95 + String? txId;
96 + bool? isRefund;
97 + bool? isSendAll;
98 + String? router;
99 + String? userCurrencyFromRaw;
100 + String? userCurrencyToRaw;
101 + bool? needToRegisterInSwapXyz;
102 + String? sourceTokenAddress;
103 + int? sourceTokenDecimals;
104 + String? routerData;
105 + String? routerValue;
106 + int? routerChainId;
107 + String? sourceTokenAmountRaw;
108 + bool? requiresTokenApproval;
109 + int? chainId;
110 + double? fee;
111 +
112 + Future<void> migrateToSqlite() async {
113 + final trade = Trade(
114 + id: id,
115 + amount: amount,
116 + receiveAmount: receiveAmount,
117 + createdAt: createdAt,
118 + expiredAt: expiredAt,
119 + inputAddress: inputAddress,
120 + extraId: extraId,
121 + outputTransaction: outputTransaction,
122 + refundAddress: refundAddress,
123 + walletId: walletId,
124 + payoutAddress: payoutAddress,
125 + password: password,
126 + providerId: providerId,
127 + providerName: providerName,
128 + fromWalletAddress: fromWalletAddress,
129 + memo: memo,
130 + fee: fee,
131 + txId: txId,
132 + isRefund: isRefund,
133 + isSendAll: isSendAll,
134 + router: router,
135 + from:
136 + TradeCurrencySnapshot.fromLegacyHive(raw: fromRaw, displayTitleTag: userCurrencyFromRaw),
137 + to: TradeCurrencySnapshot.fromLegacyHive(raw: toRaw, displayTitleTag: userCurrencyToRaw),
138 + needToRegisterInSwapXyz: needToRegisterInSwapXyz,
139 + sourceTokenAddress: sourceTokenAddress,
140 + sourceTokenDecimals: sourceTokenDecimals,
141 + routerData: routerData,
142 + routerValue: routerValue,
143 + routerChainId: routerChainId,
144 + sourceTokenAmountRaw: sourceTokenAmountRaw,
145 + requiresTokenApproval: requiresTokenApproval,
146 + chainId: chainId,
147 + );
148 + trade.providerRaw = providerRaw;
149 + trade.stateRaw = stateRaw;
150 + await trade.save();
151 + }
152 +
153 + static Future<void> migrateAllToSqlite(
154 + Box<TradeLegacy> box,
155 + ) async {
156 + printV('Migrating Trades to SQLite: start');
157 + final list = box.values.toList();
158 + for (final trade in list) {
159 + try {
160 + await trade.migrateToSqlite();
161 + await trade.delete();
162 + } catch (e) {
163 + printV('Error migrating trade ${trade.id}: $e');
164 + }
165 + }
166 + printV('Migrating Trades to SQLite: end');
167 + }
168 +}
lib/exchange/trade_legacy.part.dart new
+146
@@ -0,0 +1,146 @@
1 +part of 'trade_legacy.dart';
2 +
3 +class TradeLegacyAdapter extends TypeAdapter<TradeLegacy> {
4 + @override
5 + final int typeId = TradeLegacy.typeId;
6 +
7 + @override
8 + TradeLegacy read(BinaryReader reader) {
9 + final numOfFields = reader.readByte();
10 + final fields = <int, dynamic>{};
11 + for (int i = 0; i < numOfFields; i++) {
12 + try {
13 + fields[reader.readByte()] = reader.read();
14 + } catch (_) {}
15 + }
16 +
17 + return TradeLegacy(
18 + id: fields[0] == null ? '' : fields[0] as String,
19 + amount: fields[7] == null ? '' : fields[7] as String,
20 + receiveAmount: fields[23] as String?,
21 + createdAt: fields[5] as DateTime?,
22 + expiredAt: fields[6] as DateTime?,
23 + inputAddress: fields[8] as String?,
24 + extraId: fields[9] as String?,
25 + outputTransaction: fields[10] as String?,
26 + refundAddress: fields[11] as String?,
27 + walletId: fields[12] as String?,
28 + payoutAddress: fields[13] as String?,
29 + password: fields[14] as String?,
30 + providerId: fields[15] as String?,
31 + providerName: fields[16] as String?,
32 + fromWalletAddress: fields[17] as String?,
33 + memo: fields[18] as String?,
34 + txId: fields[19] as String?,
35 + isRefund: fields[20] as bool?,
36 + isSendAll: fields[21] as bool?,
37 + router: fields[22] as String?,
38 + userCurrencyFromRaw: fields[24] as String?,
39 + userCurrencyToRaw: fields[25] as String?,
40 + needToRegisterInSwapXyz: fields[26] as bool?,
41 + sourceTokenAddress: fields[27] as String?,
42 + sourceTokenDecimals: fields[28] as int?,
43 + routerData: fields[29] as String?,
44 + routerValue: fields[30] as String?,
45 + routerChainId: fields[31] as int?,
46 + sourceTokenAmountRaw: fields[32] as String?,
47 + requiresTokenApproval: fields[33] as bool?,
48 + chainId: fields[34] as int?,
49 + fee: fields[35] as double?,
50 + )
51 + ..providerRaw =
52 + fields[1] == null ? 0 : fields[1] as int
53 + ..fromRaw = (fields[2] as int?) ?? -1
54 + ..toRaw = (fields[3] as int?) ?? -1
55 + ..stateRaw =
56 + fields[4] == null ? '' : fields[4] as String;
57 + }
58 +
59 + @override
60 + void write(BinaryWriter writer, TradeLegacy obj) {
61 + writer
62 + ..writeByte(36)
63 + ..writeByte(0)
64 + ..write(obj.id)
65 + ..writeByte(1)
66 + ..write(obj.providerRaw)
67 + ..writeByte(2)
68 + ..write(obj.fromRaw)
69 + ..writeByte(3)
70 + ..write(obj.toRaw)
71 + ..writeByte(4)
72 + ..write(obj.stateRaw)
73 + ..writeByte(5)
74 + ..write(obj.createdAt)
75 + ..writeByte(6)
76 + ..write(obj.expiredAt)
77 + ..writeByte(7)
78 + ..write(obj.amount)
79 + ..writeByte(8)
80 + ..write(obj.inputAddress)
81 + ..writeByte(9)
82 + ..write(obj.extraId)
83 + ..writeByte(10)
84 + ..write(obj.outputTransaction)
85 + ..writeByte(11)
86 + ..write(obj.refundAddress)
87 + ..writeByte(12)
88 + ..write(obj.walletId)
89 + ..writeByte(13)
90 + ..write(obj.payoutAddress)
91 + ..writeByte(14)
92 + ..write(obj.password)
93 + ..writeByte(15)
94 + ..write(obj.providerId)
95 + ..writeByte(16)
96 + ..write(obj.providerName)
97 + ..writeByte(17)
98 + ..write(obj.fromWalletAddress)
99 + ..writeByte(18)
100 + ..write(obj.memo)
101 + ..writeByte(19)
102 + ..write(obj.txId)
103 + ..writeByte(20)
104 + ..write(obj.isRefund)
105 + ..writeByte(21)
106 + ..write(obj.isSendAll)
107 + ..writeByte(22)
108 + ..write(obj.router)
109 + ..writeByte(23)
110 + ..write(obj.receiveAmount)
111 + ..writeByte(24)
112 + ..write(obj.userCurrencyFromRaw)
113 + ..writeByte(25)
114 + ..write(obj.userCurrencyToRaw)
115 + ..writeByte(26)
116 + ..write(obj.needToRegisterInSwapXyz)
117 + ..writeByte(27)
118 + ..write(obj.sourceTokenAddress)
119 + ..writeByte(28)
120 + ..write(obj.sourceTokenDecimals)
121 + ..writeByte(29)
122 + ..write(obj.routerData)
123 + ..writeByte(30)
124 + ..write(obj.routerValue)
125 + ..writeByte(31)
126 + ..write(obj.routerChainId)
127 + ..writeByte(32)
128 + ..write(obj.sourceTokenAmountRaw)
129 + ..writeByte(33)
130 + ..write(obj.requiresTokenApproval)
131 + ..writeByte(34)
132 + ..write(obj.chainId)
133 + ..writeByte(35)
134 + ..write(obj.fee);
135 + }
136 +
137 + @override
138 + int get hashCode => typeId.hashCode;
139 +
140 + @override
141 + bool operator ==(Object other) =>
142 + identical(this, other) ||
143 + other is TradeLegacyAdapter &&
144 + runtimeType == other.runtimeType &&
145 + typeId == other.typeId;
146 +}
lib/main.dart
+11 -20
@@ -19,7 +19,7 @@ import 'package:cake_wallet/entities/language_service.dart';
19 import 'package:cake_wallet/entities/template.dart';
20 import 'package:cake_wallet/entities/transaction_description.dart';
21 import 'package:cake_wallet/exchange/exchange_template.dart';
22 -import 'package:cake_wallet/exchange/trade.dart';
22 +import 'package:cake_wallet/exchange/trade_legacy.dart';
23 import 'package:cake_wallet/generated/i18n.dart';
24 import 'package:cake_wallet/locales/locale.dart';
25 import 'package:cake_wallet/order/order.dart';
@@ -37,7 +37,6 @@ import 'package:cake_wallet/utils/exception_handler.dart';
37 import 'package:cake_wallet/utils/feature_flag.dart';
38 import 'package:cake_wallet/utils/responsive_layout_util.dart';
39 import 'package:cake_wallet/view_model/link_view_model.dart';
40 -import 'package:cake_wallet/utils/responsive_layout_util.dart';
40 import 'package:cake_wallet/zcash/zcash.dart';
41 import 'package:cw_core/address_info.dart';
42 import 'package:cw_core/cake_hive.dart';
@@ -65,7 +64,6 @@ import 'package:flutter/services.dart';
64 import 'package:flutter_daemon/flutter_daemon.dart';
65 import 'package:flutter_mobx/flutter_mobx.dart';
66 import 'package:hive/hive.dart';
68 -import 'package:cw_core/root_dir.dart';
67 import 'package:quick_actions/quick_actions.dart';
68 import 'package:logging/logging.dart';
69 import 'package:shared_preferences/shared_preferences.dart';
@@ -230,10 +228,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
228 CakeHive.registerAdapter(TransactionDescriptionAdapter());
229 }
230
233 - if (!CakeHive.isAdapterRegistered(Trade.typeId)) {
234 - CakeHive.registerAdapter(TradeAdapter());
235 - }
236 -
231 if (!CakeHive.isAdapterRegistered(AddressInfo.typeId)) {
232 CakeHive.registerAdapter(AddressInfoAdapter());
233 }
@@ -290,7 +284,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
284 final secureStorage = secureStorageShared;
285 final transactionDescriptionsBoxKey =
286 await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
293 - final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey);
287 final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey);
288 final contacts = await CakeHive.openBox<Contact>(Contact.boxName);
289 final nodes = await CakeHive.openBox<Node>(Node.boxName);
@@ -299,7 +292,8 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
292 final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
293 TransactionDescription.boxName,
294 encryptionKey: transactionDescriptionsBoxKey);
302 - final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
295 + await performTradeHiveMigration(secureStorage);
296 +
297 final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
298 final templates = await CakeHive.openBox<Template>(Template.boxName);
299 final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
@@ -318,7 +312,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
312 nodes: nodes,
313 powNodes: powNodes,
314 contactSource: contacts,
321 - tradesSource: trades,
315 ordersSource: orders,
316 unspentCoinsInfoSource: unspentCoinsInfoSource,
317 // fiatConvertationService: fiatConvertationService,
@@ -339,7 +332,6 @@ Future<void> initialSetup({
332 required Box<Node> nodes,
333 required Box<Node> powNodes,
334 required Box<Contact> contactSource,
342 - required Box<Trade> tradesSource,
335 required Box<Order> ordersSource,
336 // required FiatConvertationService fiatConvertationService,
337 required Box<Template> templates,
@@ -354,19 +346,18 @@ Future<void> initialSetup({
346 }) async {
347 LanguageService.loadLocaleList();
348 await defaultSettingsMigration(
357 - secureStorage: secureStorage,
358 - version: initialMigrationVersion,
359 - sharedPreferences: sharedPreferences,
360 - contactSource: contactSource,
361 - tradeSource: tradesSource,
362 - nodes: nodes,
363 - powNodes: powNodes,
364 - havenSeedStore: havenSeedStore);
349 + secureStorage: secureStorage,
350 + version: initialMigrationVersion,
351 + sharedPreferences: sharedPreferences,
352 + contactSource: contactSource,
353 + nodes: nodes,
354 + powNodes: powNodes,
355 + havenSeedStore: havenSeedStore,
356 + );
357 await setup(
358 nodeSource: nodes,
359 powNodeSource: powNodes,
360 contactSource: contactSource,
369 - tradesSource: tradesSource,
361 ordersSource: ordersSource,
362 templates: templates,
363 exchangeTemplates: exchangeTemplates,
lib/new-ui/widgets/coins_page/assets_history/history_section.dart
+137 -124
@@ -35,13 +35,15 @@ class HistorySection extends StatelessWidget {
35 ? SliverPadding(
36 padding: EdgeInsets.only(top: 24),
37 sliver: SliverToBoxAdapter(
38 - child: (dashboardViewModel.status is SyncingSyncStatus) ? SizedBox.shrink() : Center(
39 - child: Text(S.of(context).transactions_will_appear_here,
40 - style: TextStyle(
41 - fontSize: 14,
42 - fontWeight: FontWeight.w400,
43 - color: Theme.of(context).colorScheme.onSurfaceVariant)),
44 - ),
38 + child: (dashboardViewModel.status is SyncingSyncStatus)
39 + ? SizedBox.shrink()
40 + : Center(
41 + child: Text(S.of(context).transactions_will_appear_here,
42 + style: TextStyle(
43 + fontSize: 14,
44 + fontWeight: FontWeight.w400,
45 + color: Theme.of(context).colorScheme.onSurfaceVariant)),
46 + ),
47 ),
48 )
49 : SliverList(
@@ -51,136 +53,147 @@ class HistorySection extends StatelessWidget {
53 final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1];
54 final topPadding = index == 0 ? 0.0 : 18.0;
55 final item = dashboardViewModel.items[index];
54 - final nextItem = index == dashboardViewModel.items.length - 1
55 - ? null
56 - : dashboardViewModel.items[index + 1];
56 + final nextItem = index == dashboardViewModel.items.length - 1
57 + ? null
58 + : dashboardViewModel.items[index + 1];
59
58 - final roundedBottom = (nextItem == null || nextItem is DateSectionItem);
59 - final roundedTop = (prevItem == null || prevItem is DateSectionItem);
60 + final roundedBottom = (nextItem == null || nextItem is DateSectionItem);
61 + final roundedTop = (prevItem == null || prevItem is DateSectionItem);
62
61 - if (item is TransactionListItem) {
62 - final transaction = item.transaction;
63 - final transactionType = dashboardViewModel.getTransactionType(transaction);
63 + if (item is TransactionListItem) {
64 + final transaction = item.transaction;
65 + final transactionType = dashboardViewModel.getTransactionType(transaction);
66
65 - if (item.hasTokens && item.assetOfTransaction == null) {
66 - return Container();
67 - }
67 + if (item.hasTokens && item.assetOfTransaction == null) {
68 + return Container();
69 + }
70
69 - CryptoCurrency? asset;
70 - if (transaction.additionalInfo["isLightning"] == true)
71 - asset = CryptoCurrency.btcln;
72 - else
73 - asset = item.assetOfTransaction;
71 + CryptoCurrency? asset;
72 + if (transaction.additionalInfo["isLightning"] == true)
73 + asset = CryptoCurrency.btcln;
74 + else
75 + asset = item.assetOfTransaction;
76
75 - return GestureDetector(
76 - onTap: () {
77 - final page = getIt.get<TransactionDetailsModal>(param1: transaction);
78 - showModalBottomSheet(isScrollControlled:true,context: context, builder: (context) => page);
79 - },
80 - child: HistoryTile(
81 - title: item.formattedTitle + transactionType,
82 - date: DateFormat('HH:mm').format(transaction.date),
83 - amount: item.formattedCryptoAmount,
84 - amountFiat: item.formattedFiatAmount,
85 - hasTokens: item.hasTokens,
86 - chainIconPath: _getChainIconPath(),
87 - roundedBottom: roundedBottom,
88 - roundedTop: roundedTop,
89 - bottomSeparator: !roundedBottom,
90 - direction: item.transaction.direction,
91 - pending: item.transaction.isPending,
92 - asset: asset,
93 - ),
94 - );
95 - } else if (item is TradeListItem) {
96 - final trade = item.trade;
77 + return GestureDetector(
78 + onTap: () {
79 + final page = getIt.get<TransactionDetailsModal>(param1: transaction);
80 + showModalBottomSheet(
81 + isScrollControlled: true,
82 + context: context,
83 + builder: (context) => page);
84 + },
85 + child: HistoryTile(
86 + title: item.formattedTitle + transactionType,
87 + date: DateFormat('HH:mm').format(transaction.date),
88 + amount: item.formattedCryptoAmount,
89 + amountFiat: item.formattedFiatAmount,
90 + hasTokens: item.hasTokens,
91 + chainIconPath: _getChainIconPath(),
92 + roundedBottom: roundedBottom,
93 + roundedTop: roundedTop,
94 + bottomSeparator: !roundedBottom,
95 + direction: item.transaction.direction,
96 + pending: item.transaction.isPending,
97 + asset: asset,
98 + ),
99 + );
100 + } else if (item is TradeListItem) {
101 + final trade = item.trade;
102 + final tradeFrom = trade.from;
103 + final tradeTo = trade.to;
104 + if (tradeFrom == null || tradeTo == null) {
105 + return const SizedBox.shrink();
106 + }
107
98 - final tradeFrom = trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
108 + return GestureDetector(
109 + onTap: () => Navigator.of(context)
110 + .pushNamed(Routes.tradeDetails, arguments: trade),
111 + child: HistoryTradeTile(
112 + from: tradeFrom,
113 + to: tradeTo,
114 + provider: trade.provider,
115 + date:
116 + DateFormat('HH:mm').format(item.trade.createdAt ?? DateTime.now()),
117 + amount: trade.amountFormatted(),
118 + receiveAmount: trade.receiveAmountFormatted(),
119 + roundedBottom: roundedBottom,
120 + roundedTop: roundedTop,
121 + bottomSeparator: !roundedBottom,
122 + swapState: trade.state,
123 + ),
124 + );
125 + } else if (item is DateSectionItem) {
126 + return Padding(
127 + padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding),
128 + child: Text(DateFormatter.convertDateTimeToReadableString(item.date),
129 + style: TextStyle(
130 + color: Theme.of(context).colorScheme.onSurfaceVariant)));
131 + } else if (item is OrderListItem) {
132 + return GestureDetector(
133 + onTap: () => Navigator.of(context)
134 + .pushNamed(Routes.orderDetails, arguments: item.order),
135 + child: HistoryOrderTile(
136 + date: DateFormat('HH:mm').format(item.order.createdAt),
137 + amount: item.orderFormattedAmount,
138 + amountFiat: "USD 0.00",
139 + roundedBottom: roundedBottom,
140 + roundedTop: roundedTop,
141 + bottomSeparator: !roundedBottom,
142 + ),
143 + );
144 + } else if (item is PayjoinTransactionListItem) {
145 + final session = item.session;
146
100 - final tradeTo = trade.toRaw >= 0 ? trade.to : trade.userCurrencyTo;
147 + return GestureDetector(
148 + onTap: () => Navigator.of(context).pushNamed(
149 + Routes.payjoinDetails,
150 + arguments: [item.sessionId, item.transaction],
151 + ),
152 + child: PayjoinHistoryTile(
153 + createdAt: DateFormat('HH:mm').format(session.inProgressSince!),
154 + amount: dashboardViewModel.appStore.amountParsingProxy
155 + .getDisplayCryptoString(
156 + session.amount.toInt(), CryptoCurrency.btc),
157 + currency: item.transaction?.from ?? "BTC",
158 + state: item.status,
159 + isSending: session.isSenderSession,
160 + roundedTop: roundedTop,
161 + roundedBottom: roundedBottom,
162 + bottomSeparator: !roundedBottom),
163 + );
164 + } else if (item is AnonpayTransactionListItem) {
165 + final transactionInfo = item.transaction;
166
102 - return GestureDetector(
103 - onTap: () => Navigator.of(context)
104 - .pushNamed(Routes.tradeDetails, arguments: trade),
105 - child: HistoryTradeTile(
106 - from: tradeFrom!,
107 - to: tradeTo!,
108 - provider: trade.provider,
109 - date: DateFormat('HH:mm').format(item.trade.createdAt!),
110 - amount: trade.amountFormatted(),
111 - receiveAmount: trade.receiveAmountFormatted(),
112 - roundedBottom: roundedBottom,
113 - roundedTop: roundedTop,
114 - bottomSeparator: !roundedBottom,
115 - swapState: trade.state,
116 - ),
117 - );
118 - } else if (item is DateSectionItem) {
119 - return Padding(
120 - padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding),
121 - child: Text(DateFormatter.convertDateTimeToReadableString(item.date),
122 - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)));
123 - }else if(item is OrderListItem){
124 - return GestureDetector(
125 - onTap: () => Navigator.of(context)
126 - .pushNamed(Routes.orderDetails, arguments: item.order),
127 - child: HistoryOrderTile(
128 - date: DateFormat('HH:mm').format(item.order.createdAt),
129 - amount: item.orderFormattedAmount,
130 - amountFiat: "USD 0.00",
131 - roundedBottom: roundedBottom,
132 - roundedTop: roundedTop,
133 - bottomSeparator: !roundedBottom,
134 - ),
135 - );
136 - } else if (item is PayjoinTransactionListItem) {
137 - final session = item.session;
138 -
139 - return GestureDetector(
140 - onTap: () => Navigator.of(context).pushNamed(
141 - Routes.payjoinDetails,
142 - arguments: [item.sessionId, item.transaction],
167 + return GestureDetector(
168 + onTap: () => Navigator.of(context)
169 + .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo),
170 + child: AnonpayHistoryTile(
171 + provider: transactionInfo.provider,
172 + createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt),
173 + amount: transactionInfo.fiatAmount?.toString() ??
174 + (transactionInfo.amountTo?.toString() ?? ''),
175 + currency: transactionInfo.fiatAmount != null
176 + ? transactionInfo.fiatEquiv ?? ''
177 + : CryptoCurrency.fromFullName(transactionInfo.coinTo)
178 + .name
179 + .toUpperCase(),
180 + roundedTop: roundedTop,
181 + roundedBottom: roundedBottom,
182 + bottomSeparator: !roundedBottom));
183 + } else
184 + return Text(item.runtimeType.toString());
185 + }),
186 + ),
187 ),
144 - child: PayjoinHistoryTile(
145 - createdAt: DateFormat('HH:mm').format(session.inProgressSince!),
146 - amount: dashboardViewModel.appStore.amountParsingProxy
147 - .getDisplayCryptoString(session.amount.toInt(), CryptoCurrency.btc),
148 - currency: item.transaction?.from ?? "BTC",
149 - state: item.status,
150 - isSending: session.isSenderSession,
151 - roundedTop: roundedTop,
152 - roundedBottom: roundedBottom,
153 - bottomSeparator: !roundedBottom),
154 - );
155 - } else if (item is AnonpayTransactionListItem) {
156 - final transactionInfo = item.transaction;
157 -
158 - return GestureDetector(
159 - onTap: () => Navigator.of(context)
160 - .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo),
161 - child: AnonpayHistoryTile(
162 - provider: transactionInfo.provider,
163 - createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt),
164 - amount: transactionInfo.fiatAmount?.toString() ??
165 - (transactionInfo.amountTo?.toString() ?? ''),
166 - currency: transactionInfo.fiatAmount != null
167 - ? transactionInfo.fiatEquiv ?? ''
168 - : CryptoCurrency.fromFullName(transactionInfo.coinTo).name.toUpperCase(),
169 - roundedTop: roundedTop,
170 - roundedBottom: roundedBottom,
171 - bottomSeparator: !roundedBottom));
172 - } else
173 - return Text(item.runtimeType.toString());
174 - }),
175 - ),
176 - ),
177 - ));
188 + ));
189 }
190
191 String _getChainIconPath() {
192 try {
182 - return CryptoCurrency.fromString(dashboardViewModel.wallet.currency.tag ??dashboardViewModel.wallet.currency.title).chainIconPath!;
183 - } catch(e) {
193 + return CryptoCurrency.fromString(
194 + dashboardViewModel.wallet.currency.tag ?? dashboardViewModel.wallet.currency.title)
195 + .chainIconPath!;
196 + } catch (e) {
197 return dashboardViewModel.wallet.currency.chainIconPath ?? "";
198 }
199 }
lib/new-ui/widgets/coins_page/assets_history/history_trade_tile.dart
+29 -31
@@ -39,28 +39,30 @@ class HistoryTradeTile extends StatelessWidget {
39 width: 50,
40 child: Stack(
41 children: [
42 - CakeImageWidget(imageUrl: _getIconPath(from),
43 - width: currencyIconSize, height: currencyIconSize),
42 + CakeImageWidget(
43 + imageUrl: _getIconPath(from), width: currencyIconSize, height: currencyIconSize),
44 Positioned(
45 - top: currencyIconSize / 2,
46 - left: currencyIconSize / 2,
47 - child: Container(
48 - decoration: BoxDecoration(
49 - border: Border.all(
50 - width: 2,
51 - color: Theme.of(context).colorScheme.surfaceContainer),
52 - shape: BoxShape.circle),
53 - child: CakeImageWidget(imageUrl: _getIconPath(to),
54 - width: currencyIconSize, height: currencyIconSize))),
45 + top: currencyIconSize / 2,
46 + left: currencyIconSize / 2,
47 + child: Container(
48 + decoration: BoxDecoration(
49 + border:
50 + Border.all(width: 2, color: Theme.of(context).colorScheme.surfaceContainer),
51 + shape: BoxShape.circle),
52 + child: CakeImageWidget(
53 + imageUrl: _getIconPath(to),
54 + width: currencyIconSize,
55 + height: currencyIconSize,
56 + ),
57 + ),
58 + ),
59 ],
60 ),
61 );
62 }
63
60 -
64 @override
65 Widget build(BuildContext context) {
63 -
66 final fromChainIcon = _getChainIcon(from);
67 final toChainIcon = _getChainIcon(to);
68
@@ -118,36 +120,32 @@ class HistoryTradeTile extends StatelessWidget {
120 }
121
122 String _getIconPath(CryptoCurrency currency) {
121 - try { // temporarily until we migrate from hive to sqlite and store the full currency object
122 - if (currency.iconPath != null) {
123 - return currency.iconPath!;
123 + try {
124 + if (currency.title.isNotEmpty) {
125 + final live = CryptoCurrency.safeParseCurrencyFromString(currency.title, tag: currency.tag);
126 + if (live?.iconPath != null) return live!.iconPath!;
127 }
128
126 - if (currency.name.isNotEmpty) {
127 - final currencyFromName = CryptoCurrency.fromString(currency.name);
128 - if (currencyFromName.iconPath != null) {
129 - return currencyFromName.iconPath!;
130 - }
131 - }
129 + if (currency.iconPath != null) return currency.iconPath!;
130
133 - if (currency.title.isNotEmpty) {
134 - final currencyFromTitle = CryptoCurrency.fromString(currency.title);
135 - if (currencyFromTitle.iconPath != null) {
136 - return currencyFromTitle.iconPath!;
137 - }
131 + if (currency.name.isNotEmpty) {
132 + final byName = CryptoCurrency.safeParseCurrencyFromString(currency.name);
133 + if (byName?.iconPath != null) return byName!.iconPath!;
134 }
135 } catch (_) {}
136
141 - //TODO approporiate fallback
137 return "";
138 }
139
140 String _getChainIcon(CryptoCurrency currency) {
141 try {
147 - if (currency.chainIconPath != null) {
148 - return currency.chainIconPath!;
142 + if (currency.title.isNotEmpty) {
143 + final parsedCurrency = CryptoCurrency.safeParseCurrencyFromString(currency.title, tag: currency.tag);
144 + if (parsedCurrency?.chainIconPath != null) return parsedCurrency!.chainIconPath!;
145 }
146
147 + if (currency.chainIconPath != null) return currency.chainIconPath!;
148 +
149 if ((currency.tag ?? "").isNotEmpty) {
150 final currencyFromTag = CryptoCurrency.fromString(currency.tag!);
151 if (currencyFromTag.chainIconPath != null) {
lib/src/screens/dashboard/pages/transactions_page.dart
+2 -2
@@ -166,9 +166,9 @@ class TransactionsPage extends StatelessWidget {
166 final trade = item.trade;
167
168 final tradeFrom =
169 - trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
169 + trade.from;
170
171 - final tradeTo = trade.toRaw >= 0 ? trade.to : trade.userCurrencyTo;
171 + final tradeTo = trade.to;
172
173 return tradeFrom != null && tradeTo != null
174 ? Observer(
lib/src/screens/exchange_trade/exchange_trade_page.dart
+1 -1
@@ -31,7 +31,7 @@ void showInformation(ExchangeTradeViewModel exchangeTradeViewModel, BuildContext
31 final trade = exchangeTradeViewModel.trade;
32 final walletName = exchangeTradeViewModel.wallet.name;
33
34 - final from = trade.from?.toString() ?? trade.userCurrencyFrom.toString();
34 + final from = trade.from?.toString() ?? '';
35
36 final information = exchangeTradeViewModel.isSendable
37 ? S.current.exchange_trade_result_confirm(trade.amount, from, walletName) +
lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart
+2 -2
@@ -319,14 +319,14 @@ class _SwapDetailsContent extends StatelessWidget {
319 _SwapDetailsTile(
320 label: 'You Send',
321 value:
322 - '${trade.amount} ${trade.from?.title ?? trade.userCurrencyFrom?.title ?? ''}',
322 + '${trade.amount} ${trade.from?.title ?? ''}',
323 valueFiatFormatted: exchangeTradeViewModel.sendAmountFiatFormatted,
324 ),
325 const SizedBox(height: 8),
326 _SwapDetailsTile(
327 label: 'You Get',
328 value:
329 - '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? trade.userCurrencyTo?.title ?? ''}',
329 + '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}',
330 valueFiatFormatted: exchangeTradeViewModel
331 .getReceiveAmountFiatFormatted(trade.receiveAmount ?? '0.0'),
332 ),
lib/store/dashboard/trades_store.dart
+16 -14
@@ -1,9 +1,7 @@
1 -import 'dart:async';
1 import 'package:cake_wallet/exchange/trade.dart';
2 import 'package:cake_wallet/store/app_store.dart';
3 import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
4 import 'package:flutter/foundation.dart';
6 -import 'package:hive/hive.dart';
5 import 'package:mobx/mobx.dart';
6
7 part 'trades_store.g.dart';
@@ -11,14 +9,11 @@ part 'trades_store.g.dart';
9 class TradesStore = TradesStoreBase with _$TradesStore;
10
11 abstract class TradesStoreBase with Store {
14 - TradesStoreBase({required this.tradesSource, required this.appStore})
15 - : trades = <TradeListItem>[] {
16 - _onTradesChanged = tradesSource.watch().listen((_) async => await updateTradeList());
12 + TradesStoreBase({required this.appStore}) : trades = <TradeListItem>[] {
13 + Trade.onChanged.stream.listen((_) => updateTradeList());
14 updateTradeList();
15 }
16
20 - Box<Trade> tradesSource;
21 - StreamSubscription<BoxEvent>? _onTradesChanged;
17 AppStore appStore;
18
19 @observable
@@ -31,11 +26,18 @@ abstract class TradesStoreBase with Store {
26 void setTrade(Trade trade) => this.trade = trade;
27
28 @action
34 - Future<void> updateTradeList() async => trades = tradesSource.values
35 - .map((trade) => TradeListItem(
36 - trade: trade,
37 - appStore: appStore,
38 - key: ValueKey('trade_list_item_${trade.id}_key'),
39 - ))
40 - .toList();
29 + Future<void> updateTradeList() async {
30 + try {
31 + final allTrades = await Trade.getAll();
32 + runInAction(() {
33 + trades = allTrades
34 + .map((trade) => TradeListItem(
35 + trade: trade,
36 + appStore: appStore,
37 + key: ValueKey('trade_list_item_${trade.id}_key'),
38 + ))
39 + .toList();
40 + });
41 + } catch (_) {}
42 + }
43 }
lib/view_model/dashboard/trade_list_item.dart
+17 -8
@@ -15,15 +15,24 @@ class TradeListItem extends ActionListItem {
15
16 BalanceDisplayMode get displayMode => appStore.settingsStore.balanceDisplayMode;
17
18 - String get tradeFormattedAmount => displayMode == BalanceDisplayMode.hiddenBalance
19 - ? "---"
20 - : appStore.amountParsingProxy.getDisplayCryptoAmount(trade.amountFormatted(), trade.from!);
18 + String get tradeFormattedAmount {
19 + if (displayMode == BalanceDisplayMode.hiddenBalance) {
20 + return '---';
21 + }
22 + final from = trade.from;
23 + if (from == null) return trade.amountFormatted();
24 + return appStore.amountParsingProxy.getDisplayCryptoAmount(trade.amountFormatted(), from);
25 + }
26
22 - String get tradeFormattedReceiveAmount => displayMode == BalanceDisplayMode.hiddenBalance
23 - ? "---"
24 - : appStore.amountParsingProxy
25 - .getDisplayCryptoAmount(trade.receiveAmountFormatted(), trade.to!);
27 + String get tradeFormattedReceiveAmount {
28 + if (displayMode == BalanceDisplayMode.hiddenBalance) {
29 + return '---';
30 + }
31 + final to = trade.to;
32 + if (to == null) return trade.receiveAmountFormatted();
33 + return appStore.amountParsingProxy.getDisplayCryptoAmount(trade.receiveAmountFormatted(), to);
34 + }
35
36 @override
28 - DateTime get date => trade.createdAt!;
37 + DateTime get date => trade.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
38 }
lib/view_model/exchange/exchange_trade_view_model.dart
+23 -36
@@ -34,7 +34,6 @@ import 'package:cw_core/payment_uris.dart';
34 import 'package:cw_core/utils/print_verbose.dart';
35 import 'package:cw_core/wallet_base.dart';
36 import 'package:cw_core/wallet_type.dart';
37 -import 'package:hive/hive.dart';
37 import 'package:mobx/mobx.dart';
38
39 part 'exchange_trade_view_model.g.dart';
@@ -44,7 +43,6 @@ class ExchangeTradeViewModel = ExchangeTradeViewModelBase with _$ExchangeTradeVi
43 abstract class ExchangeTradeViewModelBase with Store {
44 ExchangeTradeViewModelBase({
45 required this.wallet,
47 - required this.trades,
46 required this.tradesStore,
47 required this.sendViewModel,
48 required this.feesViewModel,
@@ -78,10 +76,10 @@ abstract class ExchangeTradeViewModelBase with Store {
76 _provider = StealthExExchangeProvider();
77 break;
78 case ExchangeProviderDescription.thorChain:
81 - _provider = ThorChainExchangeProvider(tradesStore: trades);
79 + _provider = ThorChainExchangeProvider();
80 break;
81 case ExchangeProviderDescription.chainflip:
84 - _provider = ChainflipExchangeProvider(tradesStore: trades);
82 + _provider = ChainflipExchangeProvider();
83 break;
84 case ExchangeProviderDescription.xoSwap:
85 _provider = XOSwapExchangeProvider();
@@ -106,7 +104,6 @@ abstract class ExchangeTradeViewModelBase with Store {
104 }
105
106 final WalletBase wallet;
109 - final Box<Trade> trades;
107 final TradesStore tradesStore;
108 final SendViewModel sendViewModel;
109 final FeesViewModel feesViewModel;
@@ -217,7 +214,7 @@ abstract class ExchangeTradeViewModelBase with Store {
214 Future<void> confirmSending() async {
215 if (!isSendable) return;
216
220 - final selected = trade.from ?? trade.userCurrencyFrom;
217 + final selected = trade.from;
218 if (selected == null) {
219 printV('No selectable currency for trade ${trade.id}');
220 return;
@@ -231,35 +228,23 @@ abstract class ExchangeTradeViewModelBase with Store {
228 if (_provider is SwapsXyzExchangeProvider) {
229 final hash = pendingTransaction?.evmTxHashFromRawHex ?? pendingTransaction?.id ?? '';
230 trade.txId = hash;
234 -
235 - if (trade.isInBox) {
236 - await trade.save();
237 - } else {
238 - await trades.add(trade);
239 - }
231 + await trade.save();
232 }
233
234 if (_provider is ThorChainExchangeProvider) {
235 trade.id = pendingTransaction?.id ?? '';
244 - trades.add(trade);
236 + await trade.save();
237 }
238 }
239
240 @action
241 Future<void> _updateTrade() async {
242 try {
251 - final agreedAmount = tradesStore.trade!.amount;
252 - final isSendAll = tradesStore.trade!.isSendAll;
243 final updatedTrade = await _provider!.findTradeById(id: trade.id);
244
255 - if (updatedTrade.createdAt == null && trade.createdAt != null)
256 - updatedTrade.createdAt = trade.createdAt;
257 -
258 - if (updatedTrade.amount.isEmpty) updatedTrade.amount = trade.amount;
259 -
260 - trade = updatedTrade;
261 - trade.amount = agreedAmount;
262 - trade.isSendAll = isSendAll;
245 + trade.mergeFindTradeByIdResult(updatedTrade);
246 + await trade.save();
247 + tradesStore.setTrade(trade);
248
249 _updateItems();
250 } catch (e) {
@@ -268,10 +253,8 @@ abstract class ExchangeTradeViewModelBase with Store {
253 }
254
255 void _updateItems() {
271 - final trade = tradesStore.trade!;
272 -
273 - final tradeFrom = trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
274 - final tradeTo = trade.toRaw >= 0 ? trade.to : trade.userCurrencyTo;
256 + final tradeFrom = trade.from;
257 + final tradeTo = trade.to;
258
259 final tagFrom = tradeFrom?.tag != null ? "${tradeFrom!.tag} " : "";
260 final tagTo = tradeTo?.tag != null ? "${tradeTo!.tag} " : "";
@@ -289,12 +272,12 @@ abstract class ExchangeTradeViewModelBase with Store {
272 ),
273 );
274
292 - if (tradeFrom != null || tradeTo != null) {
275 + if (tradeFrom != null && tradeTo != null) {
276 items.addAll([
277 ExchangeTradeItem(
278 title: S.current.amount,
279 data:
297 - "${_amountParsingProxy.getDisplayCryptoAmount(trade.amount, tradeFrom!)} ${_amountParsingProxy.getCryptoSymbol(tradeFrom)}",
280 + "${_amountParsingProxy.getDisplayCryptoAmount(trade.amount, tradeFrom)} ${_amountParsingProxy.getCryptoSymbol(tradeFrom)}",
281 isCopied: false,
282 isReceiveDetail: false,
283 isExternalSendDetail: true,
@@ -302,14 +285,14 @@ abstract class ExchangeTradeViewModelBase with Store {
285 ExchangeTradeItem(
286 title: "${S.current.you_will_receive_estimated_amount}:",
287 data:
305 - "${_amountParsingProxy.getDisplayCryptoAmount(tradesStore.trade?.receiveAmount ?? "0", tradeTo!)} ${_amountParsingProxy.getCryptoSymbol(tradeTo)}",
288 + "${_amountParsingProxy.getDisplayCryptoAmount(trade.receiveAmount ?? "0", tradeTo)} ${_amountParsingProxy.getCryptoSymbol(tradeTo)}",
289 isCopied: true,
290 isReceiveDetail: true,
291 isExternalSendDetail: false,
292 ),
293 ExchangeTradeItem(
311 - title: "${S.current.send_to_this_address("${tradeFrom}", tagFrom)}:",
312 - data: trade.inputAddress ?? "",
294 + title: "${S.current.send_to_this_address("$tradeFrom", tagFrom)}:",
295 + data: trade.inputAddress ?? '',
296 isCopied: false,
297 isReceiveDetail: false,
298 isExternalSendDetail: true,
@@ -367,7 +350,10 @@ abstract class ExchangeTradeViewModelBase with Store {
350
351 static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
352 final trade = tradesStore.trade!;
370 - final tradeFrom = trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
353 + final tradeFrom = trade.from;
354 +
355 + bool _sameCurrency(CryptoCurrency? a, CryptoCurrency? b) =>
356 + a != null && b != null && a.titleAndTagEqual(b);
357
358 bool _isEthToken() =>
359 wallet.currency == CryptoCurrency.eth && tradeFrom?.tag == CryptoCurrency.eth.title;
@@ -393,8 +379,9 @@ abstract class ExchangeTradeViewModelBase with Store {
379 bool _isBscToken() =>
380 wallet.currency == CryptoCurrency.bnb && tradeFrom?.tag == CryptoCurrency.bnb.tag;
381
396 - return tradeFrom == wallet.currency ||
397 - tradeFrom == CryptoCurrency.btcln && wallet.currency == CryptoCurrency.btc ||
382 + return _sameCurrency(tradeFrom, wallet.currency) ||
383 + (_sameCurrency(tradeFrom, CryptoCurrency.btcln) &&
384 + wallet.currency == CryptoCurrency.btc) ||
385 tradesStore.trade!.provider == ExchangeProviderDescription.xmrto ||
386 _isEthToken() ||
387 _isPolygonToken() ||
@@ -472,7 +459,7 @@ abstract class ExchangeTradeViewModelBase with Store {
459 PaymentURI? get paymentUri {
460 final inputAddress = trade.inputAddress;
461 final amount = trade.amount;
475 - final fromCurrency = trade.from ?? trade.userCurrencyFrom;
462 + final fromCurrency = trade.from;
463
464 if (inputAddress == null || inputAddress.isEmpty || fromCurrency == null) {
465 return null;
lib/view_model/exchange/exchange_view_model.dart
+3 -6
@@ -67,7 +67,6 @@ import 'package:cw_core/utils/print_verbose.dart';
67 import 'package:cw_core/utils/proxy_wrapper.dart';
68 import 'package:cw_core/wallet_type.dart';
69 import 'package:flutter/material.dart';
70 -import 'package:hive/hive.dart';
70 import 'package:mobx/mobx.dart';
71 import 'package:shared_preferences/shared_preferences.dart';
72
@@ -95,7 +94,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
94
95 ExchangeViewModelBase(
96 this._appStore,
98 - this.trades,
97 this._exchangeTemplateStore,
98 this.tradesStore,
99 this.sharedPreferences,
@@ -307,7 +305,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
305 [WalletType.monero, WalletType.wownero, WalletType.zcash].contains(wallet.type);
306
307 bool _useTorOnly;
310 - final Box<Trade> trades;
308 final ExchangeTemplateStore _exchangeTemplateStore;
309 final TradesStore tradesStore;
310 final SharedPreferences sharedPreferences;
@@ -315,7 +312,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
312 List<ExchangeProvider> get _allProviders => [
313 ChangeNowExchangeProvider(settingsStore: _settingsStore),
314 // SideShiftExchangeProvider(),
318 - ChainflipExchangeProvider(tradesStore: trades),
315 + ChainflipExchangeProvider(),
316 if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
317 SwapTradeExchangeProvider(),
318 LetsExchangeExchangeProvider(),
@@ -1213,7 +1210,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1210 }
1211
1212 tradesStore.setTrade(trade);
1216 - if (trade.provider != ExchangeProviderDescription.thorChain) await trades.add(trade);
1213 + if (trade.provider != ExchangeProviderDescription.thorChain) await trade.save();
1214 tradeState = TradeIsCreatedSuccessfully(trade: trade);
1215
1216 /// return after the first successful trade
@@ -1626,7 +1623,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1623
1624 if (trade.provider == ExchangeProviderDescription.swapsXyz) {
1625
1629 - final tradeFrom = trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
1626 + final tradeFrom = trade.from;
1627
1628 if (tradeFrom == null) {
1629 return CreateTradeResult(
lib/view_model/send/send_view_model.dart
+1 -3
@@ -1091,9 +1091,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1091
1092 _currentTrade!.stateRaw = isSuccess ? TradeState.completed.raw : TradeState.failed.raw;
1093
1094 - if (_currentTrade!.isInBox) {
1095 - await _currentTrade!.save();
1096 - }
1094 + await _currentTrade!.save();
1095 }
1096
1097 @action
lib/view_model/trade_details_view_model.dart
+7 -44
@@ -26,11 +26,8 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.d
26 import 'package:cake_wallet/store/app_store.dart';
27 import 'package:cake_wallet/utils/date_formatter.dart';
28 import 'package:cake_wallet/utils/show_bar.dart';
29 -import 'package:collection/collection.dart';
30 -import 'package:cw_core/crypto_currency.dart';
29 import 'package:cw_core/utils/print_verbose.dart';
30 import 'package:flutter/services.dart';
33 -import 'package:hive/hive.dart';
31 import 'package:mobx/mobx.dart';
32 import 'package:url_launcher/url_launcher.dart';
33
@@ -41,11 +38,9 @@ class TradeDetailsViewModel = TradeDetailsViewModelBase with _$TradeDetailsViewM
38 abstract class TradeDetailsViewModelBase with Store {
39 TradeDetailsViewModelBase({
40 required Trade tradeForDetails,
44 - required this.trades,
41 required this.appStore,
42 }) : items = ObservableList<StandartListItem>(),
47 - trade = trades.values.firstWhereOrNull((element) => element.id == tradeForDetails.id) ??
48 - tradeForDetails {
43 + trade = tradeForDetails {
44 switch (trade.provider) {
45 case ExchangeProviderDescription.changeNow:
46 _provider = ChangeNowExchangeProvider(settingsStore: appStore.settingsStore);
@@ -63,7 +58,7 @@ abstract class TradeDetailsViewModelBase with Store {
58 _provider = ExolixExchangeProvider();
59 break;
60 case ExchangeProviderDescription.thorChain:
66 - _provider = ThorChainExchangeProvider(tradesStore: trades);
61 + _provider = ThorChainExchangeProvider();
62 break;
63 case ExchangeProviderDescription.swapTrade:
64 _provider = SwapTradeExchangeProvider();
@@ -74,7 +69,7 @@ abstract class TradeDetailsViewModelBase with Store {
69 _provider = StealthExExchangeProvider();
70 break;
71 case ExchangeProviderDescription.chainflip:
77 - _provider = ChainflipExchangeProvider(tradesStore: trades);
72 + _provider = ChainflipExchangeProvider();
73 break;
74 case ExchangeProviderDescription.xoSwap:
75 _provider = XOSwapExchangeProvider();
@@ -132,8 +127,6 @@ abstract class TradeDetailsViewModelBase with Store {
127 return null;
128 }
129
135 - final Box<Trade> trades;
136 -
130 @observable
131 Trade trade;
132
@@ -151,22 +144,8 @@ abstract class TradeDetailsViewModelBase with Store {
144 try {
145 final updatedTrade = await _provider!.findTradeById(id: trade.id);
146
154 - if (updatedTrade.createdAt == null && trade.createdAt != null) {
155 - updatedTrade.createdAt = trade.createdAt;
156 - }
157 -
158 - if (updatedTrade.toRaw == -1 && trade.toRaw != -1) {
159 - updatedTrade.toRaw = trade.toRaw;
160 - }
161 -
162 - Trade? foundElement = trades.values.firstWhereOrNull((element) => element.id == trade.id);
163 - if (foundElement != null) {
164 - final editedTrade = trades.get(foundElement.key);
165 - editedTrade?.stateRaw = updatedTrade.stateRaw;
166 - editedTrade?.save();
167 - }
168 -
169 - trade = updatedTrade;
147 + trade.mergeFindTradeByIdResult(updatedTrade);
148 + await trade.save();
149
150 _updateItems();
151 } catch (e) {
@@ -186,8 +165,8 @@ abstract class TradeDetailsViewModelBase with Store {
165 items.add(
166 DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString()));
167
189 - final tradeFrom = _safeFrom(trade);
190 - final tradeTo = _safeTo(trade);
168 + final tradeFrom = trade.from;
169 + final tradeTo = trade.to;
170
171 if (tradeFrom != null && tradeTo != null) {
172 items.add(TradeDetailsListCardItem.tradeDetails(
@@ -241,21 +220,5 @@ abstract class TradeDetailsViewModelBase with Store {
220 } catch (e) {}
221 }
222
244 - CryptoCurrency? _safeFrom(Trade trade) {
245 - try {
246 - final raw = trade.fromRaw;
247 - return raw >= 0 ? trade.from : trade.userCurrencyFrom;
248 - } catch (_) {
249 - return trade.userCurrencyFrom;
250 - }
251 - }
223
253 - CryptoCurrency? _safeTo(Trade trade) {
254 - try {
255 - final raw = trade.toRaw;
256 - return raw >= 0 ? trade.to : trade.userCurrencyTo;
257 - } catch (_) {
258 - return trade.userCurrencyTo;
259 - }
260 - }
224 }