refactor send validation and near token parsing (#3351)

* refactor send validation and near token parsing

* fix exchange send validation[skip ci]
This commit is contained in:
Serhii
2026-07-13 04:23:10 +03:00
committed by GitHub
parent 49f5a24e77
commit 0cf80439ec
2 changed files with 72 additions and 47 deletions
@@ -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<ExchangeTradeItem>() {
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<Type> _providersThatHideExternalSend = [
JupiterExchangeProvider,
@@ -212,7 +211,13 @@ abstract class ExchangeTradeViewModelBase with Store {
@action
Future<void> 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) {