Cw 1161 add user added tokens to swap currencies (#2455)

* Add user ETH and SOL tokens to exchange currency lists

* Update default_spl_tokens.dart

* fix solana token icon issue

* add user currency to Trade and update usage

* add support for user Tron and Polygon tokens in exchange

* removing the providersForCurrentPair method and related logic

* Improve currency search

* prevent crash in EVM wallets for unknown token symbols

* Improve asset lookup in XOSwap provider

* revert token enabled state for tron solana

* Move SPL and Tron token classes to cw_core

* fix updating trades for user tokens

* Improve token matching by including tag comparison

* safer currency parsing across all exchange providers

* Update edit_token_page.dart
This commit is contained in:
Serhii
2025-08-21 03:34:09 +03:00
committed by GitHub
parent 2bb11f253a
commit f6dc65424d
34 changed files with 1046 additions and 176 deletions
+40
View File
@@ -300,6 +300,46 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
return CryptoCurrency._fullNameCurrencyMap[name.split("(").first.trim().toLowerCase()]!;
}
static CryptoCurrency? safeParseCurrencyFromString(String? raw, {CryptoCurrency? walletCurrency}) {
if (raw == null || raw.isEmpty) return null;
try {
return CryptoCurrency.fromString(raw, walletCurrency: walletCurrency);
} catch (_) {}
// try cleaned (keep only A–Z/0–9)
final cleaned = raw.trim().toUpperCase().replaceAll(RegExp(r'[^A-Z0-9]'), '');
try {
return CryptoCurrency.fromString(cleaned, walletCurrency: walletCurrency);
} catch (_) {}
return null;
}
CryptoCurrency copyWith({
String? title,
int? raw,
String? name,
String? fullName,
String? iconPath,
String? tag,
int? decimals,
bool? enabled,
bool? isPotentialScam,
}) {
return CryptoCurrency(
title: title ?? this.title,
raw: raw ?? this.raw,
name: name ?? this.name,
fullName: fullName ?? this.fullName,
iconPath: iconPath ?? this.iconPath,
tag: tag ?? this.tag,
decimals: decimals ?? this.decimals,
enabled: enabled ?? this.enabled,
isPotentialScam: isPotentialScam ?? this.isPotentialScam,
);
}
@override
String toString() => title;
}
+11 -4
View File
@@ -248,10 +248,17 @@ abstract class EVMChainWalletBase
// For fixing wrongly classified tokens
if (!isPotentialScam && token.isPotentialScam) {
token.isPotentialScam = false;
final iconPath = CryptoCurrency.all
.firstWhere((element) => element.title.toUpperCase() == token.symbol.toUpperCase())
.iconPath;
token.iconPath = iconPath;
if (token.iconPath == null || token.iconPath!.isEmpty) {
try {
token.iconPath = CryptoCurrency.all
.firstWhere((e) => e.title.toUpperCase() == token.symbol.toUpperCase())
.iconPath;
} catch (_) {
printV("Token ${token.symbol} does not have an icon path");
}
}
await token.save();
}
}
+443 -1
View File
@@ -1,5 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_solana/spl_token.dart';
import 'package:cw_core/spl_token.dart';
class DefaultSPLTokens {
final List<SPLToken> _defaultTokens = [
@@ -99,6 +99,448 @@ class DefaultSPLTokens {
iconPath: 'assets/images/avdo_icon.png',
enabled: false,
),
SPLToken(
name: 'Abbott xStock',
symbol: 'ABTx',
mintAddress: 'XsHtf5RpxsQ7jeJ9ivNewouZKJHbPxhPoEy6yYvULr7',
decimal: 8,
mint: 'abtx',
enabled: false),
SPLToken(
name: 'AbbVie xStock',
symbol: 'ABBVx',
mintAddress: 'XswbinNKyPmzTa5CskMbCPvMW6G5CMnZXZEeQSSQoie',
decimal: 8,
mint: 'abbvx',
enabled: false),
SPLToken(
name: 'Accenture xStock',
symbol: 'ACNx',
mintAddress: 'Xs5UJzmCRQ8DWZjskExdSQDnbE6iLkRu2jjrRAB1JSU',
decimal: 8,
mint: 'acnx',
enabled: false),
SPLToken(
name: 'Alphabet xStock',
symbol: 'GOOGLx',
mintAddress: 'XsCPL9dNWBMvFtTmwcCA5v3xWPSMEBCszbQdiLLq6aN',
decimal: 8,
mint: 'googlx',
enabled: false),
SPLToken(
name: 'Amazon xStock',
symbol: 'AMZNx',
mintAddress: 'Xs3eBt7uRfJX8QUs4suhyU8p2M6DoUDrJyWBa8LLZsg',
decimal: 8,
mint: 'amznx',
enabled: false),
SPLToken(
name: 'Amber xStock',
symbol: 'AMBRx',
mintAddress: 'XsaQTCgebC2KPbf27KUhdv5JFvHhQ4GDAPURwrEhAzb',
decimal: 8,
mint: 'ambrx',
enabled: false),
SPLToken(
name: 'Apple xStock',
symbol: 'AAPLx',
mintAddress: 'XsbEhLAtcf6HdfpFZ5xEMdqW8nfAvcsP5bdudRLJzJp',
decimal: 8,
mint: 'aaplx',
enabled: false),
SPLToken(
name: 'AppLovin xStock',
symbol: 'APPx',
mintAddress: 'XsPdAVBi8Zc1xvv53k4JcMrQaEDTgkGqKYeh7AYgPHV',
decimal: 8,
mint: 'appx',
enabled: false),
SPLToken(
name: 'AstraZeneca xStock',
symbol: 'AZNx',
mintAddress: 'Xs3ZFkPYT2BN7qBMqf1j1bfTeTm1rFzEFSsQ1z3wAKU',
decimal: 8,
mint: 'aznx',
enabled: false),
SPLToken(
name: 'Bank of America xStock',
symbol: 'BACx',
mintAddress: 'XswsQk4duEQmCbGzfqUUWYmi7pV7xpJ9eEmLHXCaEQP',
decimal: 8,
mint: 'bacx',
enabled: false),
SPLToken(
name: 'Berkshire Hathaway xStock',
symbol: 'BRK.Bx',
mintAddress: 'Xs6B6zawENwAbWVi7w92rjazLuAr5Az59qgWKcNb45x',
decimal: 8,
mint: 'brkbx',
enabled: false),
SPLToken(
name: 'Broadcom xStock',
symbol: 'AVGOx',
mintAddress: 'XsgSaSvNSqLTtFuyWPBhK9196Xb9Bbdyjj4fH3cPJGo',
decimal: 8,
mint: 'avgox',
enabled: false),
SPLToken(
name: 'Chevron xStock',
symbol: 'CVXx',
mintAddress: 'XsNNMt7WTNA2sV3jrb1NNfNgapxRF5i4i6GcnTRRHts',
decimal: 8,
mint: 'cvxx',
enabled: false),
SPLToken(
name: 'Circle xStock',
symbol: 'CRCLx',
mintAddress: 'XsueG8BtpquVJX9LVLLEGuViXUungE6WmK5YZ3p3bd1',
decimal: 8,
mint: 'crclx',
enabled: false),
SPLToken(
name: 'Cisco xStock',
symbol: 'CSCOx',
mintAddress: 'Xsr3pdLQyXvDJBFgpR5nexCEZwXvigb8wbPYp4YoNFf',
decimal: 8,
mint: 'cscx',
enabled: false),
SPLToken(
name: 'Coca-Cola xStock',
symbol: 'KOx',
mintAddress: 'XsaBXg8dU5cPM6ehmVctMkVqoiRG2ZjMo1cyBJ3AykQ',
decimal: 8,
mint: 'kox',
enabled: false),
SPLToken(
name: 'Coinbase xStock',
symbol: 'COINx',
mintAddress: 'Xs7ZdzSHLU9ftNJsii5fCeJhoRWSC32SQGzGQtePxNu',
decimal: 8,
mint: 'coinx',
enabled: false),
SPLToken(
name: 'Comcast xStock',
symbol: 'CMCSAx',
mintAddress: 'XsvKCaNsxg2GN8jjUmq71qukMJr7Q1c5R2Mk9P8kcS8',
decimal: 8,
mint: 'cmcsax',
enabled: false),
SPLToken(
name: 'CrowdStrike xStock',
symbol: 'CRWDx',
mintAddress: 'Xs7xXqkcK7K8urEqGg52SECi79dRp2cEKKuYjUePYDw',
decimal: 8,
mint: 'crwdx',
enabled: false),
SPLToken(
name: 'Danaher xStock',
symbol: 'DHRx',
mintAddress: 'Xseo8tgCZfkHxWS9xbFYeKFyMSbWEvZGFV1Gh53GtCV',
decimal: 8,
mint: 'dhrx',
enabled: false),
SPLToken(
name: 'DFDV xStock',
symbol: 'DFDVx',
mintAddress: 'Xs2yquAgsHByNzx68WJC55WHjHBvG9JsMB7CWjTLyPy',
decimal: 8,
mint: 'dfdvx',
enabled: false),
SPLToken(
name: 'Eli Lilly xStock',
symbol: 'LLYx',
mintAddress: 'Xsnuv4omNoHozR6EEW5mXkw8Nrny5rB3jVfLqi6gKMH',
decimal: 8,
mint: 'llyx',
enabled: false),
SPLToken(
name: 'Exxon Mobil xStock',
symbol: 'XOMx',
mintAddress: 'XsaHND8sHyfMfsWPj6kSdd5VwvCayZvjYgKmmcNL5qh',
decimal: 8,
mint: 'xomx',
enabled: false),
SPLToken(
name: 'Gamestop xStock',
symbol: 'GMEx',
mintAddress: 'Xsf9mBktVB9BSU5kf4nHxPq5hCBJ2j2ui3ecFGxPRGc',
decimal: 8,
mint: 'gmex',
enabled: false),
SPLToken(
name: 'Gold xStock',
symbol: 'GLDx',
mintAddress: 'Xsv9hRk1z5ystj9MhnA7Lq4vjSsLwzL2nxrwmwtD3re',
decimal: 8,
mint: 'gldx',
enabled: false),
SPLToken(
name: 'Goldman Sachs xStock',
symbol: 'GSx',
mintAddress: 'XsgaUyp4jd1fNBCxgtTKkW64xnnhQcvgaxzsbAq5ZD1',
decimal: 8,
mint: 'gsx',
enabled: false),
SPLToken(
name: 'Home Depot xStock',
symbol: 'HDx',
mintAddress: 'XszjVtyhowGjSC5odCqBpW1CtXXwXjYokymrk7fGKD3',
decimal: 8,
mint: 'hdx',
enabled: false),
SPLToken(
name: 'Honeywell xStock',
symbol: 'HONx',
mintAddress: 'XsRbLZthfABAPAfumWNEJhPyiKDW6TvDVeAeW7oKqA2',
decimal: 8,
mint: 'honx',
enabled: false),
SPLToken(
name: 'Intel xStock',
symbol: 'INTCx',
mintAddress: 'XshPgPdXFRWB8tP1j82rebb2Q9rPgGX37RuqzohmArM',
decimal: 8,
mint: 'intcx',
enabled: false),
SPLToken(
name: 'International Business Machines xStock',
symbol: 'IBMx',
mintAddress: 'XspwhyYPdWVM8XBHZnpS9hgyag9MKjLRyE3tVfmCbSr',
decimal: 8,
mint: 'ibmx',
enabled: false),
SPLToken(
name: 'Johnson & Johnson xStock',
symbol: 'JNJx',
mintAddress: 'XsGVi5eo1Dh2zUpic4qACcjuWGjNv8GCt3dm5XcX6Dn',
decimal: 8,
mint: 'jnjx',
enabled: false),
SPLToken(
name: 'JPMorgan Chase xStock',
symbol: 'JPMx',
mintAddress: 'XsMAqkcKsUewDrzVkait4e5u4y8REgtyS7jWgCpLV2C',
decimal: 8,
mint: 'jpmx',
enabled: false),
SPLToken(
name: 'Linde xStock',
symbol: 'LINx',
mintAddress: 'XsSr8anD1hkvNMu8XQiVcmiaTP7XGvYu7Q58LdmtE8Z',
decimal: 8,
mint: 'linx',
enabled: false),
SPLToken(
name: 'Marvell xStock',
symbol: 'MRVLx',
mintAddress: 'XsuxRGDzbLjnJ72v74b7p9VY6N66uYgTCyfwwRjVCJA',
decimal: 8,
mint: 'mrvlx',
enabled: false),
SPLToken(
name: 'Mastercard xStock',
symbol: 'MAx',
mintAddress: 'XsApJFV9MAktqnAc6jqzsHVujxkGm9xcSUffaBoYLKC',
decimal: 8,
mint: 'max',
enabled: false),
SPLToken(
name: 'McDonald\'s xStock',
symbol: 'MCDx',
mintAddress: 'XsqE9cRRpzxcGKDXj1BJ7Xmg4GRhZoyY1KpmGSxAWT2',
decimal: 8,
mint: 'mcdx',
enabled: false),
SPLToken(
name: 'Medtronic xStock',
symbol: 'MDTx',
mintAddress: 'XsDgw22qRLTv5Uwuzn6T63cW69exG41T6gwQhEK22u2',
decimal: 8,
mint: 'mdtx',
enabled: false),
SPLToken(
name: 'Merck xStock',
symbol: 'MRKx',
mintAddress: 'XsnQnU7AdbRZYe2akqqpibDdXjkieGFfSkbkjX1Sd1X',
decimal: 8,
mint: 'mrkx',
enabled: false),
SPLToken(
name: 'Meta xStock',
symbol: 'METAx',
mintAddress: 'Xsa62P5mvPszXL1krVUnU5ar38bBSVcWAB6fmPCo5Zu',
decimal: 8,
mint: 'metax',
enabled: false),
SPLToken(
name: 'Microsoft xStock',
symbol: 'MSFTx',
mintAddress: 'XspzcW1PRtgf6Wj92HCiZdjzKCyFekVD8P5Ueh3dRMX',
decimal: 8,
mint: 'msftx',
enabled: false),
SPLToken(
name: 'MicroStrategy xStock',
symbol: 'MSTRx',
mintAddress: 'XsP7xzNPvEHS1m6qfanPUGjNmdnmsLKEoNAnHjdxxyZ',
decimal: 8,
mint: 'mstrx',
enabled: false),
SPLToken(
name: 'Nasdaq xStock',
symbol: 'QQQx',
mintAddress: 'Xs8S1uUs1zvS2p7iwtsG3b6fkhpvmwz4GYU3gWAmWHZ',
decimal: 8,
mint: 'qqqx',
enabled: false),
SPLToken(
name: 'Netflix xStock',
symbol: 'NFLXx',
mintAddress: 'XsEH7wWfJJu2ZT3UCFeVfALnVA6CP5ur7Ee11KmzVpL',
decimal: 8,
mint: 'nflxx',
enabled: false),
SPLToken(
name: 'Novo Nordisk xStock',
symbol: 'NVOx',
mintAddress: 'XsfAzPzYrYjd4Dpa9BU3cusBsvWfVB9gBcyGC87S57n',
decimal: 8,
mint: 'nvox',
enabled: false),
SPLToken(
name: 'NVIDIA xStock',
symbol: 'NVDAx',
mintAddress: 'Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh',
decimal: 8,
mint: 'nvdax',
enabled: false),
SPLToken(
name: 'OPEN xStock',
symbol: 'OPENx',
mintAddress: 'XsGtpmjhmC8kyjVSWL4VicGu36ceq9u55PTgF8bhGv6',
decimal: 8,
mint: 'openx',
enabled: false),
SPLToken(
name: 'Oracle xStock',
symbol: 'ORCLx',
mintAddress: 'XsjFwUPiLofddX5cWFHW35GCbXcSu1BCUGfxoQAQjeL',
decimal: 8,
mint: 'orclx',
enabled: false),
SPLToken(
name: 'Palantir xStock',
symbol: 'PLTRx',
mintAddress: 'XsoBhf2ufR8fTyNSjqfU71DYGaE6Z3SUGAidpzriAA4',
decimal: 8,
mint: 'pltrx',
enabled: false),
SPLToken(
name: 'PepsiCo xStock',
symbol: 'PEPx',
mintAddress: 'Xsv99frTRUeornyvCfvhnDesQDWuvns1M852Pez91vF',
decimal: 8,
mint: 'pepx',
enabled: false),
SPLToken(
name: 'Pfizer xStock',
symbol: 'PFEx',
mintAddress: 'XsAtbqkAP1HJxy7hFDeq7ok6yM43DQ9mQ1Rh861X8rw',
decimal: 8,
mint: 'pfex',
enabled: false),
SPLToken(
name: 'Philip Morris xStock',
symbol: 'PMx',
mintAddress: 'Xsba6tUnSjDae2VcopDB6FGGDaxRrewFCDa5hKn5vT3',
decimal: 8,
mint: 'pmx',
enabled: false),
SPLToken(
name: 'Procter & Gamble xStock',
symbol: 'PGx',
mintAddress: 'XsYdjDjNUygZ7yGKfQaB6TxLh2gC6RRjzLtLAGJrhzV',
decimal: 8,
mint: 'pgx',
enabled: false),
SPLToken(
name: 'Robinhood xStock',
symbol: 'HOODx',
mintAddress: 'XsvNBAYkrDRNhA7wPHQfX3ZUXZyZLdnCQDfHZ56bzpg',
decimal: 8,
mint: 'hoodx',
enabled: false),
SPLToken(
name: 'Salesforce xStock',
symbol: 'CRMx',
mintAddress: 'XsczbcQ3zfcgAEt9qHQES8pxKAVG5rujPSHQEXi4kaN',
decimal: 8,
mint: 'crmx',
enabled: false),
SPLToken(
name: 'SP500 xStock',
symbol: 'SPYx',
mintAddress: 'XsoCS1TfEyfFhfvj8EtZ528L3CaKBDBRqRapnBbDF2W',
decimal: 8,
mint: 'spyx',
enabled: false),
SPLToken(
name: 'TBLL xStock',
symbol: 'TBLLx',
mintAddress: 'XsqBC5tcVQLYt8wqGCHRnAUUecbRYXoJCReD6w7QEKp',
decimal: 8,
mint: 'tbllx',
enabled: false),
SPLToken(
name: 'Tesla xStock',
symbol: 'TSLAx',
mintAddress: 'XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB',
decimal: 8,
mint: 'tslax',
enabled: false),
SPLToken(
name: 'Thermo Fisher xStock',
symbol: 'TMOx',
mintAddress: 'Xs8drBWy3Sd5QY3aifG9kt9KFs2K3PGZmx7jWrsrk57',
decimal: 8,
mint: 'tmox',
enabled: false),
SPLToken(
name: 'TQQQ xStock',
symbol: 'TQQQx',
mintAddress: 'XsjQP3iMAaQ3kQScQKthQpx9ALRbjKAjQtHg6TFomoc',
decimal: 8,
mint: 'tqqqx',
enabled: false),
SPLToken(
name: 'UnitedHealth xStock',
symbol: 'UNHx',
mintAddress: 'XszvaiXGPwvk2nwb3o9C1CX4K6zH8sez11E6uyup6fe',
decimal: 8,
mint: 'unhx',
enabled: false),
SPLToken(
name: 'Vanguard xStock',
symbol: 'VTIx',
mintAddress: 'XsssYEQjzxBCFgvYFFNuhJFBeHNdLWYeUSP8F45cDr9',
decimal: 8,
mint: 'vtix',
enabled: false),
SPLToken(
name: 'Visa xStock',
symbol: 'Vx',
mintAddress: 'XsqgsbXwWogGJsNcVZ3TyVouy2MbTkfCFhCGGGcQZ2p',
decimal: 8,
mint: 'vx',
enabled: false),
SPLToken(
name: 'Walmart xStock',
symbol: 'WMTx',
mintAddress: 'Xs151QeqTCiuKtinzfRATnUESM2xTU6V9Wy8Vy538ci',
decimal: 8,
mint: 'wmtx',
enabled: false),
];
List<SPLToken> get initialSPLTokens => _defaultTokens.map((token) {
+1 -1
View File
@@ -12,7 +12,7 @@ import 'package:cw_solana/pending_solana_transaction.dart';
import 'package:cw_solana/solana_balance.dart';
import 'package:cw_solana/solana_exceptions.dart';
import 'package:cw_solana/solana_transaction_model.dart';
import 'package:cw_solana/spl_token.dart';
import 'package:cw_core/spl_token.dart';
import 'package:on_chain/solana/solana.dart';
import 'package:on_chain/solana/src/instructions/associated_token_account/constant.dart';
import 'package:on_chain/solana/src/models/pda/pda.dart';
+1 -1
View File
@@ -25,7 +25,7 @@ import 'package:cw_solana/solana_transaction_history.dart';
import 'package:cw_solana/solana_transaction_info.dart';
import 'package:cw_solana/solana_transaction_model.dart';
import 'package:cw_solana/solana_wallet_addresses.dart';
import 'package:cw_solana/spl_token.dart';
import 'package:cw_core/spl_token.dart';
import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
+1 -1
View File
@@ -1,5 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_tron/tron_token.dart';
import 'package:cw_core/tron_token.dart';
class DefaultTronTokens {
final List<TronToken> _defaultTokens = [
+1 -1
View File
@@ -10,7 +10,7 @@ import 'package:cw_tron/pending_tron_transaction.dart';
import 'package:cw_tron/tron_abi.dart';
import 'package:cw_tron/tron_balance.dart';
import 'package:cw_tron/tron_http_provider.dart';
import 'package:cw_tron/tron_token.dart';
import 'package:cw_core/tron_token.dart';
import 'package:cw_tron/tron_transaction_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
+1 -1
View File
@@ -24,7 +24,7 @@ import 'package:cw_tron/tron_abi.dart';
import 'package:cw_tron/tron_balance.dart';
import 'package:cw_tron/tron_client.dart';
import 'package:cw_tron/tron_exception.dart';
import 'package:cw_tron/tron_token.dart';
import 'package:cw_core/tron_token.dart';
import 'package:cw_tron/tron_transaction_credentials.dart';
import 'package:cw_tron/tron_transaction_history.dart';
import 'package:cw_tron/tron_transaction_info.dart';
+3 -1
View File
@@ -184,7 +184,9 @@ class TradeMonitor {
trade
..stateRaw = updated.state.raw
..receiveAmount = updated.receiveAmount ?? trade.receiveAmount
..outputTransaction = updated.outputTransaction ?? trade.outputTransaction;
..outputTransaction = updated.outputTransaction ?? trade.outputTransaction
..userCurrencyToRaw = updated.userCurrencyToRaw
..userCurrencyFromRaw = updated.userCurrencyFromRaw;
printV('Trade ${trade.id} updated: ${trade.state}');
await trade.save();
+1
View File
@@ -525,6 +525,7 @@ Future<void> setup({
getIt.get<ContactListViewModel>(),
getIt.get<UnspentCoinsListViewModel>(),
getIt.get<FeesViewModel>(),
_walletInfoSource,
),
);
@@ -167,6 +167,8 @@ class ChainflipExchangeProvider extends ExchangeProvider {
receiveAmount: request.toAmount,
state: TradeState.waiting,
payoutAddress: request.toAddress,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
isSendAll: isSendAll);
} catch (e) {
printV(e.toString());
@@ -199,6 +201,9 @@ class ChainflipExchangeProvider extends ExchangeProvider {
final refundAmount = status['refundEgress']?['amount']?.toString() ?? '0.0';
final isRefund = status['refundEgress'] != null;
final amount = isRefund ? refundAmount : receiveAmount;
final from = status['sourceAsset'].toString();
final to = status['destinationAsset'].toString();
final newTrade = Trade(
id: id,
@@ -210,7 +215,10 @@ class ChainflipExchangeProvider extends ExchangeProvider {
state: currentState,
payoutAddress: status['destinationAddress'].toString(),
outputTransaction: status['swapEgress']?['transactionReference']?.toString(),
isRefund: isRefund);
isRefund: isRefund,
userCurrencyFromRaw: '${from.toUpperCase()}' + '_',
userCurrencyToRaw: '${to.toUpperCase()}' + '_',
);
// Find trade and update receiveAmount with the real value received
final storedTrade = _getStoredTrade(id);
@@ -215,6 +215,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
receiveAmount: toAmount ?? request.toAmount,
state: TradeState.created,
payoutAddress: payoutAddress,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
isSendAll: isSendAll,
);
}
@@ -240,9 +242,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromCurrency = responseJSON['fromCurrency'] as String;
final from = CryptoCurrency.fromString(fromCurrency);
final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency);
final toCurrency = responseJSON['toCurrency'] as String;
final to = CryptoCurrency.fromString(toCurrency);
final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency);
final inputAddress = responseJSON['payinAddress'] as String;
final expectedSendAmount = responseJSON['expectedAmountFrom'].toString();
final status = responseJSON['status'] as String;
@@ -254,17 +256,20 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final expiredAt = DateTime.tryParse(expiredAtRaw ?? '')?.toLocal();
return Trade(
id: id,
from: from,
to: to,
provider: description,
inputAddress: inputAddress,
amount: expectedSendAmount,
state: state,
extraId: extraId,
expiredAt: expiredAt,
outputTransaction: outputTransaction,
payoutAddress: payoutAddress);
id: id,
from: from,
to: to,
provider: description,
inputAddress: inputAddress,
amount: expectedSendAmount,
state: state,
extraId: extraId,
expiredAt: expiredAt,
outputTransaction: outputTransaction,
payoutAddress: payoutAddress,
userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
);
}
String _getFlow(bool isFixedRate) => isFixedRate ? 'fixed-rate' : 'standard';
@@ -214,6 +214,8 @@ class ExolixExchangeProvider extends ExchangeProvider {
receiveAmount: receiveAmount ?? request.toAmount,
state: TradeState.created,
payoutAddress: payoutAddress,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
isSendAll: isSendAll,
);
}
@@ -249,15 +251,18 @@ class ExolixExchangeProvider extends ExchangeProvider {
return Trade(
id: id,
from: CryptoCurrency.fromString(coinFrom),
to: CryptoCurrency.fromString(coinTo),
from: CryptoCurrency.safeParseCurrencyFromString(coinFrom),
to: CryptoCurrency.safeParseCurrencyFromString(coinTo),
provider: description,
inputAddress: inputAddress,
amount: amount,
state: TradeState.deserialize(raw: _prepareStatus(status)),
extraId: extraId,
outputTransaction: outputTransaction,
payoutAddress: payoutAddress);
payoutAddress: payoutAddress,
userCurrencyFromRaw: '${coinFrom.toUpperCase()}' + '_',
userCurrencyToRaw: '${coinTo.toUpperCase()}' + '_',
);
}
String _getRateType(bool isFixedRate) => isFixedRate ? 'fixed' : 'float';
@@ -62,7 +62,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': '1',
'affiliate_id': _affiliateId
'affiliate_id': _affiliateId,
'float': isFixedRateMode ? 'false' : 'true',
};
final responseJSON = await _getInfo(params, isFixedRateMode);
@@ -91,7 +92,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': amount.toString(),
'affiliate_id': _affiliateId
'affiliate_id': _affiliateId,
'float': isFixedRateMode ? 'false' : 'true',
};
final responseJSON = await _getInfo(params, isFixedRateMode);
@@ -121,7 +123,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': isFixedRateMode ? request.toAmount.toString() : request.fromAmount.toString(),
'affiliate_id': _affiliateId
'affiliate_id': _affiliateId,
'float': isFixedRateMode ? 'false' : 'true',
};
final responseInfoJSON = await _getInfo(params, isFixedRateMode);
@@ -141,7 +144,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
'rate_id': rateId,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'affiliate_id': _affiliateId
'affiliate_id': _affiliateId,
'float': isFixedRateMode ? 'false' : 'true',
};
final headers = {
@@ -151,10 +155,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
};
final uri = Uri.https(_baseUrl,
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath);
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(tradeParams),
);
@@ -206,6 +211,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
createdAt: createdAt,
expiredAt: expiredAt,
extraId: extraId,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
} catch (e) {
log(e.toString());
@@ -230,7 +237,9 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final from = responseJSON['coin_from'] as String;
final fromNetwork = responseJSON['coin_from_network'] as String;
final to = responseJSON['coin_to'] as String;
final toNetwork = responseJSON['coin_to_network'] as String;
final payoutAddress = responseJSON['withdrawal'] as String;
final depositAddress = responseJSON['deposit'] as String;
final refundAddress = responseJSON['return'] as String;
@@ -246,8 +255,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
return Trade(
id: id,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
from: CryptoCurrency.safeParseCurrencyFromString(from),
to: CryptoCurrency.safeParseCurrencyFromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
@@ -259,6 +268,8 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
expiredAt: expiredAt,
isRefund: status == 'refund',
extraId: extraId,
userCurrencyFromRaw: '$from' + '_' + '$fromNetwork',
userCurrencyToRaw: '$to' + '_' + '$toNetwork',
);
}
@@ -270,10 +281,11 @@ class LetsExchangeExchangeProvider extends ExchangeProvider {
};
try {
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath);
final response = await ProxyWrapper().post(
clearnetUri: uri,
headers: headers,
body: json.encode(params),
);
if (response.statusCode != 200) {
@@ -225,7 +225,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
payoutAddress: settleAddress,
createdAt: DateTime.now(),
isSendAll: isSendAll,
extraId: depositMemo
extraId: depositMemo,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
}
@@ -264,15 +266,18 @@ class SideShiftExchangeProvider extends ExchangeProvider {
return Trade(
id: id,
from: CryptoCurrency.fromString(fromCurrency),
to: CryptoCurrency.fromString(toCurrency),
from: CryptoCurrency.safeParseCurrencyFromString(fromCurrency),
to: CryptoCurrency.safeParseCurrencyFromString(toCurrency),
provider: description,
inputAddress: inputAddress,
amount: expectedSendAmount ?? '',
state: TradeState.deserialize(raw: status ?? 'created'),
expiredAt: expiredAt,
payoutAddress: settleAddress,
extraId: depositMemo);
extraId: depositMemo,
userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
);
}
Future<String> _createQuote(TradeRequest request) async {
@@ -175,6 +175,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
payoutAddress: payoutAddress,
createdAt: DateTime.now(),
isSendAll: isSendAll,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
}
@@ -211,14 +213,16 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
return Trade(
id: id,
from: CryptoCurrency.fromString(fromCurrency),
to: CryptoCurrency.fromString(toCurrency),
from: CryptoCurrency.safeParseCurrencyFromString(fromCurrency),
to: CryptoCurrency.safeParseCurrencyFromString(toCurrency),
extraId: extraId,
provider: description,
inputAddress: inputAddress,
amount: expectedSendAmount,
state: TradeState.deserialize(raw: status),
payoutAddress: payoutAddress,
userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
);
}
@@ -198,6 +198,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
createdAt: createdAt,
expiredAt: expiredAt,
extraId: extraId,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
} catch (e) {
log(e.toString());
@@ -235,8 +237,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
return Trade(
id: respId,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
from: CryptoCurrency.safeParseCurrencyFromString(from),
to: CryptoCurrency.safeParseCurrencyFromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
@@ -247,6 +249,8 @@ class StealthExExchangeProvider extends ExchangeProvider {
createdAt: createdAt,
isRefund: status == 'refunded',
extraId: extraId,
userCurrencyFromRaw: '${from.toUpperCase()}' + '_',
userCurrencyToRaw: '${to.toUpperCase()}' + '_',
);
}
@@ -190,6 +190,8 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
state: TradeState.created,
payoutAddress: request.toAddress,
isSendAll: isSendAll,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
} catch (e) {
printV("error creating trade: ${e.toString()}");
@@ -224,9 +226,9 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
final responseData = responseBody['data'] as Map<String, dynamic>;
final fromCurrency = responseData['coin_send'] as String;
final from = CryptoCurrency.fromString(fromCurrency);
final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency);
final toCurrency = responseData['coin_receive'] as String;
final to = CryptoCurrency.fromString(toCurrency);
final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency);
final inputAddress = responseData['server_address'] as String;
final payoutAddress = responseData['recipient'] as String;
final status = responseData['status'] as String;
@@ -249,6 +251,8 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
receiveAmount: expectedReceiveAmount,
memo: memo,
createdAt: DateTime.tryParse(createdAt ?? ''),
userCurrencyFromRaw: '${fromCurrency.toUpperCase()}' + '_',
userCurrencyToRaw: '${toCurrency.toUpperCase()}' + '_',
);
} catch (e) {
printV("error getting trade: ${e.toString()}");
@@ -157,6 +157,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
payoutAddress: request.toAddress,
memo: memo,
isSendAll: isSendAll,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
}
@@ -197,8 +199,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
? parts[1].split('.')[1].split('-')[0]
: '';
final formattedToChain = CryptoCurrency.fromString(toChain);
final toAssetWithChain = CryptoCurrency.fromString(toAsset, walletCurrency: formattedToChain);
final formattedToChain = CryptoCurrency.safeParseCurrencyFromString(toChain);
final toAssetWithChain = CryptoCurrency.safeParseCurrencyFromString(toAsset, walletCurrency: formattedToChain);
final plannedOutTxs = responseJSON['planned_out_txs'] as List<dynamic>?;
final isRefund = plannedOutTxs?.any((tx) => tx['refund'] == true) ?? false;
@@ -214,6 +216,8 @@ class ThorChainExchangeProvider extends ExchangeProvider {
state: currentState,
memo: memo,
isRefund: isRefund,
userCurrencyFromRaw: '${tx['chain'] as String? ?? ''}' + '_',
userCurrencyToRaw: '$toAsset' + '_',
);
}
@@ -251,6 +251,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
payoutAddress: payoutAddress,
isSendAll: isSendAll,
extraId: addressProviderMemo,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
}
@@ -274,10 +276,13 @@ class TrocadorExchangeProvider extends ExchangeProvider {
final providerName = responseJSON['provider'] as String;
final addressProviderMemo = responseJSON['address_provider_memo'] as String?;
final from = responseJSON['ticker_from'] as String;
final to = responseJSON['ticker_to'] as String;
return Trade(
id: id,
from: CryptoCurrency.fromString(responseJSON['ticker_from'] as String),
to: CryptoCurrency.fromString(responseJSON['ticker_to'] as String),
from: CryptoCurrency.safeParseCurrencyFromString(from),
to: CryptoCurrency.safeParseCurrencyFromString(to),
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
@@ -289,6 +294,8 @@ class TrocadorExchangeProvider extends ExchangeProvider {
providerId: providerId,
providerName: providerName,
extraId: addressProviderMemo,
userCurrencyFromRaw: '${from.toUpperCase()}' + '_',
userCurrencyToRaw: '${to.toUpperCase()}' + '_',
);
});
}
@@ -76,22 +76,26 @@ class XOSwapExchangeProvider extends ExchangeProvider {
if (response.statusCode != 200) {
throw Exception('Failed to fetch assets for ${currency.title} on ${currency.tag}');
}
final assets = json.decode(response.body) as List<dynamic>;
final decoded = jsonDecode(response.body);
if (decoded is! List) throw const FormatException('Unexpected response format');
final assets = decoded.map((e) => Map<String, dynamic>.from(e as Map)).toList();
final asset = assets.firstWhere(
(asset) {
final assetSymbol = (asset['symbol'] as String).toUpperCase();
return assetSymbol == currency.title.toUpperCase();
},
orElse: () => null,
(asset) => removeNonAlphanumeric((asset['symbol'] ?? '').toString()) == currency.title,
orElse: () => const {},
);
return asset != null ? asset['id'] as String : null;
return asset.isEmpty ? null : asset['id'] as String;
} catch (e) {
printV(e.toString());
return null;
}
}
String removeNonAlphanumeric(String str) => str.toUpperCase().replaceAll(RegExp(r'[^A-Z0-9]'), '');
Future<List<dynamic>> getRatesForPair({
required CryptoCurrency from,
required CryptoCurrency to,
@@ -117,19 +121,24 @@ class XOSwapExchangeProvider extends ExchangeProvider {
required CryptoCurrency to,
required bool isFixedRateMode,
}) async {
final rates = await getRatesForPair(from: from, to: to);
if (rates.isEmpty) return Limits(min: 0, max: 0);
try {
final rates = await getRatesForPair(from: from, to: to);
if (rates.isEmpty) throw Exception('No rates found for $from to $to');
double minLimit = double.infinity;
double maxLimit = 0;
for (var rate in rates) {
final double currentMin = double.parse(rate['min']['value'].toString());
final double currentMax = double.parse(rate['max']['value'].toString());
if (currentMin < minLimit) minLimit = currentMin;
if (currentMax > maxLimit) maxLimit = currentMax;
for (var rate in rates) {
final double currentMin = double.parse(rate['min']['value'].toString());
final double currentMax = double.parse(rate['max']['value'].toString());
if (currentMin < minLimit) minLimit = currentMin;
if (currentMax > maxLimit) maxLimit = currentMax;
}
return Limits(min: minLimit, max: maxLimit);
} catch (e) {
printV(e.toString());
throw Exception('StealthEx failed to fetch limits');
}
return Limits(min: minLimit, max: maxLimit);
}
Future<double> fetchRate({
@@ -249,6 +258,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
receiveAmount: receiveAmount.toString(),
payoutAddress: payoutAddress,
extraId: extraId,
userCurrencyFromRaw: '${request.fromCurrency.title}_${request.fromCurrency.tag ?? ''}',
userCurrencyToRaw: '${request.toCurrency.title}_${request.toCurrency.tag ?? ''}',
);
} catch (e) {
printV(e.toString());
@@ -275,10 +286,10 @@ class XOSwapExchangeProvider extends ExchangeProvider {
final pairId = responseJSON['pairId'] as String;
final pairParts = pairId.split('_');
final CryptoCurrency fromCurrency =
CryptoCurrency.fromString(pairParts.isNotEmpty ? pairParts[0] : "");
final CryptoCurrency toCurrency =
CryptoCurrency.fromString(pairParts.length > 1 ? pairParts[1] : "");
final fromAsset = pairParts.isNotEmpty ? pairParts[0] : '';
final toAsset = pairParts.length > 1 ? pairParts[1] : '';
final fromCurrency = CryptoCurrency.safeParseCurrencyFromString(fromAsset);
final toCurrency = CryptoCurrency.safeParseCurrencyFromString(toAsset);
final amount = responseJSON['amount'] as Map<String, dynamic>;
final toAmount = responseJSON['toAmount'] as Map<String, dynamic>;
@@ -306,6 +317,8 @@ class XOSwapExchangeProvider extends ExchangeProvider {
receiveAmount: receiveAmount,
payoutAddress: payoutAddress,
extraId: extraId,
userCurrencyFromRaw: '$fromAsset' + '_',
userCurrencyToRaw: '$toAsset' + '_',
);
} catch (e) {
printV(e.toString());
+50 -2
View File
@@ -31,6 +31,8 @@ class Trade extends HiveObject {
this.isRefund,
this.isSendAll,
this.router,
this.userCurrencyFromRaw,
this.userCurrencyToRaw,
}) {
if (provider != null) providerRaw = provider.raw;
@@ -126,6 +128,46 @@ class Trade extends HiveObject {
@HiveField(23, defaultValue: '')
String? receiveAmount;
@HiveField(24, defaultValue: '')
String? userCurrencyFromRaw;
@HiveField(25, defaultValue: '')
String? userCurrencyToRaw;
CryptoCurrency? get userCurrencyFrom {
if (userCurrencyFromRaw == null || userCurrencyFromRaw!.isEmpty) {
return null;
}
final underscoreIndex = userCurrencyFromRaw!.indexOf('_');
final title = userCurrencyFromRaw!.substring(0, underscoreIndex);
final tag = userCurrencyFromRaw!.substring(underscoreIndex + 1);
return CryptoCurrency(
title: title,
tag: tag.isNotEmpty ? tag : null,
name: '',
raw: -1,
decimals: 1,
);
}
CryptoCurrency? get userCurrencyTo {
if (userCurrencyToRaw == null || userCurrencyToRaw!.isEmpty) {
return null;
}
final underscoreIndex = userCurrencyToRaw!.indexOf('_');
final title = userCurrencyToRaw!.substring(0, underscoreIndex);
final tag = userCurrencyToRaw!.substring(underscoreIndex + 1);
return CryptoCurrency(
title: title,
tag: tag.isNotEmpty ? tag : null,
name: '',
raw: -1,
decimals: 1,
);
}
static Trade fromMap(Map<String, Object?> map) {
return Trade(
id: map['id'] as String,
@@ -206,6 +248,8 @@ class TradeAdapter extends TypeAdapter<Trade> {
isRefund: fields[20] as bool?,
isSendAll: fields[21] as bool?,
router: fields[22] as String?,
userCurrencyFromRaw: fields[24] as String?,
userCurrencyToRaw: fields[25] as String?,
)
..providerRaw = fields[1] == null ? 0 : fields[1] as int
..fromRaw = fields[2] == null ? 0 : fields[2] as int
@@ -216,7 +260,7 @@ class TradeAdapter extends TypeAdapter<Trade> {
@override
void write(BinaryWriter writer, Trade obj) {
writer
..writeByte(24)
..writeByte(26)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -264,7 +308,11 @@ class TradeAdapter extends TypeAdapter<Trade> {
..writeByte(22)
..write(obj.router)
..writeByte(23)
..write(obj.receiveAmount);
..write(obj.receiveAmount)
..writeByte(24)
..write(obj.userCurrencyFromRaw)
..writeByte(25)
..write(obj.userCurrencyToRaw);
}
@override
+15
View File
@@ -36,10 +36,13 @@ import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/address_info.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/mweb_utxo.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/payjoin_session.dart';
import 'package:cw_core/spl_token.dart';
import 'package:cw_core/tron_token.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
@@ -206,6 +209,18 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
CakeHive.registerAdapter(PayjoinSessionAdapter());
}
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
CakeHive.registerAdapter(Erc20TokenAdapter());
}
if (!CakeHive.isAdapterRegistered(SPLToken.typeId)) {
CakeHive.registerAdapter(SPLTokenAdapter());
}
if (!CakeHive.isAdapterRegistered(TronToken.typeId)) {
CakeHive.registerAdapter(TronTokenAdapter());
}
final secureStorage = secureStorageShared;
final transactionDescriptionsBoxKey =
await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
+15 -8
View File
@@ -125,13 +125,16 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
});
}
void _checkIfTokenIsVerified(String? contractAddress) async {
if (contractAddress != null) {
final isVerified = widget.homeSettingsViewModel.checkIfTokenIsWhitelisted(contractAddress);
setState(() {
_isTokenVerified = isVerified;
});
}
void _checkIfTokenIsVerified(String? contractAddress) {
if (contractAddress == null) return;
final isVerified =
widget.homeSettingsViewModel.checkIfTokenIsWhitelisted(contractAddress);
if (!mounted) return;
setState(() {
_isTokenVerified = isVerified;
});
}
@override
@@ -253,7 +256,9 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
name: _tokenNameController.text,
title: _tokenSymbolController.text.toUpperCase(),
decimals: int.parse(_tokenDecimalController.text),
iconPath: _tokenIconPathController.text,
iconPath: _tokenIconPathController.text.isNotEmpty
? _tokenIconPathController.text
: null,
isPotentialScam: isPotentialScam,
),
contractAddress: _contractAddressController.text,
@@ -335,6 +340,8 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
if (_contractAddressController.text.isNotEmpty) {
final token = await widget.homeSettingsViewModel.getToken(_contractAddressController.text);
if (!mounted) return;
if (token != null) {
final isZano = widget.homeSettingsViewModel.walletType == WalletType.zano;
if (_tokenNameController.text.isEmpty || isZano) _tokenNameController.text = token.name;
@@ -164,22 +164,28 @@ class TransactionsPage extends StatelessWidget {
if (item is TradeListItem) {
final trade = item.trade;
return Observer(
builder: (_) => TradeRow(
key: item.key,
onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade),
swapState: trade.state,
provider: trade.provider,
from: trade.from,
to: trade.to,
createdAtFormattedDate: trade.createdAt != null
? DateFormat('HH:mm').format(trade.createdAt!)
: null,
formattedAmount: item.tradeFormattedAmount,
formattedReceiveAmount: item.tradeFormattedReceiveAmount
),
);
final tradeFrom =
trade.fromRaw >= 0 ? trade.from : trade.userCurrencyFrom;
final tradeTo = trade.toRaw >= 0 ? trade.to : trade.userCurrencyTo;
return tradeFrom != null && tradeTo != null
? Observer(
builder: (_) => TradeRow(
key: item.key,
onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade),
swapState: trade.state,
provider: trade.provider,
from: tradeFrom,
to: tradeTo,
createdAtFormattedDate: trade.createdAt != null
? DateFormat('HH:mm').format(trade.createdAt!)
: null,
formattedAmount: item.tradeFormattedAmount,
formattedReceiveAmount: item.tradeFormattedReceiveAmount),
)
: Container();
}
if (item is OrderListItem) {
final order = item.order;
@@ -39,13 +39,11 @@ class CurrencyPickerState extends State<CurrencyPicker> {
String textFieldValue;
bool currencySearchBySubstring(Currency currency, String subString) {
return currency.name.toLowerCase().contains(subString.toLowerCase()) ||
(currency.tag != null
? currency.tag!.toLowerCase().contains(subString.toLowerCase())
: false) ||
(currency.fullName != null
? currency.fullName!.toLowerCase().contains(subString.toLowerCase())
: false);
final query = subString.toLowerCase();
return currency.name.toLowerCase().contains(query) ||
currency.toString().toLowerCase().contains(query) ||
(currency.tag != null ? currency.tag!.toLowerCase().contains(query) : false) ||
(currency.fullName != null ? currency.fullName!.toLowerCase().contains(query) : false);
}
@override
@@ -76,7 +76,7 @@ class PresentProviderPicker extends StatelessWidget {
(e) => CheckBoxItem(
e.title,
exchangeViewModel.selectedProviders.contains(e),
isDisabled: !exchangeViewModel.providersForCurrentPair().contains(e),
isDisabled: !exchangeViewModel.providerList.contains(e),
),
)
.toList(),
@@ -102,15 +102,17 @@ abstract class HomeSettingsViewModelBase with Store {
}
if (_balanceViewModel.wallet.type == WalletType.solana) {
final splToken = token.copyWith(enabled: true);
await solana!.addSPLToken(
_balanceViewModel.wallet,
token,
splToken,
contractAddress,
);
}
if (_balanceViewModel.wallet.type == WalletType.tron) {
await tron!.addTronToken(_balanceViewModel.wallet, token, contractAddress);
final tronToken = token.copyWith(enabled: true);
await tron!.addTronToken(_balanceViewModel.wallet, tronToken, contractAddress);
}
if (_balanceViewModel.wallet.type == WalletType.zano) {
@@ -159,9 +159,18 @@ abstract class ExchangeTradeViewModelBase with Store {
}
void _updateItems() {
final tagFrom =
tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : '';
final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : '';
final trade = tradesStore.trade!;
final tradeFrom = trade.fromRaw >= 0
? trade.from
: trade.userCurrencyFrom;
final tradeTo = trade.toRaw >= 0
? trade.to
: trade.userCurrencyTo;
final tagFrom = tradeFrom?.tag != null ? '${tradeFrom!.tag}' + ' ' : '';
final tagTo = tradeTo?.tag != null ? '${tradeTo!.tag}' + ' ' : '';
items.clear();
if (trade.provider != ExchangeProviderDescription.thorChain)
@@ -175,36 +184,40 @@ abstract class ExchangeTradeViewModelBase with Store {
),
);
items.addAll([
ExchangeTradeItem(
title: S.current.amount,
data: '${trade.amount} ${trade.from}',
isCopied: false,
isReceiveDetail: false,
isExternalSendDetail: true,
),
ExchangeTradeItem(
title: S.current.you_will_receive_estimated_amount + ':',
data: '${tradesStore.trade?.receiveAmount} ${trade.to}',
isCopied: true,
isReceiveDetail: true,
isExternalSendDetail: false,
),
ExchangeTradeItem(
title: S.current.send_to_this_address('${tradesStore.trade!.from}', tagFrom) + ':',
data: trade.inputAddress ?? '',
isCopied: false,
isReceiveDetail: false,
isExternalSendDetail: true,
),
]);
if (tradeFrom != null || tradeTo != null) {
items.addAll([
ExchangeTradeItem(
title: S.current.amount,
data: '${trade.amount} ${tradeFrom}',
isCopied: false,
isReceiveDetail: false,
isExternalSendDetail: true,
),
ExchangeTradeItem(
title: S.current.you_will_receive_estimated_amount + ':',
data: '${tradesStore.trade?.receiveAmount} ${tradeTo}',
isCopied: true,
isReceiveDetail: true,
isExternalSendDetail: false,
),
ExchangeTradeItem(
title: S.current.send_to_this_address('${tradeFrom}', tagFrom) + ':',
data: trade.inputAddress ?? '',
isCopied: false,
isReceiveDetail: false,
isExternalSendDetail: true,
),
]);
}
final isExtraIdExist = trade.extraId != null && trade.extraId!.isNotEmpty;
if (isExtraIdExist) {
final title = trade.from == CryptoCurrency.xrp
final title = tradeFrom == CryptoCurrency.xrp
? S.current.destination_tag
: trade.from == CryptoCurrency.xlm || trade.from == CryptoCurrency.ton
: tradeFrom == CryptoCurrency.xlm || tradeFrom == CryptoCurrency.ton
? S.current.memo
: S.current.extra_id;
@@ -220,7 +233,7 @@ abstract class ExchangeTradeViewModelBase with Store {
items.add(
ExchangeTradeItem(
title: S.current.arrive_in_this_address('${tradesStore.trade!.to}', tagTo) + ':',
title: S.current.arrive_in_this_address('${tradeTo}', tagTo) + ':',
data: trade.payoutAddress ?? '',
isCopied: true,
isReceiveDetail: true,
@@ -230,23 +243,29 @@ abstract class ExchangeTradeViewModelBase with Store {
}
static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
final trade = tradesStore.trade!;
final tradeFrom = trade.fromRaw >= 0
? trade.from
: trade.userCurrencyFrom;
bool _isEthToken() =>
wallet.currency == CryptoCurrency.eth &&
tradesStore.trade!.from.tag == CryptoCurrency.eth.title;
bool _isPolygonToken() =>
wallet.currency == CryptoCurrency.maticpoly &&
tradesStore.trade!.from.tag == CryptoCurrency.maticpoly.tag;
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
bool _isTronToken() =>
wallet.currency == CryptoCurrency.trx &&
tradesStore.trade!.from.tag == CryptoCurrency.trx.title;
tradeFrom?.tag == CryptoCurrency.trx.title;
bool _isSplToken() =>
wallet.currency == CryptoCurrency.sol &&
tradesStore.trade!.from.tag == CryptoCurrency.sol.title;
tradeFrom?.tag == CryptoCurrency.sol.title;
return tradesStore.trade!.from == wallet.currency ||
return tradeFrom == wallet.currency ||
tradesStore.trade!.provider == ExchangeProviderDescription.xmrto ||
_isEthToken() ||
_isPolygonToken() ||
+213 -25
View File
@@ -37,12 +37,17 @@ import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/spl_token.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/tron_token.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:intl/intl.dart';
@@ -70,6 +75,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
this.contactListViewModel,
this.unspentCoinsListViewModel,
this.feesViewModel,
this.walletInfoSource,
) : _cryptoNumberFormat = NumberFormat(),
isSendAllEnabled = false,
isFixedRateMode = false,
@@ -81,8 +87,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
isDepositAddressEnabled = false,
isReceiveAmountEditable = false,
_useTorOnly = false,
receiveCurrencies = <CryptoCurrency>[],
depositCurrencies = <CryptoCurrency>[],
receiveCurrencies = ObservableList<CryptoCurrency>(),
depositCurrencies = ObservableList<CryptoCurrency>(),
limits = Limits(min: 0, max: 0),
tradeState = ExchangeTradeStateInitial(),
limitsState = LimitsInitialState(),
@@ -138,7 +144,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
receiveAddress = '';
depositAddress =
depositCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
provider = providersForCurrentPair().first;
provider = providerList.firstOrNull;
final initialProvider = provider;
provider!.checkIsAvailable().then((bool isAvailable) {
if (!isAvailable && provider == initialProvider) {
@@ -149,10 +156,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
});
receiveCurrencies = CryptoCurrency.all
.where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency))
.toList();
.toList()
.asObservable();
depositCurrencies = CryptoCurrency.all
.where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency))
.toList();
.toList()
.asObservable();
_injectUserEthTokensIntoCurrencyLists();
_injectUserSplTokensIntoCurrencyLists();
_injectUserTronTokensIntoCurrencyLists();
_defineIsReceiveAmountEditable();
loadLimits();
reaction((_) => isFixedRateMode, (Object _) {
@@ -165,8 +178,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
}
bool get isElectrumWallet =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash,WalletType.dogecoin ].contains(wallet.type);
bool get isElectrumWallet => [
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
WalletType.dogecoin
].contains(wallet.type);
bool get hideAddressAfterExchange =>
[WalletType.monero, WalletType.wownero].contains(wallet.type);
@@ -320,9 +337,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
bool get isMoneroWallet => wallet.type == WalletType.monero;
List<CryptoCurrency> receiveCurrencies;
@observable
ObservableList<CryptoCurrency> receiveCurrencies;
List<CryptoCurrency> depositCurrencies;
@observable
ObservableList<CryptoCurrency> depositCurrencies;
final NumberFormat _cryptoNumberFormat;
@@ -334,6 +353,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final FeesViewModel feesViewModel;
final Box<WalletInfo> walletInfoSource;
@observable
double bestRate = 0.0;
@@ -490,7 +511,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
try {
final futures = selectedProviders
.where((provider) => providersForCurrentPair().contains(provider))
.where((provider) => providerList.contains(provider))
.map((provider) async {
final limits = await provider
.fetchLimits(
@@ -717,16 +738,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
void removeTemplate({required ExchangeTemplate template}) =>
_exchangeTemplateStore.remove(template: template);
List<ExchangeProvider> providersForCurrentPair() =>
_providersForPair(from: depositCurrency, to: receiveCurrency);
List<ExchangeProvider> _providersForPair(
{required CryptoCurrency from, required CryptoCurrency to}) =>
providerList
.where((provider) =>
provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty)
.toList();
void _onPairChange() {
depositAmount = '';
receiveAmount = '';
@@ -819,7 +830,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
void addExchangeProvider(ExchangeProvider provider) {
selectedProviders.add(provider);
if (providersForCurrentPair().contains(provider)) _tradeAvailableProviders.add(provider);
if (providerList.contains(provider)) _tradeAvailableProviders.add(provider);
}
@action
@@ -863,16 +874,15 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
bool get isAvailableInSelected {
final providersForPair = providersForCurrentPair();
return selectedProviders
.any((element) => element.isAvailable && providersForPair.contains(element));
.any((element) => element.isAvailable && providerList.contains(element));
}
void _setAvailableProviders() {
_tradeAvailableProviders.clear();
_tradeAvailableProviders.addAll(
selectedProviders.where((provider) => providersForCurrentPair().contains(provider)));
selectedProviders.where((provider) => providerList.contains(provider)));
}
void _setProviders() {
@@ -989,7 +999,185 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
}
}
double initialAmountByAssets (CryptoCurrency ticker) {
// Adding user's Erc20 tokens to the list of currencies
Future<Box<Erc20Token>> _openEvmTokensBoxFor(WalletInfo walletInfo) async {
final walletKey = walletInfo.name.replaceAll(" ", "_");
final boxName = switch (walletInfo.type) {
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
};
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<Erc20Token>(boxName);
}
return CakeHive.openBox<Erc20Token>(boxName);
}
Future<List<Erc20Token>> _loadAllUniqueEvmTokens() async {
final evmWallets = walletInfoSource.values.where(
(w) => w.type == WalletType.ethereum || w.type == WalletType.polygon,
);
final seen = <String>{};
final unique = <Erc20Token>[];
for (final wallet in evmWallets) {
final chain = wallet.type == WalletType.ethereum ? 'ETH' : 'POL';
final box = await _openEvmTokensBoxFor(wallet);
for (final t in box.values.where((t) => t.enabled)) {
final key = '$chain|${t.contractAddress.toLowerCase()}';
if (seen.add(key)) {
unique.add(t);
}
}
}
return unique;
}
@action
Future<void> _injectUserEthTokensIntoCurrencyLists() async {
final userTokens = await _loadAllUniqueEvmTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];
for (final token in userTokens) {
if (!_listContainsToken(receiveCurrencies, token)) toAddReceive.add(token);
if (!_listContainsToken(depositCurrencies, token)) toAddDeposit.add(token);
}
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
}
bool _listContainsToken(List<CryptoCurrency> list, Erc20Token token) {
return list.any((item) {
if (item is Erc20Token) {
return item.contractAddress.toLowerCase() == token.contractAddress.toLowerCase();
}
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
});
}
// Adding user's Solana tokens to the list of currencies
Future<Box<SPLToken>> _openSolTokensBoxFor(WalletInfo wallet) async {
final boxName = '${wallet.name.replaceAll(" ", "_")}_${SPLToken.boxName}';
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<SPLToken>(boxName);
}
return CakeHive.openBox<SPLToken>(boxName);
}
Future<List<SPLToken>> _loadAllUniqueSolTokens() async {
final solWallets = walletInfoSource.values.where((wallet) => wallet.type == WalletType.solana);
final tokens = <SPLToken>[];
for (final wallet in solWallets) {
final box = await _openSolTokensBoxFor(wallet);
tokens.addAll(box.values.where((t) => t.enabled));
}
final seen = <String>{};
final unique = <SPLToken>[];
for (final token in tokens) {
final key = token.mintAddress.toLowerCase();
if (!seen.contains(key)) {
seen.add(key);
unique.add(token);
}
}
return unique;
}
bool _listContainsSplToken(List<CryptoCurrency> list, SPLToken token) {
return list.any((item) {
if (item is SPLToken) {
return item.mintAddress.toLowerCase() == token.mintAddress.toLowerCase();
}
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
});
}
@action
Future<void> _injectUserSplTokensIntoCurrencyLists() async {
final userTokens = await _loadAllUniqueSolTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];
for (final token in userTokens) {
if (!_listContainsSplToken(receiveCurrencies, token)) toAddReceive.add(token);
if (!_listContainsSplToken(depositCurrencies, token)) toAddDeposit.add(token);
}
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
}
// Adding user's Tron tokens to the list of currencies
Future<Box<TronToken>> _openTronTokensBoxFor(WalletInfo walletInfo) async {
final boxName = '${walletInfo.name.replaceAll(" ", "_")}_${TronToken.boxName}';
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<TronToken>(boxName);
}
return CakeHive.openBox<TronToken>(boxName);
}
Future<List<TronToken>> _loadAllUniqueTronTokens() async {
final tronWallets =
walletInfoSource.values.where((w) => w.type == WalletType.tron);
final seen = <String>{};
final unique = <TronToken>[];
for (final wallet in tronWallets) {
final box = await _openTronTokensBoxFor(wallet);
for (final t in box.values.where((t) => t.enabled)) {
final key = t.contractAddress.toLowerCase();
if (seen.add(key)) unique.add(t);
}
}
return unique;
}
bool _listContainsTronToken(List<CryptoCurrency> list, TronToken token) {
return list.any((item) {
if (item is TronToken) {
return item.contractAddress.toLowerCase() == token.contractAddress.toLowerCase();
}
return item.title.toUpperCase() == token.symbol.toUpperCase() &&
(item.tag?.toUpperCase() == token.tag?.toUpperCase() || item.tag == null);
});
}
@action
Future<void> _injectUserTronTokensIntoCurrencyLists() async {
final userTokens = await _loadAllUniqueTronTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];
for (final token in userTokens) {
if (!_listContainsTronToken(receiveCurrencies, token)) toAddReceive.add(token);
if (!_listContainsTronToken(depositCurrencies, token)) toAddDeposit.add(token);
}
if (toAddReceive.isNotEmpty) receiveCurrencies.addAll(toAddReceive);
if (toAddDeposit.isNotEmpty) depositCurrencies.addAll(toAddDeposit);
}
double initialAmountByAssets(CryptoCurrency ticker) {
final amount = switch (ticker) {
CryptoCurrency.trx => 1000,
CryptoCurrency.nano => 10,
+35 -11
View File
@@ -24,6 +24,7 @@ import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
@@ -163,17 +164,22 @@ abstract class TradeDetailsViewModelBase with Store {
items.add(
DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString()));
items.add(TradeDetailsListCardItem.tradeDetails(
id: trade.id,
extraId: trade.extraId,
createdAt: trade.createdAt != null ? dateFormat.format(trade.createdAt!) : '',
from: trade.from,
to: trade.to,
onTap: (BuildContext context) {
Clipboard.setData(ClipboardData(text: '${trade.id}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
));
final tradeFrom = _safeFrom(trade);
final tradeTo = _safeTo(trade);
if (tradeFrom != null && tradeTo != null) {
items.add(TradeDetailsListCardItem.tradeDetails(
id: trade.id,
extraId: trade.extraId,
createdAt: trade.createdAt != null ? dateFormat.format(trade.createdAt!) : '',
from: tradeFrom,
to: tradeTo,
onTap: (context) {
Clipboard.setData(ClipboardData(text: trade.id));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
));
}
items.add(StandartListItem(
title: S.current.trade_details_provider, value: trade.provider.toString()));
@@ -207,4 +213,22 @@ abstract class TradeDetailsViewModelBase with Store {
launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (e) {}
}
CryptoCurrency? _safeFrom(Trade trade) {
try {
final raw = trade.fromRaw;
return raw >= 0 ? trade.from : trade.userCurrencyFrom;
} catch (_) {
return trade.userCurrencyFrom;
}
}
CryptoCurrency? _safeTo(Trade trade) {
try {
final raw = trade.toRaw;
return raw >= 0 ? trade.to : trade.userCurrencyTo;
} catch (_) {
return trade.userCurrencyTo;
}
}
}
+2 -2
View File
@@ -1113,11 +1113,11 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/spl_token.dart';
import 'package:hive/hive.dart';
""";
const solanaCWHeaders = """
import 'package:cw_solana/spl_token.dart';
import 'package:cw_solana/solana_wallet.dart';
import 'package:cw_solana/solana_mnemonics.dart';
import 'package:cw_solana/solana_wallet_service.dart';
@@ -1199,6 +1199,7 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/tron_token.dart';
import 'package:hive/hive.dart';
""";
@@ -1209,7 +1210,6 @@ import 'package:cw_tron/tron_transaction_info.dart';
import 'package:cw_tron/tron_wallet_creation_credentials.dart';
import 'package:cw_tron/tron_client.dart';
import 'package:cw_tron/tron_token.dart';
import 'package:cw_tron/tron_wallet.dart';
import 'package:cw_tron/tron_wallet_service.dart';
import 'package:cw_tron/default_tron_tokens.dart';