diff --git a/lib/exchange/provider/near_Intents_exchange_provider.dart b/lib/exchange/provider/near_Intents_exchange_provider.dart index 2a6a86143..f2ebe1006 100644 --- a/lib/exchange/provider/near_Intents_exchange_provider.dart +++ b/lib/exchange/provider/near_Intents_exchange_provider.dart @@ -271,17 +271,15 @@ class NearIntentsExchangeProvider extends ExchangeProvider { throw Exception('Failed to parse to currency from assetId: $toAssetId'); } - final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency.$1, - tag: fromCurrency.$2); - final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency.$1, - tag: toCurrency.$2); + final from = CryptoCurrency.safeParseCurrencyFromString(fromCurrency.$1, tag: fromCurrency.$2); + final to = CryptoCurrency.safeParseCurrencyFromString(toCurrency.$1, tag: toCurrency.$2); final trade = Trade( id: depositAddress, // Using deposit address as trade ID from: request.fromCurrency, - to: request.toCurrency, + to: request.fromCurrency, provider: description, providerName: title, state: TradeState.created, @@ -575,10 +573,17 @@ class NearIntentsExchangeProvider extends ExchangeProvider { final token = supported.firstWhereOrNull((t) => t.assetId == assetId); if (token == null) return null; - final title = token.symbol; + + final title = token.symbol.toUpperCase() + .replaceAll(RegExp(r'\s*\([^)]*\)'), ''); + final normalizedNetwork = _normalizeNearBlockchainToTag(token.blockchain); - final tag = - normalizedNetwork == title.toUpperCase() ? null : normalizedNetwork; + + final isNativeAsset = assetId.contains(':native:coin'); + + final tag = isNativeAsset || normalizedNetwork == title + ? null + : normalizedNetwork; return (title, tag); } diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 2579b4644..58540f8fc 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:cake_wallet/core/amount_parsing_proxy.dart'; +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; @@ -24,6 +25,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; import 'package:cake_wallet/store/dashboard/trades_store.dart'; +import 'package:cake_wallet/utils/exchange_provider_logger.dart'; import 'package:cake_wallet/utils/qr_util.dart'; import 'package:cake_wallet/utils/token_utilities.dart'; import 'package:cake_wallet/view_model/send/fees_view_model.dart'; @@ -48,7 +50,6 @@ abstract class ExchangeTradeViewModelBase with Store { required this.feesViewModel, required this.fiatConversionStore, }) : trade = tradesStore.trade!, - isSendable = _checkIfCanSend(tradesStore, wallet), isSwapsXYZCanSendFromExternal = _checkIfSwapsXYZCanSendFromExternal(tradesStore.trade!, wallet), items = ObservableList() { setUpOutput(); @@ -115,12 +116,10 @@ abstract class ExchangeTradeViewModelBase with Store { @observable Trade trade; - @observable - bool isSendable; - - bool isSwapsXYZCanSendFromExternal; + bool get isSendable => checkIfCanSend(trade, wallet) == null; + /// Providers that should hide the "send from external" button static const List _providersThatHideExternalSend = [ JupiterExchangeProvider, @@ -212,7 +211,13 @@ abstract class ExchangeTradeViewModelBase with Store { @action Future confirmSending() async { - if (!isSendable) return; + final canSendError = checkIfCanSend(trade, wallet); + + if (canSendError != null) { + sendViewModel.state = FailureState(canSendError); + return; + } + final selected = trade.from; if (selected == null) { @@ -348,48 +353,63 @@ abstract class ExchangeTradeViewModelBase with Store { ); } - static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) { - final trade = tradesStore.trade!; - final tradeFrom = trade.from; + String? checkIfCanSend(Trade? trade, WalletBase wallet) { + try { - bool _sameCurrency(CryptoCurrency? a, CryptoCurrency? b) => - a != null && b != null && a.titleAndTagEqual(b); + if (trade == null) throw Exception('Trade is null'); - bool _isEthToken() => - wallet.currency == CryptoCurrency.eth && tradeFrom?.tag == CryptoCurrency.eth.title; + final tradeFrom = trade.from; + if (tradeFrom == null) throw Exception('Trade from currency is null'); - bool _isPolygonToken() => - wallet.currency == CryptoCurrency.maticpoly && - tradeFrom?.tag == CryptoCurrency.maticpoly.tag; + bool _sameCurrency(CryptoCurrency a, CryptoCurrency b) => a.titleAndTagEqual(b); - bool _isBaseToken() => - wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.tag; + bool _isTokenBelongingToWallet(CryptoCurrency cur) { + final chainTag = cur.tag ?? cur.title; + return wallet.currency == cur && + tradeFrom.tag?.toUpperCase() == chainTag.toUpperCase(); + } - bool _isArbitrumToken() => - wallet.currency == CryptoCurrency.arbEth && - (tradeFrom?.tag == CryptoCurrency.arbEth.tag || - tradeFrom?.title == CryptoCurrency.arbEth.tag); // This is to handle the CryptoCurrency.arb that doesn't have a tag but fully belongs to the Arbitrum chain + final canSend = _sameCurrency(tradeFrom, wallet.currency) || + (_sameCurrency(tradeFrom, CryptoCurrency.btcln) && + wallet.currency == CryptoCurrency.btc) || + trade.provider == ExchangeProviderDescription.xmrto || + _isTokenBelongingToWallet(CryptoCurrency.eth) || + _isTokenBelongingToWallet(CryptoCurrency.maticpoly) || + _isTokenBelongingToWallet(CryptoCurrency.baseEth) || + _isTokenBelongingToWallet(CryptoCurrency.arbEth) || + _isTokenBelongingToWallet(CryptoCurrency.trx) || + _isTokenBelongingToWallet(CryptoCurrency.sol) || + _isTokenBelongingToWallet(CryptoCurrency.bnb); - bool _isTronToken() => - wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title; + if (!canSend) { + throw Exception( + 'Wallet currency ${wallet.currency.title} does not match trade from currency ${tradeFrom.title} or is not a supported token for this wallet.', + ); + } - bool _isSplToken() => - wallet.currency == CryptoCurrency.sol && tradeFrom?.tag == CryptoCurrency.sol.title; + return null; + } catch (e, s) { + final trade = tradesStore.trade; - bool _isBscToken() => - wallet.currency == CryptoCurrency.bnb && tradeFrom?.tag == CryptoCurrency.bnb.tag; + ExchangeProviderLogger.logError( + provider: trade?.provider, + function: '_checkIfCanSend', + error: e, + stackTrace: s, + requestData: { + 'tradeId': trade?.id, + 'tradeFrom': trade?.from?.title, + 'tradeFromTag': trade?.from?.tag, + 'tradeTo': trade?.to?.title, + 'tradeToTag': trade?.to?.tag, + 'walletName': wallet.name, + 'walletCurrency': wallet.currency.title, + 'walletCurrencyTag': wallet.currency.tag, + }, + ); - return _sameCurrency(tradeFrom, wallet.currency) || - (_sameCurrency(tradeFrom, CryptoCurrency.btcln) && - wallet.currency == CryptoCurrency.btc) || - tradesStore.trade!.provider == ExchangeProviderDescription.xmrto || - _isEthToken() || - _isPolygonToken() || - _isSplToken() || - _isTronToken() || - _isBaseToken() || - _isArbitrumToken() || - _isBscToken(); + return e.toString(); + } } static bool _checkIfSwapsXYZCanSendFromExternal(Trade trade, WalletBase wallet) {