What changed, and why it matters
This commit fixes two bugs in Cake Wallet's exchange flow. First, when creating a trade record for the NEAR Intents exchange provider, the app was accidentally recording the 'from' currency as both the source and destination currency. Second, the code that checks whether the user's wallet can send funds for a trade was refactored to return error strings instead of throwing exceptions, and logging of those errors was moved to the caller. The first fix is a real functional bug that could mislead users about what they are receiving in a trade. The second is mostly a code-quality and reliability improvement.
Treat this as a functional bug fix with possible user-impact. Review whether the incorrect `to` currency was displayed to users in trade confirmation screens or stored persistently, which could affect user trust or support cases. No immediate security patch is indicated, but QA should verify trade display accuracy for NEAR Intents swaps.
Security signals we found
Data integrity bug: trade destination currency incorrectly set to source currency
UI/display impact: user may be shown incorrect trade details before sending funds
Refactoring of error handling from exceptions to return values
Minor expansion of currency matching logic for token wallets
Evidence from the diff
In near_Intents_exchange_provider.dart, the Trade constructor previously set to: request.fromCurrency, which is corrected to to: request.toCurrency. This caused the trade object’s destination currency to be wrong. In exchange_trade_view_model.dart, checkIfCanSend was rewritten from a try/catch-throw style to an early-return style, returning error strings and moving logging into a new _logCanSendError helper called by the caller. A small logic expansion was added in _isTokenBelongingToWallet to also compare tradeFrom.title.toUpperCase() == chainTag.toUpperCase().
Changed components
lib/exchange/provider/near_Intents_exchange_provider.dartlib/view_model/exchange/exchange_trade_view_model.dartInspect captured patch +49 / −54
diff --git a/lib/exchange/provider/near_Intents_exchange_provider.dart b/lib/exchange/provider/near_Intents_exchange_provider.dart
index f2ebe100..2ac942de 100644
--- a/lib/exchange/provider/near_Intents_exchange_provider.dart
+++ b/lib/exchange/provider/near_Intents_exchange_provider.dart
@@ -271,15 +271,17 @@ 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.fromCurrency,
+ to: request.toCurrency,
provider: description,
providerName: title,
state: TradeState.created,
diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart
index 58540f8f..154c1ab9 100644
--- a/lib/view_model/exchange/exchange_trade_view_model.dart
+++ b/lib/view_model/exchange/exchange_trade_view_model.dart
@@ -214,11 +214,11 @@ abstract class ExchangeTradeViewModelBase with Store {
final canSendError = checkIfCanSend(trade, wallet);
if (canSendError != null) {
+ _logCanSendError(trade, wallet, canSendError);
sendViewModel.state = FailureState(canSendError);
return;
}
-
final selected = trade.from;
if (selected == null) {
printV('No selectable currency for trade ${trade.id}');
@@ -354,62 +354,55 @@ abstract class ExchangeTradeViewModelBase with Store {
}
String? checkIfCanSend(Trade? trade, WalletBase wallet) {
- try {
+ if (trade == null) return 'Trade is null';
- if (trade == null) throw Exception('Trade is null');
-
- final tradeFrom = trade.from;
- if (tradeFrom == null) throw Exception('Trade from currency is null');
+ final tradeFrom = trade.from;
+ if (tradeFrom == null) return 'Trade from currency is null';
- bool _sameCurrency(CryptoCurrency a, CryptoCurrency b) => a.titleAndTagEqual(b);
+ bool _sameCurrency(CryptoCurrency a, CryptoCurrency b) => a.titleAndTagEqual(b);
- bool _isTokenBelongingToWallet(CryptoCurrency cur) {
- final chainTag = cur.tag ?? cur.title;
- return wallet.currency == cur &&
- tradeFrom.tag?.toUpperCase() == chainTag.toUpperCase();
- }
+ bool _isTokenBelongingToWallet(CryptoCurrency cur) {
+ final chainTag = cur.tag ?? cur.title;
+ return wallet.currency == cur &&
+ (tradeFrom.tag?.toUpperCase() == chainTag.toUpperCase() ||
+ tradeFrom.title.toUpperCase() == chainTag.toUpperCase());
+ }
- 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);
-
- 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.',
- );
- }
+ 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);
+
+ if (!canSend) {
+ return 'Wallet currency ${wallet.currency.title} does not match trade from currency ${tradeFrom.title} or is not a supported token for this wallet.';
+ }
- return null;
- } catch (e, s) {
- final trade = tradesStore.trade;
-
- 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 null;
+ }
- return e.toString();
- }
+ void _logCanSendError(Trade? trade, WalletBase wallet, String error) {
+ ExchangeProviderLogger.logError(
+ provider: trade?.provider,
+ function: '_checkIfCanSend',
+ error: error,
+ 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,
+ },
+ );
}
static bool _checkIfSwapsXYZCanSendFromExternal(Trade trade, WalletBase wallet) {
Why this scored 44/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.