mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-22 06:28:39 +02:00
CW-1175-Implement-swaps.xyz-swap-provider (#2526)
* Integrate SwapsXYZ * Remove print * enhance SwapsXyz EVM token swap handling * Disable fixed rate support in SwapsXyz provider * enable fixed rate swap * fix timestamp parsing * remove sender and recipient address from fetchRate * Improve SwapsXYZ trade approval logic * fix trade ID overflow * add Base chain support for swaps xyz * Update lib/view_model/exchange/exchange_trade_view_model.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * add missing support for Base --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -181,6 +181,11 @@ abstract class DashboardViewModelBase with Store {
|
||||
caption: ExchangeProviderDescription.swapTrade.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapTrade)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displaySwapXyz,
|
||||
caption: ExchangeProviderDescription.swapsXyz.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)),
|
||||
]
|
||||
},
|
||||
subname = '',
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
@@ -80,6 +81,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
break;
|
||||
case ExchangeProviderDescription.xoSwap:
|
||||
_provider = XOSwapExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.swapsXyz:
|
||||
_provider = SwapsXyzExchangeProvider();
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
@@ -104,6 +109,15 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
@observable
|
||||
bool isSendable;
|
||||
|
||||
|
||||
bool get isSwapsXyzSendingEVMTokenSwap => (_provider is SwapsXyzExchangeProvider) &&
|
||||
(wallet.type == WalletType.ethereum &&
|
||||
wallet.currency != trade.from ||
|
||||
(wallet.type == WalletType.polygon &&
|
||||
wallet.currency != trade.from) ||
|
||||
(wallet.type == WalletType.base &&
|
||||
wallet.currency != trade.from));
|
||||
|
||||
String get extraInfo => trade.extraId != null && trade.extraId!.isNotEmpty
|
||||
? '\n\n' + S.current.exchange_extra_info
|
||||
: '';
|
||||
@@ -189,7 +203,19 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
|
||||
sendViewModel.selectedCryptoCurrency = selected;
|
||||
|
||||
final pendingTransaction = await sendViewModel.createTransaction(provider: _provider);
|
||||
final pendingTransaction = await sendViewModel.createTransaction(provider: _provider, trade: trade);
|
||||
|
||||
if (_provider is SwapsXyzExchangeProvider) {
|
||||
final hash = pendingTransaction?.evmTxHashFromRawHex ?? pendingTransaction?.id ?? '';
|
||||
trade.txId = hash;
|
||||
|
||||
if (trade.isInBox) {
|
||||
await trade.save();
|
||||
} else {
|
||||
await trades.add(trade);
|
||||
}
|
||||
}
|
||||
|
||||
if (_provider is ThorChainExchangeProvider) {
|
||||
trade.id = pendingTransaction?.id ?? '';
|
||||
trades.add(trade);
|
||||
@@ -308,7 +334,7 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
|
||||
|
||||
bool _isBaseToken() =>
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.title;
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.tag;
|
||||
|
||||
bool _isTronToken() =>
|
||||
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
|
||||
@@ -324,6 +350,53 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
_isTronToken() ||
|
||||
_isBaseToken();
|
||||
}
|
||||
|
||||
Future<void> registerSwapsXyzTransaction() async {
|
||||
try {
|
||||
if (!(_provider is SwapsXyzExchangeProvider)) return;
|
||||
final swaps = _provider as SwapsXyzExchangeProvider;
|
||||
|
||||
// register only for vmId is alt-vm or bridgeId is alt-vm (trade.needToRegisterInSwapXyz)
|
||||
final needToRegister = trade.needToRegisterInSwapXyz ?? false;
|
||||
if (!needToRegister) return;
|
||||
|
||||
final vmId = (trade.providerId ?? '').toLowerCase();
|
||||
if (vmId.isEmpty) {
|
||||
printV('SwapsXyz: transaction register: skipped (vmId empty)');
|
||||
return;
|
||||
}
|
||||
|
||||
final txHash = sendViewModel.pendingTransaction?.evmTxHashFromRawHex ?? sendViewModel.pendingTransaction?.id ?? '';
|
||||
|
||||
if (txHash.isEmpty) {
|
||||
printV('SwapsXyz: transaction register: skipped (txHash empty)');
|
||||
return;
|
||||
}
|
||||
|
||||
final chainId = int.tryParse(trade.router ?? '') ?? 0;
|
||||
if (chainId <= 0) {
|
||||
printV('SwapsXyz: transaction register: skipped (invalid chainId)');
|
||||
return;
|
||||
}
|
||||
|
||||
printV('SwapsXyz: attempting to register transaction: tradeId = ${trade.id}, txHash = $txHash, chainId = $chainId, vmId = $vmId');
|
||||
|
||||
final registered = await swaps.registerAltVmTx(
|
||||
txId: trade.id,
|
||||
txHash: txHash,
|
||||
chainId: chainId,
|
||||
vmId: vmId,
|
||||
);
|
||||
|
||||
if (!registered) {
|
||||
printV('SwapsXyz: transaction register: failed');
|
||||
} else {
|
||||
printV('SwapsXyz: transaction register: success');
|
||||
}
|
||||
} catch (e) {
|
||||
printV('registerSwapsXyzTransaction error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
PaymentURI? get paymentUri {
|
||||
final inputAddress = trade.inputAddress;
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
|
||||
@@ -214,6 +215,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
LetsExchangeExchangeProvider(),
|
||||
StealthExExchangeProvider(),
|
||||
XOSwapExchangeProvider(),
|
||||
SwapsXyzExchangeProvider(),
|
||||
TrocadorExchangeProvider(
|
||||
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
|
||||
];
|
||||
@@ -536,7 +538,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
to: receiveCurrency,
|
||||
amount: amount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: isFixedRateMode,
|
||||
isReceiveAmount: isFixedRateMode
|
||||
)
|
||||
.timeout(
|
||||
Duration(seconds: 7),
|
||||
|
||||
@@ -22,7 +22,9 @@ import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/wallet_contact.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
@@ -46,6 +48,7 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_mod
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/exceptions.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
@@ -119,6 +122,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
});
|
||||
}
|
||||
|
||||
PendingTransaction? _pendingApprovalTx;
|
||||
bool _isSwapsXYZCallDataTx = false;
|
||||
|
||||
@observable
|
||||
ExecutionState state;
|
||||
|
||||
@@ -133,6 +139,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
bool get isMwebEnabled => balanceViewModel.mwebEnabled;
|
||||
|
||||
bool get isEVMWallet => walletType == WalletType.ethereum || walletType == WalletType.polygon ||
|
||||
walletType == WalletType.base;
|
||||
|
||||
@action
|
||||
void setShowAddressBookPopup(bool value) {
|
||||
_settingsStore.showAddressBookPopupEnabled = value;
|
||||
@@ -490,7 +499,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
Timer? _ledgerTxStateTimer;
|
||||
|
||||
@action
|
||||
Future<PendingTransaction?> createTransaction({ExchangeProvider? provider}) async {
|
||||
Future<PendingTransaction?> createTransaction({ExchangeProvider? provider, Trade? trade}) async {
|
||||
try {
|
||||
if (!(state is IsExecutingState)) state = IsExecutingState();
|
||||
|
||||
@@ -505,6 +514,151 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Swaps.xyz (EVM) path
|
||||
|
||||
if (isEVMWallet && trade != null && provider is SwapsXyzExchangeProvider) {
|
||||
final routerTo = trade.inputAddress;
|
||||
final routerData = trade.routerData;
|
||||
final routerValueWei =
|
||||
BigInt.tryParse((trade.routerValue ?? '0').toString()) ?? BigInt.zero;
|
||||
|
||||
if (routerTo?.isNotEmpty == true && routerData?.isNotEmpty == true) {
|
||||
|
||||
// detect prepared ERC-20 transfer(...) (alt-vm deposit pattern)
|
||||
String _selector(String s) =>
|
||||
(s.startsWith('0x') && s.length >= 10) ? s.substring(0, 10) : '';
|
||||
const _transferSig = '0xa9059cbb';
|
||||
final _sel = _selector(routerData!);
|
||||
final _isPreparedTransfer = _sel == _transferSig &&
|
||||
(trade.sourceTokenAddress ?? '').toLowerCase() == (routerTo ?? '').toLowerCase();
|
||||
|
||||
_pendingApprovalTx = null;
|
||||
|
||||
// Optionally prebuild approval (SKIP for prepared transfer)
|
||||
final tokenContract = trade.sourceTokenAddress ?? '';
|
||||
final requiredAmount = BigInt.tryParse(
|
||||
(trade.sourceTokenAmountRaw ?? '0').replaceAll('n', ''),
|
||||
) ?? BigInt.zero;
|
||||
|
||||
// Only do approval when NOT a prepared transfer, and only if the API hinted we might need it
|
||||
final requiresTokenApproval = (trade.requiresTokenApproval ?? false) && !_isPreparedTransfer;
|
||||
|
||||
if (requiresTokenApproval && tokenContract.isNotEmpty && requiredAmount > BigInt.zero) {
|
||||
if (walletType == WalletType.ethereum) {
|
||||
final priority = _settingsStore.priority[WalletType.ethereum]!;
|
||||
_pendingApprovalTx = await buildApprovalIfNeeded(
|
||||
spender: routerTo!, // if API provides a specific spender, use that instead
|
||||
tokenContract: tokenContract,
|
||||
requiredAmount: requiredAmount,
|
||||
sourceTokenDecimals: trade.sourceTokenDecimals,
|
||||
);
|
||||
|
||||
// Build the callData tx
|
||||
pendingTransaction = await ethereum!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction; // do NOT fall back to regular flow
|
||||
}
|
||||
if (walletType == WalletType.polygon) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
_pendingApprovalTx = await buildApprovalIfNeeded(
|
||||
spender: routerTo!,
|
||||
tokenContract: tokenContract,
|
||||
requiredAmount: requiredAmount,
|
||||
sourceTokenDecimals: trade.sourceTokenDecimals,
|
||||
);
|
||||
|
||||
// Build the callData tx
|
||||
pendingTransaction = await polygon!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction; // do NOT fall back to regular flow
|
||||
}
|
||||
if (walletType == WalletType.base) {
|
||||
final priority = _settingsStore.priority[WalletType.base]!;
|
||||
_pendingApprovalTx = await buildApprovalIfNeeded(
|
||||
spender: routerTo!,
|
||||
tokenContract: tokenContract,
|
||||
requiredAmount: requiredAmount,
|
||||
sourceTokenDecimals: trade.sourceTokenDecimals,
|
||||
);
|
||||
|
||||
// Build the callData tx
|
||||
pendingTransaction = await base!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction; // do NOT fall back to regular flow
|
||||
}
|
||||
}
|
||||
|
||||
// No approval needed (or prepared transfer): send exactly what backend prepared
|
||||
if (walletType == WalletType.ethereum) {
|
||||
final priority = _settingsStore.priority[WalletType.ethereum]!;
|
||||
pendingTransaction = await ethereum!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo!,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
if (walletType == WalletType.polygon) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
pendingTransaction = await polygon!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo!,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
if (walletType == WalletType.base) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
pendingTransaction = await base!.createRawCallDataTransaction(
|
||||
wallet,
|
||||
routerTo!,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
priority,
|
||||
);
|
||||
_isSwapsXYZCallDataTx = true;
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Regular flow
|
||||
|
||||
pendingTransaction = await wallet.createTransaction(_credentials(provider));
|
||||
|
||||
if (provider is ThorChainExchangeProvider) {
|
||||
@@ -602,6 +756,37 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return;
|
||||
}
|
||||
|
||||
// Swaps.xyz approval (if any), then commit the prebuilt router tx
|
||||
if (_isSwapsXYZCallDataTx) {
|
||||
if (_pendingApprovalTx != null) {
|
||||
await _pendingApprovalTx!.commit();
|
||||
_pendingApprovalTx = null;
|
||||
// Small pause to ensure allowance is indexed
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
await pendingTransaction!.commit();
|
||||
_isSwapsXYZCallDataTx = false;
|
||||
|
||||
state = TransactionCommitted();
|
||||
return; // skip the regular flow below
|
||||
}
|
||||
|
||||
// Regular flow (non-Swaps)
|
||||
if (pendingTransaction!.shouldCommitUR()) {
|
||||
final urstr = await pendingTransaction!.commitUR();
|
||||
final result = await Navigator.of(context).pushNamed(
|
||||
Routes.urqrAnimatedPage,
|
||||
arguments: urstr,
|
||||
);
|
||||
if (result == null) {
|
||||
state = FailureState("Canceled by user");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await pendingTransaction!.commit();
|
||||
}
|
||||
|
||||
String address = outputs.fold('', (acc, value) {
|
||||
return value.isParsedAddress
|
||||
? '$acc${value.address}\n${value.extractedAddress}\n\n'
|
||||
@@ -933,6 +1118,71 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<PendingTransaction?> buildApprovalIfNeeded({
|
||||
required String spender,
|
||||
required String tokenContract,
|
||||
required BigInt requiredAmount,
|
||||
int? sourceTokenDecimals,
|
||||
}) async {
|
||||
|
||||
// Only EVM chains support ERC20 approvals
|
||||
if (!isEVMWallet) return null;
|
||||
|
||||
const zero = '0x0000000000000000000000000000000000000000';
|
||||
const evmNative = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
final tokenLc = tokenContract.toLowerCase();
|
||||
if (tokenLc == zero || tokenLc == evmNative.toLowerCase()) return null;
|
||||
if (requiredAmount <= BigInt.zero) return null;
|
||||
|
||||
bool needsApproval = false;
|
||||
if (walletType == WalletType.ethereum) {
|
||||
needsApproval = await ethereum!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
);
|
||||
} else if (walletType == WalletType.polygon) {
|
||||
needsApproval = await polygon!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
);
|
||||
} else if (walletType == WalletType.base) {
|
||||
needsApproval = await base!.isApprovalRequired(
|
||||
wallet, tokenContract, spender, requiredAmount,
|
||||
);
|
||||
}
|
||||
|
||||
if (!needsApproval) return null;
|
||||
|
||||
final erc20Token = wallet.balance.keys.whereType<Erc20Token>().firstWhere(
|
||||
(t) => t.contractAddress.toLowerCase() == tokenLc,
|
||||
orElse: () => Erc20Token(
|
||||
name: '',
|
||||
symbol: '',
|
||||
contractAddress: tokenContract,
|
||||
decimal: sourceTokenDecimals ?? 18,
|
||||
enabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
if (walletType == WalletType.ethereum) {
|
||||
final priority = _settingsStore.priority[WalletType.ethereum]!;
|
||||
return await ethereum!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
);
|
||||
} else if (walletType == WalletType.polygon) {
|
||||
final priority = _settingsStore.priority[WalletType.polygon]!;
|
||||
return await polygon!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
);
|
||||
} else if (walletType == WalletType.base) {
|
||||
final priority = _settingsStore.priority[WalletType.base]!;
|
||||
return await base!.createTokenApproval(
|
||||
wallet, requiredAmount, spender, erc20Token, priority,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@computed
|
||||
bool get usePayjoin => _settingsStore.usePayjoin;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swapsxyz_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
@@ -77,6 +78,9 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
case ExchangeProviderDescription.xoSwap:
|
||||
_provider = XOSwapExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.swapsXyz:
|
||||
_provider = SwapsXyzExchangeProvider();
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
@@ -134,8 +138,13 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
try {
|
||||
final updatedTrade = await _provider!.findTradeById(id: trade.id);
|
||||
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null)
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null) {
|
||||
updatedTrade.createdAt = trade.createdAt;
|
||||
}
|
||||
|
||||
if (updatedTrade.toRaw == -1 && trade.toRaw != -1) {
|
||||
updatedTrade.toRaw = trade.toRaw;
|
||||
}
|
||||
|
||||
Trade? foundElement = trades.values.firstWhereOrNull((element) => element.id == trade.id);
|
||||
if (foundElement != null) {
|
||||
@@ -205,6 +214,11 @@ abstract class TradeDetailsViewModelBase with Store {
|
||||
title: '${trade.providerName} ${S.current.password}', value: trade.password ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
if (trade.provider == ExchangeProviderDescription.swapsXyz && trade.txId != null && trade.txId!.isNotEmpty) {
|
||||
items.add(StandartListItem(
|
||||
title: 'Transaction ID', value: trade.txId!));
|
||||
}
|
||||
}
|
||||
|
||||
void _launchUrl(String url) {
|
||||
|
||||
Reference in New Issue
Block a user