mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-31 06:58:42 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hash_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:hash_wallet/core/execution_state.dart';
|
||||
import 'package:hash_wallet/entities/calculate_fiat_amount.dart';
|
||||
import 'package:hash_wallet/entities/fiat_currency.dart';
|
||||
import 'package:hash_wallet/exchange/exchange_provider_description.dart';
|
||||
@@ -13,6 +14,7 @@ import 'package:hash_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:hash_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:hash_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:hash_wallet/utils/exchange_provider_logger.dart';
|
||||
import 'package:hash_wallet/utils/qr_util.dart';
|
||||
import 'package:hash_wallet/utils/token_utilities.dart';
|
||||
import 'package:hash_wallet/view_model/send/fees_view_model.dart';
|
||||
@@ -37,10 +39,8 @@ 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>() {
|
||||
_checkIfSwapsXYZCanSendFromExternal(tradesStore.trade!, wallet) {
|
||||
setUpOutput();
|
||||
// Trades from providers whose implementations were removed keep a null
|
||||
// _provider; status polling is simply skipped for them.
|
||||
@@ -53,7 +53,6 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
|
||||
if (_provider != null) {
|
||||
_updateTrade();
|
||||
@@ -73,11 +72,10 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
@observable
|
||||
Trade trade;
|
||||
|
||||
@observable
|
||||
bool isSendable;
|
||||
|
||||
bool isSwapsXYZCanSendFromExternal;
|
||||
|
||||
bool get isSendable => checkIfCanSend(trade, wallet) == null;
|
||||
|
||||
/// Returns true if the current provider should hide the external send button
|
||||
bool get shouldHideExternalSendButton {
|
||||
if (_provider == null) return false;
|
||||
@@ -101,8 +99,6 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
? ""
|
||||
: "${sendViewModel.pendingTransactionFeeFiatAmount} ${sendViewModel.fiat.title}";
|
||||
|
||||
@observable
|
||||
ObservableList<ExchangeTradeItem> items;
|
||||
|
||||
ExchangeProvider? _provider;
|
||||
|
||||
@@ -161,7 +157,13 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
|
||||
@action
|
||||
Future<void> confirmSending() async {
|
||||
if (!isSendable) return;
|
||||
final canSendError = checkIfCanSend(trade, wallet);
|
||||
|
||||
if (canSendError != null) {
|
||||
_logCanSendError(trade, wallet, canSendError);
|
||||
sendViewModel.state = FailureState(canSendError);
|
||||
return;
|
||||
}
|
||||
|
||||
final selected = trade.from;
|
||||
if (selected == null) {
|
||||
@@ -183,150 +185,60 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
await trade.save();
|
||||
tradesStore.setTrade(trade);
|
||||
|
||||
_updateItems();
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _updateItems() {
|
||||
String? checkIfCanSend(Trade? trade, WalletBase wallet) {
|
||||
if (trade == null) return 'Trade is null';
|
||||
|
||||
final tradeFrom = trade.from;
|
||||
final tradeTo = trade.to;
|
||||
if (tradeFrom == null) return 'Trade from currency is null';
|
||||
|
||||
final tagFrom = tradeFrom?.tag != null ? "${tradeFrom!.tag} " : "";
|
||||
final tagTo = tradeTo?.tag != null ? "${tradeTo!.tag} " : "";
|
||||
bool _sameCurrency(CryptoCurrency a, CryptoCurrency b) => a.titleAndTagEqual(b);
|
||||
|
||||
items.clear();
|
||||
|
||||
if (trade.provider != ExchangeProviderDescription.thorChain)
|
||||
items.add(
|
||||
ExchangeTradeItem(
|
||||
title: "${trade.provider.title} ${S.current.id}",
|
||||
data: "${trade.id}",
|
||||
isCopied: true,
|
||||
isReceiveDetail: true,
|
||||
isExternalSendDetail: false,
|
||||
),
|
||||
);
|
||||
|
||||
if (tradeFrom != null && tradeTo != null) {
|
||||
items.addAll([
|
||||
ExchangeTradeItem(
|
||||
title: S.current.amount,
|
||||
data:
|
||||
"${_amountParsingProxy.getDisplayCryptoAmount(trade.amount, tradeFrom)} ${_amountParsingProxy.getCryptoSymbol(tradeFrom)}",
|
||||
isCopied: false,
|
||||
isReceiveDetail: false,
|
||||
isExternalSendDetail: true,
|
||||
),
|
||||
ExchangeTradeItem(
|
||||
title: "${S.current.you_will_receive_estimated_amount}:",
|
||||
data:
|
||||
"${_amountParsingProxy.getDisplayCryptoAmount(trade.receiveAmount ?? "0", tradeTo)} ${_amountParsingProxy.getCryptoSymbol(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,
|
||||
),
|
||||
]);
|
||||
|
||||
items.add(
|
||||
isSwapsXYZCanSendFromExternal
|
||||
? ExchangeTradeItem(
|
||||
title: S.current.send_to_this_address('${tradeFrom}', tagFrom) + ':',
|
||||
data: trade.inputAddress ?? '',
|
||||
isCopied: false,
|
||||
isReceiveDetail: false,
|
||||
isExternalSendDetail: true)
|
||||
: ExchangeTradeItem(
|
||||
title: 'Smart contract call (no address required)',
|
||||
data: 'Wallet will execute a contract call. On-chain transaction',
|
||||
isCopied: false,
|
||||
isReceiveDetail: false,
|
||||
isExternalSendDetail: true),
|
||||
);
|
||||
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 isExtraIdExist = trade.extraId != null && trade.extraId!.isNotEmpty;
|
||||
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 (isExtraIdExist) {
|
||||
final title = tradeFrom == CryptoCurrency.xrp
|
||||
? S.current.destination_tag
|
||||
: [CryptoCurrency.xlm, CryptoCurrency.ton].contains(tradeFrom)
|
||||
? S.current.memo
|
||||
: S.current.extra_id;
|
||||
|
||||
items.add(
|
||||
ExchangeTradeItem(
|
||||
title: title,
|
||||
data: trade.extraId ?? "",
|
||||
isCopied: true,
|
||||
isReceiveDetail: !isExtraIdExist,
|
||||
isExternalSendDetail: isExtraIdExist,
|
||||
),
|
||||
);
|
||||
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.';
|
||||
}
|
||||
|
||||
items.add(
|
||||
ExchangeTradeItem(
|
||||
title: "${S.current.arrive_in_this_address("${tradeTo}", tagTo)}:",
|
||||
data: trade.payoutAddress ?? "",
|
||||
isCopied: true,
|
||||
isReceiveDetail: true,
|
||||
isExternalSendDetail: false,
|
||||
),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool _checkIfCanSend(TradesStore tradesStore, WalletBase wallet) {
|
||||
final trade = tradesStore.trade!;
|
||||
final tradeFrom = trade.from;
|
||||
|
||||
bool _sameCurrency(CryptoCurrency? a, CryptoCurrency? b) =>
|
||||
a != null && b != null && a.titleAndTagEqual(b);
|
||||
|
||||
bool _isEthToken() =>
|
||||
wallet.currency == CryptoCurrency.eth && tradeFrom?.tag == CryptoCurrency.eth.title;
|
||||
|
||||
bool _isPolygonToken() =>
|
||||
wallet.currency == CryptoCurrency.maticpoly &&
|
||||
tradeFrom?.tag == CryptoCurrency.maticpoly.tag;
|
||||
|
||||
bool _isBaseToken() =>
|
||||
wallet.currency == CryptoCurrency.baseEth && tradeFrom?.tag == CryptoCurrency.baseEth.tag;
|
||||
|
||||
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
|
||||
|
||||
bool _isTronToken() =>
|
||||
wallet.currency == CryptoCurrency.trx && tradeFrom?.tag == CryptoCurrency.trx.title;
|
||||
|
||||
bool _isSplToken() =>
|
||||
wallet.currency == CryptoCurrency.sol && tradeFrom?.tag == CryptoCurrency.sol.title;
|
||||
|
||||
bool _isBscToken() =>
|
||||
wallet.currency == CryptoCurrency.bnb && tradeFrom?.tag == CryptoCurrency.bnb.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();
|
||||
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) {
|
||||
@@ -349,8 +261,13 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Using trade currency here so the external-send QR encodes the correct scheme
|
||||
final uriWalletType = cryptoCurrencyOrTokenToWalletType(fromCurrency) ?? wallet.type;
|
||||
// Using the trade's `from` currency so the external-send QR encodes the correct scheme.
|
||||
final uriWalletType = cryptoCurrencyOrTokenToWalletType(fromCurrency);
|
||||
|
||||
// for other currencies that we don't have wallets for
|
||||
if (uriWalletType == null) {
|
||||
return ExternalAddressURI(address: inputAddress, amount: amount);
|
||||
}
|
||||
|
||||
printV(uriWalletType);
|
||||
|
||||
@@ -362,16 +279,11 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
case WalletType.dogecoin:
|
||||
return DogeURI(address: inputAddress, amount: amount);
|
||||
case WalletType.ethereum:
|
||||
return _createERC681URI(fromCurrency, inputAddress, amount);
|
||||
// TODO: Expand ERC681URI support to Polygon(modify decoding flow for QRs, pay anything, and deep link handling)
|
||||
case WalletType.polygon:
|
||||
return PolygonURI(amount: amount, address: inputAddress);
|
||||
case WalletType.base:
|
||||
return BaseURI(amount: amount, address: inputAddress);
|
||||
case WalletType.arbitrum:
|
||||
return ArbitrumURI(amount: amount, address: inputAddress);
|
||||
case WalletType.bsc:
|
||||
return BSCURI(amount: amount, address: inputAddress);
|
||||
return _createERC681URI(fromCurrency, inputAddress, amount);
|
||||
case WalletType.monero:
|
||||
return MoneroURI(address: inputAddress, amount: amount);
|
||||
case WalletType.wownero:
|
||||
@@ -408,6 +320,7 @@ abstract class ExchangeTradeViewModelBase with Store {
|
||||
address: address,
|
||||
amount: amount,
|
||||
contractAddress: erc20Token.contractAddress,
|
||||
tokenDecimals: erc20Token.decimal,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -28,16 +28,16 @@ import 'package:hash_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:hash_wallet/exchange/trade.dart';
|
||||
import 'package:hash_wallet/exchange/trade_request.dart';
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
import 'package:hash_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
|
||||
import 'package:hash_wallet/store/app_store.dart';
|
||||
import 'package:hash_wallet/utils/exchange_provider_logger.dart';
|
||||
import 'package:hash_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/amount/money_double.dart';
|
||||
import "package:cw_core/wallet_info.dart";
|
||||
import 'package:hash_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:hash_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:hash_wallet/store/settings_store.dart';
|
||||
import 'package:hash_wallet/store/templates/exchange_template_store.dart';
|
||||
import 'package:hash_wallet/evm/evm.dart';
|
||||
import 'package:hash_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:hash_wallet/utils/token_utilities.dart';
|
||||
@@ -46,6 +46,7 @@ import 'package:hash_wallet/view_model/send/fees_view_model.dart';
|
||||
import 'package:hash_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currencies_with_memo.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/spl_token.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
@@ -83,7 +84,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
ExchangeViewModelBase(
|
||||
this._appStore,
|
||||
this._exchangeTemplateStore,
|
||||
this.tradesStore,
|
||||
this.sharedPreferences,
|
||||
this.contactListViewModel,
|
||||
@@ -93,8 +93,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
) : isSendAllEnabled = false,
|
||||
isFixedRateMode = false,
|
||||
isReceiveAmountEntered = false,
|
||||
_depositAmount = '',
|
||||
_receiveAmount = '',
|
||||
_depositAmount = null,
|
||||
_receiveAmount = null,
|
||||
receiveAddress = '',
|
||||
depositAddress = '',
|
||||
isDepositAddressEnabled = false,
|
||||
@@ -192,8 +192,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
});
|
||||
|
||||
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
receiveAddress = '';
|
||||
depositAddress =
|
||||
useSameWalletAddress(depositCurrency) ? wallet.walletAddresses.addressForExchange : '';
|
||||
@@ -261,12 +261,11 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
final balance = wallet.balance[depositCurrency];
|
||||
if (balance != null) {
|
||||
depositAvailableAmount = _appStore.amountParsingProxy
|
||||
.getDisplayCryptoStringFromBigInt(balance.fullAvailableBalance, depositCurrency);
|
||||
depositAvailableAmount = _appStore.amountParsingProxy.asDisplayString(balance.available);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}).timeout(Duration(seconds: 20));
|
||||
} else if (isEVMCompatibleChain(wallet.type)) {
|
||||
final currency = depositCurrency;
|
||||
final balanceCurrency = wallet.balance.keys.firstWhereOrNull(
|
||||
@@ -276,14 +275,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
);
|
||||
final balanceForCurrency = balanceCurrency != null ? wallet.balance[balanceCurrency] : null;
|
||||
if (depositCurrency == currency && balanceForCurrency != null) {
|
||||
depositAvailableAmount = _appStore.amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
balanceForCurrency.fullAvailableBalance, balanceCurrency!);
|
||||
depositAvailableAmount =
|
||||
_appStore.amountParsingProxy.asDisplayStringWithSymbol(balanceForCurrency.available);
|
||||
}
|
||||
} else {
|
||||
final currency = depositCurrency;
|
||||
final amount = _appStore.amountParsingProxy.getDisplayCryptoString(
|
||||
(await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any)),
|
||||
depositCurrency);
|
||||
final sendingBalance = Money.fromInt(
|
||||
await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any), currency);
|
||||
final amount = _appStore.amountParsingProxy.asDisplayStringWithSymbol(sendingBalance);
|
||||
if (depositCurrency == currency) {
|
||||
depositAvailableAmount = amount;
|
||||
}
|
||||
@@ -291,17 +290,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
void showFiatCurrencyPicker(BuildContext context) {
|
||||
showPopUp<void>(
|
||||
FiatCurrencyPickerSheet.show(
|
||||
context: context,
|
||||
builder: (_) => CurrencyPicker(
|
||||
key: ValueKey('send_page_currency_picker_dialog_button_key'),
|
||||
selectedAtIndex: FiatCurrency.all.indexOf(fiat),
|
||||
items: FiatCurrency.all,
|
||||
hintText: S.of(context).search_currency,
|
||||
onItemSelected: (Currency cur) async {
|
||||
_settingsStore.fiatCurrency = (cur as FiatCurrency);
|
||||
},
|
||||
),
|
||||
selected: fiat,
|
||||
onSelected: (cur) => _settingsStore.fiatCurrency = cur,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -321,10 +313,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
].contains(wallet.type);
|
||||
|
||||
bool get hideAddressAfterExchange =>
|
||||
[WalletType.monero, WalletType.wownero].contains(wallet.type);
|
||||
[WalletType.monero, WalletType.wownero].contains(wallet.type) || isElectrumWallet;
|
||||
|
||||
bool _useTorOnly;
|
||||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||||
final TradesStore tradesStore;
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
@@ -378,27 +369,29 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
ExchangeTradeState tradeState;
|
||||
|
||||
@observable
|
||||
String _depositAmount;
|
||||
Money? _depositAmount;
|
||||
|
||||
@computed
|
||||
String get depositAmount => _depositAmount == "..."
|
||||
? _depositAmount
|
||||
: amountParsingProxy.getDisplayCryptoAmount(_depositAmount, depositCurrency);
|
||||
bool get hasDepositAmount => _depositAmount != null;
|
||||
|
||||
@computed
|
||||
String get depositAmountCanonical => _depositAmount;
|
||||
String get depositAmount =>
|
||||
_depositAmount == null ? "" : amountParsingProxy.asDisplayString(_depositAmount!);
|
||||
|
||||
@computed
|
||||
String get depositAmountCanonical => _depositAmount == null ? "0.0" : _depositAmount.toString();
|
||||
|
||||
@observable
|
||||
String _receiveAmount;
|
||||
Money? _receiveAmount;
|
||||
|
||||
@computed
|
||||
String get receiveAmount => _receiveAmount == "..."
|
||||
? _receiveAmount
|
||||
: amountParsingProxy.getDisplayCryptoAmount(_receiveAmount, receiveCurrency);
|
||||
String get receiveAmount =>
|
||||
_receiveAmount == null ? "" : amountParsingProxy.asDisplayString(_receiveAmount!);
|
||||
|
||||
@action
|
||||
// only set canonical formated amounts here;
|
||||
void setCanonicalReceiveAmount(String value) => _receiveAmount = value;
|
||||
void setCanonicalReceiveAmount(String value) =>
|
||||
_receiveAmount = Money.tryParse(value, receiveCurrency);
|
||||
|
||||
@observable
|
||||
String depositAddress;
|
||||
@@ -406,6 +399,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
@observable
|
||||
String receiveAddress;
|
||||
|
||||
@observable
|
||||
String receiveAddressExtraId = '';
|
||||
|
||||
@observable
|
||||
String? receiveAddressDisplayName;
|
||||
|
||||
@@ -430,14 +426,15 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
@observable
|
||||
bool tradeStarted = false;
|
||||
|
||||
@observable
|
||||
bool noProviderForPair = false;
|
||||
|
||||
@observable
|
||||
bool isSendFromExternal = false;
|
||||
|
||||
@computed
|
||||
SyncStatus get status => wallet.syncStatus;
|
||||
|
||||
@computed
|
||||
ObservableList<ExchangeTemplate> get templates => _exchangeTemplateStore.templates;
|
||||
|
||||
@computed
|
||||
List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
|
||||
@@ -451,14 +448,18 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@computed
|
||||
Future<List<WalletInfo>> get receiveWallets async {
|
||||
WalletType? type;
|
||||
type = cryptoCurrencyOrTokenToWalletType(receiveCurrency);
|
||||
if (type == null) {
|
||||
type =
|
||||
cryptoCurrencyOrTokenToWalletType(CryptoCurrency.fromString(receiveCurrency.tag ?? ""));
|
||||
}
|
||||
try {
|
||||
WalletType? type;
|
||||
type = cryptoCurrencyOrTokenToWalletType(receiveCurrency);
|
||||
if (type == null) {
|
||||
type =
|
||||
cryptoCurrencyOrTokenToWalletType(CryptoCurrency.fromString(receiveCurrency.tag ?? ""));
|
||||
}
|
||||
|
||||
return await WalletInfo.selectList("type = ?", [type!.index]);
|
||||
return await WalletInfo.selectList("type = ?", [type!.index]);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<WalletInfoAddressInfo>> addressesForAccountsWallet(WalletInfo wallet) async {
|
||||
@@ -517,11 +518,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
} else {
|
||||
balanceCurrency = depositCurrency;
|
||||
}
|
||||
final bal =
|
||||
balanceCurrency != null ? wallet.balance[balanceCurrency]?.fullAvailableBalance : null;
|
||||
final bal = balanceCurrency != null ? wallet.balance[balanceCurrency]?.available : null;
|
||||
if (bal == null) return null;
|
||||
return amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
bal, balanceCurrency ?? depositCurrency);
|
||||
return amountParsingProxy.asDisplayString(bal);
|
||||
}
|
||||
|
||||
//* Still open to further optimize these checks
|
||||
@@ -596,10 +595,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
double bestRate = 0.0;
|
||||
|
||||
@computed
|
||||
bool get useSatoshiDeposit => _appStore.amountParsingProxy.useSatoshi(depositCurrency);
|
||||
bool get useDepositBaseUnit => _appStore.amountParsingProxy.useSatoshi(depositCurrency);
|
||||
|
||||
@computed
|
||||
bool get useSatoshisReceive => _appStore.amountParsingProxy.useSatoshi(receiveCurrency);
|
||||
bool get useReceiveBaseUnit => _appStore.amountParsingProxy.useSatoshi(receiveCurrency);
|
||||
|
||||
@computed
|
||||
AmountParsingProxy get amountParsingProxy => _appStore.amountParsingProxy;
|
||||
@@ -648,12 +647,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
String get receiveAmountFiatFormatted {
|
||||
var amount = '0.00';
|
||||
try {
|
||||
if (_receiveAmount.isNotEmpty) {
|
||||
if (_receiveAmount != null) {
|
||||
if (fiatConversionStore.prices[receiveCurrency] == null) return '';
|
||||
|
||||
amount = calculateFiatAmount(
|
||||
price: fiatConversionStore.prices[receiveCurrency]!,
|
||||
cryptoAmount: _receiveAmount,
|
||||
cryptoAmount: _receiveAmount.toString(),
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -666,12 +665,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
String get depositAmountFiatFormatted {
|
||||
var amount = '0.00';
|
||||
try {
|
||||
if (_depositAmount.isNotEmpty) {
|
||||
if (_depositAmount != null) {
|
||||
if (fiatConversionStore.prices[depositCurrency] == null) return '';
|
||||
|
||||
amount = calculateFiatAmount(
|
||||
price: fiatConversionStore.prices[depositCurrency]!,
|
||||
cryptoAmount: _depositAmount,
|
||||
cryptoAmount: _depositAmount.toString(),
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -684,12 +683,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
String get receiveAmountFiat {
|
||||
var amount = '';
|
||||
try {
|
||||
if (_receiveAmount.isNotEmpty) {
|
||||
if (_receiveAmount != null) {
|
||||
if (fiatConversionStore.prices[receiveCurrency] == null) return '';
|
||||
|
||||
amount = calculateFiatAmount(
|
||||
price: fiatConversionStore.prices[receiveCurrency]!,
|
||||
cryptoAmount: _receiveAmount,
|
||||
cryptoAmount: _receiveAmount.toString(),
|
||||
raw: true);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -702,13 +701,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
String get depositAmountFiat {
|
||||
var amount = '';
|
||||
try {
|
||||
if (_depositAmount.isNotEmpty) {
|
||||
if (_depositAmount != null) {
|
||||
if (fiatConversionStore.prices[depositCurrency] == null) return '';
|
||||
|
||||
amount = calculateFiatAmount(
|
||||
price: fiatConversionStore.prices[depositCurrency]!,
|
||||
cryptoAmount: _depositAmount,
|
||||
raw: true);
|
||||
price: fiatConversionStore.prices[depositCurrency]!,
|
||||
cryptoAmount: _depositAmount.toString(),
|
||||
raw: true,
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
log('Error calculating deposit amount fiat formatted: $_');
|
||||
@@ -780,6 +780,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
currency == receiveCurrency)) {
|
||||
receiveAddress = "";
|
||||
}
|
||||
if (currency != receiveCurrency) {
|
||||
receiveAddressExtraId = "";
|
||||
}
|
||||
|
||||
receiveCurrency = currency;
|
||||
isFixedRateMode = false;
|
||||
@@ -792,32 +795,37 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> changeReceiveAmount({required String amount}) async {
|
||||
amount = amountParsingProxy.getCanonicalCryptoAmount(amount, receiveCurrency);
|
||||
|
||||
_receiveAmount = amount;
|
||||
|
||||
Future<void> changeReceiveAmount({required String amount, bool isCanonical = false}) async {
|
||||
if (amount.isEmpty) {
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final _enteredAmount = double.tryParse(_receiveAmount.replaceAll(',', '.')) ?? 0;
|
||||
_receiveAmount = isCanonical
|
||||
? Money.tryParse(amount.sanitized(), receiveCurrency)
|
||||
: _appStore.amountParsingProxy.tryParseCryptoString(amount.sanitized(), receiveCurrency);
|
||||
|
||||
if (_receiveAmount == null) {
|
||||
_depositAmount = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final _enteredAmount = double.tryParse(_receiveAmount.toString()) ?? 0;
|
||||
|
||||
if (bestRate == 0) {
|
||||
_depositAmount = "...";
|
||||
_depositAmount = null;
|
||||
|
||||
await calculateBestRate();
|
||||
}
|
||||
|
||||
final amount_ = _enteredAmount / (forcedProvider == null ? bestRate : forcedProviderRate);
|
||||
_depositAmount = amount_.toString().withMaxDecimals(depositCurrency.decimals);
|
||||
_depositAmount = amount_.tryToMoney(depositCurrency);
|
||||
}
|
||||
|
||||
@action
|
||||
void setReceiveAmountFromFiat({required String fiatAmount}) {
|
||||
final _enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0.0;
|
||||
final _enteredAmount = double.tryParse(fiatAmount.sanitized()) ?? 0.0;
|
||||
final price = fiatConversionStore.prices[receiveCurrency];
|
||||
if (price == null || price == 0.0) return;
|
||||
|
||||
@@ -830,7 +838,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
void setDepositAmountFromFiat({required String fiatAmount}) {
|
||||
final _enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0.0;
|
||||
final _enteredAmount = double.tryParse(fiatAmount.sanitized()) ?? 0.0;
|
||||
final price = fiatConversionStore.prices[depositCurrency];
|
||||
if (price == null || price == 0.0) return;
|
||||
|
||||
@@ -843,14 +851,18 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
Future<void> changeDepositAmount({required String amount, bool isCanonical = false}) async {
|
||||
_depositAmount = isCanonical
|
||||
? amount.replaceAll(',', '.')
|
||||
: _appStore.amountParsingProxy
|
||||
.getCanonicalCryptoAmount(amount.replaceAll(',', '.'), depositCurrency);
|
||||
|
||||
if (amount.isEmpty) {
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_depositAmount = isCanonical
|
||||
? Money.tryParse(amount.sanitized(), depositCurrency)
|
||||
: _appStore.amountParsingProxy.tryParseCryptoString(amount.sanitized(), depositCurrency);
|
||||
|
||||
if (_depositAmount == null) {
|
||||
_receiveAmount = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -858,21 +870,21 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
/// as it should remain exactly what the user set
|
||||
if (isFixedRateMode) return;
|
||||
|
||||
final _enteredAmount = double.tryParse(_depositAmount) ?? 0;
|
||||
final _enteredAmount = double.tryParse(_depositAmount.toString()) ?? 0;
|
||||
|
||||
/// in case the best rate was not calculated yet
|
||||
if (bestRate == 0) {
|
||||
_receiveAmount = "...";
|
||||
_receiveAmount = null;
|
||||
|
||||
await calculateBestRate();
|
||||
}
|
||||
|
||||
final amount_ = _enteredAmount * (forcedProvider == null ? bestRate : forcedProviderRate);
|
||||
_receiveAmount = amount_.toString().withMaxDecimals(receiveCurrency.decimals);
|
||||
_receiveAmount = amount_.tryToMoney(receiveCurrency);
|
||||
}
|
||||
|
||||
bool checkIfInputMeetsMinOrMaxCondition(String input) {
|
||||
final _enteredAmount = double.tryParse(input.replaceAll(',', '.')) ?? 0;
|
||||
final _enteredAmount = double.tryParse(input.sanitized()) ?? 0;
|
||||
double minLimit = limits.min ?? 0;
|
||||
double? maxLimit = limits.max;
|
||||
|
||||
@@ -889,8 +901,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
return;
|
||||
}
|
||||
|
||||
final amount = double.tryParse(isFixedRateMode ? _receiveAmount : _depositAmount) ??
|
||||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||||
final amount =
|
||||
double.tryParse(isFixedRateMode ? _receiveAmount.toString() : _depositAmount.toString()) ??
|
||||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||||
|
||||
forcedProviderRate = await forcedProvider!.fetchRate(
|
||||
from: depositCurrency,
|
||||
@@ -905,17 +918,22 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
(provider.description == ExchangeProviderDescription.swapsXyz ||
|
||||
provider.description == ExchangeProviderDescription.nearIntents);
|
||||
|
||||
bool _excludeProviderForReceiveExtraId(ExchangeProvider provider) =>
|
||||
memoLabelTypeFor(receiveCurrency) != null && !provider.supportsMemoOrDestinationTag;
|
||||
|
||||
Future<void> calculateBestRate() async {
|
||||
if (depositCurrency == receiveCurrency) {
|
||||
bestRate = 0.0;
|
||||
bestRateProvider = null;
|
||||
return;
|
||||
}
|
||||
final amount = double.tryParse(isFixedRateMode ? _receiveAmount : _depositAmount) ??
|
||||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||||
final amount =
|
||||
double.tryParse(isFixedRateMode ? _receiveAmount.toString() : _depositAmount.toString()) ??
|
||||
initialAmountByAssets(isFixedRateMode ? receiveCurrency : depositCurrency);
|
||||
|
||||
final validProvidersForAmount = _tradeAvailableProviders.where((provider) {
|
||||
if (_excludeProviderForSwapAll(provider)) return false;
|
||||
if (_excludeProviderForReceiveExtraId(provider)) return false;
|
||||
|
||||
final limits = _providerLimits[provider];
|
||||
|
||||
@@ -972,6 +990,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
bestRate = _sortedAvailableProviders.keys.first;
|
||||
bestRateProvider = _sortedAvailableProviders.values.first;
|
||||
}
|
||||
noProviderForPair = _sortedAvailableProviders.isEmpty;
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -993,6 +1012,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
try {
|
||||
final futures = selectedProviders
|
||||
.where((provider) => providerList.contains(provider))
|
||||
.where((provider) => !_excludeProviderForReceiveExtraId(provider))
|
||||
.map((provider) async {
|
||||
final limits = await provider
|
||||
.fetchLimits(
|
||||
@@ -1041,19 +1061,73 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
Future<void> createTrade() async {
|
||||
// Ignore repeated taps while a trade is already being created
|
||||
if (tradeState is TradeIsCreating) return;
|
||||
|
||||
// Set synchronously, before the first await, so the swap button shows its
|
||||
// loading state and further taps are ignored right away
|
||||
tradeState = TradeIsCreating();
|
||||
|
||||
try {
|
||||
await _createTrade();
|
||||
} finally {
|
||||
// Never leave the swap button stuck in its loading state
|
||||
// (e.g. if an unexpected exception is thrown)
|
||||
if (tradeState is TradeIsCreating) {
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.none_of_selected_providers_can_exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _createTrade() async {
|
||||
final depositAmountValue = _depositAmount ?? Money.zero(depositCurrency);
|
||||
final receiveAmountValue = _receiveAmount ?? Money.zero(receiveCurrency);
|
||||
|
||||
if (depositAmountValue.isZero ||
|
||||
depositAmountValue.isNegative ||
|
||||
receiveAmountValue.isZero ||
|
||||
receiveAmountValue.isNegative) {
|
||||
ExchangeProviderLogger.logError(
|
||||
provider: forcedProvider?.description ?? bestRateProvider?.description,
|
||||
function: 'createTrade',
|
||||
error: 'Invalid swap amount: deposit=$_depositAmount receive=$_receiveAmount',
|
||||
requestData: {
|
||||
'from': depositCurrency.title,
|
||||
'to': receiveCurrency.title,
|
||||
'fromAmount': _depositAmount,
|
||||
'toAmount': _receiveAmount,
|
||||
'isFixedRateMode': isFixedRateMode,
|
||||
'forcedProvider': forcedProvider?.title,
|
||||
'bestRateProvider': bestRateProvider?.title,
|
||||
},
|
||||
);
|
||||
|
||||
final invalidAmountError = depositAmountValue.isZero || depositAmountValue.isNegative
|
||||
? '$depositAmountValue is not a valid amount for depositAmount'
|
||||
: '$receiveAmountValue is not a valid amount for receiveAmount';
|
||||
|
||||
tradeState =
|
||||
TradeIsCreatedFailure(title: S.current.trade_not_created, error: invalidAmountError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSendAllEnabled) {
|
||||
await calculateDepositAllAmount();
|
||||
final amount = double.tryParse(_depositAmount);
|
||||
final amount = double.tryParse(_depositAmount.toString());
|
||||
|
||||
if (limits.min != null && amount != null && amount < limits.min!) {
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.amount_is_below_minimum_limit(limits.min!.toString()));
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.amount_is_below_minimum_limit(limits.min!.toString()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (depositCurrency == CryptoCurrency.btcln &&
|
||||
wallet.type == WalletType.bitcoin &&
|
||||
depositAddress == wallet.walletAddresses.addressForExchange) {
|
||||
final invoice = await bitcoin!.getLightningInvoice(wallet, BigInt.zero);
|
||||
if (invoice != null) {
|
||||
@@ -1062,6 +1136,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
if (receiveCurrency == CryptoCurrency.btcln &&
|
||||
wallet.type == WalletType.bitcoin &&
|
||||
receiveAddress == wallet.walletAddresses.addressForExchange) {
|
||||
final invoice = await bitcoin!.getLightningInvoice(wallet, BigInt.zero);
|
||||
if (invoice != null) {
|
||||
@@ -1069,6 +1144,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
}
|
||||
|
||||
if (forcedProvider != null && _excludeProviderForReceiveExtraId(forcedProvider!)) {
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.none_of_selected_providers_can_exchange,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<double, ExchangeProvider> providers;
|
||||
if (forcedProvider != null) {
|
||||
providers = {forcedProviderRate: forcedProvider!};
|
||||
@@ -1158,6 +1241,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_excludeProviderForReceiveExtraId(provider)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip Swaps.xyz when sending from external
|
||||
if (isSendFromExternal && provider.description == ExchangeProviderDescription.swapsXyz) {
|
||||
printV('Skipping Swaps.xyz for external send');
|
||||
@@ -1169,15 +1256,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
bestRate = providerRate;
|
||||
bestRateProvider = provider;
|
||||
|
||||
await changeDepositAmount(amount: _depositAmount, isCanonical: true);
|
||||
await changeDepositAmount(amount: _depositAmount.toString(), isCanonical: true);
|
||||
|
||||
final request = TradeRequest(
|
||||
fromCurrency: depositCurrency,
|
||||
toCurrency: receiveCurrency,
|
||||
fromAmount: _depositAmount.replaceAll(',', '.'),
|
||||
toAmount: _receiveAmount.replaceAll(',', '.'),
|
||||
fromAmount: _depositAmount.toString(),
|
||||
toAmount: _receiveAmount.toString(),
|
||||
refundAddress: depositAddress,
|
||||
toAddress: receiveAddress,
|
||||
toAddressExtraId: receiveAddressExtraId.trim(),
|
||||
isFixedRate: isFixedRateMode,
|
||||
);
|
||||
|
||||
@@ -1186,8 +1274,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
await wallet.walletAddresses.saveAddressesInBox();
|
||||
}
|
||||
|
||||
var amount = isFixedRateMode ? _receiveAmount : _depositAmount;
|
||||
amount = amount.replaceAll(',', '.');
|
||||
var amount = isFixedRateMode ? _receiveAmount.toString() : _depositAmount.toString();
|
||||
|
||||
if (limitsState is LimitsLoadedSuccessfully) {
|
||||
if (double.tryParse(amount) == null) {
|
||||
@@ -1210,6 +1297,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
trade.walletId = wallet.id;
|
||||
trade.chainId = wallet.chainId;
|
||||
trade.fromWalletAddress = wallet.walletAddresses.address;
|
||||
if (trade.from == null) {
|
||||
trade.from = depositCurrency;
|
||||
}
|
||||
if (trade.to == null) {
|
||||
trade.to = receiveCurrency;
|
||||
}
|
||||
|
||||
final canCreateTrade = await isCanCreateTrade(trade);
|
||||
if (!canCreateTrade.result) {
|
||||
@@ -1264,7 +1357,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
///
|
||||
/// this is because the limitation of the SplayTreeMap that
|
||||
/// you can't modify it while iterating through it
|
||||
Future.delayed(Duration(milliseconds: 200), createTrade);
|
||||
await Future<void>.delayed(Duration(milliseconds: 200));
|
||||
await _createTrade();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,12 +1366,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
void reset() {
|
||||
_initialPairBasedOnWallet();
|
||||
isReceiveAmountEntered = false;
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
depositAddress =
|
||||
depositCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||||
receiveAddress =
|
||||
receiveCurrency == wallet.currency ? wallet.walletAddresses.addressForExchange : '';
|
||||
receiveAddressExtraId = '';
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isFixedRateMode = false;
|
||||
_onPairChange();
|
||||
@@ -1308,8 +1403,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
final amount = depositCurrency == CryptoCurrency.btcln
|
||||
// FIXME amount estimation is broken/impossible for ln, konsti suggested this
|
||||
? (amountParsingProxy.parseCryptoString(depositAvailableAmount, depositCurrency).toInt() -
|
||||
10)
|
||||
? (amountParsingProxy.parseCryptoString(depositAvailableAmount, depositCurrency) -
|
||||
Money.fromInt(10, depositCurrency))
|
||||
: await bitcoin!.estimateFakeSendAllTxAmount(
|
||||
wallet,
|
||||
priority,
|
||||
@@ -1320,8 +1415,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
: UnspentCoinType.any,
|
||||
);
|
||||
|
||||
changeDepositAmount(
|
||||
amount: wallet.currency.formatAmount(BigInt.from(amount)), isCanonical: true);
|
||||
changeDepositAmount(amount: amount.toString(), isCanonical: true);
|
||||
} else if (wallet.type == WalletType.monero) {
|
||||
final amount = await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any);
|
||||
|
||||
@@ -1340,8 +1434,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
return;
|
||||
}
|
||||
|
||||
final balanceAmount = _appStore.amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
balanceForCurrency.fullAvailableBalance, balanceCurrency!);
|
||||
final balanceAmount =
|
||||
_appStore.amountParsingProxy.asDisplayString(balanceForCurrency.available);
|
||||
final balanceDouble = double.tryParse(balanceAmount.replaceAll(',', '.')) ?? 0.0;
|
||||
if (balanceDouble <= 0) {
|
||||
changeDepositAmount(amount: wallet.currency.formatAmount(BigInt.zero), isCanonical: true);
|
||||
@@ -1369,7 +1463,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
return;
|
||||
}
|
||||
|
||||
final balanceWei = balanceForCurrency.fullAvailableBalance;
|
||||
final balanceWei = balanceForCurrency.available.amount;
|
||||
final feeWei = BigInt.parse(feeString);
|
||||
final amountAfterFeeWei = balanceWei > feeWei ? balanceWei - feeWei : BigInt.zero;
|
||||
changeDepositAmount(
|
||||
@@ -1393,40 +1487,16 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
_depositAmount = tmpAmount;
|
||||
}
|
||||
|
||||
void updateTemplate() => _exchangeTemplateStore.update();
|
||||
|
||||
void addTemplate(
|
||||
{required String amount,
|
||||
required String depositCurrency,
|
||||
required String receiveCurrency,
|
||||
required String provider,
|
||||
required String depositAddress,
|
||||
required String receiveAddress,
|
||||
required String depositCurrencyTitle,
|
||||
required String receiveCurrencyTitle}) =>
|
||||
_exchangeTemplateStore.addTemplate(
|
||||
amount: amount,
|
||||
depositCurrency: depositCurrency,
|
||||
receiveCurrency: receiveCurrency,
|
||||
provider: provider,
|
||||
depositAddress: depositAddress,
|
||||
receiveAddress: receiveAddress,
|
||||
depositCurrencyTitle: depositCurrencyTitle,
|
||||
receiveCurrencyTitle: receiveCurrencyTitle);
|
||||
|
||||
void removeTemplate({required ExchangeTemplate template}) =>
|
||||
_exchangeTemplateStore.remove(template: template);
|
||||
|
||||
void _onPairChange({bool clearBoth = false}) {
|
||||
if (clearBoth) {
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
if (!hasAllAmount) isSendAllEnabled = false;
|
||||
} else {
|
||||
if (isFixedRateMode) {
|
||||
_depositAmount = '';
|
||||
_depositAmount = null;
|
||||
} else {
|
||||
_receiveAmount = '';
|
||||
_receiveAmount = null;
|
||||
}
|
||||
}
|
||||
bestRate = 0.0;
|
||||
@@ -1527,8 +1597,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
void saveSelectedProviders() {
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
isFixedRateMode = false;
|
||||
_defineIsReceiveAmountEditable();
|
||||
bestRateProvider = null;
|
||||
|
||||
Reference in New Issue
Block a user