Cw 1294 fix xo swaps bug (#2645)

* Improve currency parsing with tag support * changenow currency parsing fix * exolix currency parsing fix * letsExchange currency parsing fix * stealth currency parsing fix * trocador currency parsing fix --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

Serhii committed Nov 21, 2025 at 23:45 UTC c552ab86d25307959d0326a963f4c223947d2f10
10 files changed +218 -54
cw_core/lib/crypto_currency.dart
+16 -1
@@ -1,5 +1,6 @@
1 import 'package:cw_core/currency.dart';
2 import 'package:cw_core/enumerable_item.dart';
3 +import 'package:collection/collection.dart';
4
5 class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implements Currency {
6 const CryptoCurrency({
@@ -336,9 +337,23 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
337 return CryptoCurrency._fullNameCurrencyMap[name.split("(").first.trim().toLowerCase()]!;
338 }
339
339 - static CryptoCurrency? safeParseCurrencyFromString(String? raw, {CryptoCurrency? walletCurrency}) {
340 + static CryptoCurrency? safeParseCurrencyFromString(
341 + String? raw, {
342 + String? tag,
343 + CryptoCurrency? walletCurrency,
344 + }) {
345 if (raw == null || raw.isEmpty) return null;
346
347 + if (tag != null && tag.isNotEmpty) {
348 + final match = CryptoCurrency.all.firstWhereOrNull(
349 + (e) =>
350 + e.title.toUpperCase() == raw.toUpperCase() &&
351 + e.tag?.toUpperCase() == tag.toUpperCase(),
352 + );
353 + if (match != null) return match;
354 + return null;
355 + }
356 +
357 try {
358 return CryptoCurrency.fromString(raw, walletCurrency: walletCurrency);
359 } catch (_) {}
cw_evm/lib/evm_chain_wallet.dart
+6 -2
@@ -476,8 +476,12 @@ abstract class EVMChainWalletBase
476 '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}';
477 }
478
479 - final CryptoCurrency transactionCurrency =
480 - balance.keys.firstWhere((element) => element.title == _credentials.currency.title);
479 + final transactionCurrency = balance.keys.firstWhere(
480 + (currency) =>
481 + currency.title == _credentials.currency.title &&
482 + currency.tag == _credentials.currency.tag,
483 + orElse: () => throw Exception(
484 + 'Currency ${_credentials.currency.title} ${_credentials.currency.tag} is not accessible in the wallet, try to enable it first.'));
485
486 final currencyBalance = balance[transactionCurrency]!;
487 BigInt totalAmount = BigInt.zero;
cw_solana/lib/solana_wallet.dart
+6 -2
@@ -226,8 +226,12 @@ abstract class SolanaWalletBase
226
227 await _updateBalance();
228
229 - final CryptoCurrency transactionCurrency =
230 - balance.keys.firstWhere((element) => element.title == solCredentials.currency.title);
229 + final transactionCurrency = balance.keys.firstWhere(
230 + (currency) =>
231 + currency.title == credentials.currency.title &&
232 + currency.tag == credentials.currency.tag,
233 + orElse: () => throw Exception(
234 + 'Currency ${credentials.currency.title} ${credentials.currency.tag} is not accessible in the wallet, try to enable it first.'));
235
236 final walletBalanceForCurrency = balance[transactionCurrency]!.balance;
237
cw_tron/lib/tron_wallet.dart
+7 -2
@@ -312,8 +312,13 @@ abstract class TronWalletBase
312
313 final hasMultiDestination = outputs.length > 1;
314
315 - final CryptoCurrency transactionCurrency =
316 - balance.keys.firstWhere((element) => element.title == tronCredentials.currency.title);
315 + final transactionCurrency = balance.keys.firstWhere(
316 + (currency) =>
317 + currency.title == tronCredentials.currency.title &&
318 + currency.tag == tronCredentials.currency.tag,
319 + orElse: () => throw Exception(
320 + 'Currency ${tronCredentials.currency.title} ${tronCredentials.currency.tag} is not accessible in the wallet, try to enable it first.'));
321 +
322
323 final walletBalanceForCurrency = balance[transactionCurrency]!.balance;
324
lib/exchange/provider/changenow_exchange_provider.dart
+13 -7
@@ -278,12 +278,21 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
278 throw Exception('Unexpected http status: ${response.statusCode}');
279
280 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
281 +
282 + // Parsing 'from' currency
283 final fromCurrency = responseJSON['fromCurrency'] as String;
284 final fromNetwork = responseJSON['fromNetwork'] as String?;
283 - final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency);
285 + final _normalizedFromNetwork = _normalizeNetworkType(fromNetwork ?? '');
286 + final fromTag = fromCurrency == _normalizedFromNetwork ? null : _normalizedFromNetwork;
287 + final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency, tag: fromTag);
288 +
289 + // Parsing 'to' currency
290 final toCurrency = responseJSON['toCurrency'] as String;
291 final toNetwork = responseJSON['toNetwork'] as String?;
286 - final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency);
292 + final _normalizedToNetwork = _normalizeNetworkType(toNetwork ?? '');
293 + final toTag = toCurrency == _normalizedToNetwork ? null : _normalizedToNetwork;
294 + final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency, tag: toTag);
295 +
296 final inputAddress = responseJSON['payinAddress'] as String;
297 final expectedSendAmount = responseJSON['expectedAmountFrom'].toString();
298 final status = responseJSON['status'] as String;
@@ -294,9 +303,6 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
303 final payoutAddress = responseJSON['payoutAddress'] as String;
304 final expiredAt = DateTime.tryParse(expiredAtRaw ?? '')?.toLocal();
305
297 - final _normalizedFromNetwork = _normalizeNetworkType(fromNetwork ?? '');
298 - final _normalizedToNetwork = _normalizeNetworkType(toNetwork ?? '');
299 -
306 return Trade(
307 id: id,
308 from: from,
@@ -309,8 +315,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
315 expiredAt: expiredAt,
316 outputTransaction: outputTransaction,
317 payoutAddress: payoutAddress,
312 - userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${_normalizedFromNetwork.toUpperCase()}',
313 - userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${_normalizedToNetwork.toUpperCase()}',
318 + userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
319 + userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
320 );
321 }
322
lib/exchange/provider/exolix_exchange_provider.dart
+13 -4
@@ -360,10 +360,19 @@ class ExolixExchangeProvider extends ExchangeProvider {
360 throw Exception('Unexpected http status: ${response.statusCode}');
361
362 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
363 +
364 + // Parsing 'from' currency
365 final coinFrom = responseJSON['coinFrom']['coinCode'] as String;
366 final coinFromNetwork = responseJSON['coinFrom']['network'] as String?;
367 + final fromTag = coinFrom == coinFromNetwork ? null : coinFromNetwork;
368 + final from = CryptoCurrency.safeParseCurrencyFromString(coinFrom, tag: fromTag);
369 +
370 + // Parsing 'to' currency
371 final coinTo = responseJSON['coinTo']['coinCode'] as String;
372 final coinToNetwork = responseJSON['coinTo']['network'] as String?;
373 + final toTag = coinTo == coinToNetwork ? null : coinToNetwork;
374 + final to = CryptoCurrency.safeParseCurrencyFromString(coinTo, tag: toTag);
375 +
376 final inputAddress = responseJSON['depositAddress'] as String;
377 final amount = responseJSON['amount'].toString();
378 final status = responseJSON['status'] as String;
@@ -373,8 +382,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
382
383 return Trade(
384 id: id,
376 - from: CryptoCurrency.safeParseCurrencyFromString(coinFrom),
377 - to: CryptoCurrency.safeParseCurrencyFromString(coinTo),
385 + from: from,
386 + to: to,
387 provider: description,
388 inputAddress: inputAddress,
389 amount: amount,
@@ -382,8 +391,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
391 extraId: extraId,
392 outputTransaction: outputTransaction,
393 payoutAddress: payoutAddress,
385 - userCurrencyFromRaw: '${coinFrom.toUpperCase()}' + '_' + '${coinFromNetwork ?? ''}',
386 - userCurrencyToRaw: '${coinTo.toUpperCase()}' + '_' + '${coinToNetwork ?? ''}',
394 + userCurrencyFromRaw: '${coinFrom.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
395 + userCurrencyToRaw: '${coinTo.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
396 );
397 }
398
lib/exchange/provider/letsexchange_exchange_provider.dart
+18 -9
@@ -348,10 +348,22 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
348 throw Exception('LetsExchange fetch trade failed: ${response.body}');
349 }
350 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
351 - final from = responseJSON['coin_from'] as String;
351 +
352 + // Parsing 'from' currency
353 + final fromCurrency = responseJSON['coin_from'] as String;
354 final fromNetwork = responseJSON['coin_from_network'] as String?;
353 - final to = responseJSON['coin_to'] as String;
355 + final normalizedFromNetwork = _normalizeNetworkType(fromNetwork ?? '');
356 + final fromTag = fromCurrency == normalizedFromNetwork ? null : normalizedFromNetwork;
357 + final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency, tag: fromTag);
358 +
359 + // Parsing 'to' currency
360 + final toCurrency = responseJSON['coin_to'] as String;
361 final toNetwork = responseJSON['coin_to_network'] as String?;
362 + final normalizedToNetwork = _normalizeNetworkType(toNetwork ?? '');
363 + final toTag = toCurrency == normalizedToNetwork ? null : normalizedToNetwork;
364 + final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency, tag: toTag);
365 +
366 +
367 final payoutAddress = responseJSON['withdrawal'] as String;
368 final depositAddress = responseJSON['deposit'] as String;
369 final refundAddress = responseJSON['return'] as String;
@@ -365,13 +377,10 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
377 final createdAt = DateTime.parse(createdAtString).toLocal();
378 final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000).toLocal();
379
368 - final normalizedFromNetwork = _normalizeNetworkType(fromNetwork ?? '');
369 - final normalizedToNetwork = _normalizeNetworkType(toNetwork ?? '');
370 -
380 return Trade(
381 id: id,
373 - from: CryptoCurrency.safeParseCurrencyFromString(from),
374 - to: CryptoCurrency.safeParseCurrencyFromString(to),
382 + from: from,
383 + to: to,
384 provider: description,
385 inputAddress: depositAddress,
386 payoutAddress: payoutAddress,
@@ -383,8 +392,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
392 expiredAt: expiredAt,
393 isRefund: status == 'refund',
394 extraId: extraId,
386 - userCurrencyFromRaw: '$from' + '_' + normalizedFromNetwork,
387 - userCurrencyToRaw: '$to' + '_' + '$normalizedToNetwork',
395 + userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
396 + userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
397 );
398 }
399
lib/exchange/provider/stealth_ex_exchange_provider.dart
+26 -15
@@ -74,8 +74,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
74 throw Exception('StealthEx fetch limits failed: ${response.body}');
75 }
76 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
77 - final min = toDouble(responseJSON['min_amount']);
78 - final max = responseJSON['max_amount'] as double?;
77 + final min = _toDouble(responseJSON['min_amount']);
78 + final max = _toDouble(responseJSON['max_amount']);
79 return Limits(min: min, max: max);
80 } catch (e) {
81 log(e.toString());
@@ -230,8 +230,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
230 final payoutAddress = withdrawal['address'] as String;
231 final depositAddress = deposit['address'] as String;
232 final refundAddress = responseJSON['refund_address'] as String;
233 - final depositAmount = toDouble(deposit['amount']);
234 - final receiveAmount = toDouble(withdrawal['amount']);
233 + final depositAmount = _toDouble(deposit['amount']);
234 + final receiveAmount = _toDouble(withdrawal['amount']);
235 final status = responseJSON['status'] as String;
236 final createdAtString = responseJSON['created_at'] as String;
237 final extraId = deposit['extra_id'] as String?;
@@ -342,15 +342,24 @@ class StealthExExchangeProvider extends ExchangeProvider {
342 final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
343
344 final respId = responseJSON['id'] as String;
345 - final from = deposit['symbol'] as String;
345 +
346 + // Parsing 'from' currency with network tag
347 + final fromCurrency = deposit['symbol'] as String;
348 final fromNetwork = deposit['network'] as String?;
347 - final to = withdrawal['symbol'] as String;
349 + final fromTag = fromNetwork == 'mainnet' ? null : fromNetwork;
350 + final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency, tag: fromTag);
351 +
352 + // Parsing 'to' currency with network tag
353 + final toCurrency = withdrawal['symbol'] as String;
354 final toNetwork = withdrawal['network'] as String?;
355 + final toTag = toNetwork == 'mainnet' ? null : toNetwork;
356 + final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency, tag: toTag);
357 +
358 final payoutAddress = withdrawal['address'] as String;
359 final depositAddress = deposit['address'] as String;
360 final refundAddress = responseJSON['refund_address'] as String;
352 - final depositAmount = toDouble(deposit['amount']);
353 - final receiveAmount = toDouble(withdrawal['amount']);
361 + final depositAmount = _toDouble(deposit['amount']);
362 + final receiveAmount = _toDouble(withdrawal['amount']);
363 final status = responseJSON['status'] as String;
364 final createdAtString = responseJSON['created_at'] as String;
365 final createdAt = DateTime.parse(createdAtString).toLocal();
@@ -358,8 +367,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
367
368 return Trade(
369 id: respId,
361 - from: CryptoCurrency.safeParseCurrencyFromString(from),
362 - to: CryptoCurrency.safeParseCurrencyFromString(to),
370 + from: from,
371 + to: to,
372 provider: description,
373 inputAddress: depositAddress,
374 payoutAddress: payoutAddress,
@@ -370,8 +379,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
379 createdAt: createdAt,
380 isRefund: status == 'refunded',
381 extraId: extraId,
373 - userCurrencyFromRaw: '${from.toUpperCase()}' + '_' + '${fromNetwork?.toUpperCase() ?? ''}',
374 - userCurrencyToRaw: '${to.toUpperCase()}' + '_' + '${toNetwork?.toUpperCase() ?? ''}',
382 + userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
383 + userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
384 );
385 }
386
@@ -414,16 +423,18 @@ class StealthExExchangeProvider extends ExchangeProvider {
423 }
424 }
425
417 - double toDouble(dynamic value) {
426 + static double? _toDouble(dynamic value) {
427 if (value is int) {
428 return value.toDouble();
429 } else if (value is double) {
430 return value;
422 - } else {
423 - return 0.0;
431 + } else if (value is String) {
432 + return double.tryParse(value);
433 }
434 + return null;
435 }
436
437 +
438 String _getName(CryptoCurrency currency) {
439 if (currency == CryptoCurrency.usdcEPoly) return 'usdce';
440 return currency.title.toLowerCase();
lib/exchange/provider/trocador_exchange_provider.dart
+14 -7
@@ -420,15 +420,22 @@ class TrocadorExchangeProvider extends ExchangeProvider {
420 final providerName = responseJSON['provider'] as String;
421 final addressProviderMemo = responseJSON['address_provider_memo'] as String?;
422
423 - final from = responseJSON['ticker_from'] as String;
424 - final networkFrom = responseJSON['network_from'] as String?;
425 - final to = responseJSON['ticker_to'] as String;
423 + final fromCurrency = responseJSON['ticker_from'] as String;
424 + final fromNetwork = responseJSON['network_from'] as String?;
425 + final _normalizedFromNetwork = _normalizeNetworkType(fromNetwork ?? '');
426 + final fromTag = _normalizedFromNetwork.isEmpty || _normalizedFromNetwork == 'Mainnet' ? null : _normalizedFromNetwork;
427 + final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency, tag: fromTag);
428 +
429 + final toCurrency = responseJSON['ticker_to'] as String;
430 final networkTo = responseJSON['network_to'] as String?;
431 + final _normalizedToNetwork = _normalizeNetworkType(networkTo ?? '');
432 + final toTag = _normalizedToNetwork.isEmpty || _normalizedToNetwork == 'Mainnet' ? null : _normalizedToNetwork;
433 + final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency, tag: toTag);
434
435 return Trade(
436 id: id,
430 - from: CryptoCurrency.safeParseCurrencyFromString(from),
431 - to: CryptoCurrency.safeParseCurrencyFromString(to),
437 + from: from,
438 + to: to,
439 provider: description,
440 inputAddress: inputAddress,
441 refundAddress: refundAddress,
@@ -440,8 +447,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
447 providerId: providerId,
448 providerName: providerName,
449 extraId: addressProviderMemo,
443 - userCurrencyFromRaw: '${from.toUpperCase()}' + '_' + _normalizeNetworkType(networkFrom ?? ''),
444 - userCurrencyToRaw: '${to.toUpperCase()}' + '_' + _normalizeNetworkType(networkTo ?? ''), // Handle null network
450 + userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_' + '${fromTag?.toUpperCase() ?? ''}',
451 + userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_' + '${toTag?.toUpperCase() ?? ''}',
452 );
453 });
454 }
lib/exchange/provider/xoswap_exchange_provider.dart
+99 -5
@@ -43,8 +43,41 @@ class XOSwapExchangeProvider extends ExchangeProvider {
43 'LTC': 'litecoin',
44 'EOS': 'eosio',
45 'XLM': 'stellar',
46 + 'BASE': 'basemainnet',
47 };
47 -
48 +
49 + static const supportedTags = [
50 + 'POL',
51 + 'ETH',
52 + 'BTC',
53 + 'BSC',
54 + 'SOL',
55 + 'TRX',
56 + 'ZEC',
57 + 'ADA',
58 + 'DOGE',
59 + 'XMR',
60 + 'BCH',
61 + 'BSV',
62 + 'XRP',
63 + 'LTC',
64 + 'EOS',
65 + 'XLM',
66 + 'BASE',
67 + ];
68 +
69 +
70 + String _normalizeXOSwapsNetwork(String string) {
71 + final lower = string.toLowerCase();
72 +
73 + if (lower.endsWith('matic0a883d9b')) return string.replaceFirst(RegExp(r'matic0a883d9b$', caseSensitive: false), 'POL');
74 + if (lower.endsWith('matic86e249c1')) return string.replaceFirst(RegExp(r'matic86e249c1$', caseSensitive: false), 'POL');
75 + if (lower.endsWith('bscddedf0f8')) return string.replaceFirst(RegExp(r'bscddedf0f8$', caseSensitive: false), 'BSC');
76 + if (lower.endsWith('basemainnetb5a52617')) return string.replaceFirst(RegExp(r'basemainnetb5a52617$', caseSensitive: false), 'BASE');
77 +
78 + return string;
79 + }
80 +
81 @override
82 String get title => 'XOSwap';
83
@@ -422,9 +455,46 @@ class XOSwapExchangeProvider extends ExchangeProvider {
455 final pairId = responseJSON['pairId'] as String;
456 final pairParts = pairId.split('_');
457 final fromAsset = pairParts.isNotEmpty ? pairParts[0] : '';
458 + final normalizedFromAsset = _normalizeXOSwapsNetwork(fromAsset);
459 + String? fromAssetTag = _extractTagFromAsset(normalizedFromAsset);
460 +
461 + String fromAssetBase = fromAssetTag != null
462 + ? normalizedFromAsset.substring(0, normalizedFromAsset.length - fromAssetTag.length)
463 + : normalizedFromAsset;
464 +
465 + // Special case for USDT defaulting to ETH tag
466 + if (fromAssetBase == 'USDT' && fromAssetTag == null) {
467 + fromAssetTag = 'ETH';
468 + }
469 +
470 + // Special case for BASE defaulting to BASE tag
471 + if (fromAssetBase == 'BASE' && fromAssetTag == null) {
472 + fromAssetTag = 'BASE';
473 + fromAssetBase = 'ETH';
474 + }
475 +
476 final toAsset = pairParts.length > 1 ? pairParts[1] : '';
426 - final fromCurrency = CryptoCurrency.safeParseCurrencyFromString(fromAsset);
427 - final toCurrency = CryptoCurrency.safeParseCurrencyFromString(toAsset);
477 + final normalizedToAsset = _normalizeXOSwapsNetwork(toAsset);
478 + String? toAssetTag = _extractTagFromAsset(normalizedToAsset);
479 +
480 + String toAssetBase = toAssetTag != null
481 + ? normalizedToAsset.substring(0, normalizedToAsset.length - toAssetTag.length)
482 + : normalizedToAsset;
483 +
484 + // Special case for USDT defaulting to ETH tag
485 + if (toAssetBase == 'USDT' && toAssetTag == null) {
486 + toAssetTag = 'ETH';
487 + }
488 +
489 + // Special case for BASE defaulting to BASE tag
490 + if (toAssetBase == 'BASE' && toAssetTag == null) {
491 + toAssetTag = 'ETH';
492 + toAssetBase = 'BASE';
493 + }
494 +
495 + final fromCurrency = CryptoCurrency.safeParseCurrencyFromString(fromAssetBase,tag: fromAssetTag);
496 + final toCurrency = CryptoCurrency.safeParseCurrencyFromString(toAssetBase,tag: toAssetTag);
497 +
498
499 final amount = responseJSON['amount'] as Map<String, dynamic>;
500 final toAmount = responseJSON['toAmount'] as Map<String, dynamic>;
@@ -439,6 +509,14 @@ class XOSwapExchangeProvider extends ExchangeProvider {
509 final createdAt = DateTime.parse(createdAtString).toLocal();
510 final extraId = responseJSON['payInAddressTag'] as String?;
511
512 + final userCurrencyFromRaw = fromCurrency != null
513 + ? '${fromCurrency.title}' + '_' + '${fromCurrency.tag ?? ''}'
514 + : '${fromAssetBase}' + '_' + '${fromAssetTag ?? ''}';
515 +
516 + final userCurrencyToRaw = toCurrency != null
517 + ? '${toCurrency.title}' + '_' + '${toCurrency.tag ?? ''}'
518 + : '${toAssetBase}' + '_' + '${toAssetTag ?? ''}';
519 +
520 return Trade(
521 id: orderId,
522 from: fromCurrency,
@@ -452,8 +530,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
530 receiveAmount: receiveAmount,
531 payoutAddress: payoutAddress,
532 extraId: extraId,
455 - userCurrencyFromRaw: '$fromAsset' + '_',
456 - userCurrencyToRaw: '$toAsset' + '_',
533 + userCurrencyFromRaw: userCurrencyFromRaw,
534 + userCurrencyToRaw: userCurrencyToRaw,
535 );
536 } catch (e) {
537 printV(e.toString());
@@ -461,6 +539,22 @@ class XOSwapExchangeProvider extends ExchangeProvider {
539 }
540 }
541
542 + // ensure something remains before tag (at least 2 chars)
543 + String? _extractTagFromAsset(String asset) {
544 +
545 +
546 +
547 + for (final tag in supportedTags) {
548 + if (asset.endsWith(tag)) {
549 + final prefixLength = asset.length - tag.length;
550 + if (prefixLength >= 2) {
551 + return tag;
552 + }
553 + }
554 + }
555 + return null;
556 + }
557 +
558 double _toDouble(dynamic value) {
559 if (value is int) {
560 return value.toDouble();