mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1497-transaction-history-improved-layout
# Conflicts: # lib/new-ui/widgets/coins_page/assets_history/history_section.dart # lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart
This commit is contained in:
@@ -3,37 +3,37 @@ import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'anonpay_invoice_info.g.dart';
|
||||
part 'anonpay_invoice_info.part.dart';
|
||||
|
||||
@HiveType(typeId: AnonpayInvoiceInfo.typeId)
|
||||
// @HiveType(typeId: AnonpayInvoiceInfo.typeId)
|
||||
class AnonpayInvoiceInfo extends HiveObject with Keyable implements AnonpayInfoBase {
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String invoiceId;
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String status;
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final double? fiatAmount;
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final String? fiatEquiv;
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
final double? amountTo;
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
final String coinTo;
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
final String address;
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
final String clearnetUrl;
|
||||
@HiveField(8)
|
||||
// @HiveField(8)
|
||||
final String onionUrl;
|
||||
@HiveField(9)
|
||||
// @HiveField(9)
|
||||
final String clearnetStatusUrl;
|
||||
@HiveField(10)
|
||||
// @HiveField(10)
|
||||
final String onionStatusUrl;
|
||||
@HiveField(11)
|
||||
// @HiveField(11)
|
||||
final DateTime createdAt;
|
||||
@HiveField(12)
|
||||
// @HiveField(12)
|
||||
final String walletId;
|
||||
@HiveField(13)
|
||||
// @HiveField(13)
|
||||
final String provider;
|
||||
|
||||
static const typeId = ANONPAY_INVOICE_INFO_TYPE_ID;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'anonpay_invoice_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class AnonpayInvoiceInfoAdapter extends TypeAdapter<AnonpayInvoiceInfo> {
|
||||
@override
|
||||
final int typeId = 10;
|
||||
|
||||
@override
|
||||
AnonpayInvoiceInfo read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return AnonpayInvoiceInfo(
|
||||
invoiceId: fields[0] as String,
|
||||
clearnetUrl: fields[7] as String,
|
||||
onionUrl: fields[8] as String,
|
||||
clearnetStatusUrl: fields[9] as String,
|
||||
onionStatusUrl: fields[10] as String,
|
||||
status: fields[1] as String,
|
||||
fiatAmount: fields[2] as double?,
|
||||
fiatEquiv: fields[3] as String?,
|
||||
amountTo: fields[4] as double?,
|
||||
coinTo: fields[5] as String,
|
||||
address: fields[6] as String,
|
||||
createdAt: fields[11] as DateTime,
|
||||
walletId: fields[12] as String,
|
||||
provider: fields[13] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, AnonpayInvoiceInfo obj) {
|
||||
writer
|
||||
..writeByte(14)
|
||||
..writeByte(0)
|
||||
..write(obj.invoiceId)
|
||||
..writeByte(1)
|
||||
..write(obj.status)
|
||||
..writeByte(2)
|
||||
..write(obj.fiatAmount)
|
||||
..writeByte(3)
|
||||
..write(obj.fiatEquiv)
|
||||
..writeByte(4)
|
||||
..write(obj.amountTo)
|
||||
..writeByte(5)
|
||||
..write(obj.coinTo)
|
||||
..writeByte(6)
|
||||
..write(obj.address)
|
||||
..writeByte(7)
|
||||
..write(obj.clearnetUrl)
|
||||
..writeByte(8)
|
||||
..write(obj.onionUrl)
|
||||
..writeByte(9)
|
||||
..write(obj.clearnetStatusUrl)
|
||||
..writeByte(10)
|
||||
..write(obj.onionStatusUrl)
|
||||
..writeByte(11)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(12)
|
||||
..write(obj.walletId)
|
||||
..writeByte(13)
|
||||
..write(obj.provider);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is AnonpayInvoiceInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -156,13 +156,12 @@ class CWBitcoin extends Bitcoin {
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
memo: out.memo.isNotEmpty ? out.memo : null,
|
||||
extra: out.extra,
|
||||
))
|
||||
@@ -191,7 +190,7 @@ class CWBitcoin extends Bitcoin {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority,
|
||||
Future<Money> estimateFakeSendAllTxAmount(WalletBase wallet, TransactionPriority priority,
|
||||
{UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async {
|
||||
try {
|
||||
final sk = ECPrivate.random();
|
||||
@@ -233,7 +232,7 @@ class CWBitcoin extends Bitcoin {
|
||||
|
||||
return estimatedTx.amount;
|
||||
} catch (_) {
|
||||
return 0;
|
||||
return Money.zero(wallet.currency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,10 +246,6 @@ class CWBitcoin extends Bitcoin {
|
||||
String formatterBitcoinAmountToString({required int amount}) =>
|
||||
bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
double formatterBitcoinAmountToDouble({required int amount}) =>
|
||||
bitcoinAmountToDouble(amount: amount);
|
||||
|
||||
@override
|
||||
int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount);
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.da
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/cake_pay_transaction_sent_bottom_sheet.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
@@ -36,7 +35,6 @@ import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class CakePayBuyCardPage extends BasePage {
|
||||
@@ -612,16 +610,13 @@ class CakePayBuyCardPage extends BasePage {
|
||||
titleIconPath: _sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: _sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue: _sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
|
||||
_sendViewModel.pendingTransaction!.amountFormatted,
|
||||
_sendViewModel.selectedCryptoCurrency),
|
||||
amountValue: _sendViewModel.amountParsingProxy.asDisplayStringWithSymbol(
|
||||
_sendViewModel.pendingTransaction!.amount),
|
||||
quantity: 'QTY: ${cakePayBuyCardViewModel.quantity}',
|
||||
fiatAmountValue: _sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: "${_sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
|
||||
_sendViewModel.pendingTransaction!.feeFormattedValue,
|
||||
_sendViewModel.selectedCryptoCurrency)} ${_sendViewModel.amountParsingProxy
|
||||
.getCryptoSymbol(_sendViewModel.wallet.currency)}",
|
||||
feeValue: _sendViewModel.amountParsingProxy.asDisplayStringWithSymbol(
|
||||
_sendViewModel.pendingTransaction!.fee),
|
||||
feeFiatAmount: _sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: displayingOutputs,
|
||||
footerType: FooterType.slideActionButton,
|
||||
@@ -699,13 +694,12 @@ class CakePayBuyCardPage extends BasePage {
|
||||
child: Icon(Icons.check, size: 16, color: Colors.white),
|
||||
),
|
||||
output: displayingOutputs.first,
|
||||
currency: _sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(sheetCtx).send_amount,
|
||||
amountValue: _sendViewModel.pendingTransaction!.amountFormatted,
|
||||
amountValue: _sendViewModel.pendingTransaction!.amount.toStringWithSymbol(),
|
||||
quantity: 'QTY: ${cakePayBuyCardViewModel.quantity}',
|
||||
fiatAmountValue: _sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(sheetCtx).send_fee,
|
||||
feeValue: _sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeValue: _sendViewModel.pendingTransaction!.fee.toStringWithSymbol(),
|
||||
feeFiatAmount: _sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
paymentId: 'Order ID',
|
||||
paymentIdValue: order?.orderId ?? '',
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
|
||||
class AmountParsingProxy {
|
||||
@@ -20,70 +22,54 @@ class AmountParsingProxy {
|
||||
|
||||
/// [getCryptoOutputAmount] turns the input [amount] into the preferred representation of [cryptoCurrency]
|
||||
String getDisplayCryptoAmount(String amount, CryptoCurrency cryptoCurrency) {
|
||||
|
||||
try {
|
||||
if (useSatoshi(cryptoCurrency) && amount.isNotEmpty) {
|
||||
return cryptoCurrency.parseAmount(amount.withMaxDecimals(cryptoCurrency.decimals)).toString();
|
||||
return cryptoCurrency
|
||||
.parseAmount(amount.withMaxDecimals(cryptoCurrency.decimals))
|
||||
.toStringWithPrecision(useBaseUnit: true);
|
||||
}
|
||||
|
||||
return amount.withMaxDecimals(cryptoCurrency.decimals);
|
||||
|
||||
} catch(_) {
|
||||
printV("failed to parse amount $amount for currency ${cryptoCurrency.title}, falling back to showing unparsed");
|
||||
} catch (_) {
|
||||
printV(
|
||||
"failed to parse amount $amount for currency ${cryptoCurrency.title}, falling back to showing unparsed");
|
||||
return amount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// [getCryptoStringRepresentation] turns the input [amount] into the preferred representation of [cryptoCurrency]
|
||||
String getDisplayCryptoString(int amount, CryptoCurrency cryptoCurrency) {
|
||||
if (useSatoshi(cryptoCurrency)) {
|
||||
return "$amount";
|
||||
}
|
||||
String getDisplayCryptoString(int amount, CryptoCurrency cryptoCurrency) =>
|
||||
asDisplayString(Money.fromInt(amount, cryptoCurrency));
|
||||
|
||||
return cryptoCurrency.formatAmount(BigInt.from(amount));
|
||||
}
|
||||
/// [getDisplayCryptoString] turns the input [amount] into the preferred representation of [cryptoCurrency]
|
||||
///
|
||||
/// if [displayMode] is [BitcoinAmountDisplayMode.satoshi] it returns 1 BTC as 100000000
|
||||
/// if [displayMode] is [BitcoinAmountDisplayMode.bitcoin] it returns 1 BTC as 1
|
||||
/// hardcoding fractionalDigits to 8 as it's a display string, so no need to show more than that
|
||||
String asDisplayString(Money amount) =>
|
||||
amount.toStringWithPrecision(useBaseUnit: useSatoshi(amount.currency), fractionalDigits: 8);
|
||||
|
||||
String getDisplayCryptoStringFromBigInt(BigInt amount, CryptoCurrency cryptoCurrency) {
|
||||
if (useSatoshi(cryptoCurrency)) {
|
||||
return "$amount";
|
||||
}
|
||||
/// [asDisplayStringWithSymbol] turns the input [amount] into the preferred representation of
|
||||
/// [cryptoCurrency] following the symbol of the unit
|
||||
///
|
||||
/// if [displayMode] is [BitcoinAmountDisplayMode.satoshi] it returns 1 BTC as 100000000 sats
|
||||
/// if [displayMode] is [BitcoinAmountDisplayMode.bitcoin] it returns 1 BTC as 1 BTC
|
||||
/// hardcoding fractionalDigits to 8 as it's a display string, so no need to show more than that
|
||||
String asDisplayStringWithSymbol(Money amount) =>
|
||||
amount.toStringWithSymbol(useBaseUnit: useSatoshi(amount.currency), fractionalDigits: 8);
|
||||
|
||||
return cryptoCurrency.formatAmount(amount);
|
||||
}
|
||||
/// [parseCryptoString] turns the the display representation [string] into `Money` with [cryptoCurrency]
|
||||
Money parseCryptoString(String amount, CryptoCurrency cryptoCurrency) =>
|
||||
Money.parse(amount, cryptoCurrency, isBaseUnit: useSatoshi(cryptoCurrency));
|
||||
|
||||
/// [getCryptoStringFromDouble] turns the input [amount] into the preferred representation of [cryptoCurrency] and
|
||||
String getDisplayCryptoStringFromDouble(double amount, CryptoCurrency cryptoCurrency) {
|
||||
if (useSatoshi(cryptoCurrency)) {
|
||||
return "$amount";
|
||||
}
|
||||
|
||||
return cryptoCurrency.formatAmount(BigInt.from(amount));
|
||||
}
|
||||
|
||||
/// [parseCryptoString] turns the input [string] into a `BigInt` presentation of the [cryptoCurrency]
|
||||
BigInt parseCryptoString(String amount, CryptoCurrency cryptoCurrency) {
|
||||
if (useSatoshi(cryptoCurrency)) {
|
||||
return BigInt.parse(amount);
|
||||
}
|
||||
|
||||
return cryptoCurrency.parseAmount(amount);
|
||||
}
|
||||
|
||||
/// [tryParseCryptoString] tries to turn the display representation [string] into a `BigInt` presentation of the [cryptoCurrency]
|
||||
BigInt? tryParseCryptoString(String amount, CryptoCurrency cryptoCurrency) {
|
||||
if (useSatoshi(cryptoCurrency)) {
|
||||
return BigInt.tryParse(amount);
|
||||
}
|
||||
|
||||
return cryptoCurrency.tryParseAmount(amount);
|
||||
}
|
||||
/// [tryParseCryptoString] tries to turn the display representation [string] into `Money` with [cryptoCurrency]
|
||||
Money? tryParseCryptoString(String amount, CryptoCurrency cryptoCurrency) =>
|
||||
Money.tryParse(amount, cryptoCurrency, isBaseUnit: useSatoshi(cryptoCurrency));
|
||||
|
||||
/// [getCryptoSymbol] returns the correct Symbol related to the presentation
|
||||
String getCryptoSymbol(CryptoCurrency cryptoCurrency) =>
|
||||
useSatoshi(cryptoCurrency) ? "sats" : cryptoCurrency.title.safeSubString(0, 8);
|
||||
|
||||
bool useSatoshi(CryptoCurrency cryptoCurrency) =>
|
||||
bool useSatoshi(Currency cryptoCurrency) =>
|
||||
([CryptoCurrency.btc, CryptoCurrency.btcln].contains(cryptoCurrency) &&
|
||||
displayMode == BitcoinAmountDisplayMode.satoshi) ||
|
||||
(CryptoCurrency.btcln == cryptoCurrency &&
|
||||
|
||||
@@ -213,7 +213,7 @@ class BackgroundSync {
|
||||
final action = tx.direction == TransactionDirection.incoming ? "Received" : "Sent";
|
||||
if (sharedPreferences.getBool(PreferencesKey.backgroundSyncNotificationsEnabled) ?? false) {
|
||||
await showNotification(
|
||||
"$action ${wallet.currency.fullName} in ${wallet.name}", "${tx.amountFormatted()}");
|
||||
"$action ${wallet.currency.fullName} in ${wallet.name}", "${tx.amount.toString()}");
|
||||
}
|
||||
printV(
|
||||
"${wallet.currency.fullName} in ${wallet.name}: TX: ${tx.date} ${tx.amount} ${tx.direction}");
|
||||
|
||||
@@ -144,6 +144,21 @@ class BackupMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
class IncompatibleBackupAppException implements Exception {
|
||||
IncompatibleBackupAppException({
|
||||
required this.sourceAppName,
|
||||
required this.currentAppName,
|
||||
});
|
||||
|
||||
final String sourceAppName;
|
||||
final String currentAppName;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'This backup was created in $sourceAppName and cannot be restored in $currentAppName.';
|
||||
}
|
||||
}
|
||||
|
||||
class BackupServiceV3 extends $BackupService {
|
||||
BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
|
||||
|
||||
@@ -197,7 +212,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> importBackupFile(File file, String password, {String nonce = secrets.backupSalt}) {
|
||||
Future<void> importBackupFile(File file, String password,
|
||||
{String nonce = secrets.backupSalt, bool checkBackupApp = true}) {
|
||||
final version = getVersionFile(file);
|
||||
switch (version) {
|
||||
case BackupVersion.unknown:
|
||||
@@ -210,11 +226,12 @@ class BackupServiceV3 extends $BackupService {
|
||||
case BackupVersion.v2:
|
||||
return super.importBackupV2(file.readAsBytesSync(), password);
|
||||
case BackupVersion.v3:
|
||||
return importBackupFileV3(file, password, nonce: nonce);
|
||||
return importBackupFileV3(file, password, nonce: nonce, checkBackupApp: checkBackupApp);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> importBackupFileV3(File file, String password, {String nonce = secrets.backupSalt}) async{
|
||||
Future<void> importBackupFileV3(File file, String password,
|
||||
{String nonce = secrets.backupSalt,bool checkBackupApp = true}) async{
|
||||
// Overall design of v3 backup is the following:
|
||||
// 1. backup.zip - plaintext zip file that user can open with any archive manager
|
||||
// 2. backup.zip/README.txt - text file to let user know what is inside of this file
|
||||
@@ -223,6 +240,11 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
final inputStream = InputFileStream(file.path);
|
||||
final archive = ZipDecoder().decodeStream(inputStream);
|
||||
|
||||
if (checkBackupApp) {
|
||||
await _throwIfBackupWasCreatedInAnotherApp(archive);
|
||||
}
|
||||
|
||||
final metadataFile = archive.findFile('metadata.json');
|
||||
if (metadataFile == null) {
|
||||
throw Exception('Invalid v3 backup: missing metadata.json');
|
||||
@@ -425,7 +447,7 @@ class BackupServiceV3 extends $BackupService {
|
||||
metadata.sha512sum = (await sha512.bind(dataBinUnencrypted.openRead()).first).toString();
|
||||
|
||||
final raf = await dataBinUnencrypted.open();
|
||||
|
||||
|
||||
|
||||
while (true) {
|
||||
printV("Reading chunk ${chunkIndex++}");
|
||||
@@ -491,6 +513,33 @@ This backup was created on ${DateTime.now().toIso8601String()}
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<void> _throwIfBackupWasCreatedInAnotherApp(Archive archive) async {
|
||||
final readmeFile = archive.findFile('README.txt');
|
||||
if (readmeFile == null) return;
|
||||
|
||||
final readmeBytes = readmeFile.rawContent?.readBytes();
|
||||
if (readmeBytes == null) return;
|
||||
|
||||
final readmeString = utf8.decode(readmeBytes, allowMalformed: true);
|
||||
final sourceAppName = _extractBackupAppName(readmeString);
|
||||
if (sourceAppName == null) return;
|
||||
|
||||
|
||||
final currentAppName = (await PackageInfo.fromPlatform()).appName;
|
||||
if (sourceAppName == currentAppName) return;
|
||||
|
||||
|
||||
throw IncompatibleBackupAppException(
|
||||
sourceAppName: sourceAppName,
|
||||
currentAppName: currentAppName,
|
||||
);
|
||||
}
|
||||
|
||||
String? _extractBackupAppName(String readme) {
|
||||
final match = RegExp(r'^This is a (.+) backup\.', multiLine: true).firstMatch(readme);
|
||||
return match?.group(1)?.trim();
|
||||
}
|
||||
|
||||
static const chunkSize = 24 * 1024 * 1024; // 24MiB
|
||||
|
||||
File setVersionFile(File file, BackupVersion version) {
|
||||
|
||||
@@ -80,21 +80,15 @@ class CsvExportService {
|
||||
? tx.to!
|
||||
: (tx.outputAddresses?.firstOrNull ?? ''));
|
||||
|
||||
// Split "0.2769 LTC" → amount="0.2769", currency="LTC"
|
||||
final (amount, currency) = _splitAmountCurrency(tx.amountFormatted());
|
||||
|
||||
// fee is stored as (inputAmount - totalOutputAmount) which is negative for incoming
|
||||
// transactions — only include it when the raw value is a sensible positive fee.
|
||||
final fee = tx.fee != null && tx.fee! > 0 ? (tx.feeFormatted() ?? '') : '';
|
||||
final (feeAmount, _) = fee.isNotEmpty ? _splitAmountCurrency(fee) : ('', '');
|
||||
final fee = tx.fee != null && !tx.fee!.isZero ? tx.fee.toString() : '';
|
||||
|
||||
return _row([
|
||||
'transaction',
|
||||
_isoDate(tx.date),
|
||||
type,
|
||||
amount,
|
||||
currency,
|
||||
feeAmount,
|
||||
tx.amount.toString(),
|
||||
tx.amount.currency.symbol,
|
||||
fee,
|
||||
tx.id,
|
||||
address,
|
||||
status,
|
||||
@@ -109,15 +103,6 @@ class CsvExportService {
|
||||
]);
|
||||
}
|
||||
|
||||
/// Splits a formatted amount like "0.2769 LTC" into ("0.2769", "LTC").
|
||||
/// If there is no trailing symbol the whole string is returned as the amount.
|
||||
(String, String) _splitAmountCurrency(String formatted) {
|
||||
final trimmed = formatted.trim();
|
||||
final idx = trimmed.lastIndexOf(' ');
|
||||
if (idx == -1) return (trimmed, '');
|
||||
return (trimmed.substring(0, idx), trimmed.substring(idx + 1));
|
||||
}
|
||||
|
||||
String _tradeRow(TradeListItem item) {
|
||||
final trade = item.trade;
|
||||
return _row([
|
||||
|
||||
@@ -19,9 +19,8 @@ class CWDecred extends Decred {
|
||||
DecredRestoreWalletFromPubkeyCredentials(name: name, pubkey: pubkey, password: password);
|
||||
|
||||
@override
|
||||
WalletService createDecredWalletService(Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||
return DecredWalletService(unspentCoinSource);
|
||||
}
|
||||
WalletService createDecredWalletService(Box<UnspentCoinsInfo> unspentCoinSource) =>
|
||||
DecredWalletService(unspentCoinSource);
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() => DecredTransactionPriority.all;
|
||||
@@ -39,18 +38,19 @@ class CWDecred extends Decred {
|
||||
@override
|
||||
Object createDecredTransactionCredentials(List<Output> outputs, TransactionPriority priority) =>
|
||||
DecredTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as DecredTransactionPriority);
|
||||
))
|
||||
.toList(),
|
||||
priority: priority as DecredTransactionPriority,
|
||||
);
|
||||
|
||||
List<WalletInfoAddressInfo> getAddressInfos(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
@@ -69,17 +69,6 @@ class CWDecred extends Decred {
|
||||
await decredWallet.walletAddresses.generateNewAddress(label);
|
||||
}
|
||||
|
||||
@override
|
||||
String formatterDecredAmountToString({required int amount}) =>
|
||||
decredAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
double formatterDecredAmountToDouble({required int amount}) =>
|
||||
decredAmountToDouble(amount: amount);
|
||||
|
||||
@override
|
||||
int formatterStringDoubleToDecredAmount(String amount) => stringDoubleToDecredAmount(amount);
|
||||
|
||||
@override
|
||||
List<Unspent> getUnspents(Object wallet) {
|
||||
final decredWallet = wallet as DecredWallet;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
|
||||
String calculateFiatAmount({double? price, String? cryptoAmount, bool raw = false}) {
|
||||
if (price == null || cryptoAmount == null) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
cryptoAmount = cryptoAmount.replaceAll(',', '.');
|
||||
cryptoAmount = cryptoAmount.sanitized();
|
||||
|
||||
final _amount = double.tryParse(cryptoAmount);
|
||||
if (_amount == null || _amount.isNaN) return '0.00';
|
||||
|
||||
@@ -3,9 +3,9 @@ import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'contact.g.dart';
|
||||
part 'contact.part.dart';
|
||||
|
||||
@HiveType(typeId: Contact.typeId)
|
||||
// @HiveType(typeId: Contact.typeId)
|
||||
class Contact extends HiveObject with Keyable {
|
||||
Contact({required this.name, required this.address, CryptoCurrency? type, DateTime? lastChange, this.displayName = ""})
|
||||
: lastChange = lastChange ?? DateTime.now() {
|
||||
@@ -17,19 +17,19 @@ class Contact extends HiveObject with Keyable {
|
||||
static const typeId = CONTACT_TYPE_ID;
|
||||
static const boxName = 'Contacts';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String name;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
// @HiveField(1, defaultValue: '')
|
||||
String address;
|
||||
|
||||
@HiveField(2, defaultValue: 0)
|
||||
// @HiveField(2, defaultValue: 0)
|
||||
late int raw;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
DateTime lastChange;
|
||||
|
||||
@HiveField(4, defaultValue: "")
|
||||
// @HiveField(4, defaultValue: "")
|
||||
String displayName;
|
||||
|
||||
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'contact.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ContactAdapter extends TypeAdapter<Contact> {
|
||||
@override
|
||||
final int typeId = 0;
|
||||
|
||||
@override
|
||||
Contact read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Contact(
|
||||
name: fields[0] == null ? '' : fields[0] as String,
|
||||
address: fields[1] == null ? '' : fields[1] as String,
|
||||
lastChange: fields[3] as DateTime?,
|
||||
displayName: fields[4] == null ? '' : fields[4] as String,
|
||||
)..raw = fields[2] == null ? 0 : fields[2] as int;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Contact obj) {
|
||||
writer
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.name)
|
||||
..writeByte(1)
|
||||
..write(obj.address)
|
||||
..writeByte(2)
|
||||
..write(obj.raw)
|
||||
..writeByte(3)
|
||||
..write(obj.lastChange)
|
||||
..writeByte(4)
|
||||
..write(obj.displayName);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ContactAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,18 +1,26 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class FiatCurrency extends EnumerableItem<String> with Serializable<String> implements Currency {
|
||||
const FiatCurrency({
|
||||
required String symbol,
|
||||
required this.symbol,
|
||||
required this.countryCode,
|
||||
required this.fullName,
|
||||
this.decimals = 2,
|
||||
}) : super(title: symbol, raw: symbol);
|
||||
|
||||
final String countryCode;
|
||||
|
||||
@override
|
||||
final String fullName;
|
||||
|
||||
@override
|
||||
final int decimals;
|
||||
|
||||
@override
|
||||
final String symbol;
|
||||
|
||||
static List<FiatCurrency> get all => _all.values.toList();
|
||||
|
||||
static List<FiatCurrency> get currenciesAvailableToBuyWith => [
|
||||
@@ -182,4 +190,10 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> impl
|
||||
|
||||
@override
|
||||
String get iconPath => "assets/images/flags/$countryCode.png";
|
||||
|
||||
@override
|
||||
Money parseAmount(String value) => Money.parse(value, this);
|
||||
|
||||
@override
|
||||
Money? tryParseAmount(String value) => Money.tryParse(value, this);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'haven_seed_store.g.dart';
|
||||
part 'haven_seed_store.part.dart';
|
||||
|
||||
@HiveType(typeId: HavenSeedStore.typeId)
|
||||
// @HiveType(typeId: HavenSeedStore.typeId)
|
||||
class HavenSeedStore extends HiveObject {
|
||||
HavenSeedStore({required this.id, this.seed});
|
||||
|
||||
@@ -11,9 +11,9 @@ class HavenSeedStore extends HiveObject {
|
||||
static const boxName = 'HavenSeedStore';
|
||||
static const boxKey = 'havenSeedStoreKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? seed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'haven_seed_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class HavenSeedStoreAdapter extends TypeAdapter<HavenSeedStore> {
|
||||
@override
|
||||
final int typeId = 21;
|
||||
|
||||
@override
|
||||
HavenSeedStore read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return HavenSeedStore(
|
||||
id: fields[0] == null ? '' : fields[0] as String,
|
||||
seed: fields[2] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, HavenSeedStore obj) {
|
||||
writer
|
||||
..writeByte(2)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(2)
|
||||
..write(obj.seed);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is HavenSeedStoreAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
+10
-10
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'template.g.dart';
|
||||
part 'template.part.dart';
|
||||
|
||||
@HiveType(typeId: Template.typeId)
|
||||
// @HiveType(typeId: Template.typeId)
|
||||
class Template extends HiveObject {
|
||||
Template(
|
||||
{required this.nameRaw,
|
||||
@@ -18,28 +18,28 @@ class Template extends HiveObject {
|
||||
static const typeId = TEMPLATE_TYPE_ID;
|
||||
static const boxName = 'Template';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
String? nameRaw;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String? addressRaw;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? cryptoCurrencyRaw;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? amountRaw;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
String? fiatCurrencyRaw;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
bool? isCurrencySelectedRaw;
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
String? amountFiatRaw;
|
||||
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
List<Template>? additionalRecipientsRaw;
|
||||
|
||||
bool get isCurrencySelected => isCurrencySelectedRaw ?? false;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'template.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class TemplateAdapter extends TypeAdapter<Template> {
|
||||
@override
|
||||
final int typeId = 6;
|
||||
|
||||
@override
|
||||
Template read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Template(
|
||||
nameRaw: fields[0] as String?,
|
||||
isCurrencySelectedRaw: fields[5] as bool?,
|
||||
addressRaw: fields[1] as String?,
|
||||
cryptoCurrencyRaw: fields[2] as String?,
|
||||
amountRaw: fields[3] as String?,
|
||||
fiatCurrencyRaw: fields[4] as String?,
|
||||
amountFiatRaw: fields[6] as String?,
|
||||
additionalRecipientsRaw: (fields[7] as List?)?.cast<Template>(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Template obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.nameRaw)
|
||||
..writeByte(1)
|
||||
..write(obj.addressRaw)
|
||||
..writeByte(2)
|
||||
..write(obj.cryptoCurrencyRaw)
|
||||
..writeByte(3)
|
||||
..write(obj.amountRaw)
|
||||
..writeByte(4)
|
||||
..write(obj.fiatCurrencyRaw)
|
||||
..writeByte(5)
|
||||
..write(obj.isCurrencySelectedRaw)
|
||||
..writeByte(6)
|
||||
..write(obj.amountFiatRaw)
|
||||
..writeByte(7)
|
||||
..write(obj.additionalRecipientsRaw);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TemplateAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'transaction_description.g.dart';
|
||||
part 'transaction_description.part.dart';
|
||||
|
||||
@HiveType(typeId: TransactionDescription.typeId)
|
||||
// @HiveType(typeId: TransactionDescription.typeId)
|
||||
class TransactionDescription extends HiveObject {
|
||||
TransactionDescription({required this.id, this.recipientAddress, this.transactionNote, this.transactionKey});
|
||||
|
||||
@@ -11,16 +11,16 @@ class TransactionDescription extends HiveObject {
|
||||
static const boxName = 'TransactionDescriptions';
|
||||
static const boxKey = 'transactionDescriptionsBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String? recipientAddress;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? transactionNote;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? transactionKey;
|
||||
|
||||
String get note => transactionNote ?? '';
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'transaction_description.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class TransactionDescriptionAdapter
|
||||
extends TypeAdapter<TransactionDescription> {
|
||||
@override
|
||||
final int typeId = 2;
|
||||
|
||||
@override
|
||||
TransactionDescription read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return TransactionDescription(
|
||||
id: fields[0] == null ? '' : fields[0] as String,
|
||||
recipientAddress: fields[1] as String?,
|
||||
transactionNote: fields[2] as String?,
|
||||
transactionKey: fields[3] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, TransactionDescription obj) {
|
||||
writer
|
||||
..writeByte(4)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.recipientAddress)
|
||||
..writeByte(2)
|
||||
..write(obj.transactionNote)
|
||||
..writeByte(3)
|
||||
..write(obj.transactionKey);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TransactionDescriptionAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
+61
-81
@@ -105,26 +105,25 @@ class CWEVM extends EVM {
|
||||
TransactionPriority? priority,
|
||||
int? feeRate,
|
||||
bool useBlinkProtection = true,
|
||||
}) {
|
||||
return EVMChainTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
memo: out.memo))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority?,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
);
|
||||
}
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
memo: out.memo,
|
||||
))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority?,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
);
|
||||
|
||||
@override
|
||||
Object createEVMTransactionCredentialsRaw(
|
||||
@@ -144,29 +143,28 @@ class CWEVM extends EVM {
|
||||
}
|
||||
|
||||
@override
|
||||
TransactionInfo getTransactionInfo(
|
||||
|
||||
{
|
||||
required String id,
|
||||
required int height,
|
||||
required BigInt ethAmount,
|
||||
required BigInt ethFee,
|
||||
required String tokenSymbol,
|
||||
int exponent = 18,
|
||||
required TransactionDirection direction,
|
||||
required bool isPending,
|
||||
required DateTime date,
|
||||
required int confirmations,
|
||||
String? to,
|
||||
String? from,
|
||||
String? evmSignatureName,
|
||||
String? contractAddress,
|
||||
required int chainId,
|
||||
}) =>
|
||||
EVMChainTransactionInfo(id: id,
|
||||
TransactionInfo getTransactionInfo({
|
||||
required String id,
|
||||
required int height,
|
||||
required Money amount,
|
||||
required Money fee,
|
||||
required String tokenSymbol,
|
||||
int exponent = 18,
|
||||
required TransactionDirection direction,
|
||||
required bool isPending,
|
||||
required DateTime date,
|
||||
required int confirmations,
|
||||
String? to,
|
||||
String? from,
|
||||
String? evmSignatureName,
|
||||
String? contractAddress,
|
||||
required int chainId,
|
||||
}) =>
|
||||
EVMChainTransactionInfo(
|
||||
id: id,
|
||||
height: height,
|
||||
ethAmount: ethAmount,
|
||||
ethFee: ethFee,
|
||||
amount: amount,
|
||||
fee: fee,
|
||||
tokenSymbol: tokenSymbol,
|
||||
direction: direction,
|
||||
isPending: isPending,
|
||||
@@ -179,22 +177,6 @@ class CWEVM extends EVM {
|
||||
@override
|
||||
int formatterEVMParseAmount(String amount) => EVMChainFormatter.parseEVMChainAmount(amount);
|
||||
|
||||
@override
|
||||
double formatterEVMAmountToDouble({
|
||||
TransactionInfo? transaction,
|
||||
BigInt? amount,
|
||||
int exponent = 18,
|
||||
}) {
|
||||
assert(transaction != null || amount != null);
|
||||
|
||||
if (transaction != null) {
|
||||
transaction as EVMChainTransactionInfo;
|
||||
return transaction.ethAmount / BigInt.from(10).pow(transaction.exponent);
|
||||
} else {
|
||||
return (amount!) / BigInt.from(10).pow(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Erc20Token> getERC20Currencies(WalletBase wallet) =>
|
||||
(wallet as EVMChainWallet).erc20Currencies;
|
||||
@@ -294,20 +276,16 @@ class CWEVM extends EVM {
|
||||
@override
|
||||
Future<PendingTransaction> createTokenApproval(
|
||||
WalletBase wallet,
|
||||
BigInt amount,
|
||||
Money amount,
|
||||
String spender,
|
||||
CryptoCurrency token,
|
||||
TransactionPriority? priority, {
|
||||
bool useBlinkProtection = true,
|
||||
}) {
|
||||
final evmWallet = wallet as EVMChainWallet;
|
||||
final feeCurrency = EVMChainUtils.getFeeCurrency(evmWallet.selectedChainId);
|
||||
return evmWallet.createApprovalTransaction(
|
||||
amount,
|
||||
spender,
|
||||
token,
|
||||
priority as EVMChainTransactionPriority?,
|
||||
feeCurrency,
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
);
|
||||
}
|
||||
@@ -317,7 +295,7 @@ class CWEVM extends EVM {
|
||||
WalletBase wallet,
|
||||
String to,
|
||||
String dataHex,
|
||||
BigInt valueWei,
|
||||
Money valueWei,
|
||||
TransactionPriority? priority, {
|
||||
bool useBlinkProtection = true,
|
||||
String? sourceTokenAddress,
|
||||
@@ -399,7 +377,7 @@ class CWEVM extends EVM {
|
||||
|
||||
// Chain-specific integrations (only for Ethereum)
|
||||
@override
|
||||
Future<BigInt>? getDEuroSavingsBalance(WalletBase wallet) {
|
||||
Future<Money>? getDEuroSavingsBalance(WalletBase wallet) {
|
||||
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
||||
return DEuro(wallet).savingsBalance;
|
||||
}
|
||||
@@ -407,7 +385,15 @@ class CWEVM extends EVM {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BigInt>? getDEuroAccruedInterest(WalletBase wallet) {
|
||||
Future<Money>? getDEuroSavingsV1Balance(WalletBase wallet) {
|
||||
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
||||
return DEuro(wallet).savingsBalanceV1;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Money>? getDEuroAccruedInterest(WalletBase wallet) {
|
||||
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
||||
return DEuro(wallet).accruedInterest;
|
||||
}
|
||||
@@ -448,6 +434,15 @@ class CWEVM extends EVM {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction>? withdrawDEuroSavingV1(
|
||||
WalletBase wallet, TransactionPriority priority) {
|
||||
if (wallet.chainId == 1 && wallet is EVMChainWallet) {
|
||||
return DEuro(wallet).withdrawSavingsV1(priority as EVMChainTransactionPriority);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction>? reinvestDEuroInterest(
|
||||
WalletBase wallet, TransactionPriority priority) {
|
||||
@@ -465,13 +460,6 @@ class CWEVM extends EVM {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
BigInt getPendingTransactionFee(PendingTransaction tx) => (tx as PendingEVMChainTransaction).fee;
|
||||
|
||||
@override
|
||||
String getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingEVMChainTransaction).amount;
|
||||
|
||||
// Registry helper methods
|
||||
static final EvmChainRegistry _registry = EvmChainRegistry();
|
||||
|
||||
@@ -532,14 +520,6 @@ class CWEVM extends EVM {
|
||||
return _registry.getWalletTypeByChainId(chainId);
|
||||
}
|
||||
|
||||
@override
|
||||
BigInt? getERC20AvailableBalance(Object balance) {
|
||||
if (balance is EVMChainERC20Balance) {
|
||||
return balance.balance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<ChainInfo> getAllChains() {
|
||||
final allChains = _registry.getAllChains();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'exchange_template.g.dart';
|
||||
part 'exchange_template.part.dart';
|
||||
|
||||
@HiveType(typeId: ExchangeTemplate.typeId)
|
||||
// @HiveType(typeId: ExchangeTemplate.typeId)
|
||||
class ExchangeTemplate extends HiveObject {
|
||||
ExchangeTemplate(
|
||||
{required this.amountRaw,
|
||||
@@ -18,28 +18,28 @@ class ExchangeTemplate extends HiveObject {
|
||||
static const typeId = EXCHANGE_TEMPLATE_TYPE_ID;
|
||||
static const boxName = 'ExchangeTemplate';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
String? amountRaw;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
String? depositCurrencyRaw;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? receiveCurrencyRaw;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? providerRaw;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
String? depositAddressRaw;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
String? receiveAddressRaw;
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
String? depositCurrencyTitleRaw;
|
||||
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
String? receiveCurrencyTitleRaw;
|
||||
|
||||
String get amount => amountRaw ?? '';
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'exchange_template.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ExchangeTemplateAdapter extends TypeAdapter<ExchangeTemplate> {
|
||||
@override
|
||||
final int typeId = 7;
|
||||
|
||||
@override
|
||||
ExchangeTemplate read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return ExchangeTemplate(
|
||||
amountRaw: fields[0] as String?,
|
||||
depositCurrencyRaw: fields[1] as String?,
|
||||
receiveCurrencyRaw: fields[2] as String?,
|
||||
providerRaw: fields[3] as String?,
|
||||
depositAddressRaw: fields[4] as String?,
|
||||
receiveAddressRaw: fields[5] as String?,
|
||||
depositCurrencyTitleRaw: fields[6] as String?,
|
||||
receiveCurrencyTitleRaw: fields[7] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, ExchangeTemplate obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.amountRaw)
|
||||
..writeByte(1)
|
||||
..write(obj.depositCurrencyRaw)
|
||||
..writeByte(2)
|
||||
..write(obj.receiveCurrencyRaw)
|
||||
..writeByte(3)
|
||||
..write(obj.providerRaw)
|
||||
..writeByte(4)
|
||||
..write(obj.depositAddressRaw)
|
||||
..writeByte(5)
|
||||
..write(obj.receiveAddressRaw)
|
||||
..writeByte(6)
|
||||
..write(obj.depositCurrencyTitleRaw)
|
||||
..writeByte(7)
|
||||
..write(obj.receiveCurrencyTitleRaw);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ExchangeTemplateAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -280,8 +280,8 @@ class NearIntentsExchangeProvider extends ExchangeProvider {
|
||||
final trade = Trade(
|
||||
id: depositAddress,
|
||||
// Using deposit address as trade ID
|
||||
from: from,
|
||||
to: to,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
providerName: title,
|
||||
state: TradeState.created,
|
||||
|
||||
@@ -18,9 +18,11 @@ class XOSwapExchangeProvider extends ExchangeProvider {
|
||||
}
|
||||
|
||||
void _addAppVersionHeader() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final currentVersion = packageInfo.version;
|
||||
_headers['App-Version'] = currentVersion;
|
||||
try {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final currentVersion = packageInfo.version;
|
||||
_headers['App-Version'] = currentVersion;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
static const _apiAuthority = 'exchange.exodus.io';
|
||||
|
||||
@@ -1036,4 +1036,23 @@ class HaCupertinoLocalizations extends GlobalCupertinoLocalizations {
|
||||
@override
|
||||
// TODO: implement cancelButtonLabel
|
||||
String get cancelButtonLabel => "cancelButtonLabel";
|
||||
|
||||
@override
|
||||
String get collapsedHint => "collapsedHint";
|
||||
|
||||
@override
|
||||
String get expandedHint => "expandedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileCollapsedHint => "expansionTileCollapsedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileCollapsedTapHint => "expansionTileCollapsedTapHint";
|
||||
|
||||
@override
|
||||
String get expansionTileExpandedHint => "expansionTileExpandedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileExpandedTapHint => "expansionTileExpandedTapHint";
|
||||
|
||||
}
|
||||
|
||||
@@ -1036,4 +1036,23 @@ class YoCupertinoLocalizations extends GlobalCupertinoLocalizations {
|
||||
@override
|
||||
// TODO: implement cancelButtonLabel
|
||||
String get cancelButtonLabel => "cancelButtonLabel";
|
||||
|
||||
@override
|
||||
String get collapsedHint => "collapsedHint";
|
||||
|
||||
@override
|
||||
String get expandedHint => "expandedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileCollapsedHint => "expansionTileCollapsedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileCollapsedTapHint => "expansionTileCollapsedTapHint";
|
||||
|
||||
@override
|
||||
String get expansionTileExpandedHint => "expansionTileExpandedHint";
|
||||
|
||||
@override
|
||||
String get expansionTileExpandedTapHint => "expansionTileExpandedTapHint";
|
||||
|
||||
}
|
||||
|
||||
@@ -301,14 +301,14 @@ class CWMonero extends Monero {
|
||||
MoneroTransactionCreationCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
))
|
||||
.toList(),
|
||||
priority: priority as MoneroTransactionPriority);
|
||||
|
||||
@@ -411,7 +411,7 @@ class CWMonero extends Monero {
|
||||
}
|
||||
|
||||
@override
|
||||
bool needExportOutputs(Object wallet, int amount) {
|
||||
bool needExportOutputs(Object wallet, Money amount) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
return moneroWallet.needExportOutputs(amount);
|
||||
}
|
||||
|
||||
@@ -150,13 +150,12 @@ class CWNano extends Nano {
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
@@ -175,11 +174,6 @@ class CWNano extends Nano {
|
||||
return (wallet as NanoWallet).updateTransactions();
|
||||
}
|
||||
|
||||
@override
|
||||
BigInt getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
||||
return (transactionInfo as NanoTransactionInfo).amountRaw;
|
||||
}
|
||||
|
||||
@override
|
||||
String getRepresentative(Object wallet) {
|
||||
return (wallet as NanoWallet).representative;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/core/utilities.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/payjoin_copy_modal.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_type.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_widget.dart';
|
||||
@@ -22,6 +23,7 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
@@ -194,29 +196,37 @@ class _NewReceivePageState extends State<NewReceivePage> {
|
||||
ModalTopBar(
|
||||
title: _largeQrMode ? "" : S.of(context).receive,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
trailingIcon: _largeQrMode
|
||||
? Icon(Icons.share)
|
||||
: widget.addressListViewModel.hasAddressRotation
|
||||
/* TODO rotating is broken on mweb, disabling for now, fix after mvp*/
|
||||
&&
|
||||
!(widget.receiveOptionViewModel.selectedReceiveOption.description ?? "")
|
||||
.toLowerCase()
|
||||
.contains("mweb")
|
||||
? Icon(Icons.refresh)
|
||||
: null,
|
||||
onLeadingPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
},
|
||||
onTrailingPressed: () {
|
||||
if (_largeQrMode) {
|
||||
ShareUtil.share(
|
||||
text: widget.addressListViewModel.uri.toString(),
|
||||
context: context,
|
||||
);
|
||||
} else if (widget.addressListViewModel.hasAddressRotation) {
|
||||
widget.addressListViewModel.rotateAddress();
|
||||
}
|
||||
},
|
||||
trailingWidget: Observer(
|
||||
builder: (_) => AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: _largeQrMode || widget.addressListViewModel.hasAddressRotation
|
||||
/* TODO rotating is broken on mweb, disabling for now, fix after mvp*/
|
||||
&&
|
||||
!(widget.receiveOptionViewModel.selectedReceiveOption.description ?? "")
|
||||
.toLowerCase()
|
||||
.contains("mweb") ? ModernButton(
|
||||
key: ValueKey(_largeQrMode),
|
||||
size: 36,
|
||||
icon: _largeQrMode ? Icon(Icons.share) : widget.addressListViewModel.isRotatingAddress
|
||||
? CupertinoActivityIndicator()
|
||||
: Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
if(_largeQrMode) {
|
||||
ShareUtil.share(
|
||||
text: widget.addressListViewModel.uri.toString(),
|
||||
context: context,
|
||||
);
|
||||
} else {
|
||||
if(widget.addressListViewModel.hasAddressRotation) {
|
||||
widget.addressListViewModel.rotateAddress();
|
||||
}
|
||||
}
|
||||
}):SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
||||
@@ -26,6 +26,8 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/new_list_row/list_item_regular_row_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
@@ -123,7 +125,7 @@ class SendPageModes {
|
||||
helpContent: SendPageHelpContent(
|
||||
title: S.current.about_litecoin_privacy,
|
||||
imagePath: "assets/new-ui/mweb_help.svg",
|
||||
description: S.current.mweb_help_desc_1 + "\n\n" + S.current.mweb_help_desc_2,
|
||||
description: "${S.current.mweb_help_desc_1}\n\n${S.current.mweb_help_desc_2}",
|
||||
disclaimer: S.current.mweb_help_disclaimer),
|
||||
popOnConfirmation: false);
|
||||
|
||||
@@ -134,7 +136,7 @@ class SendPageModes {
|
||||
helpContent: SendPageHelpContent(
|
||||
title: S.current.about_litecoin_privacy,
|
||||
imagePath: "assets/new-ui/mweb_help.svg",
|
||||
description: S.current.mweb_help_desc_1 + "\n\n" + S.current.mweb_help_desc_2,
|
||||
description: "${S.current.mweb_help_desc_1}\n\n${S.current.mweb_help_desc_2}",
|
||||
disclaimer: S.current.mweb_help_disclaimer),
|
||||
popOnConfirmation: false);
|
||||
|
||||
@@ -184,12 +186,12 @@ class NewSendPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NewSendPageState extends State<NewSendPage> {
|
||||
bool _fiatInputMode = false;
|
||||
|
||||
int _selectedOutput = 0;
|
||||
|
||||
List<TextEditingController> _amountControllers = [];
|
||||
List<TextEditingController> _addressControllers = [];
|
||||
List<TextEditingController> _memoControllers = [];
|
||||
final _amountControllers = <TextEditingController>[];
|
||||
final _addressControllers = <TextEditingController>[];
|
||||
final _memoControllers = <TextEditingController>[];
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _addressFocusNode = FocusNode();
|
||||
BuildContext? loadingBottomSheetContext;
|
||||
@@ -205,7 +207,7 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
|
||||
reaction((_) => widget.sendViewModel.outputs[_selectedOutput].sendAll, ((bool all) {
|
||||
if (all) {
|
||||
_fiatInputMode = false;
|
||||
widget.sendViewModel.outputs[_selectedOutput].isFiatEntry = false;
|
||||
_amountControllers[_selectedOutput].text = S.current.all;
|
||||
}
|
||||
}));
|
||||
@@ -439,46 +441,46 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
? widget.sendViewModel.allAmountValidator
|
||||
: widget.sendViewModel.amountValidator(output),
|
||||
amountController: _amountControllers[_selectedOutput],
|
||||
currency: _fiatInputMode
|
||||
currency: output.isFiatEntry
|
||||
? widget.sendViewModel.fiatCurrency.title
|
||||
: widget.sendViewModel.selectedCryptoCurrencySymbol,
|
||||
currencyIconPath: _fiatInputMode
|
||||
currencyIconPath: output.isFiatEntry
|
||||
? ""
|
||||
: widget.sendViewModel.selectedCryptoCurrency
|
||||
.iconPath ??
|
||||
"",
|
||||
hasPicker: (_fiatInputMode ||
|
||||
hasPicker: (output.isFiatEntry ||
|
||||
widget.sendViewModel.hasMultipleTokens),
|
||||
onPickerClicked: () {
|
||||
_presentCurrencyPicker(context);
|
||||
},
|
||||
onPickerClicked: () => _presentCurrencyPicker(context),
|
||||
maxDecimals: output.isFiatEntry
|
||||
? widget.sendViewModel.fiatCurrency.decimals
|
||||
: widget.sendViewModel.useBaseUnits
|
||||
? 0
|
||||
: widget
|
||||
.sendViewModel.selectedCryptoCurrency.decimals,
|
||||
),
|
||||
FiatAmountBar(
|
||||
fiatInputMode: _fiatInputMode,
|
||||
fiatInputMode: output.isFiatEntry,
|
||||
onSwitchButtonPressed: () {
|
||||
setState(() {
|
||||
if(!_fiatInputMode) {
|
||||
widget.sendViewModel.outputs[_selectedOutput]
|
||||
.cryptoAmount =
|
||||
_amountControllers[_selectedOutput].text;
|
||||
}
|
||||
_fiatInputMode = !_fiatInputMode;
|
||||
_amountControllers[_selectedOutput].text =
|
||||
_fiatInputMode
|
||||
? output.fiatAmount
|
||||
: output.displayCryptoAmount;
|
||||
});
|
||||
widget.sendViewModel.outputs[_selectedOutput]
|
||||
.isFiatEntry = !output.isFiatEntry;
|
||||
_amountControllers[_selectedOutput].text =
|
||||
output.isFiatEntry
|
||||
? output.fiatAmount
|
||||
: output.displayCryptoAmount;
|
||||
},
|
||||
fiatAmount: _wrapAmount(output.roundedFiatAmount(6), 20),
|
||||
cryptoAmount:
|
||||
_wrapAmount(output.roundedCryptoAmount(6), 20),
|
||||
allAmount: widget.sendViewModel.balance,
|
||||
cryptoCurrency:
|
||||
cryptoCurrencySymbol:
|
||||
widget.sendViewModel.selectedCryptoCurrencySymbol,
|
||||
fiatCurrency: widget.sendViewModel.fiatCurrency.title,
|
||||
fiatCurrencySymbol:
|
||||
widget.sendViewModel.fiatCurrency.symbol,
|
||||
onAllButtonPressed: () async {
|
||||
output.setSendAll(
|
||||
await widget.sendViewModel.sendingBalance);
|
||||
await output.calculateEstimatedFee();
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -642,10 +644,10 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final amount = _amountControllers[_selectedOutput].text.replaceAll(',', '.');
|
||||
final amount = _amountControllers[_selectedOutput].text.sanitized();
|
||||
final output = widget.sendViewModel.outputs[_selectedOutput];
|
||||
|
||||
if (_fiatInputMode) {
|
||||
if (output.isFiatEntry) {
|
||||
if (amount != output.fiatAmount) {
|
||||
output.sendAll = false;
|
||||
output.setFiatAmount(amount);
|
||||
@@ -705,11 +707,11 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
|
||||
for (var i = 0; i < widget.sendViewModel.outputs.length; i++) {
|
||||
if (i < _amountControllers.length && !widget.sendViewModel.outputs[i].sendAll) {
|
||||
if (_fiatInputMode) {
|
||||
if (widget.sendViewModel.outputs[i].isFiatEntry) {
|
||||
widget.sendViewModel.outputs[i].setFiatAmount(_amountControllers[i].text);
|
||||
} else {
|
||||
final amount = widget.sendViewModel.amountParsingProxy.getCanonicalCryptoAmount(
|
||||
_amountControllers[i].text.replaceAll(",", "."),
|
||||
_amountControllers[i].text.sanitized(),
|
||||
widget.sendViewModel.selectedCryptoCurrency);
|
||||
widget.sendViewModel.outputs[i].setCryptoAmount(amount);
|
||||
}
|
||||
@@ -752,9 +754,9 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
}
|
||||
|
||||
if (widget.sendViewModel.wallet.type == WalletType.monero) {
|
||||
int amount = 0;
|
||||
var amount = Money.zero(widget.sendViewModel.wallet.currency);
|
||||
for (var item in widget.sendViewModel.outputs) {
|
||||
amount += item.formattedCryptoAmount;
|
||||
amount += item.cryptoAmountMoney;
|
||||
}
|
||||
if (monero!.needExportOutputs(widget.sendViewModel.wallet, amount)) {
|
||||
if (widget.sendViewModel.wallet.hardwareWalletType == HardwareWalletType.trezor) {
|
||||
@@ -811,13 +813,11 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
}
|
||||
|
||||
void _presentCurrencyPicker(BuildContext context) {
|
||||
if (!_fiatInputMode && !widget.sendViewModel.hasMultipleTokens) {
|
||||
return;
|
||||
}
|
||||
|
||||
final output = widget.sendViewModel.outputs[_selectedOutput];
|
||||
|
||||
if (_fiatInputMode) {
|
||||
if (!output.isFiatEntry && !widget.sendViewModel.hasMultipleTokens) return;
|
||||
|
||||
if (output.isFiatEntry) {
|
||||
FiatCurrencyPickerSheet.show(
|
||||
context: context,
|
||||
selected: widget.sendViewModel.fiatCurrency,
|
||||
@@ -836,7 +836,7 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
amount: '${r.availableBalance} ${r.asset.title}',
|
||||
fiat: isFiatDisabled
|
||||
? null
|
||||
: '${r.fiatAvailableBalanceRaw} ${r.fiatCurrencyTicker}',
|
||||
: '${r.fiatAvailableBalanceRaw} ${r.fiatCurrency?.symbol}',
|
||||
fiatValue: isFiatDisabled ? null : double.tryParse(r.fiatAvailableBalanceRaw),
|
||||
),
|
||||
};
|
||||
@@ -859,10 +859,11 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
|
||||
void _handleLightningInvoicePaste() {
|
||||
try {
|
||||
final lnAmount = CryptoCurrency.btcln.formatAmount(
|
||||
BigInt.from(getBolt11Amount(_addressControllers[_selectedOutput].text) ?? 0));
|
||||
if (lnAmount != 0) {
|
||||
_amountControllers[_selectedOutput].text = lnAmount;
|
||||
final lnAmount = getBolt11Amount(_addressControllers[_selectedOutput].text) ??
|
||||
Money.zero(CryptoCurrency.btcln);
|
||||
if (!lnAmount.isZero) {
|
||||
_amountControllers[_selectedOutput].text =
|
||||
widget.sendViewModel.amountParsingProxy.asDisplayString(lnAmount);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
@@ -885,6 +886,12 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
await widget.sendViewModel.fetchTokenForContractAddress(paymentRequest.contractAddress!);
|
||||
}
|
||||
|
||||
// This automatically switches to lightning mode if you are in a bitcoin wallet
|
||||
if (result.addressDetectionResult?.detectedCurrency == CryptoCurrency.btcln) {
|
||||
widget.sendViewModel.selectedCryptoCurrency = CryptoCurrency.btcln;
|
||||
widget.sendViewModel.coinTypeToSpendFrom = UnspentCoinType.lightning;
|
||||
}
|
||||
|
||||
switch (result.type) {
|
||||
case PaymentFlowType.singleWallet:
|
||||
case PaymentFlowType.multipleWallets:
|
||||
@@ -1216,7 +1223,7 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
try{
|
||||
_amountControllers[_selectedOutput].text = widget.sendViewModel.amountParsingProxy
|
||||
.getDisplayCryptoAmount(
|
||||
paymentRequest.amount, widget.sendViewModel.selectedCryptoCurrency);
|
||||
paymentRequest.amount, widget.sendViewModel.selectedCryptoCurrency);
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
+166
-160
@@ -8,7 +8,6 @@ import 'package:cake_wallet/exchange/exchange_trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
@@ -30,6 +29,7 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/utils/debounce.dart';
|
||||
import 'package:cake_wallet/utils/decimal_input_formatter.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
@@ -132,14 +132,13 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
if (double.tryParse(depositFiatAmountController.text) != null) {
|
||||
widget.exchangeViewModel
|
||||
.setDepositAmountFromFiat(fiatAmount: depositFiatAmountController.text);
|
||||
receiveKey.currentState!.updateFiatAmount();
|
||||
receiveKey.currentState?.updateFiatAmount();
|
||||
}
|
||||
});
|
||||
});
|
||||
receiveFiatAmountController.addListener(() {
|
||||
if (!receiveKey.currentState!.amountFocusNode.hasFocus) {
|
||||
return;
|
||||
}
|
||||
if (!receiveKey.currentState!.amountFocusNode.hasFocus) return;
|
||||
|
||||
widget.exchangeViewModel.enableFixedRateMode();
|
||||
widget.exchangeViewModel.isSendAllEnabled = false;
|
||||
Future.delayed(Duration(milliseconds: 200)).then((_) {
|
||||
@@ -168,6 +167,8 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
});
|
||||
|
||||
reaction((_) => widget.exchangeViewModel.depositAmount, (String amount) {
|
||||
if (depositKey.currentState!.amountFocusNode.hasFocus) return;
|
||||
|
||||
if (widget.exchangeViewModel.isSendAllEnabled) {
|
||||
depositAmountController.text = S.of(context).all;
|
||||
} else if (depositAmountController.text != amount && amount != S.of(context).all) {
|
||||
@@ -191,14 +192,14 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
widget.exchangeViewModel, widget.exchangeViewModel.depositCurrency, depositKey));
|
||||
|
||||
reaction(
|
||||
(_) => widget.exchangeViewModel.receiveCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
_onCurrencyChange(currency, widget.exchangeViewModel, receiveKey));
|
||||
(_) => widget.exchangeViewModel.receiveCurrency,
|
||||
(currency) => _onCurrencyChange(currency, widget.exchangeViewModel, receiveKey),
|
||||
);
|
||||
|
||||
reaction(
|
||||
(_) => widget.exchangeViewModel.depositCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
_onCurrencyChange(currency, widget.exchangeViewModel, depositKey));
|
||||
(_) => widget.exchangeViewModel.depositCurrency,
|
||||
(currency) => _onCurrencyChange(currency, widget.exchangeViewModel, depositKey),
|
||||
);
|
||||
|
||||
reaction((_) => widget.exchangeViewModel.depositAddress, (String address) {
|
||||
if (depositKey.currentState!.addressController.text != address) {
|
||||
@@ -206,7 +207,7 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => widget.exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {});
|
||||
reaction((_) => widget.exchangeViewModel.isDepositAddressEnabled, (isEnabled) {});
|
||||
|
||||
reaction((_) => widget.exchangeViewModel.receiveAmount, (String amount) {
|
||||
if (receiveKey.currentState!.amountController.text != amount) {
|
||||
@@ -302,7 +303,8 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
}
|
||||
widget.exchangeViewModel.isReceiveAmountEntered = false;
|
||||
widget.exchangeViewModel.isFixedRateMode = false;
|
||||
if (!receiveKey.currentState!.amountFocusNode.hasFocus) {
|
||||
if (receiveKey.currentState != null &&
|
||||
!receiveKey.currentState!.amountFocusNode.hasFocus) {
|
||||
receiveKey.currentState!.updateFiatAmount();
|
||||
}
|
||||
});
|
||||
@@ -397,14 +399,12 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
if (key == depositKey && !isCurrentTypeWallet) exchangeViewModel.isSendFromExternal = true;
|
||||
if (key == depositKey && isCurrentTypeWallet) exchangeViewModel.isSendFromExternal = false;
|
||||
|
||||
|
||||
key.currentState!.changeSelectedCurrency(currency);
|
||||
|
||||
if (key == depositKey)
|
||||
if (key == depositKey) {
|
||||
key.currentState!.changeAddress(
|
||||
address: isCurrentTypeWallet
|
||||
? exchangeViewModel.wallet.walletAddresses.addressForExchange
|
||||
: '');
|
||||
address:
|
||||
isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.addressForExchange : '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
|
||||
@@ -491,7 +491,7 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
physics: ClampingScrollPhysics(),
|
||||
physics: const ClampingScrollPhysics(),
|
||||
controller: ModalScrollController.of(context),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -507,7 +507,8 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
: null,
|
||||
key: depositKey,
|
||||
title: S.of(context).send,
|
||||
initialCurrency: widget.exchangeViewModel.depositCurrency,
|
||||
currency: widget.exchangeViewModel.depositCurrency,
|
||||
useBaseUnit: widget.exchangeViewModel.useDepositBaseUnit,
|
||||
hasRefundAddress: true,
|
||||
currencies: widget.exchangeViewModel.depositCurrencies,
|
||||
onCurrencySelected: (currency) {
|
||||
@@ -521,11 +522,13 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
value != S.of(context).all
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.isFixedRateMode
|
||||
currency: widget.exchangeViewModel.isFixedRateMode
|
||||
? widget.exchangeViewModel.receiveCurrency
|
||||
: widget.exchangeViewModel.depositCurrency,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
minValue:
|
||||
widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue:
|
||||
widget.exchangeViewModel.limits.max.toString(),
|
||||
amountParsingProxy:
|
||||
widget.exchangeViewModel.amountParsingProxy,
|
||||
).call(value)
|
||||
@@ -578,8 +581,9 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
onDispose: disposeBestRateSync,
|
||||
key: receiveKey,
|
||||
title: S.of(context).receive,
|
||||
initialCurrency: widget.exchangeViewModel.receiveCurrency,
|
||||
currencies: widget.exchangeViewModel.receiveCurrencies,
|
||||
currency: widget.exchangeViewModel.receiveCurrency,
|
||||
useBaseUnit: widget.exchangeViewModel.useReceiveBaseUnit,
|
||||
onCurrencySelected: (currency) {
|
||||
if (currency is CryptoCurrency) {
|
||||
widget.exchangeViewModel
|
||||
@@ -684,7 +688,6 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
isLoading: widget.exchangeViewModel.tradeState is TradeIsCreating,
|
||||
),
|
||||
),
|
||||
SizedBox()
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -700,18 +703,14 @@ class _NewSwapPageState extends State<NewSwapPage> {
|
||||
}
|
||||
|
||||
bool _swapButtonDisabled() {
|
||||
if (widget.exchangeViewModel.selectedProviders.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
if (widget.exchangeViewModel.receiveAddress.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
if (widget.exchangeViewModel.status is! SyncedSyncStatus) {
|
||||
return true;
|
||||
}
|
||||
if (widget.exchangeViewModel.depositAmount.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
if (widget.exchangeViewModel.selectedProviders.isEmpty) return true;
|
||||
|
||||
if (widget.exchangeViewModel.receiveAddress.isEmpty) return true;
|
||||
|
||||
if (widget.exchangeViewModel.status is! SyncedSyncStatus) return true;
|
||||
|
||||
if (widget.exchangeViewModel.depositAmount.isEmpty) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -724,9 +723,7 @@ class SwapProviderPreview extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
if (exchangeViewModel.depositAmount.isEmpty) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
if (exchangeViewModel.depositAmount.isEmpty) return SizedBox.shrink();
|
||||
|
||||
final provider = exchangeViewModel.forcedProvider ?? exchangeViewModel.providerDisplay;
|
||||
final rate = exchangeViewModel.forcedProvider == null
|
||||
@@ -737,8 +734,10 @@ class SwapProviderPreview extends StatelessWidget {
|
||||
onTap: () {
|
||||
if (provider != null) {
|
||||
Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) =>
|
||||
Material(child: ProviderSelectorPage(exchangeViewModel: exchangeViewModel))));
|
||||
builder: (context) => Material(
|
||||
child: ProviderSelectorPage(exchangeViewModel: exchangeViewModel),
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
@@ -756,7 +755,7 @@ class SwapProviderPreview extends StatelessWidget {
|
||||
spacing: 12,
|
||||
children: [
|
||||
if (provider != null)
|
||||
CakeImageWidget(imageUrl:provider.description.image,width:28,height:28),
|
||||
CakeImageWidget(imageUrl: provider.description.image, width: 28, height: 28),
|
||||
if (provider == null) CupertinoActivityIndicator(),
|
||||
Text(
|
||||
provider?.title ?? "${S.of(context).finding_provider}...",
|
||||
@@ -798,7 +797,7 @@ class SwapProviderPreview extends StatelessWidget {
|
||||
class SwapAmountBox extends StatefulWidget {
|
||||
SwapAmountBox({
|
||||
Key? key,
|
||||
required this.initialCurrency,
|
||||
required this.currency,
|
||||
required this.currencies,
|
||||
required this.onCurrencySelected,
|
||||
this.currencyValueValidator,
|
||||
@@ -806,6 +805,7 @@ class SwapAmountBox extends StatefulWidget {
|
||||
this.title = '',
|
||||
this.hasRefundAddress = false,
|
||||
this.hasAllAmount = false,
|
||||
this.useBaseUnit = false,
|
||||
this.allAmount,
|
||||
this.onPushPasteButton,
|
||||
this.onPushAddressBookButton,
|
||||
@@ -819,7 +819,8 @@ class SwapAmountBox extends StatefulWidget {
|
||||
final Function(Currency) onCurrencySelected;
|
||||
final String title;
|
||||
final bool isReceiverCard;
|
||||
final Currency initialCurrency;
|
||||
final Currency currency;
|
||||
final bool useBaseUnit;
|
||||
final bool hasRefundAddress;
|
||||
final FormFieldValidator<String>? currencyValueValidator;
|
||||
final FormFieldValidator<String>? addressTextFieldValidator;
|
||||
@@ -847,10 +848,6 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedCurrency = widget.initialCurrency;
|
||||
|
||||
super.initState();
|
||||
|
||||
if (widget.isReceiverCard) {
|
||||
memoController.text = widget.exchangeViewModel.receiveAddressExtraId;
|
||||
|
||||
@@ -868,6 +865,8 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -880,18 +879,18 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
late Currency _selectedCurrency;
|
||||
bool _fiatInputMode = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currencyToShow = (_selectedCurrency is CryptoCurrency)
|
||||
final currencyToShow = (widget.currency is CryptoCurrency)
|
||||
? widget.exchangeViewModel.amountParsingProxy
|
||||
.getCryptoSymbol(_selectedCurrency as CryptoCurrency)
|
||||
: _selectedCurrency.name.toUpperCase();
|
||||
.getCryptoSymbol(widget.currency as CryptoCurrency)
|
||||
: widget.currency.name.toUpperCase();
|
||||
|
||||
|
||||
final chainIconPath = (_selectedCurrency is CryptoCurrency) ? _getCurrencyChainIconPath(_selectedCurrency as CryptoCurrency) : null;
|
||||
final chainIconPath = (widget.currency is CryptoCurrency)
|
||||
? _getCurrencyChainIconPath(widget.currency as CryptoCurrency)
|
||||
: null;
|
||||
|
||||
return Column(
|
||||
spacing: 12,
|
||||
@@ -927,36 +926,48 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
children: [
|
||||
Flexible(
|
||||
child: IntrinsicWidth(
|
||||
child: TextFormField(
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||
validator: _fiatInputMode ? null : widget.currencyValueValidator,
|
||||
controller:
|
||||
_fiatInputMode ? fiatAmountController : amountController,
|
||||
focusNode: amountFocusNode,
|
||||
style: TextStyle(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: !widget.useBaseUnit,
|
||||
),
|
||||
validator: _fiatInputMode ? null : widget.currencyValueValidator,
|
||||
controller:
|
||||
_fiatInputMode ? fiatAmountController : amountController,
|
||||
focusNode: amountFocusNode,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w400),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
isDense: true,
|
||||
hintText: "0",
|
||||
fillColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
isDense: true,
|
||||
hintText: "0",
|
||||
fillColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
),
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
DecimalInputFormatter(
|
||||
maxDecimals:
|
||||
widget.useBaseUnit ? 0 : widget.currency.decimals,
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
if (_fiatInputMode)
|
||||
Center(
|
||||
child: Text(
|
||||
widget.exchangeViewModel.fiat.title,
|
||||
style: TextStyle(
|
||||
child: Text(
|
||||
widget.exchangeViewModel.fiat.title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
)),
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -965,17 +976,19 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(999999)),
|
||||
borderRadius: BorderRadius.circular(999999),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0, bottom: 4, left: 4, right: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TokenImageWidget(
|
||||
imageUrl: _selectedCurrency.iconPath ?? "",
|
||||
size: 28,
|
||||
CakeImageWidget(
|
||||
imageUrl: widget.currency.iconPath ?? "",
|
||||
width: 28,
|
||||
height: 28,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
currencyToShow,
|
||||
textAlign: TextAlign.center,
|
||||
@@ -987,12 +1000,13 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
width: 12,
|
||||
height: 12,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn),
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 6)
|
||||
const SizedBox(width: 6)
|
||||
] else
|
||||
SizedBox(width: 10),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
width:16,height:16,
|
||||
decoration: BoxDecoration(
|
||||
@@ -1002,17 +1016,20 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: CakeImageWidget(imageUrl:
|
||||
"assets/new-ui/dropdown_arrow.svg",
|
||||
width: 4,
|
||||
height: 4,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
)),
|
||||
quarterTurns: 2,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/dropdown_arrow.svg",
|
||||
width: 4,
|
||||
height: 4,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width:4),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -1028,36 +1045,33 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
!widget.exchangeViewModel.hasAllAmount);
|
||||
|
||||
return FiatAmountBar(
|
||||
foregroundElementColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
allAmountColor: hasAllAmount ? null : Colors.transparent,
|
||||
allAmountTextColor:
|
||||
hasAllAmount ? null : Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fiatInputMode: _fiatInputMode,
|
||||
allAmount: !hasAllAmount
|
||||
? widget.isReceiverCard
|
||||
? null
|
||||
: widget.exchangeViewModel.balanceDisplay
|
||||
: widget.exchangeViewModel.depositAvailableAmount,
|
||||
onSwitchButtonPressed: () {
|
||||
setState(() {
|
||||
_fiatInputMode = !_fiatInputMode;
|
||||
});
|
||||
if (_fiatInputMode) {
|
||||
updateFiatAmount();
|
||||
}
|
||||
},
|
||||
onAllButtonPressed: widget.allAmount,
|
||||
cryptoAmount: widget.isReceiverCard
|
||||
? widget.exchangeViewModel.roundedReceiveAmount(6)
|
||||
: widget.exchangeViewModel.roundedDepositAmount(6),
|
||||
fiatAmount: widget.isReceiverCard
|
||||
? widget.exchangeViewModel.roundedReceiveAmountFiat(6)
|
||||
: widget.exchangeViewModel.roundedDepositAmountFiat(6),
|
||||
cryptoCurrency: widget.isReceiverCard
|
||||
? widget.exchangeViewModel.receiveCurrency.title
|
||||
: widget.exchangeViewModel.depositCurrency.title,
|
||||
fiatCurrency: widget.exchangeViewModel.fiat.name);
|
||||
foregroundElementColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
allAmountColor: hasAllAmount ? null : Colors.transparent,
|
||||
allAmountTextColor:
|
||||
hasAllAmount ? null : Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fiatInputMode: _fiatInputMode,
|
||||
allAmount: !hasAllAmount
|
||||
? widget.isReceiverCard
|
||||
? null
|
||||
: widget.exchangeViewModel.balanceDisplay
|
||||
: widget.exchangeViewModel.depositAvailableAmount,
|
||||
onSwitchButtonPressed: () {
|
||||
setState(() => _fiatInputMode = !_fiatInputMode);
|
||||
if (_fiatInputMode) {
|
||||
updateFiatAmount();
|
||||
}
|
||||
},
|
||||
onAllButtonPressed: widget.allAmount,
|
||||
cryptoAmount: widget.isReceiverCard
|
||||
? widget.exchangeViewModel.roundedReceiveAmount(6)
|
||||
: widget.exchangeViewModel.roundedDepositAmount(6),
|
||||
fiatAmount: widget.isReceiverCard
|
||||
? widget.exchangeViewModel.roundedReceiveAmountFiat(6)
|
||||
: widget.exchangeViewModel.roundedDepositAmountFiat(6),
|
||||
cryptoCurrencySymbol: currencyToShow,
|
||||
fiatCurrencySymbol: widget.exchangeViewModel.fiat.symbol,
|
||||
);
|
||||
},
|
||||
),
|
||||
Observer(
|
||||
@@ -1106,22 +1120,25 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
Text(addressDescription,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.primary))
|
||||
Text(
|
||||
addressDescription,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
)
|
||||
],
|
||||
),
|
||||
RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: CakeImageWidget(imageUrl:
|
||||
"assets/new-ui/dropdown_arrow.svg",
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/dropdown_arrow.svg",
|
||||
colorFilter: ColorFilter.mode(
|
||||
(addressEmpty && widget.isReceiverCard)
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
BlendMode.srcIn),
|
||||
(addressEmpty && widget.isReceiverCard)
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -1145,18 +1162,14 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
widget.exchangeViewModel.receiveAddress.isEmpty) ...[
|
||||
ModernButton.svg(
|
||||
svgPath: "assets/new-ui/paste.svg",
|
||||
onPressed: () {
|
||||
widget.onPushPasteButton?.call(context);
|
||||
},
|
||||
onPressed: () => widget.onPushPasteButton?.call(context),
|
||||
size: 36,
|
||||
iconSize: 20,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
ModernButton.svg(
|
||||
svgPath: "assets/new-ui/scan.svg",
|
||||
onPressed: () {
|
||||
_presentQRScanner(context);
|
||||
},
|
||||
onPressed: () => _presentQRScanner(context),
|
||||
size: 36,
|
||||
iconSize: 20,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
@@ -1171,7 +1184,7 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
final selected = widget.exchangeViewModel.receiveCurrency;
|
||||
final labelType = memoLabelTypeFor(selected);
|
||||
if (labelType == null) return const SizedBox.shrink();
|
||||
|
||||
|
||||
final isDestinationTag = labelType == MemoLabelType.destinationTag;
|
||||
final hint = isDestinationTag
|
||||
? S.of(context).destination_tag_optional
|
||||
@@ -1179,7 +1192,7 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
final disclaimer = isDestinationTag
|
||||
? S.of(context).destination_tag_swap_disclaimer
|
||||
: S.of(context).memo_swap_disclaimer;
|
||||
|
||||
|
||||
return NewSendMemoInput(
|
||||
memoController: memoController,
|
||||
maxMemoLength: isDestinationTag ? 20 : 256,
|
||||
@@ -1196,10 +1209,6 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
);
|
||||
}
|
||||
|
||||
void changeSelectedCurrency(Currency currency) {
|
||||
setState(() => _selectedCurrency = currency);
|
||||
}
|
||||
|
||||
void changeAddress({required String address}) {
|
||||
setState(() => addressController.text = _normalizeAddressFormat(address));
|
||||
}
|
||||
@@ -1342,18 +1351,15 @@ class SwapAmountBoxState extends State<SwapAmountBox> {
|
||||
}
|
||||
|
||||
String? _getCurrencyChainIconPath(CryptoCurrency curr) {
|
||||
try{
|
||||
try {
|
||||
if (curr.chainIconPath != null) return curr.chainIconPath!;
|
||||
|
||||
if(curr.chainIconPath != null) return curr.chainIconPath!;
|
||||
|
||||
|
||||
if(curr.tag != null) {
|
||||
if (curr.tag != null) {
|
||||
final currencyFromTag = CryptoCurrency.fromString(curr.tag!);
|
||||
if(currencyFromTag.chainIconPath != null) {
|
||||
return currencyFromTag.chainIconPath!;
|
||||
}
|
||||
|
||||
if (currencyFromTag.chainIconPath != null) return currencyFromTag.chainIconPath!;
|
||||
}
|
||||
}catch(_){}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
import '../../../pages/receive_page.dart';
|
||||
import '../../../pages/scan_page.dart';
|
||||
import 'coin_action_button.dart';
|
||||
|
||||
class CoinActionRow extends StatelessWidget {
|
||||
@@ -165,11 +164,11 @@ class CoinActionRow extends StatelessWidget {
|
||||
var unspentCoinType = UnspentCoinType.any;
|
||||
if (SendViewModelBase.isNonZeroAmountLightningInvoice(code)) {
|
||||
unspentCoinType = UnspentCoinType.lightning;
|
||||
final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(getBolt11Amount(code) ?? 0));
|
||||
final amount = getBolt11Amount(code)?.toString() ?? "0";
|
||||
req = PaymentRequest(code, amount, "", "", "");
|
||||
} else if (SendViewModelBase.isLnurlInvoice(code)) {
|
||||
unspentCoinType = UnspentCoinType.lightning;
|
||||
final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(await LNURL.getPayRequestAmount(code) ?? 0));
|
||||
final amount = (await LNURL.getPayRequestAmount(code))?.toString() ?? "0";
|
||||
req = PaymentRequest(code, amount, "", "", "");
|
||||
} else if (OpenCryptoPayService.isOpenCryptoPayQR(code)) {
|
||||
req = PaymentRequest(code, "", "", "", "");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/anonpay_history_tile.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_order_tile.dart';
|
||||
@@ -14,6 +15,7 @@ import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/payjoin_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -115,9 +117,6 @@ class HistorySection extends StatelessWidget {
|
||||
final trade = item.trade;
|
||||
final tradeFrom = trade.from;
|
||||
final tradeTo = trade.to;
|
||||
if (tradeFrom == null || tradeTo == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
@@ -127,8 +126,8 @@ class HistorySection extends StatelessWidget {
|
||||
to: tradeTo,
|
||||
provider: trade.provider,
|
||||
date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now(), localeName),
|
||||
amount: trade.amountFormatted(),
|
||||
receiveAmount: trade.receiveAmountFormatted(),
|
||||
amount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.amountFormatted(),
|
||||
receiveAmount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.receiveAmountFormatted(),
|
||||
roundedBottom: roundedBottom,
|
||||
roundedTop: roundedTop,
|
||||
bottomSeparator: !roundedBottom,
|
||||
@@ -154,7 +153,7 @@ class HistorySection extends StatelessWidget {
|
||||
child: HistoryOrderTile(
|
||||
date: _formatTransactionDate(item.order.createdAt, localeName),
|
||||
amount: item.orderFormattedAmount,
|
||||
amountFiat: "USD 0.00",
|
||||
amountFiat: "",
|
||||
roundedBottom: roundedBottom,
|
||||
roundedTop: roundedTop,
|
||||
bottomSeparator: !roundedBottom,
|
||||
@@ -171,8 +170,8 @@ class HistorySection extends StatelessWidget {
|
||||
child: PayjoinHistoryTile(
|
||||
createdAt: _formatTransactionDate(session.inProgressSince!, localeName),
|
||||
amount: dashboardViewModel.appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(
|
||||
session.amount.toInt(), CryptoCurrency.btc),
|
||||
.asDisplayString(Money(
|
||||
session.amount, CryptoCurrency.btc)),
|
||||
currency: item.transaction?.from ?? "BTC",
|
||||
state: item.status,
|
||||
isSending: session.isSenderSession,
|
||||
|
||||
@@ -15,13 +15,13 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
required this.roundedTop,
|
||||
required this.roundedBottom,
|
||||
required this.bottomSeparator,
|
||||
required this.from,
|
||||
required this.to,
|
||||
this.from,
|
||||
this.to,
|
||||
required this.swapState,
|
||||
required this.provider});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final CryptoCurrency? from;
|
||||
final CryptoCurrency? to;
|
||||
final ExchangeProviderDescription provider;
|
||||
final String date;
|
||||
final String amount;
|
||||
@@ -32,6 +32,11 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
final TradeState swapState;
|
||||
|
||||
Widget _getLeadingStack(BuildContext context) {
|
||||
if (from == null || to == null) {
|
||||
return SizedBox(width: 50, height: 50);
|
||||
}
|
||||
|
||||
|
||||
double currencyIconSize = 22.0;
|
||||
|
||||
return SizedBox(
|
||||
@@ -40,7 +45,7 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: _getIconPath(from), width: currencyIconSize, height: currencyIconSize),
|
||||
imageUrl: _getIconPath(from!), width: currencyIconSize, height: currencyIconSize),
|
||||
Positioned(
|
||||
top: currencyIconSize / 2,
|
||||
left: currencyIconSize / 2,
|
||||
@@ -50,7 +55,7 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
Border.all(width: 2, color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
shape: BoxShape.circle),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: _getIconPath(to),
|
||||
imageUrl: _getIconPath(to!),
|
||||
width: currencyIconSize,
|
||||
height: currencyIconSize,
|
||||
),
|
||||
@@ -85,8 +90,8 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500)),
|
||||
Text(from.title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)),
|
||||
if (fromChainIcon.isNotEmpty)
|
||||
if(from?.title != null) Text(from!.title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)),
|
||||
if (fromChainIcon?.isNotEmpty ?? false)
|
||||
CakeImageWidget(
|
||||
imageUrl: fromChainIcon,
|
||||
width: 12,
|
||||
@@ -106,10 +111,11 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
receiveAmount.withMaxDecimals(8),
|
||||
style: TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
if(to!=null)
|
||||
Text(
|
||||
to.title,
|
||||
to!.title,
|
||||
),
|
||||
if (toChainIcon.isNotEmpty) CakeImageWidget(imageUrl: toChainIcon, width: 12, height: 12)
|
||||
if (toChainIcon?.isNotEmpty ?? false) CakeImageWidget(imageUrl: toChainIcon, width: 12, height: 12)
|
||||
],
|
||||
),
|
||||
leadingIcon: _getLeadingStack(context),
|
||||
@@ -137,7 +143,8 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
return "";
|
||||
}
|
||||
|
||||
String _getChainIcon(CryptoCurrency currency) {
|
||||
String? _getChainIcon(CryptoCurrency? currency) {
|
||||
if(currency == null) return null;
|
||||
try {
|
||||
if (currency.title.isNotEmpty) {
|
||||
final parsedCurrency = CryptoCurrency.safeParseCurrencyFromString(currency.title, tag: currency.tag);
|
||||
|
||||
@@ -48,6 +48,8 @@ class _TransactionDetailsModalState extends State<TransactionDetailsModal> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final transactionInfoAmount = widget.transactionDetailsViewModel.transactionInfo.amount;
|
||||
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
@@ -84,12 +86,12 @@ class _TransactionDetailsModalState extends State<TransactionDetailsModal> {
|
||||
),
|
||||
CopyWrapper(
|
||||
requireLongPress: true,
|
||||
data: ClipboardData(text: widget.transactionDetailsViewModel.formattedCryptoAmount),
|
||||
data: ClipboardData(text: transactionInfoAmount.toString()),
|
||||
builder: (context, copied)=> AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: Text(
|
||||
key: ValueKey(copied),
|
||||
copied ? S.of(context).copied : widget.transactionDetailsViewModel.formattedCryptoAmount,
|
||||
copied ? S.of(context).copied : transactionInfoAmount.toStringWithSymbol(),
|
||||
style: TextStyle(fontSize: 28, color: copied ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -70,8 +70,6 @@ class BalanceCard extends StatelessWidget {
|
||||
|
||||
final height = width * 0.62;
|
||||
|
||||
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: designSwitchDuration,
|
||||
width: width,
|
||||
@@ -208,7 +206,7 @@ class BalanceCard extends StatelessWidget {
|
||||
},
|
||||
child: Text(
|
||||
key: ValueKey(fiatFirst ? balance : fiatBalance),
|
||||
fiatFirst ? "$assetName $balance" : "$fiatCurrencyTitle $fiatBalance",
|
||||
fiatFirst ? "$assetName $balance" : fiatBalance,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -119,23 +119,22 @@ class _CardsViewState extends State<CardsView> {
|
||||
late final String walletBalance;
|
||||
late final String walletFiatBalance;
|
||||
if (widget.dashboardViewModel.mwebEnabled && widget.dashboardViewModel.hasMweb) {
|
||||
if(widget.dashboardViewModel.balanceViewModel.displayMode == BalanceDisplayMode.hiddenBalance) {
|
||||
if (widget.dashboardViewModel.balanceViewModel.displayMode ==
|
||||
BalanceDisplayMode.hiddenBalance) {
|
||||
walletBalance = '●●●●●●';
|
||||
walletFiatBalance = '●●●●●●';
|
||||
} else {
|
||||
walletBalance = walletBalanceRecord?.combinedAvailableBalance ?? "0";
|
||||
walletFiatBalance = walletBalanceRecord?.combinedFiatAvailableBalance ?? "0.00";
|
||||
}
|
||||
} else if(widget.dashboardViewModel.balanceViewModel.showCombinedBalance){
|
||||
} else if (widget.dashboardViewModel.balanceViewModel.showCombinedBalance) {
|
||||
walletBalance = "";
|
||||
walletFiatBalance = widget.dashboardViewModel.balanceViewModel.combinedFiatBalance;
|
||||
|
||||
}else {
|
||||
} else {
|
||||
walletBalance = walletBalanceRecord?.availableBalance ?? "0";
|
||||
walletFiatBalance = walletBalanceRecord?.fiatAvailableBalanceRaw ?? "0.00";
|
||||
walletFiatBalance = walletBalanceRecord?.fiatAvailableBalance ?? "0.00";
|
||||
}
|
||||
|
||||
|
||||
// the card designs is empty if widget gets built before it loads.
|
||||
// should get populated before user sees anything
|
||||
final CardDesign cardDesign;
|
||||
@@ -191,7 +190,8 @@ class _CardsViewState extends State<CardsView> {
|
||||
assetName: assetName,
|
||||
capitalizeAssetName: _shouldCapitalizeAssetName(),
|
||||
balance: walletBalance,
|
||||
fiatCurrencyTitle: walletBalanceRecord?.fiatCurrencyTicker ?? widget.dashboardViewModel.settingsStore.fiatCurrency.title,
|
||||
fiatCurrencyTitle: walletBalanceRecord?.fiatCurrency?.title ??
|
||||
widget.dashboardViewModel.settingsStore.fiatCurrency.title,
|
||||
fiatFirst: widget.dashboardViewModel.balanceViewModel.showCombinedBalance,
|
||||
fiatBalance: walletFiatBalance,
|
||||
selected: _selectedIndex == visualIndex,
|
||||
|
||||
@@ -48,12 +48,10 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
|
||||
showMaterialModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return UnconfirmedBalanceModal(
|
||||
balance: "${balance} ${currency.title}",
|
||||
builder: (context) => UnconfirmedBalanceModal(
|
||||
balance: balance.toStringWithSymbol(),
|
||||
currencyIconPath: currency.iconPath ?? "",
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
@@ -101,9 +99,8 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox(
|
||||
width: double.infinity,
|
||||
));
|
||||
: const SizedBox(width: double.infinity),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/decimal_input_formatter.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -134,8 +135,16 @@ class _ReceiveAmountModalState extends State<ReceiveAmountModal> {
|
||||
textAlign: TextAlign.left,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
controller: _amountController,
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(signed: false, decimal: !widget.walletAddressListViewModel.useSatoshi),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: widget.walletAddressListViewModel.selectedCurrencyDecimals > 0,
|
||||
),
|
||||
inputFormatters: [
|
||||
DecimalInputFormatter(
|
||||
maxDecimals:
|
||||
widget.walletAddressListViewModel.selectedCurrencyDecimals,
|
||||
),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hint: Text(
|
||||
widget.walletAddressListViewModel.useSatoshi ? "0" : "0.00000000",
|
||||
@@ -150,6 +159,7 @@ class _ReceiveAmountModalState extends State<ReceiveAmountModal> {
|
||||
border: InputBorder.none,
|
||||
filled: true,
|
||||
fillColor: Colors.transparent,
|
||||
|
||||
),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class FiatAmountBar extends StatelessWidget {
|
||||
const FiatAmountBar(
|
||||
@@ -12,8 +11,8 @@ class FiatAmountBar extends StatelessWidget {
|
||||
this.onAllButtonPressed,
|
||||
required this.cryptoAmount,
|
||||
required this.fiatAmount,
|
||||
required this.cryptoCurrency,
|
||||
required this.fiatCurrency,
|
||||
required this.cryptoCurrencySymbol,
|
||||
required this.fiatCurrencySymbol,
|
||||
this.allAmount,
|
||||
this.foregroundElementColor,
|
||||
this.textColor, this.allAmountColor, this.allAmountTextColor});
|
||||
@@ -24,8 +23,8 @@ class FiatAmountBar extends StatelessWidget {
|
||||
|
||||
final String cryptoAmount;
|
||||
final String fiatAmount;
|
||||
final String cryptoCurrency;
|
||||
final String fiatCurrency;
|
||||
final String cryptoCurrencySymbol;
|
||||
final String fiatCurrencySymbol;
|
||||
final String? allAmount;
|
||||
final Color? foregroundElementColor;
|
||||
final Color? textColor;
|
||||
@@ -47,16 +46,15 @@ class FiatAmountBar extends StatelessWidget {
|
||||
iconSize: 18,
|
||||
onPressed: onSwitchButtonPressed,
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => GestureDetector(
|
||||
onTap: onSwitchButtonPressed,
|
||||
child: Text(
|
||||
fiatInputMode
|
||||
? "${cryptoAmount.isEmpty ? "0" : cryptoAmount.withMaxDecimals(8)} ${cryptoCurrency}"
|
||||
: "${fiatAmount.isEmpty ? "0" : fiatAmount} ${fiatCurrency}",
|
||||
style: TextStyle(color: textColor ?? Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: onSwitchButtonPressed,
|
||||
child: Text(
|
||||
fiatInputMode
|
||||
? "${cryptoAmount.isEmpty ? "0" : cryptoAmount.withMaxDecimals(8)} $cryptoCurrencySymbol"
|
||||
: "${fiatAmount.isEmpty ? "0" : fiatAmount} $fiatCurrencySymbol",
|
||||
style: TextStyle(color: textColor ?? Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (allAmount != null && allAmount!.isNotEmpty)
|
||||
|
||||
@@ -18,7 +18,6 @@ import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
enum l2actions { deposit, withdraw }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/send_page/floating_icon_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/decimal_input_formatter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -9,15 +10,18 @@ class NewSendAmountInput extends StatefulWidget {
|
||||
const NewSendAmountInput(
|
||||
{super.key,
|
||||
required this.currency,
|
||||
required this.maxDecimals,
|
||||
required this.hasPicker,
|
||||
required this.onPickerClicked,
|
||||
required this.currencyIconPath,
|
||||
required this.amountController,
|
||||
this.validator});
|
||||
this.validator,
|
||||
});
|
||||
|
||||
final String currency;
|
||||
final String currencyIconPath;
|
||||
final bool hasPicker;
|
||||
final int maxDecimals;
|
||||
final VoidCallback onPickerClicked;
|
||||
final TextEditingController amountController;
|
||||
final FormFieldValidator<String>? validator;
|
||||
@@ -63,14 +67,19 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: widget.maxDecimals > 0,
|
||||
),
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*[.,]?\d*$'))
|
||||
DecimalInputFormatter(maxDecimals: widget.maxDecimals),
|
||||
],
|
||||
onChanged: state.didChange,
|
||||
controller: widget.amountController,
|
||||
decoration: InputDecoration(hintText: "0", errorMaxLines: 3),
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.maxDecimals > 0 ? "0" : "0.00",
|
||||
errorMaxLines: 3,
|
||||
),
|
||||
onChanged: state.didChange,
|
||||
),
|
||||
),
|
||||
FloatingIconButton(
|
||||
@@ -81,7 +90,6 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
|
||||
widget.amountController.text = data.text!;
|
||||
}
|
||||
}),
|
||||
SizedBox.shrink()
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -17,13 +17,13 @@ import 'package:cake_wallet/utils/address_formatter.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class SendConfirmSheet extends StatefulWidget {
|
||||
@@ -173,8 +173,8 @@ class SendTransactionDetails extends StatelessWidget {
|
||||
double sumBy<T>(List<T> list, double Function(T) picker) =>
|
||||
list.map(picker).fold(0.0, (a, b) => a + b);
|
||||
|
||||
BigInt sumByBigInt<T>(List<T> list, BigInt Function(T) picker) =>
|
||||
list.map(picker).fold(BigInt.zero, (a, b) => a + b);
|
||||
Money sumByMoney<T>(List<T> list, Money Function(T) picker, CryptoCurrency currency) =>
|
||||
list.map(picker).fold(Money.zero(currency), (a, b) => a + b);
|
||||
|
||||
String sumStr<T>(List<T> list, double Function(T) picker) =>
|
||||
sumBy(list, picker).toString();
|
||||
@@ -184,220 +184,188 @@ class SendTransactionDetails extends StatelessWidget {
|
||||
return "${decimals == null ? str : str.withDecimals(decimals)} $unit";
|
||||
}
|
||||
|
||||
|
||||
Widget _buildMainContent(BuildContext context) {
|
||||
final transaction = sendViewModel.pendingTransaction;
|
||||
return Observer(builder: (context) {
|
||||
final transaction = sendViewModel.pendingTransaction;
|
||||
|
||||
final currencySymbol = sendViewModel.amountParsingProxy.getCryptoSymbol(sendViewModel.selectedCryptoCurrency);
|
||||
final currencySymbol =
|
||||
sendViewModel.amountParsingProxy.getCryptoSymbol(sendViewModel.selectedCryptoCurrency);
|
||||
|
||||
final amount = (transaction == null)
|
||||
? sendViewModel.amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
sumByBigInt(sendViewModel.outputs, (o) {
|
||||
final amount = (transaction == null)
|
||||
? sendViewModel.amountParsingProxy.asDisplayString(sumByMoney(sendViewModel.outputs, (o) {
|
||||
final zero = Money.zero(sendViewModel.selectedCryptoCurrency);
|
||||
if (o.sendAll)
|
||||
return sendViewModel.amountParsingProxy.tryParseCryptoString(
|
||||
sendViewModel.balance, sendViewModel.selectedCryptoCurrency) ??
|
||||
BigInt.zero;
|
||||
zero;
|
||||
|
||||
return sendViewModel.selectedCryptoCurrency.tryParseAmount(o.cryptoAmount) ??
|
||||
BigInt.zero;
|
||||
}),
|
||||
sendViewModel.selectedCryptoCurrency)
|
||||
: sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
|
||||
formatAmount(transaction.amountFormatted), sendViewModel.selectedCryptoCurrency).withMaxDecimals(8);
|
||||
return sendViewModel.selectedCryptoCurrency.tryParseAmount(o.cryptoAmount) ?? zero;
|
||||
}, sendViewModel.selectedCryptoCurrency))
|
||||
: sendViewModel.amountParsingProxy.asDisplayString(transaction.amount);
|
||||
|
||||
final fee =
|
||||
"${(transaction == null) ? sendViewModel.amountParsingProxy.getDisplayCryptoStringFromBigInt(sumByBigInt(
|
||||
sendViewModel.outputs.where((e) => !e.sendAll).toList(),
|
||||
(o) {
|
||||
if (sendViewModel.selectedCryptoCurrency == CryptoCurrency.btcln) {
|
||||
return sendViewModel.amountParsingProxy.tryParseCryptoString(
|
||||
o.estimatedFee.replaceAll(",", ""),
|
||||
sendViewModel.selectedCryptoCurrency) ??
|
||||
BigInt.zero;
|
||||
}
|
||||
return sendViewModel.currency.tryParseAmount(o.estimatedFee.replaceAll(",", "")) ??
|
||||
BigInt.zero;
|
||||
},
|
||||
), sendViewModel.currency) : sendViewModel.amountParsingProxy.getDisplayCryptoAmount(transaction.feeFormattedValue, sendViewModel.currency)} ${sendViewModel.currencySymbol}";
|
||||
final fee = (transaction == null)
|
||||
? sendViewModel.amountParsingProxy.asDisplayString(sumByMoney(
|
||||
sendViewModel.outputs,
|
||||
(o) => o.estimatedFee,
|
||||
sendViewModel.currency,
|
||||
))
|
||||
: sendViewModel.amountParsingProxy.asDisplayString(transaction.fee);
|
||||
|
||||
final fiatAmount = (transaction == null)
|
||||
? sumWithUnit(
|
||||
sendViewModel.outputs,
|
||||
(o) => double.tryParse(o.fiatAmount.replaceAll(",", "")) ?? 0,
|
||||
sendViewModel.fiatCurrency.title,
|
||||
decimals:2
|
||||
)
|
||||
: sendViewModel.pendingTransactionFiatAmountFormatted;
|
||||
final fiatAmount = (transaction == null)
|
||||
? sumWithUnit(
|
||||
sendViewModel.outputs,
|
||||
(o) => double.tryParse(o.fiatAmount.replaceAll(",", "")) ?? 0,
|
||||
sendViewModel.fiatCurrency.title,
|
||||
decimals: 2)
|
||||
: sendViewModel.pendingTransactionFiatAmountFormatted;
|
||||
|
||||
final fiatFee = (transaction == null)
|
||||
? sumWithUnit(
|
||||
sendViewModel.outputs,
|
||||
(o) => double.tryParse(o.estimatedFeeFiatAmount.replaceAll(",", "")) ?? 0,
|
||||
sendViewModel.fiatCurrency.title,
|
||||
decimals:2
|
||||
)
|
||||
: sendViewModel.pendingTransactionFeeFiatAmountFormatted;
|
||||
final fiatFee = (transaction == null)
|
||||
? sumWithUnit(
|
||||
sendViewModel.outputs,
|
||||
(o) => double.tryParse(o.estimatedFeeFiatAmount.replaceAll(",", "")) ?? 0,
|
||||
sendViewModel.fiatCurrency.title,
|
||||
decimals: 2)
|
||||
: sendViewModel.pendingTransactionFeeFiatAmountFormatted;
|
||||
|
||||
final showAddress = !sendViewModel.outputs.any((e) =>
|
||||
RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
|
||||
RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
|
||||
(e.isParsedAddress &&
|
||||
e.parsedAddress.addresses.isNotEmpty &&
|
||||
RegExp(AddressValidator.lnurlMatcher)
|
||||
.hasMatch(e.parsedAddress.addresses.first.toLowerCase())));
|
||||
final showAddress = !sendViewModel.outputs.any((e) =>
|
||||
RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()) ||
|
||||
RegExp(AddressValidator.lnurlMatcher).hasMatch(e.address.toLowerCase()) ||
|
||||
(e.isParsedAddress &&
|
||||
e.parsedAddress.addresses.isNotEmpty &&
|
||||
RegExp(AddressValidator.lnurlMatcher)
|
||||
.hasMatch(e.parsedAddress.addresses.firstOrNull?.toLowerCase() ?? "")));
|
||||
|
||||
final outputs = sendViewModel.outputs;
|
||||
final outputs = sendViewModel.outputs;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 24,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
amount,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
Text(currencySymbol,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
fiatAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (outputs.length >= 1 && (outputs.first.extractedAddress.isNotEmpty || outputs.first.address.isNotEmpty) && showAddress)
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 24,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
amount,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
Text(currencySymbol,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
S.of(context).send_to,
|
||||
fiatAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
if (outputs.length == 1)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: AddressFormatter.buildSegmentedAddress(
|
||||
address: outputs.first.isParsedAddress ? outputs.first.extractedAddress : outputs.first.address,
|
||||
evenTextStyle:
|
||||
TextStyle(color: Theme.of(context).colorScheme.onSurface)),
|
||||
),
|
||||
)
|
||||
else
|
||||
AnimatedDropdown(
|
||||
content: Column(
|
||||
children: outputs
|
||||
.map(
|
||||
(item) => Column(children: [
|
||||
MultiSendAddressPreview(
|
||||
index: outputs.indexOf(item) + 1,
|
||||
address: item.isParsedAddress
|
||||
? item.extractedAddress
|
||||
: item.address,
|
||||
amount:
|
||||
"${item.roundedCryptoAmount(8).withLocalSeperator(sendViewModel.languageCode)} ${sendViewModel.currency.title}",
|
||||
fiatAmount:
|
||||
"${item.fiatAmount.withLocalSeperator(sendViewModel.languageCode)} ${sendViewModel.fiatCurrency.title}",
|
||||
),
|
||||
if(item != outputs.last)
|
||||
Container(width: double.infinity, height: 1, color: Theme.of(context).colorScheme.surfaceContainerHigh)
|
||||
],),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
dropdownText: "${outputs.length} ${S.of(context).addresses}"),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).fee,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface)),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
fee.withLocalSeperator(sendViewModel.languageCode),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
Text(fiatFee.withLocalSeperator(sendViewModel.languageCode),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (sendViewModel.isElectrumWallet) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
if (outputs.length >= 1 &&
|
||||
(outputs.first.extractedAddress.isNotEmpty || outputs.first.address.isNotEmpty) &&
|
||||
showAddress)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).send_to,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
if (outputs.length == 1)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: AddressFormatter.buildSegmentedAddress(
|
||||
address: outputs.first.isParsedAddress
|
||||
? outputs.first.extractedAddress
|
||||
: outputs.first.address,
|
||||
evenTextStyle:
|
||||
TextStyle(color: Theme.of(context).colorScheme.onSurface)),
|
||||
),
|
||||
)
|
||||
else
|
||||
AnimatedDropdown(
|
||||
content: Column(
|
||||
children: outputs
|
||||
.map(
|
||||
(item) => Column(
|
||||
children: [
|
||||
MultiSendAddressPreview(
|
||||
index: outputs.indexOf(item) + 1,
|
||||
address: item.isParsedAddress
|
||||
? item.extractedAddress
|
||||
: item.address,
|
||||
amount:
|
||||
"${item.roundedCryptoAmount(8).withLocalSeperator(sendViewModel.languageCode)} ${sendViewModel.currency.title}",
|
||||
fiatAmount:
|
||||
"${item.fiatAmount.withDecimals(2).withLocalSeperator(sendViewModel.languageCode)} ${sendViewModel.fiatCurrency.title}",
|
||||
),
|
||||
if (item != outputs.last)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color:
|
||||
Theme.of(context).colorScheme.surfaceContainerHigh)
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
dropdownText: "${outputs.length} ${S.of(context).addresses}"),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).network,
|
||||
Text(S.of(context).fee,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface)),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
sendViewModel.selectedCryptoCurrency == CryptoCurrency.btcln
|
||||
? "Lightning"
|
||||
: bitcoin!.getNetworkName(sendViewModel.wallet),
|
||||
"${fee.withLocalSeperator(sendViewModel.languageCode)} ${sendViewModel.currencySymbol}",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
Text(fiatFee.withLocalSeperator(sendViewModel.languageCode),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
@@ -406,18 +374,52 @@ class SendTransactionDetails extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
if (sendViewModel.isElectrumWallet) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).network,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface)),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
sendViewModel.selectedCryptoCurrency == CryptoCurrency.btcln
|
||||
? "Lightning"
|
||||
: bitcoin!.getNetworkName(sendViewModel.wallet),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SendConfirmBottomWidget(sendViewModel: sendViewModel),
|
||||
if(Platform.isAndroid) // spacing between bottom widget and system navbar
|
||||
SizedBox(),
|
||||
],
|
||||
SendConfirmBottomWidget(sendViewModel: sendViewModel),
|
||||
if (Platform.isAndroid) // spacing between bottom widget and system navbar
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
String formatAmount(String amount) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.da
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class ProviderOptionsPage extends StatelessWidget {
|
||||
const ProviderOptionsPage({super.key, required this.exchangeViewModel});
|
||||
@@ -39,6 +40,7 @@ class ProviderOptionsPage extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
|
||||
@@ -14,6 +14,8 @@ import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/amount/money.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/currency_for_wallet_type.dart';
|
||||
@@ -159,14 +161,16 @@ class SwapTransactionDetails extends StatelessWidget {
|
||||
builder: (_) => NewListSections(showHeader: true, sections: {
|
||||
S.of(context).send: [
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
keyValue: "send value",
|
||||
label: exchangeViewModel.depositCurrency.fullName ?? "",
|
||||
iconPath: exchangeViewModel.depositCurrency.iconPath ?? "",
|
||||
badgeIconPath: _resolveChainBadgePath(exchangeViewModel.depositCurrency),
|
||||
trailingText: exchangeTradeViewModel.trade.amountFormatted() +
|
||||
" " +
|
||||
(exchangeViewModel.depositCurrency.title)),
|
||||
showArrow: false,
|
||||
keyValue: "send value",
|
||||
label: exchangeViewModel.depositCurrency.fullName ?? "",
|
||||
iconPath: exchangeViewModel.depositCurrency.iconPath ?? "",
|
||||
badgeIconPath: _resolveChainBadgePath(exchangeViewModel.depositCurrency),
|
||||
trailingText: exchangeViewModel.amountParsingProxy
|
||||
.asDisplayStringWithSymbol(exchangeViewModel.depositCurrency
|
||||
.tryParseAmount(exchangeTradeViewModel.trade.amount) ??
|
||||
Money.zero(exchangeViewModel.depositCurrency)),
|
||||
),
|
||||
if (exchangeTradeViewModel.sendViewModel.pendingTransaction != null)
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
@@ -184,13 +188,15 @@ class SwapTransactionDetails extends StatelessWidget {
|
||||
],
|
||||
S.of(context).receive: [
|
||||
ListItemRegularRow(
|
||||
showArrow: false,
|
||||
keyValue: "receive value",
|
||||
label: exchangeViewModel.receiveCurrency.fullName ?? "",
|
||||
iconPath: exchangeViewModel.receiveCurrency.iconPath ?? "",
|
||||
badgeIconPath: _resolveChainBadgePath(exchangeViewModel.receiveCurrency),
|
||||
trailingText:
|
||||
(receiveAmount) + " " + (exchangeViewModel.receiveCurrency.title)),
|
||||
showArrow: false,
|
||||
keyValue: "receive value",
|
||||
label: exchangeViewModel.receiveCurrency.fullName ?? "",
|
||||
iconPath: exchangeViewModel.receiveCurrency.iconPath ?? "",
|
||||
badgeIconPath: _resolveChainBadgePath(exchangeViewModel.receiveCurrency),
|
||||
trailingText: (receiveAmount.withMaxDecimals(8)) +
|
||||
" " +
|
||||
(exchangeViewModel.receiveCurrency.title),
|
||||
),
|
||||
ListItemRegularRow(
|
||||
keyValue: "receiver",
|
||||
label: S.of(context).to,
|
||||
|
||||
@@ -20,7 +20,9 @@ class SwapLimitPopup extends StatelessWidget {
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
child: Observer(builder: (_) {
|
||||
final double? amount = double.tryParse(exchangeViewModel.depositAmountCanonical);
|
||||
final amount = exchangeViewModel.hasDepositAmount
|
||||
? double.tryParse(exchangeViewModel.depositAmountCanonical)
|
||||
: null;
|
||||
final max = exchangeViewModel.limits.max ?? double.infinity;
|
||||
final min = exchangeViewModel.limits.min ?? 0;
|
||||
final tooLarge = amount != null && max != 0 && amount > max;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SwapModalHeader extends StatelessWidget {
|
||||
@@ -17,8 +18,12 @@ class SwapModalHeader extends StatelessWidget {
|
||||
width: 36,
|
||||
child: Stack(
|
||||
children: [
|
||||
Image.asset(fromIconPath, width: 24, height: 24),
|
||||
Positioned(top: 12, left: 12, child: Image.asset(toIconPath, width: 24, height: 24)),
|
||||
CakeImageWidget(imageUrl: fromIconPath, width: 24, height: 24),
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: 12,
|
||||
child: CakeImageWidget(imageUrl: toIconPath, width: 24, height: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
+15
-15
@@ -7,9 +7,9 @@ import 'package:hive/hive.dart';
|
||||
import 'order_provider_description.dart';
|
||||
import 'order_source_description.dart';
|
||||
|
||||
part 'order.g.dart';
|
||||
part 'order.part.dart';
|
||||
|
||||
@HiveType(typeId: Order.typeId)
|
||||
// @HiveType(typeId: Order.typeId)
|
||||
class Order extends HiveObject {
|
||||
Order({
|
||||
required this.id,
|
||||
@@ -37,45 +37,45 @@ class Order extends HiveObject {
|
||||
static const boxName = 'Orders';
|
||||
static const boxKey = 'ordersBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
// @HiveField(1, defaultValue: '')
|
||||
String transferId;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String? from;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? to;
|
||||
|
||||
@HiveField(4, defaultValue: '')
|
||||
// @HiveField(4, defaultValue: '')
|
||||
late String stateRaw;
|
||||
|
||||
TradeState get state => TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
DateTime createdAt;
|
||||
|
||||
@HiveField(6, defaultValue: '')
|
||||
// @HiveField(6, defaultValue: '')
|
||||
String amount;
|
||||
|
||||
@HiveField(7, defaultValue: '')
|
||||
// @HiveField(7, defaultValue: '')
|
||||
String receiveAddress;
|
||||
|
||||
@HiveField(8, defaultValue: '')
|
||||
// @HiveField(8, defaultValue: '')
|
||||
String walletId;
|
||||
|
||||
@HiveField(9, defaultValue: 0)
|
||||
// @HiveField(9, defaultValue: 0)
|
||||
late int providerRaw;
|
||||
|
||||
@HiveField(10, defaultValue: '')
|
||||
// @HiveField(10, defaultValue: '')
|
||||
String? receiveAmount;
|
||||
|
||||
@HiveField(11, defaultValue: 0)
|
||||
// @HiveField(11, defaultValue: 0)
|
||||
late int? sourceRaw;
|
||||
|
||||
@HiveField(12, defaultValue: '')
|
||||
// @HiveField(12, defaultValue: '')
|
||||
String? quantity;
|
||||
|
||||
OrderSourceDescription get source => OrderSourceDescription.deserialize(raw: sourceRaw ?? 0);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'order.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class OrderAdapter extends TypeAdapter<Order> {
|
||||
@override
|
||||
final int typeId = 8;
|
||||
|
||||
@override
|
||||
Order read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Order(
|
||||
id: fields[0] == null ? '' : fields[0] as String,
|
||||
transferId: fields[1] == null ? '' : fields[1] as String,
|
||||
createdAt: fields[5] as DateTime,
|
||||
amount: fields[6] == null ? '' : fields[6] as String,
|
||||
receiveAmount: fields[10] == null ? '' : fields[10] as String?,
|
||||
quantity: fields[12] == null ? '' : fields[12] as String?,
|
||||
receiveAddress: fields[7] == null ? '' : fields[7] as String,
|
||||
walletId: fields[8] == null ? '' : fields[8] as String,
|
||||
from: fields[2] as String?,
|
||||
to: fields[3] as String?,
|
||||
)
|
||||
..stateRaw = fields[4] == null ? '' : fields[4] as String
|
||||
..providerRaw = fields[9] == null ? 0 : fields[9] as int
|
||||
..sourceRaw = fields[11] == null ? 0 : fields[11] as int?;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Order obj) {
|
||||
writer
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.transferId)
|
||||
..writeByte(2)
|
||||
..write(obj.from)
|
||||
..writeByte(3)
|
||||
..write(obj.to)
|
||||
..writeByte(4)
|
||||
..write(obj.stateRaw)
|
||||
..writeByte(5)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(6)
|
||||
..write(obj.amount)
|
||||
..writeByte(7)
|
||||
..write(obj.receiveAddress)
|
||||
..writeByte(8)
|
||||
..write(obj.walletId)
|
||||
..writeByte(9)
|
||||
..write(obj.providerRaw)
|
||||
..writeByte(10)
|
||||
..write(obj.receiveAmount)
|
||||
..writeByte(11)
|
||||
..write(obj.sourceRaw)
|
||||
..writeByte(12)
|
||||
..write(obj.quantity);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is OrderAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
+27
-41
@@ -53,6 +53,8 @@ class CWSolana extends Solana {
|
||||
@override
|
||||
String getPublicKey(WalletBase wallet) =>
|
||||
(wallet as SolanaWallet).solanaPublicKey.toAddress().address;
|
||||
|
||||
@override
|
||||
Object createSolanaTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
@@ -60,14 +62,14 @@ class CWSolana extends Solana {
|
||||
SolanaTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
))
|
||||
.toList(),
|
||||
currency: currency,
|
||||
);
|
||||
@@ -116,23 +118,15 @@ class CWSolana extends Solana {
|
||||
|
||||
@override
|
||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||
transaction as SolanaTransactionInfo;
|
||||
if (transaction.tokenSymbol == CryptoCurrency.sol.title) {
|
||||
if (transaction.amount.currency.symbol == CryptoCurrency.sol.symbol) {
|
||||
return CryptoCurrency.sol;
|
||||
}
|
||||
|
||||
wallet as SolanaWallet;
|
||||
|
||||
return wallet.splTokenCurrencies.firstWhere(
|
||||
(element) => transaction.tokenSymbol == element.symbol,
|
||||
return (wallet as SolanaWallet).splTokenCurrencies.firstWhere(
|
||||
(element) => transaction.amount.currency.symbol == element.symbol,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
||||
return (transactionInfo as SolanaTransactionInfo).solAmount.toDouble();
|
||||
}
|
||||
|
||||
@override
|
||||
String getTokenAddress(CryptoCurrency asset) {
|
||||
// If it's already an SPLToken, use its mint address
|
||||
@@ -168,9 +162,7 @@ class CWSolana extends Solana {
|
||||
}
|
||||
|
||||
@override
|
||||
double? getEstimateFees(WalletBase wallet) {
|
||||
return (wallet as SolanaWallet).estimatedFee;
|
||||
}
|
||||
Money? getEstimateFees(WalletBase wallet) => (wallet as SolanaWallet).estimatedFee;
|
||||
|
||||
@override
|
||||
List<SPLToken> getDefaultSPLTokens() => DefaultSPLTokens().initialSPLTokens;
|
||||
@@ -197,8 +189,8 @@ class CWSolana extends Solana {
|
||||
String base64Transaction,
|
||||
String requestId,
|
||||
String destinationAddress,
|
||||
double amount,
|
||||
double fee,
|
||||
Money amount,
|
||||
Money fee,
|
||||
) async {
|
||||
final solanaWallet = wallet as SolanaWallet;
|
||||
final privateKey = solanaWallet.solanaPrivateKey;
|
||||
@@ -418,24 +410,18 @@ class CWSolana extends Solana {
|
||||
required String from,
|
||||
String? tokenSymbol,
|
||||
required TransactionDirection direction,
|
||||
required double solAmount,
|
||||
required Money amount,
|
||||
required bool isPending,
|
||||
required double txFee,
|
||||
required Money fee,
|
||||
}) =>
|
||||
SolanaTransactionInfo(
|
||||
id: id,
|
||||
blockTime: blockTime,
|
||||
to: to,
|
||||
from: from,
|
||||
tokenSymbol: tokenSymbol ?? "SOL",
|
||||
direction: direction,
|
||||
solAmount: solAmount,
|
||||
isPending: isPending,
|
||||
txFee: txFee);
|
||||
|
||||
@override
|
||||
double getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingSolanaTransaction).amount;
|
||||
|
||||
@override
|
||||
double getPendingTransactionFee(PendingTransaction tx) => (tx as PendingSolanaTransaction).fee;
|
||||
id: id,
|
||||
date: blockTime,
|
||||
to: to,
|
||||
from: from,
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
isPending: isPending,
|
||||
fee: fee,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class DevMoneroWalletCacheDebugPage extends StatelessWidget {
|
||||
@@ -263,6 +264,7 @@ class _JsonExplorerState extends State<JsonExplorer> {
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
controller: ModalScrollController.of(context),
|
||||
itemCount: _filteredItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _filteredItems[index];
|
||||
|
||||
@@ -733,7 +733,7 @@ class ExchangePage extends BasePage {
|
||||
exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||
},
|
||||
useSatoshis: exchangeViewModel.useSatoshiDeposit,
|
||||
useSatoshis: exchangeViewModel.useDepositBaseUnit,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -782,7 +782,7 @@ class ExchangePage extends BasePage {
|
||||
exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
useSatoshis: exchangeViewModel.useSatoshisReceive,
|
||||
useSatoshis: exchangeViewModel.useReceiveBaseUnit,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -11,7 +10,6 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_shee
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/utils/request_review_handler.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -330,10 +328,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||
fee: isEVMCompatibleChain(sendVM.walletType)
|
||||
? S.of(bottomSheetContext).send_estimated_fee
|
||||
: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: "${sendVM.amountParsingProxy.getDisplayCryptoAmount(
|
||||
sendVM.pendingTransaction!.feeFormattedValue,
|
||||
sendVM.selectedCryptoCurrency)} ${sendVM.amountParsingProxy
|
||||
.getCryptoSymbol(sendVM.wallet.currency)}",
|
||||
feeValue: sendVM.amountParsingProxy.asDisplayStringWithSymbol(
|
||||
sendVM.pendingTransaction!.fee),
|
||||
feeFiatAmount: sendVM.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendVM.outputs,
|
||||
onSlideActionComplete: () async {
|
||||
|
||||
@@ -58,7 +58,8 @@ class DEuroSavingsPage extends BasePage {
|
||||
Observer(
|
||||
builder: (_) => SavingsCard(
|
||||
interestRate: "${_dEuroViewModel.interestRateFormated}%",
|
||||
savingsBalance: _dEuroViewModel.savingsBalanceFormated,
|
||||
savingsBalance:
|
||||
_dEuroViewModel.savingsBalance.toStringWithPrecision(fractionalDigits: 6),
|
||||
fiatSavingsBalance: _dEuroViewModel.fiatSavingsBalanceFormated,
|
||||
currency: CryptoCurrency.deuro,
|
||||
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
|
||||
@@ -68,6 +69,10 @@ class DEuroSavingsPage extends BasePage {
|
||||
onTooltipPressed: () => _onSavingsTooltipPressed(context),
|
||||
isEnabled: _dEuroViewModel.isEnabled,
|
||||
isLoading: _dEuroViewModel.isLoading,
|
||||
onWithdrawV1Pressed: () => _onWithdrawV1(context),
|
||||
savingsBalanceV1: _dEuroViewModel.savingsBalanceV1
|
||||
?.toStringWithPrecision(fractionalDigits: 6),
|
||||
fiatSavingsBalanceV1: _dEuroViewModel.fiatSavingsBalanceV1Formated,
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
@@ -126,6 +131,16 @@ class DEuroSavingsPage extends BasePage {
|
||||
_editSheetIsOpen = false;
|
||||
}
|
||||
|
||||
Future<void> _onWithdrawV1(BuildContext context) async {
|
||||
if (_editSheetIsOpen) return;
|
||||
_editSheetIsOpen = true;
|
||||
try {
|
||||
await _requireHardwareWallet(context);
|
||||
await _dEuroViewModel.prepareSavingsV1Withdraw();
|
||||
} catch (_) {}
|
||||
_editSheetIsOpen = false;
|
||||
}
|
||||
|
||||
Future<void> _onSavingsRemove(BuildContext context) async {
|
||||
if (_editSheetIsOpen) return;
|
||||
_editSheetIsOpen = true;
|
||||
@@ -215,7 +230,7 @@ class DEuroSavingsPage extends BasePage {
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
|
||||
? _dEuroViewModel.accruedInterestFormated
|
||||
: tx.amountFormatted,
|
||||
: tx.amount.toStringWithSymbol(),
|
||||
fiatAmountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
|
||||
? _dEuroViewModel.fiatAccruedInterestFormated
|
||||
: _dEuroViewModel.pendingTransactionFiatAmountFormatted,
|
||||
@@ -251,7 +266,7 @@ class DEuroSavingsPage extends BasePage {
|
||||
amountValue: tx.amountFormatted,
|
||||
fiatAmountValue: "",
|
||||
fee: S.of(bottomSheetContext).send_estimated_fee,
|
||||
feeValue: tx.feeFormatted,
|
||||
feeValue: tx.fee.toStringWithSymbol(),
|
||||
feeFiatAmount: "",
|
||||
outputs: [],
|
||||
onSlideActionComplete: () {
|
||||
@@ -411,8 +426,8 @@ class DEuroSavingsPage extends BasePage {
|
||||
? S.of(context).deuro_savings_available_to_add
|
||||
: S.of(context).deuro_savings_available_to_remove,
|
||||
balance: isAdding
|
||||
? _dEuroViewModel.accountBalanceFormated
|
||||
: _dEuroViewModel.savingsBalanceFormated,
|
||||
? _dEuroViewModel.accountBalance
|
||||
: _dEuroViewModel.savingsBalance,
|
||||
footerType: FooterType.none,
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.8,
|
||||
),
|
||||
|
||||
@@ -23,6 +23,9 @@ class SavingsCard extends StatelessWidget {
|
||||
this.isLoading = false,
|
||||
this.fiatSavingsBalance,
|
||||
this.fiatCurrency,
|
||||
this.savingsBalanceV1,
|
||||
this.fiatSavingsBalanceV1,
|
||||
this.onWithdrawV1Pressed,
|
||||
});
|
||||
|
||||
final bool isEnabled;
|
||||
@@ -30,8 +33,11 @@ class SavingsCard extends StatelessWidget {
|
||||
final String interestRate;
|
||||
final String savingsBalance;
|
||||
final String? fiatSavingsBalance;
|
||||
final String? savingsBalanceV1;
|
||||
final String? fiatSavingsBalanceV1;
|
||||
final FiatCurrency? fiatCurrency;
|
||||
final CryptoCurrency currency;
|
||||
final VoidCallback? onWithdrawV1Pressed;
|
||||
final VoidCallback onAddSavingsPressed;
|
||||
final VoidCallback onRemoveSavingsPressed;
|
||||
final VoidCallback onApproveSavingsPressed;
|
||||
@@ -98,46 +104,72 @@ class SavingsCard extends StatelessWidget {
|
||||
onTooltipPressed: onTooltipPressed,
|
||||
),
|
||||
),
|
||||
if (savingsBalanceV1 != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: getAssetBalanceRow(
|
||||
context,
|
||||
title: "${S.of(context).deuro_savings_balance} V1",
|
||||
amount: savingsBalanceV1!,
|
||||
fiatAmount: fiatSavingsBalanceV1,
|
||||
currency: currency,
|
||||
fiatCurrency: fiatCurrency,
|
||||
hideSymbol: true,
|
||||
),
|
||||
),
|
||||
isLoading
|
||||
? CupertinoActivityIndicator(color: Theme.of(context).colorScheme.onSurface)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: isEnabled
|
||||
children: savingsBalanceV1 != null
|
||||
? [
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_add,
|
||||
imagePath: 'assets/images/received.png',
|
||||
onPressed: onAddSavingsPressed,
|
||||
label: "${S.of(context).deuro_savings_remove} V1",
|
||||
onPressed: onWithdrawV1Pressed ?? () {},
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_remove,
|
||||
imagePath: 'assets/images/upload.png',
|
||||
onPressed: onRemoveSavingsPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_approve_app,
|
||||
onPressed: onApproveSavingsPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
icon: Icons.check,
|
||||
icon: Icons.arrow_downward,
|
||||
),
|
||||
)
|
||||
],
|
||||
]
|
||||
: isEnabled
|
||||
? [
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_add,
|
||||
imagePath: 'assets/images/received.png',
|
||||
onPressed: onAddSavingsPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_remove,
|
||||
imagePath: 'assets/images/upload.png',
|
||||
onPressed: onRemoveSavingsPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
Expanded(
|
||||
child: getButton(
|
||||
context,
|
||||
label: S.of(context).deuro_savings_approve_app,
|
||||
onPressed: onApproveSavingsPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
icon: Icons.check,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -264,33 +296,33 @@ class SavingsCard extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: currency.iconPath,
|
||||
height: 40,
|
||||
width: 40,
|
||||
errorWidget: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
currency.title.substring(0, min(currency.title.length, 2)),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
if (!hideSymbol)
|
||||
SizedBox(
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: currency.iconPath,
|
||||
height: 40,
|
||||
width: 40,
|
||||
errorWidget: Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
currency.title.substring(0, min(currency.title.length, 2)),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!hideSymbol) ...[
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
currency.title,
|
||||
@@ -301,11 +333,10 @@ class SavingsCard extends StatelessWidget {
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/numpad.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SavingsEditSheet extends BaseBottomSheet {
|
||||
final String? balance;
|
||||
final Money? balance;
|
||||
final String? balanceTitle;
|
||||
|
||||
const SavingsEditSheet({
|
||||
@@ -32,7 +32,7 @@ class SavingsEditSheet extends BaseBottomSheet {
|
||||
}
|
||||
|
||||
class _SavingsEditBody extends StatefulWidget {
|
||||
final String? balance;
|
||||
final Money? balance;
|
||||
final String? balanceTitle;
|
||||
|
||||
const _SavingsEditBody({this.balance, this.balanceTitle});
|
||||
@@ -59,7 +59,7 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
|
||||
final FocusNode _numpadFocusNode = FocusNode();
|
||||
|
||||
void _onPressedAll() => setState(() {
|
||||
amount = widget.balance!;
|
||||
amount = widget.balance!.toString();
|
||||
isValid = _validate();
|
||||
});
|
||||
|
||||
@@ -79,9 +79,12 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
|
||||
});
|
||||
|
||||
bool _validate() {
|
||||
final amountBigInt = parseFixed(amount, 18);
|
||||
final balanceBigInt = parseFixed(widget.balance!, 18);
|
||||
return balanceBigInt >= amountBigInt && amountBigInt > BigInt.zero;
|
||||
try {
|
||||
final amountBigInt = Money.parse(amount, widget.balance!.currency);
|
||||
return widget.balance! >= amountBigInt && amountBigInt.sign > 0;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -108,7 +111,7 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
|
||||
Divider(),
|
||||
AssetBalanceRow(
|
||||
title: widget.balanceTitle!,
|
||||
amount: widget.balance!,
|
||||
amount: widget.balance!.toStringWithSymbol(fractionalDigits: 6),
|
||||
onAllPressed: _onPressedAll,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -198,8 +199,7 @@ class CurrencyAmountTextField extends StatelessWidget {
|
||||
),
|
||||
validator: isAmountEditable ? currencyValueValidator : null,
|
||||
onChanged: (value) {
|
||||
var sanitized =
|
||||
value.replaceAll(',', '.').withMaxDecimals(selectedCurrencyDecimals);
|
||||
var sanitized = value.sanitized().withMaxDecimals(selectedCurrencyDecimals);
|
||||
|
||||
if (selectedCurrencyDecimals == 0) {
|
||||
sanitized = sanitized.replaceAll('.', '');
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:cake_wallet/core/backup_service_v3.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
@@ -18,6 +20,7 @@ class RestoreFromBackupPage extends BasePage {
|
||||
|
||||
final RestoreFromBackupViewModel restoreFromBackupViewModel;
|
||||
final TextEditingController textEditingController;
|
||||
bool _isStateReactionSet = false;
|
||||
|
||||
@override
|
||||
String get title => S.current.restore_title_from_backup;
|
||||
@@ -32,22 +35,26 @@ class RestoreFromBackupPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!_isStateReactionSet) {
|
||||
_isStateReactionSet = true;
|
||||
|
||||
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: ConstrainedBox(
|
||||
@@ -175,6 +182,29 @@ class RestoreFromBackupPage extends BasePage {
|
||||
}
|
||||
try {
|
||||
await restoreFromBackupViewModel.import(textEditingController.text);
|
||||
} on IncompatibleBackupAppException catch (e) {
|
||||
final isConfirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.current.warning,
|
||||
alertContent:
|
||||
'This backup was created in ${e.sourceAppName}, but you are restoring it in ${e.currentAppName}. '
|
||||
'Please make sure this is the correct backup file before continuing.',
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).ok,
|
||||
actionLeftButton: () => Navigator.of(context).pop(false),
|
||||
actionRightButton: () => Navigator.of(context).pop(true),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (isConfirmed ?? false) {
|
||||
await restoreFromBackupViewModel.import(
|
||||
textEditingController.text,
|
||||
checkBackupApp: false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
|
||||
@@ -35,6 +35,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -445,9 +446,9 @@ class SendPage extends BasePage {
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.type == WalletType.monero) {
|
||||
int amount = 0;
|
||||
var amount = Money.zero(sendViewModel.wallet.currency);
|
||||
for (var item in sendViewModel.outputs) {
|
||||
amount += item.formattedCryptoAmount;
|
||||
amount += item.cryptoAmountMoney;
|
||||
}
|
||||
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
|
||||
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/rounded_icon_button.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@@ -11,7 +10,6 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
const CakePayTransactionSentBottomSheet({
|
||||
super.key,
|
||||
required this.titleText,
|
||||
required this.currency,
|
||||
required this.amount,
|
||||
required this.amountValue,
|
||||
required this.fiatAmountValue,
|
||||
@@ -28,7 +26,6 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
|
||||
final String titleText;
|
||||
final Widget? titleIconWidget;
|
||||
final CryptoCurrency currency;
|
||||
final String amount;
|
||||
final String amountValue;
|
||||
final String fiatAmountValue;
|
||||
@@ -41,32 +38,15 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
final String paymentId;
|
||||
final String paymentIdValue;
|
||||
|
||||
TextStyle _titleStyle(BuildContext ctx) =>
|
||||
Theme
|
||||
.of(ctx)
|
||||
.textTheme
|
||||
.bodyLarge!;
|
||||
TextStyle _titleStyle(BuildContext ctx) => Theme.of(ctx).textTheme.bodyLarge!;
|
||||
|
||||
TextStyle _valueStyle(BuildContext ctx) =>
|
||||
Theme
|
||||
.of(ctx)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(
|
||||
TextStyle _valueStyle(BuildContext ctx) => Theme.of(ctx).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
TextStyle _subStyle(BuildContext ctx) =>
|
||||
Theme
|
||||
.of(ctx)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.copyWith(
|
||||
color: Theme
|
||||
.of(ctx)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
TextStyle _subStyle(BuildContext ctx) => Theme.of(ctx).textTheme.labelSmall!.copyWith(
|
||||
color: Theme.of(ctx).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
|
||||
Widget _buildHeader(BuildContext ctx) =>
|
||||
@@ -78,10 +58,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: Theme
|
||||
.of(ctx)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
color: Theme.of(ctx).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@@ -92,15 +69,11 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
titleText,
|
||||
style: Theme
|
||||
.of(ctx)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
style: Theme.of(ctx).textTheme.titleLarge!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -116,7 +89,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
_StandardTile(
|
||||
itemTitle: amount,
|
||||
titleStyle: _titleStyle(context),
|
||||
itemValue: '$amountValue ${currency.title}',
|
||||
itemValue: amountValue,
|
||||
itemValueStyle: _valueStyle(context),
|
||||
itemSubTitle: fiatAmountValue,
|
||||
itemSubTitleStyle: _subStyle(context),
|
||||
@@ -143,9 +116,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
_StandardTile(
|
||||
itemTitle: paymentId + ': \n' + paymentIdValue,
|
||||
titleStyle: _titleStyle(context).copyWith(
|
||||
fontSize: 14
|
||||
),
|
||||
titleStyle: _titleStyle(context).copyWith(fontSize: 14),
|
||||
itemValue: '',
|
||||
itemValueStyle: _valueStyle(context),
|
||||
copyButton: true,
|
||||
@@ -161,28 +132,15 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
Image.asset('assets/images/envelope.png'),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
S
|
||||
.of(context)
|
||||
.cake_pay_card_email_delivered_message,
|
||||
style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
.bodyLarge,
|
||||
S.of(context).cake_pay_card_email_delivered_message,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
PrimaryButton(
|
||||
text: S
|
||||
.of(context)
|
||||
.close,
|
||||
color: Theme
|
||||
.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
textColor: Theme
|
||||
.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
text: S.of(context).close,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
onPressed: onClose,
|
||||
),
|
||||
],
|
||||
@@ -194,10 +152,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final maxHeight = MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.height * 0.9;
|
||||
final maxHeight = MediaQuery.of(context).size.height * 0.9;
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:cake_wallet/utils/address_formatter.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -134,7 +135,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
),
|
||||
StandardTile(
|
||||
itemTitle: amount,
|
||||
itemValue: '$amountValue ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}',
|
||||
itemValue: amountValue,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
itemSubTitle: fiatAmountValue,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
@@ -167,7 +168,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
final batchContactTitle =
|
||||
'${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}';
|
||||
final _address = item.isParsedAddress ? item.extractedAddress : item.address;
|
||||
final _amount = '${item.cryptoAmount.replaceAll(',', '.')} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}';
|
||||
final _amount = '${item.cryptoAmount.sanitized()} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}';
|
||||
return isBatchSending || (contactName.isNotEmpty && !isCakePayName)
|
||||
? ExpansionAddressTile(
|
||||
contactType: isOpenCryptoPay ? 'Open CryptoPay' : S.of(context).contact,
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/swap_details_bottom_sheet.d
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||
import 'package:cake_wallet/utils/debounce.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
|
||||
@@ -197,7 +198,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
],
|
||||
onChanged: (value) {
|
||||
final sanitized = value
|
||||
.replaceAll(',', '.')
|
||||
.sanitized()
|
||||
.withMaxDecimals(widget.exchangeViewModel.receiveCurrency.decimals);
|
||||
if (sanitized != _amountController.text) {
|
||||
// Update text while preserving a sane cursor position to avoid auto-selection
|
||||
|
||||
+10
-25
@@ -58,13 +58,12 @@ class CWTron extends Tron {
|
||||
.map(
|
||||
(out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -97,22 +96,16 @@ class CWTron extends Tron {
|
||||
Future<TronToken?> getTronToken(WalletBase wallet, String contractAddress) async =>
|
||||
(wallet as TronWallet).getTronToken(contractAddress);
|
||||
|
||||
@override
|
||||
double getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
||||
final amount = (transactionInfo as TronTransactionInfo).rawTronAmount();
|
||||
return double.parse(amount);
|
||||
}
|
||||
|
||||
@override
|
||||
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
|
||||
transaction as TronTransactionInfo;
|
||||
if (transaction.tokenSymbol == CryptoCurrency.trx.title) {
|
||||
if (transaction.amount.currency.symbol == CryptoCurrency.trx.title) {
|
||||
return CryptoCurrency.trx;
|
||||
}
|
||||
|
||||
wallet as TronWallet;
|
||||
return wallet.tronTokenCurrencies.firstWhere(
|
||||
(element) => transaction.tokenSymbol.toLowerCase() == element.symbol.toLowerCase());
|
||||
return wallet.tronTokenCurrencies.firstWhere((element) =>
|
||||
transaction.amount.currency.symbol.toLowerCase() == element.symbol.toLowerCase());
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -123,11 +116,11 @@ class CWTron extends Tron {
|
||||
(wallet as TronWallet).getTronBase58AddressFromHex(hexAddress);
|
||||
|
||||
@override
|
||||
String? getTronNativeEstimatedFee(WalletBase wallet) =>
|
||||
Money? getTronNativeEstimatedFee(WalletBase wallet) =>
|
||||
(wallet as TronWallet).nativeTxEstimatedFee;
|
||||
|
||||
@override
|
||||
String? getTronTRC20EstimatedFee(WalletBase wallet) => (wallet as TronWallet).trc20EstimatedFee;
|
||||
Money? getTronTRC20EstimatedFee(WalletBase wallet) => (wallet as TronWallet).trc20EstimatedFee;
|
||||
|
||||
@override
|
||||
void updateTronGridUsageState(WalletBase wallet, bool isEnabled) {
|
||||
@@ -157,9 +150,8 @@ class CWTron extends Tron {
|
||||
@override
|
||||
TransactionInfo getTransactionInfo({
|
||||
required String id,
|
||||
required BigInt tronAmount,
|
||||
int? txFee,
|
||||
String? tokenSymbol,
|
||||
required Money amount,
|
||||
Money? fee,
|
||||
required TransactionDirection direction,
|
||||
required DateTime blockTime,
|
||||
String? to,
|
||||
@@ -167,18 +159,11 @@ class CWTron extends Tron {
|
||||
required bool isPending,
|
||||
}) =>
|
||||
TronTransactionInfo(id: id,
|
||||
tronAmount: tronAmount,
|
||||
tokenSymbol: tokenSymbol ?? "TRX",
|
||||
txFee: txFee,
|
||||
amount: amount,
|
||||
fee: fee,
|
||||
direction: direction,
|
||||
blockTime: blockTime,
|
||||
to: to,
|
||||
from: from,
|
||||
isPending: isPending);
|
||||
|
||||
@override
|
||||
String getPendingTransactionAmount(PendingTransaction tx) => (tx as PendingTronTransaction).amount;
|
||||
|
||||
@override
|
||||
String getPendingTransactionFee(PendingTransaction tx) => (tx as PendingTronTransaction).amount;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class DecimalInputFormatter extends TextInputFormatter {
|
||||
const DecimalInputFormatter({this.maxDecimals = 7}) : assert(maxDecimals >= 0);
|
||||
|
||||
final int maxDecimals;
|
||||
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
final text = newValue.text;
|
||||
if (text.isEmpty) return newValue;
|
||||
|
||||
if (S.current.all.startsWith(text)) return TextEditingValue(text: "");
|
||||
|
||||
final regex = maxDecimals == 0 ? RegExp(r'^\d*$') : RegExp('^\\d*([.,]\\d{0,$maxDecimals})?\$');
|
||||
return regex.hasMatch(text) ? newValue.copyWith(text: text.replaceAll(',', '.')) : oldValue;
|
||||
}
|
||||
}
|
||||
@@ -306,6 +306,7 @@ class ExceptionHandler {
|
||||
"the timeout of the request was reached",
|
||||
|
||||
"support for coin removed, your seedphrase:"
|
||||
"Exception: Invalid image data"
|
||||
];
|
||||
|
||||
static Future<void> _addDeviceInfo(File file) async {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
@@ -16,9 +18,8 @@ class PaymentRequest {
|
||||
}
|
||||
|
||||
factory PaymentRequest.fromBolt11(String invoice) {
|
||||
final amountRaw = getBolt11Amount(invoice) ?? 0;
|
||||
final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(amountRaw));
|
||||
return PaymentRequest(invoice, amount, '', 'lightning', null);
|
||||
final amount = getBolt11Amount(invoice) ?? Money.zero(CryptoCurrency.btcln);
|
||||
return PaymentRequest(invoice, amount.toString(), '', 'lightning', null);
|
||||
}
|
||||
|
||||
factory PaymentRequest.fromUri(Uri? uri) {
|
||||
@@ -39,10 +40,10 @@ class PaymentRequest {
|
||||
|
||||
address = uri.queryParameters['address'] ?? uri.path;
|
||||
try {
|
||||
final lnAmount =
|
||||
CryptoCurrency.btcln.formatAmount(BigInt.from(getBolt11Amount(uri.path) ?? 0));
|
||||
final lnAmount = getBolt11Amount(uri.path) ?? Money.zero(CryptoCurrency.btcln);
|
||||
|
||||
if (lnAmount != 0) {
|
||||
amount = lnAmount;
|
||||
amount = lnAmount.toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
if (amount.isEmpty) {
|
||||
@@ -116,8 +117,7 @@ class PaymentRequest {
|
||||
static bool _isAlreadyUsableAmount(String amount) {
|
||||
if (amount.isEmpty) return false;
|
||||
|
||||
// Try to parse as double - if successful, it's already in usable format
|
||||
final parsed = double.tryParse(amount.replaceAll(',', '.'));
|
||||
final parsed = double.tryParse(amount.sanitized());
|
||||
if (parsed == null) return false;
|
||||
|
||||
// Check if the amount contains a decimal point and is a reasonable number,
|
||||
|
||||
@@ -17,6 +17,8 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/bridge_transfers_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
@@ -130,18 +132,18 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
}
|
||||
|
||||
@computed
|
||||
String get tokenBalanceFormatted {
|
||||
if (selectedToken == null) return "0.00";
|
||||
|
||||
return amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
selectedTokenBalance, selectedToken!,
|
||||
);
|
||||
}
|
||||
String get tokenBalanceFormatted {
|
||||
if (selectedToken == null) return "0.00";
|
||||
|
||||
return amountParsingProxy.asDisplayString(
|
||||
Money(selectedTokenBalance, selectedToken!),
|
||||
);
|
||||
}
|
||||
|
||||
@computed
|
||||
String get amountDisplayFormatted {
|
||||
if (selectedToken == null) return "0.00";
|
||||
|
||||
|
||||
return amountParsingProxy.getDisplayCryptoAmount(
|
||||
amount.replaceAll(',', '.'),
|
||||
selectedToken!,
|
||||
@@ -174,10 +176,8 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
String get quoteNativeFee {
|
||||
if (quote == null) return '—';
|
||||
|
||||
return amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
quote!.nativeFee,
|
||||
wallet.currency,
|
||||
);
|
||||
return amountParsingProxy.asDisplayString(
|
||||
Money(quote!.nativeFee, wallet.currency));
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -218,10 +218,10 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
if (amountError != null) return false;
|
||||
|
||||
final validAmount = amountParsingProxy.tryParseCryptoString(
|
||||
amount.replaceAll(',', '.'),
|
||||
amount.sanitized(),
|
||||
selectedToken!,
|
||||
);
|
||||
return validAmount != null && validAmount > BigInt.zero;
|
||||
return validAmount != null && validAmount > Money.zero(selectedToken!);
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -231,7 +231,7 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
try {
|
||||
final bal = wallet.balance[selectedToken!];
|
||||
|
||||
return bal?.fullAvailableBalance ?? BigInt.zero;
|
||||
return bal?.available.amount ?? BigInt.zero;
|
||||
} catch (e) {
|
||||
return BigInt.zero;
|
||||
}
|
||||
@@ -247,7 +247,7 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
);
|
||||
|
||||
if (amountBigInt == null || amountBigInt == BigInt.zero) return null;
|
||||
if (amountBigInt > selectedTokenBalance) {
|
||||
if (amountBigInt.amount > selectedTokenBalance) {
|
||||
return 'Insufficient balance for ${selectedToken!.title} token.';
|
||||
}
|
||||
|
||||
@@ -281,9 +281,9 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
return;
|
||||
}
|
||||
setAmount(
|
||||
amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
amountParsingProxy.asDisplayString(Money(
|
||||
selectedTokenBalance,
|
||||
token,
|
||||
token)
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -382,23 +382,23 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
}
|
||||
|
||||
({String? error, BigInt? parsedAmount}) _parseAndValidateAmount(Erc20Token token) {
|
||||
final amountBigInt = amountParsingProxy.tryParseCryptoString(
|
||||
final parsedAmount = amountParsingProxy.tryParseCryptoString(
|
||||
amount.replaceAll(',', '.'),
|
||||
token,
|
||||
);
|
||||
|
||||
if (amountBigInt == null || amountBigInt == BigInt.zero) {
|
||||
if (parsedAmount == null || parsedAmount == Money(BigInt.zero, token)) {
|
||||
return (error: 'Invalid amount', parsedAmount: null);
|
||||
}
|
||||
|
||||
if (amountBigInt > selectedTokenBalance) {
|
||||
if (parsedAmount.amount > selectedTokenBalance) {
|
||||
return (
|
||||
error: 'Insufficient balance for ${token.title} token.',
|
||||
parsedAmount: null,
|
||||
);
|
||||
}
|
||||
|
||||
return (error: null, parsedAmount: amountBigInt);
|
||||
return (error: null, parsedAmount: parsedAmount.amount);
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:cake_wallet/core/utilities.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/sort_balance_types.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
@@ -10,8 +11,10 @@ import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
@@ -28,47 +31,74 @@ part 'balance_view_model.g.dart';
|
||||
|
||||
class BalanceRecord {
|
||||
const BalanceRecord({
|
||||
required this.raw,
|
||||
required this.availableBalance,
|
||||
required this.additionalBalance,
|
||||
required this.secondAvailableBalance,
|
||||
required this.secondAdditionalBalance,
|
||||
required this.frozenBalance,
|
||||
required this.fiatAvailableBalance,
|
||||
required this.fiatAdditionalBalance,
|
||||
required this.fiatFrozenBalance,
|
||||
required this.fiatSecondAvailableBalance,
|
||||
required this.fiatSecondAdditionalBalance,
|
||||
required this.fiatAvailableBalanceRaw,
|
||||
required this.fiatAdditionalBalanceRaw,
|
||||
required this.fiatFrozenBalanceRaw,
|
||||
required this.fiatSecondAvailableBalanceRaw,
|
||||
required this.fiatSecondAdditionalBalanceRaw,
|
||||
required this.asset,
|
||||
required this.secondAsset,
|
||||
required this.fiatCurrency,
|
||||
required this.formattedAssetTitle,
|
||||
this.languageCode,
|
||||
});
|
||||
|
||||
final String fiatAdditionalBalance;
|
||||
final String fiatAvailableBalance;
|
||||
final String fiatFrozenBalance;
|
||||
final Balance raw;
|
||||
|
||||
final String fiatAvailableBalanceRaw;
|
||||
final String fiatAdditionalBalanceRaw;
|
||||
final String fiatFrozenBalanceRaw;
|
||||
final String fiatSecondAvailableBalanceRaw;
|
||||
final String fiatSecondAdditionalBalanceRaw;
|
||||
|
||||
final String additionalBalance;
|
||||
final String availableBalance;
|
||||
final String frozenBalance;
|
||||
final String secondAvailableBalance;
|
||||
final String secondAdditionalBalance;
|
||||
final String fiatSecondAdditionalBalance;
|
||||
final String fiatSecondAvailableBalance;
|
||||
final CryptoCurrency asset;
|
||||
final CryptoCurrency secondAsset;
|
||||
final FiatCurrency? fiatCurrency;
|
||||
final String formattedAssetTitle;
|
||||
final String? languageCode;
|
||||
|
||||
String get combinedAvailableBalance =>
|
||||
((double.tryParse(availableBalance) ?? 0) + (double.tryParse(secondAvailableBalance) ?? 0))
|
||||
.toString().withMaxDecimals(8);
|
||||
(raw.available + (raw.secondAvailable ?? Money.zero(raw.available.currency)))
|
||||
.toString()
|
||||
.withMaxDecimals(8);
|
||||
|
||||
String get combinedFiatAvailableBalance =>
|
||||
((double.tryParse(fiatAvailableBalanceRaw) ?? 0) +
|
||||
(double.tryParse(fiatSecondAvailableBalanceRaw) ?? 0))
|
||||
.toStringAsFixed(2);
|
||||
String get combinedFiatAvailableBalance => fiatCurrency != null
|
||||
? "$fiatCurrency " +
|
||||
_withLocalSeperator(((double.tryParse(fiatAvailableBalanceRaw) ?? 0) +
|
||||
(double.tryParse(fiatSecondAvailableBalanceRaw) ?? 0))
|
||||
.toStringAsFixed(2))
|
||||
: "";
|
||||
|
||||
String get fiatAvailableBalanceRaw => fiatAvailableBalance.split(" ").last;
|
||||
String get fiatSecondAvailableBalanceRaw => fiatSecondAvailableBalance.split(" ").last;
|
||||
String get fiatCurrencyTicker => fiatAvailableBalance.split(" ").first;
|
||||
String get fiatAvailableBalance =>
|
||||
fiatCurrency != null ? "$fiatCurrency ${_withLocalSeperator(fiatAvailableBalanceRaw)}" : "";
|
||||
|
||||
String get fiatAdditionalBalance =>
|
||||
fiatCurrency != null ? "$fiatCurrency ${_withLocalSeperator(fiatAdditionalBalanceRaw)}" : "";
|
||||
|
||||
String get fiatFrozenBalance =>
|
||||
fiatCurrency != null ? "$fiatCurrency ${_withLocalSeperator(fiatFrozenBalanceRaw)}" : "";
|
||||
|
||||
String get fiatSecondAvailableBalance => fiatCurrency != null
|
||||
? "$fiatCurrency ${_withLocalSeperator(fiatSecondAvailableBalanceRaw)}"
|
||||
: "";
|
||||
|
||||
String get fiatSecondAdditionalBalance => fiatCurrency != null
|
||||
? "$fiatCurrency ${_withLocalSeperator(fiatSecondAdditionalBalanceRaw)}"
|
||||
: "";
|
||||
|
||||
String _withLocalSeperator(String rawAmount) =>
|
||||
languageCode == null ? rawAmount : rawAmount.withLocalSeperator(languageCode);
|
||||
}
|
||||
|
||||
class BalanceViewModel = BalanceViewModelBase with _$BalanceViewModel;
|
||||
@@ -221,12 +251,13 @@ abstract class BalanceViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
|
||||
String additionalBalance(CryptoCurrency cryptoCurrency) {
|
||||
Money additionalBalance(CryptoCurrency cryptoCurrency) {
|
||||
final balance = _currencyBalance(cryptoCurrency);
|
||||
|
||||
if (displayMode == BalanceDisplayMode.hiddenBalance || balance.additional == BigInt.zero) return '0.0';
|
||||
if (displayMode == BalanceDisplayMode.hiddenBalance || balance.unavailable.isZero)
|
||||
return Money.zero(cryptoCurrency);
|
||||
|
||||
return cryptoCurrency.formatAmount(balance.additional);
|
||||
return balance.unavailable;
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -237,25 +268,26 @@ abstract class BalanceViewModelBase with Store {
|
||||
if (displayMode == BalanceDisplayMode.hiddenBalance) {
|
||||
final fiatCurrency = settingsStore.fiatCurrency;
|
||||
return MapEntry(
|
||||
key,
|
||||
BalanceRecord(
|
||||
availableBalance: '●●●●●●',
|
||||
additionalBalance: additionalBalance(key),
|
||||
frozenBalance: '',
|
||||
secondAvailableBalance: '●●●●●●',
|
||||
secondAdditionalBalance: '●●●●●●',
|
||||
fiatAdditionalBalance: isFiatDisabled ? '' : '${fiatCurrency.toString()} ●●●●●',
|
||||
fiatAvailableBalance: isFiatDisabled ? '' : '${fiatCurrency.toString()} ●●●●●',
|
||||
fiatFrozenBalance: isFiatDisabled ? '' : '',
|
||||
fiatSecondAvailableBalance:
|
||||
isFiatDisabled ? '' : '${fiatCurrency.toString()}',
|
||||
fiatSecondAdditionalBalance:
|
||||
isFiatDisabled ? '' : '${fiatCurrency.toString()} ●●●●●',
|
||||
asset: key,
|
||||
secondAsset: secondAsset,
|
||||
formattedAssetTitle: _formatterAsset(key)));
|
||||
key,
|
||||
BalanceRecord(
|
||||
raw: value,
|
||||
availableBalance: '●●●●●●',
|
||||
additionalBalance: '',
|
||||
frozenBalance: '',
|
||||
secondAvailableBalance: '●●●●●●',
|
||||
secondAdditionalBalance: '●●●●●●',
|
||||
fiatAdditionalBalanceRaw: '●●●●●',
|
||||
fiatAvailableBalanceRaw: '●●●●●',
|
||||
fiatFrozenBalanceRaw: '',
|
||||
fiatSecondAvailableBalanceRaw: '●●●●●',
|
||||
fiatSecondAdditionalBalanceRaw: '●●●●●',
|
||||
asset: key,
|
||||
secondAsset: secondAsset,
|
||||
fiatCurrency: isFiatDisabled ? null : fiatCurrency,
|
||||
formattedAssetTitle: _formatterAsset(key),
|
||||
),
|
||||
);
|
||||
}
|
||||
final fiatCurrency = settingsStore.fiatCurrency;
|
||||
final price = key.isPotentialScam
|
||||
? 0.0
|
||||
: fiatConversionStore.prices[key == CryptoCurrency.btcln ? CryptoCurrency.btc : key] ?? 0;
|
||||
@@ -264,48 +296,41 @@ abstract class BalanceViewModelBase with Store {
|
||||
// throw Exception('Price is null for: $key');
|
||||
// }
|
||||
|
||||
final available = evm?.getERC20AvailableBalance(value) ??
|
||||
(value.fullAvailableBalance - (value.secondAvailable ?? BigInt.zero));
|
||||
final additional = evm?.getERC20AvailableBalance(value) ??
|
||||
(value.additional - (value.secondAdditional ?? BigInt.zero));
|
||||
final availableFiatBalance =
|
||||
isFiatDisabled ? '' : _getFiatBalance(price: price, cryptoAmount: value.available);
|
||||
|
||||
final availableFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: '$fiatCurrency ${_getFiatBalance(price: price, cryptoAmount: key.formatAmount(available))}';
|
||||
final additionalFiatBalance =
|
||||
isFiatDisabled ? '' : _getFiatBalance(price: price, cryptoAmount: value.unavailable);
|
||||
|
||||
final additionalFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: '$fiatCurrency ${_getFiatBalance(price: price, cryptoAmount: key.formatAmount(additional))}';
|
||||
final frozenFiatBalance =
|
||||
isFiatDisabled ? '' : _getFiatBalance(price: price, cryptoAmount: value.frozen);
|
||||
|
||||
final frozenFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: '$fiatCurrency ${_getFiatBalance(price: price, cryptoAmount: value.frozen != null ? key.formatAmount(value.frozen!) : null)}';
|
||||
|
||||
final secondAvailableFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: '$fiatCurrency ${_getFiatBalance(price: price, cryptoAmount: value.secondAvailable != null ? key.formatAmount(value.secondAvailable!) : null)}';
|
||||
final secondAvailableFiatBalance =
|
||||
isFiatDisabled ? '' : _getFiatBalance(price: price, cryptoAmount: value.secondAvailable);
|
||||
|
||||
final secondAdditionalFiatBalance = isFiatDisabled
|
||||
? ''
|
||||
: '$fiatCurrency ${_getFiatBalance(price: price, cryptoAmount: value.secondAdditional != null ? key.formatAmount(value.secondAdditional!) : null)}';
|
||||
: _getFiatBalance(price: price, cryptoAmount: value.secondUnavailable);
|
||||
|
||||
return MapEntry(
|
||||
key,
|
||||
BalanceRecord(
|
||||
availableBalance: _getFormattedCryptoAmount(key, available),
|
||||
fiatAvailableBalance: availableFiatBalance,
|
||||
additionalBalance: _getFormattedCryptoAmount(key, additional),
|
||||
fiatAdditionalBalance: additionalFiatBalance,
|
||||
frozenBalance:
|
||||
(value.frozen ?? BigInt.zero) > BigInt.zero ? _getFormattedCryptoAmount(key, value.frozen??BigInt.zero) : '',
|
||||
fiatFrozenBalance: frozenFiatBalance,
|
||||
secondAvailableBalance: _getFormattedCryptoAmount(secondAsset, value.secondAvailable??BigInt.zero),
|
||||
fiatSecondAvailableBalance: secondAvailableFiatBalance,
|
||||
secondAdditionalBalance: _getFormattedCryptoAmount(secondAsset, value.secondAdditional??BigInt.zero),
|
||||
fiatSecondAdditionalBalance: secondAdditionalFiatBalance,
|
||||
raw: value,
|
||||
availableBalance: _getFormattedCryptoAmount(value.available),
|
||||
fiatAvailableBalanceRaw: availableFiatBalance,
|
||||
additionalBalance: _getFormattedCryptoAmount(value.unavailable),
|
||||
fiatAdditionalBalanceRaw: additionalFiatBalance,
|
||||
frozenBalance: _getFormattedCryptoAmount(value.frozen),
|
||||
fiatFrozenBalanceRaw: frozenFiatBalance,
|
||||
secondAvailableBalance: _getFormattedCryptoAmount(value.secondAvailable),
|
||||
fiatSecondAvailableBalanceRaw: secondAvailableFiatBalance,
|
||||
secondAdditionalBalance: _getFormattedCryptoAmount(value.secondUnavailable),
|
||||
fiatSecondAdditionalBalanceRaw: secondAdditionalFiatBalance,
|
||||
asset: key,
|
||||
secondAsset: secondAsset,
|
||||
fiatCurrency: isFiatDisabled ? null : settingsStore.fiatCurrency,
|
||||
formattedAssetTitle: _formatterAsset(key),
|
||||
languageCode: settingsStore.languageCode,
|
||||
),
|
||||
);
|
||||
});
|
||||
@@ -316,7 +341,7 @@ abstract class BalanceViewModelBase with Store {
|
||||
|
||||
bool hasAdditionalBalance(CryptoCurrency currency) {
|
||||
final isWalletTypeActivated = _hasAdditionalBalanceForWalletType(wallet.type);
|
||||
final isNotZeroAmount = additionalBalance(currency) != "0.0";
|
||||
final isNotZeroAmount = !additionalBalance(currency).isZero;
|
||||
|
||||
return isWalletTypeActivated && isNotZeroAmount;
|
||||
}
|
||||
@@ -324,9 +349,9 @@ abstract class BalanceViewModelBase with Store {
|
||||
@computed
|
||||
bool get hasSecondAdditionalBalance {
|
||||
if (wallet.type == WalletType.litecoin && mwebEnabled) {
|
||||
return (wallet.balance[CryptoCurrency.ltc]?.secondAdditional ?? 0) != 0;
|
||||
return (wallet.balance[CryptoCurrency.ltc]?.secondUnavailable ?? 0) != 0;
|
||||
} else if (wallet.type == WalletType.bitcoin) {
|
||||
return (wallet.balance[CryptoCurrency.btc]?.secondAdditional ?? 0) != 0;
|
||||
return (wallet.balance[CryptoCurrency.btc]?.secondUnavailable ?? 0) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -351,12 +376,12 @@ abstract class BalanceViewModelBase with Store {
|
||||
WalletType.zcash
|
||||
].contains(type);
|
||||
|
||||
String _getFormattedCryptoAmount(CryptoCurrency cryptoCurrency, BigInt? amount) {
|
||||
String _getFormattedCryptoAmount(Money? amount) {
|
||||
if (amount == null) return "";
|
||||
|
||||
return appStore.amountParsingProxy
|
||||
.getDisplayCryptoStringFromBigInt(amount, cryptoCurrency)
|
||||
.withMaxDecimals(8).withLocalSeperator(settingsStore.languageCode);
|
||||
.asDisplayString(amount)
|
||||
.withLocalSeperator(settingsStore.languageCode);
|
||||
}
|
||||
|
||||
@computed
|
||||
@@ -402,9 +427,9 @@ abstract class BalanceViewModelBase with Store {
|
||||
switch (sortBalanceBy) {
|
||||
case SortBalanceBy.FiatBalance:
|
||||
final aFiatBalance = _getFiatBalance(
|
||||
price: fiatConversionStore.prices[a.asset] ?? 0, cryptoAmount: a.availableBalance);
|
||||
price: fiatConversionStore.prices[a.asset] ?? 0, cryptoAmount: a.raw.available);
|
||||
final bFiatBalance = _getFiatBalance(
|
||||
price: fiatConversionStore.prices[b.asset] ?? 0, cryptoAmount: b.availableBalance);
|
||||
price: fiatConversionStore.prices[b.asset] ?? 0, cryptoAmount: b.raw.available);
|
||||
|
||||
return (double.tryParse(bFiatBalance) ?? 0)
|
||||
.compareTo((double.tryParse(aFiatBalance)) ?? 0);
|
||||
@@ -425,7 +450,8 @@ abstract class BalanceViewModelBase with Store {
|
||||
}
|
||||
|
||||
if (wallet.walletInfo.favoriteTokenAddress != null) {
|
||||
return formattedBalances.firstWhereOrNull((item) => (getTokenAddressBasedOnWallet(item.asset) ==
|
||||
return formattedBalances.firstWhereOrNull((item) =>
|
||||
(getTokenAddressBasedOnWallet(item.asset) ==
|
||||
wallet.walletInfo.favoriteTokenAddress)) ??
|
||||
formattedBalances.elementAtOrNull(0);
|
||||
}
|
||||
@@ -455,8 +481,8 @@ abstract class BalanceViewModelBase with Store {
|
||||
|
||||
@computed
|
||||
bool get showCombinedBalance {
|
||||
if(wallet.type == WalletType.bitcoin) return false;
|
||||
if(balances.values.length == 1) return false;
|
||||
if (wallet.type == WalletType.bitcoin) return false;
|
||||
if (balances.values.length == 1) return false;
|
||||
|
||||
return wallet.walletInfo.showCombinedBalance;
|
||||
}
|
||||
@@ -470,15 +496,14 @@ abstract class BalanceViewModelBase with Store {
|
||||
double ret = 0.0;
|
||||
for (final curr in wallet.balance.keys) {
|
||||
final record = wallet.balance[curr]!;
|
||||
final available = evm?.getERC20AvailableBalance(record) ??
|
||||
(record.fullAvailableBalance - (record.secondAvailable ?? BigInt.zero));
|
||||
final available = record.available - (record.secondAvailable ?? Money.zero(curr));
|
||||
final price = fiatConversionStore.prices[curr] ?? 0;
|
||||
ret += double.tryParse(calculateFiatAmount(
|
||||
price: price, cryptoAmount: curr.formatAmount(available).replaceAll(",", ""))
|
||||
printV(record.available);
|
||||
ret += double.tryParse(calculateFiatAmount(price: price, cryptoAmount: available.toString())
|
||||
.replaceAll(",", "")) ??
|
||||
0;
|
||||
}
|
||||
return ret.toStringAsFixed(2);
|
||||
return ret.toStringAsFixed(2).withLocalSeperator(settingsStore.languageCode);
|
||||
}
|
||||
|
||||
Balance _currencyBalance(CryptoCurrency cryptoCurrency) {
|
||||
@@ -522,13 +547,12 @@ abstract class BalanceViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
|
||||
String _getFiatBalance({required double price, String? cryptoAmount}) {
|
||||
if (cryptoAmount == null || cryptoAmount.isEmpty || double.tryParse(cryptoAmount) == null) {
|
||||
return '0.00'.withLocalSeperator(settingsStore.languageCode);
|
||||
String _getFiatBalance({required double price, Money? cryptoAmount}) {
|
||||
if (cryptoAmount == null) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
return calculateFiatAmount(price: price, cryptoAmount: cryptoAmount)
|
||||
.withLocalSeperator(settingsStore.languageCode);
|
||||
return calculateFiatAmount(price: price, cryptoAmount: cryptoAmount.toString(), raw: true);
|
||||
}
|
||||
|
||||
String _formatterAsset(CryptoCurrency asset) {
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
@@ -52,17 +46,12 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
String get formattedCryptoAmount {
|
||||
if (displayMode == BalanceDisplayMode.hiddenBalance) return '---';
|
||||
if (balanceViewModel.wallet.type == WalletType.bitcoin) {
|
||||
final isLightning = (transaction.additionalInfo["isLightning"] as bool?) ?? false;
|
||||
final crypto = isLightning ? CryptoCurrency.btcln : CryptoCurrency.btc;
|
||||
final amount = _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(transaction.amount, crypto)
|
||||
.withMaxDecimals(8)
|
||||
return _appStore.amountParsingProxy
|
||||
.asDisplayStringWithSymbol(transaction.amount)
|
||||
.withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
|
||||
return '$amount ${_appStore.amountParsingProxy.getCryptoSymbol(crypto)}';
|
||||
}
|
||||
|
||||
return transaction.amountFormatted();
|
||||
return transaction.amount.toStringWithSymbol();
|
||||
}
|
||||
|
||||
String get formattedTitle {
|
||||
@@ -193,26 +182,18 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
|
||||
switch (balanceViewModel.wallet.type) {
|
||||
case WalletType.monero:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: monero!.formatterMoneroAmountToDouble(amount: transaction.amount),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: wownero!.formatterWowneroAmountToDouble(amount: transaction.amount),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.nano:
|
||||
case WalletType.decred:
|
||||
case WalletType.zcash:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: bitcoin!.formatterBitcoinAmountToDouble(amount: transaction.amount),
|
||||
cryptoAmount: double.parse(transaction.amount.toString()),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
@@ -221,14 +202,7 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
final asset = assetOfTransaction;
|
||||
final price = balanceViewModel.fiatConversionStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: evm!.formatterEVMAmountToDouble(transaction: transaction),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.nano:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: double.parse(nanoUtil!.getRawAsUsableString(
|
||||
nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)),
|
||||
cryptoAmount: double.parse(transaction.amount.toString()),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
@@ -236,16 +210,15 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
final asset = solana!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
final price = balanceViewModel.fiatConversionStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: solana!.getTransactionAmountRaw(transaction),
|
||||
cryptoAmount: double.parse(transaction.amount.toString()),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.tron:
|
||||
final asset = tron!.assetOfTransaction(balanceViewModel.wallet, transaction);
|
||||
final price = balanceViewModel.fiatConversionStore.prices[asset];
|
||||
final cryptoAmount = tron!.getTransactionAmountRaw(transaction);
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: cryptoAmount,
|
||||
cryptoAmount: double.parse(transaction.amount.toString()),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
@@ -257,24 +230,10 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||
}
|
||||
final price = balanceViewModel.fiatConversionStore.prices[asset];
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: zano!.formatterIntAmountToDouble(
|
||||
amount: transaction.amount, currency: asset, forFee: false),
|
||||
cryptoAmount: double.parse(transaction.amount.toString()),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.decred:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount: decred!.formatterDecredAmountToDouble(amount: transaction.amount),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
break;
|
||||
case WalletType.zcash:
|
||||
amount = calculateFiatAmountRaw(
|
||||
cryptoAmount:
|
||||
zcash!.formatterZcashAmountToDouble(amount: BigInt.from(transaction.amount)),
|
||||
price: price,
|
||||
).withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
|
||||
case WalletType.none:
|
||||
case WalletType.banano:
|
||||
case WalletType.haven:
|
||||
|
||||
@@ -2,10 +2,12 @@ import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:math' show min;
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/core/create_trade_result.dart';
|
||||
import 'package:cake_wallet/core/fiat_conversion_service.dart';
|
||||
@@ -41,6 +43,8 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/utils/exchange_provider_logger.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import "package:cw_core/wallet_info.dart";
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
@@ -103,8 +107,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
) : isSendAllEnabled = false,
|
||||
isFixedRateMode = false,
|
||||
isReceiveAmountEntered = false,
|
||||
_depositAmount = '',
|
||||
_receiveAmount = '',
|
||||
_depositAmount = null,
|
||||
_receiveAmount = null,
|
||||
receiveAddress = '',
|
||||
depositAddress = '',
|
||||
isDepositAddressEnabled = false,
|
||||
@@ -163,8 +167,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
});
|
||||
|
||||
isDepositAddressEnabled = !useSameWalletAddress(depositCurrency);
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
receiveAddress = '';
|
||||
depositAddress =
|
||||
useSameWalletAddress(depositCurrency) ? wallet.walletAddresses.addressForExchange : '';
|
||||
@@ -237,12 +241,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(
|
||||
@@ -253,15 +256,15 @@ 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);
|
||||
if(depositCurrency == currency) {
|
||||
final sendingBalance = Money.fromInt(
|
||||
await unspentCoinsListViewModel.getSendingBalance(UnspentCoinType.any), currency);
|
||||
final amount = _appStore.amountParsingProxy.asDisplayStringWithSymbol(sendingBalance);
|
||||
if (depositCurrency == currency) {
|
||||
depositAvailableAmount = amount;
|
||||
}
|
||||
}
|
||||
@@ -289,7 +292,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
].contains(wallet.type);
|
||||
|
||||
bool get hideAddressAfterExchange =>
|
||||
[WalletType.monero, WalletType.wownero, WalletType.zcash].contains(wallet.type);
|
||||
[WalletType.monero, WalletType.wownero, WalletType.zcash].contains(wallet.type) ||
|
||||
isElectrumWallet;
|
||||
|
||||
bool _useTorOnly;
|
||||
final ExchangeTemplateStore _exchangeTemplateStore;
|
||||
@@ -352,25 +356,29 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
ExchangeTradeState tradeState;
|
||||
|
||||
@observable
|
||||
String _depositAmount;
|
||||
Money? _depositAmount;
|
||||
|
||||
@computed
|
||||
bool get hasDepositAmount => _depositAmount != null;
|
||||
|
||||
@computed
|
||||
String get depositAmount =>
|
||||
_depositAmount == "..."? _depositAmount:amountParsingProxy.getDisplayCryptoAmount(_depositAmount, depositCurrency);
|
||||
_depositAmount == null ? "" : amountParsingProxy.asDisplayString(_depositAmount!);
|
||||
|
||||
@computed
|
||||
String get depositAmountCanonical => _depositAmount;
|
||||
String get depositAmountCanonical => _depositAmount == null ? "0.0" : _depositAmount.toString();
|
||||
|
||||
@observable
|
||||
String _receiveAmount;
|
||||
Money? _receiveAmount;
|
||||
|
||||
@computed
|
||||
String get receiveAmount =>
|
||||
_receiveAmount == "..." ? _receiveAmount:amountParsingProxy.getDisplayCryptoAmount(_receiveAmount, receiveCurrency);
|
||||
_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;
|
||||
@@ -486,7 +494,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
@computed
|
||||
String? get balanceDisplay {
|
||||
CryptoCurrency? balanceCurrency;
|
||||
if (isEVMCompatibleChain(wallet.type)) {
|
||||
if (isEVMCompatibleChain(wallet.type) || wallet.type == WalletType.solana || wallet.type == WalletType.tron) {
|
||||
balanceCurrency = wallet.balance.keys.firstWhereOrNull(
|
||||
(c) =>
|
||||
c.title == depositCurrency.title &&
|
||||
@@ -495,10 +503,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
|
||||
@@ -572,10 +579,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;
|
||||
@@ -624,12 +631,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 (_) {
|
||||
@@ -642,12 +649,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 (_) {
|
||||
@@ -660,12 +667,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 (_) {
|
||||
@@ -678,13 +685,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: $_');
|
||||
@@ -771,32 +779,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 = Money.tryParse(amount_.toStringAsFixed(depositCurrency.decimals), 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;
|
||||
|
||||
@@ -809,7 +822,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;
|
||||
|
||||
@@ -822,34 +835,40 @@ 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;
|
||||
}
|
||||
/// For fixed-rate transactions, we don't want to recalculate receive amount
|
||||
/// 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 =
|
||||
Money.tryParse(amount_.toStringAsFixed(min(20, receiveCurrency.decimals)), 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;
|
||||
|
||||
@@ -866,8 +885,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,
|
||||
@@ -891,8 +911,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
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;
|
||||
@@ -1025,13 +1046,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
Future<void> createTrade() async {
|
||||
final depositAmountValue =
|
||||
double.tryParse(_depositAmount.replaceAll(',', '.')) ?? 0.0;
|
||||
final depositAmountValue = _depositAmount ?? Money.zero(depositCurrency);
|
||||
final receiveAmountValue = _receiveAmount ?? Money.zero(receiveCurrency);
|
||||
|
||||
final receiveAmountValue =
|
||||
double.tryParse(_receiveAmount.replaceAll(',', '.')) ?? 0.0;
|
||||
|
||||
if (depositAmountValue <= 0 || receiveAmountValue <= 0) {
|
||||
if (depositAmountValue.isZero ||
|
||||
depositAmountValue.isNegative ||
|
||||
receiveAmountValue.isZero ||
|
||||
receiveAmountValue.isNegative) {
|
||||
ExchangeProviderLogger.logError(
|
||||
provider: forcedProvider?.description ?? bestRateProvider?.description,
|
||||
function: 'createTrade',
|
||||
@@ -1047,25 +1068,26 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
},
|
||||
);
|
||||
|
||||
final invalidAmountError = depositAmountValue <= 0
|
||||
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,
|
||||
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()));
|
||||
error: S.current.amount_is_below_minimum_limit(limits.min!.toString()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1090,8 +1112,9 @@ 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);
|
||||
title: S.current.trade_not_created,
|
||||
error: S.current.none_of_selected_providers_can_exchange,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1183,7 +1206,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
if (isFixedRateMode && provider.supportsFixedRate == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (_excludeProviderForReceiveExtraId(provider)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1200,13 +1223,13 @@ 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(),
|
||||
@@ -1218,8 +1241,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) {
|
||||
@@ -1242,6 +1264,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) {
|
||||
@@ -1260,7 +1288,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
|
||||
tradesStore.setTrade(trade);
|
||||
if (trade.provider != ExchangeProviderDescription.thorChain) await trade.save();
|
||||
if (trade.provider != ExchangeProviderDescription.thorChain) await trade.save();
|
||||
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
||||
|
||||
/// return after the first successful trade
|
||||
@@ -1304,8 +1332,8 @@ 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 =
|
||||
@@ -1341,8 +1369,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,
|
||||
@@ -1353,7 +1381,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);
|
||||
|
||||
@@ -1371,7 +1399,7 @@ 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);
|
||||
@@ -1399,7 +1427,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(
|
||||
@@ -1449,14 +1477,14 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
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;
|
||||
@@ -1559,6 +1587,21 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
}
|
||||
}
|
||||
|
||||
String? _addressTypeValidation(String refundAddress, String receiveAddress) {
|
||||
final isRefundAddressSP = RegExp(AddressValidator.silentPaymentAddressPatternMainnet).hasMatch(refundAddress);
|
||||
if (isRefundAddressSP) return 'Silent Payment ${S.current.address_not_allowed_as_refund}';
|
||||
|
||||
final isReceiveAddressSP = RegExp(AddressValidator.silentPaymentAddressPatternMainnet).hasMatch(receiveAddress);
|
||||
if (isReceiveAddressSP) return 'Silent Payment ${S.current.address_not_allowed_as_receive}';
|
||||
|
||||
final isRefundAddressMWEB = RegExp(AddressValidator.mWebAddressPattern).hasMatch(refundAddress);
|
||||
if (isRefundAddressMWEB) return 'MWEB ${S.current.address_not_allowed_as_refund}';
|
||||
|
||||
final isReceiveAddressMWEB = RegExp(AddressValidator.mWebAddressPattern).hasMatch(receiveAddress);
|
||||
if (isReceiveAddressMWEB) return 'MWEB ${S.current.address_not_allowed_as_receive}';
|
||||
return null;
|
||||
}
|
||||
|
||||
void _defineIsReceiveAmountEditable() {
|
||||
/*if ((provider is ChangeNowExchangeProvider)
|
||||
&&(depositCurrency == CryptoCurrency.xmr)
|
||||
@@ -1591,8 +1634,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
|
||||
@action
|
||||
void saveSelectedProviders() {
|
||||
_depositAmount = '';
|
||||
_receiveAmount = '';
|
||||
_depositAmount = null;
|
||||
_receiveAmount = null;
|
||||
isFixedRateMode = false;
|
||||
_defineIsReceiveAmountEditable();
|
||||
bestRateProvider = null;
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
@@ -22,7 +23,9 @@ class DEuroViewModel = DEuroViewModelBase with _$DEuroViewModel;
|
||||
abstract class DEuroViewModelBase with Store {
|
||||
final AppStore _appStore;
|
||||
|
||||
static BigInt get MIN_ACCRUED_INTEREST => BigInt.parse("1000000000000");
|
||||
static Money get MIN_ACCRUED_INTEREST => Money(BigInt.parse("1000000000000"), CryptoCurrency.deuro);
|
||||
|
||||
static Money get ZERO => Money.zero(CryptoCurrency.deuro);
|
||||
|
||||
DEuroViewModelBase(
|
||||
this._appStore, this.balanceViewModel, this._settingsStore, this._fiatConversationStore,
|
||||
@@ -51,13 +54,13 @@ abstract class DEuroViewModelBase with Store {
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmountFormatted {
|
||||
final amount = transaction == null ? '0.00' : _getDEuroFiatAmount(transaction!.amountFormatted);
|
||||
return isFiatDisabled ? '' : '$amount ${fiat.title}';
|
||||
final amount = transaction == null ? "0.00" : _getDEuroFiatAmount(transaction!.amount);
|
||||
return isFiatDisabled ? "" : "$amount ${fiat.symbol}";
|
||||
}
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFeeFiatAmountFormatted {
|
||||
var amount = '0.00';
|
||||
var amount = "0.00";
|
||||
try {
|
||||
if (transaction != null) {
|
||||
final feeCurrency = CryptoCurrency.eth;
|
||||
@@ -67,42 +70,43 @@ abstract class DEuroViewModelBase with Store {
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
return isFiatDisabled ? '' : '$amount ${fiat.title}';
|
||||
return isFiatDisabled ? "" : "$amount ${fiat.symbol}";
|
||||
}
|
||||
|
||||
@computed
|
||||
String get accountBalanceFormated {
|
||||
Money get accountBalance {
|
||||
final dEuroKey = balanceViewModel.balances.keys
|
||||
.firstWhereOrNull((e) => e.title == CryptoCurrency.deuro.title);
|
||||
if (dEuroKey == null) return '0.00';
|
||||
return balanceViewModel.balances[dEuroKey]?.availableBalance ?? '0.00';
|
||||
if (dEuroKey == null) return ZERO;
|
||||
return wallet.balance[dEuroKey]?.available ?? ZERO;
|
||||
}
|
||||
|
||||
@observable
|
||||
BigInt savingsBalance = BigInt.zero;
|
||||
Money savingsBalance = ZERO;
|
||||
|
||||
@computed
|
||||
String get savingsBalanceFormated =>
|
||||
evm!.formatterEVMAmountToDouble(amount: savingsBalance).toStringAsFixed(6);
|
||||
String get fiatSavingsBalanceFormated => _getDEuroFiatAmount(savingsBalance);
|
||||
|
||||
@observable
|
||||
Money? savingsBalanceV1 = null;
|
||||
|
||||
@computed
|
||||
String get fiatSavingsBalanceFormated => _getDEuroFiatAmount(savingsBalanceFormated);
|
||||
String get fiatSavingsBalanceV1Formated => _getDEuroFiatAmount(savingsBalanceV1);
|
||||
|
||||
@observable
|
||||
ExecutionState state = InitialExecutionState();
|
||||
|
||||
@observable
|
||||
String interestRateFormated = '0';
|
||||
String interestRateFormated = "0";
|
||||
|
||||
@observable
|
||||
BigInt accruedInterest = BigInt.zero;
|
||||
Money accruedInterest = ZERO;
|
||||
|
||||
@computed
|
||||
String get accruedInterestFormated =>
|
||||
evm!.formatterEVMAmountToDouble(amount: accruedInterest).toStringAsFixed(6);
|
||||
String get accruedInterestFormated => accruedInterest.toStringWithPrecision(fractionalDigits: 6);
|
||||
|
||||
@computed
|
||||
String get fiatAccruedInterestFormated => _getDEuroFiatAmount(accruedInterestFormated);
|
||||
String get fiatAccruedInterestFormated => _getDEuroFiatAmount(accruedInterest);
|
||||
|
||||
@observable
|
||||
BigInt approvedTokens = BigInt.zero;
|
||||
@@ -128,8 +132,12 @@ abstract class DEuroViewModelBase with Store {
|
||||
@action
|
||||
Future<void> reloadSavingsUserData() async {
|
||||
approvedTokens = await evm!.getDEuroSavingsApproved(_appStore.wallet!) ?? BigInt.zero;
|
||||
savingsBalance = await evm!.getDEuroSavingsBalance(_appStore.wallet!) ?? BigInt.zero;
|
||||
accruedInterest = await evm!.getDEuroAccruedInterest(_appStore.wallet!) ?? BigInt.zero;
|
||||
savingsBalance = await evm!.getDEuroSavingsBalance(_appStore.wallet!) ?? ZERO;
|
||||
accruedInterest = await evm!.getDEuroAccruedInterest(_appStore.wallet!) ?? ZERO;
|
||||
|
||||
final v1Balance = await evm!.getDEuroSavingsV1Balance(_appStore.wallet!) ?? ZERO;
|
||||
savingsBalanceV1 = v1Balance.isZero ? null : v1Balance;
|
||||
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
@@ -151,9 +159,8 @@ abstract class DEuroViewModelBase with Store {
|
||||
state = TransactionCommitting();
|
||||
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
|
||||
final approval = await evm!.enableDEuroSaving(_appStore.wallet!, priority);
|
||||
if (approval == null) {
|
||||
throw Exception('DEuro saving not available');
|
||||
}
|
||||
if (approval == null) throw Exception("DEuro saving not available");
|
||||
|
||||
approvalTransaction = approval;
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
@@ -167,13 +174,13 @@ abstract class DEuroViewModelBase with Store {
|
||||
state = TransactionCommitting();
|
||||
|
||||
if (amountRaw.isEmpty || amountRaw.trim().isEmpty) {
|
||||
throw Exception('Invalid amount: amount cannot be empty');
|
||||
throw Exception("Invalid amount: amount cannot be empty");
|
||||
}
|
||||
|
||||
final amount = tryParseFixed(amountRaw, 18);
|
||||
|
||||
if (amount == BigInt.zero || amount == null) {
|
||||
throw Exception('Invalid amount: amount cannot be zero');
|
||||
throw Exception("Invalid amount: amount cannot be zero");
|
||||
}
|
||||
|
||||
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
|
||||
@@ -181,9 +188,24 @@ abstract class DEuroViewModelBase with Store {
|
||||
final tx = await (isAdding
|
||||
? evm!.addDEuroSaving(_appStore.wallet!, amount, priority)
|
||||
: evm!.removeDEuroSaving(_appStore.wallet!, amount, priority));
|
||||
if (tx == null) {
|
||||
throw Exception('DEuro saving not available');
|
||||
}
|
||||
if (tx == null) throw Exception("DEuro saving not available");
|
||||
|
||||
transaction = tx;
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> prepareSavingsV1Withdraw() async {
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
actionType = DEuroActionType.withdraw;
|
||||
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
|
||||
final tx = await evm!.withdrawDEuroSavingV1(_appStore.wallet!, priority);
|
||||
if (tx == null) throw Exception("DEuro saving not available");
|
||||
|
||||
transaction = tx;
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
@@ -193,13 +215,13 @@ abstract class DEuroViewModelBase with Store {
|
||||
|
||||
Future<void> prepareCollectInterest() async {
|
||||
if (accruedInterest < MIN_ACCRUED_INTEREST) {
|
||||
state = FailureState('Accrued interest is below minimum threshold');
|
||||
state = FailureState("Accrued interest is below minimum threshold");
|
||||
return;
|
||||
}
|
||||
|
||||
final formatted = accruedInterestFormated;
|
||||
if (formatted.isEmpty || formatted == '0.000000') {
|
||||
state = FailureState('Invalid accrued interest amount');
|
||||
if (formatted.isEmpty || formatted == "0.000000") {
|
||||
state = FailureState("Invalid accrued interest amount");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -212,9 +234,8 @@ abstract class DEuroViewModelBase with Store {
|
||||
actionType = DEuroActionType.reinvest;
|
||||
final priority = _appStore.settingsStore.getPriority(wallet.type, chainId: wallet.chainId)!;
|
||||
final tx = await evm!.reinvestDEuroInterest(_appStore.wallet!, priority);
|
||||
if (tx == null) {
|
||||
throw Exception('DEuro saving not available');
|
||||
}
|
||||
if (tx == null) throw Exception("DEuro saving not available");
|
||||
|
||||
transaction = tx;
|
||||
state = InitialExecutionState();
|
||||
} catch (e) {
|
||||
@@ -261,7 +282,9 @@ abstract class DEuroViewModelBase with Store {
|
||||
state = InitialExecutionState();
|
||||
}
|
||||
|
||||
String _getDEuroFiatAmount(String amount) {
|
||||
String _getDEuroFiatAmount(Money? amount) {
|
||||
if (amount == null) return "0.00";
|
||||
|
||||
try {
|
||||
var dEuro = CryptoCurrency.deuro;
|
||||
final keys = _fiatConversationStore.prices.keys.toList();
|
||||
@@ -270,19 +293,14 @@ abstract class DEuroViewModelBase with Store {
|
||||
}
|
||||
return calculateFiatAmount(
|
||||
price: _fiatConversationStore.prices[dEuro]!,
|
||||
cryptoAmount: amount,
|
||||
cryptoAmount: amount.toStringWithPrecision(fractionalDigits: 6),
|
||||
);
|
||||
} catch (_) {
|
||||
return '0.00';
|
||||
return "0.00";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoEtherState extends ExecutionState {}
|
||||
|
||||
enum DEuroActionType {
|
||||
deposit,
|
||||
withdraw,
|
||||
reinvest,
|
||||
none;
|
||||
}
|
||||
enum DEuroActionType { deposit, withdraw, reinvest, none }
|
||||
|
||||
@@ -110,10 +110,10 @@ abstract class PayjoinDetailsViewModelBase with Store {
|
||||
value: '${transactionInfo!.height}',
|
||||
key: ValueKey('standard_list_item_transaction_details_height_key'),
|
||||
),
|
||||
if (transactionInfo!.feeFormatted()?.isNotEmpty ?? false)
|
||||
if (transactionInfo!.fee != null)
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_fee,
|
||||
value: transactionInfo!.feeFormatted()!,
|
||||
value: transactionInfo!.fee!.toStringWithSymbol(),
|
||||
key: ValueKey('standard_list_item_transaction_details_fee_key'),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -93,7 +93,7 @@ abstract class PaymentViewModelBase with Store {
|
||||
if (currentWallet != null &&
|
||||
currentWallet.type == detectedWalletType &&
|
||||
!isEVMCompatibleChain(detectedWalletType!)) {
|
||||
return PaymentFlowResult.currentWalletCompatible();
|
||||
return PaymentFlowResult.currentWalletCompatible(detectionResult);
|
||||
}
|
||||
|
||||
if (detectedWalletType == WalletType.solana) {
|
||||
@@ -229,8 +229,12 @@ class PaymentFlowResult {
|
||||
);
|
||||
|
||||
/// Current wallet is compatible
|
||||
factory PaymentFlowResult.currentWalletCompatible() =>
|
||||
PaymentFlowResult._(type: PaymentFlowType.currentWalletCompatible);
|
||||
factory PaymentFlowResult.currentWalletCompatible(
|
||||
AddressDetectionResult addressDetectionResult) =>
|
||||
PaymentFlowResult._(
|
||||
type: PaymentFlowType.currentWalletCompatible,
|
||||
addressDetectionResult: addressDetectionResult,
|
||||
);
|
||||
|
||||
/// Single compatible wallet available
|
||||
factory PaymentFlowResult.singleWallet(
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
void reset() => filePath = '';
|
||||
|
||||
@action
|
||||
Future<void> import(String password) async {
|
||||
Future<void> import(String password,{bool checkBackupApp = true}) async {
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
|
||||
@@ -44,7 +44,9 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
final file = File(filePath);
|
||||
|
||||
try {
|
||||
await backupService.importBackupFile(file, password);
|
||||
await backupService.importBackupFile(file, password, checkBackupApp: checkBackupApp);
|
||||
} on IncompatibleBackupAppException {
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
if (e.toString().contains("unknown_backup_version")) {
|
||||
state = FailureState('This is not a valid backup file, please make sure you have selected the correct one');
|
||||
@@ -74,6 +76,9 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
});
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
} on IncompatibleBackupAppException {
|
||||
state = InitialExecutionState();
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
var msg = e.toString().toLowerCase();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:math' show min;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/decred/decred.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
|
||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||
@@ -9,7 +8,6 @@ import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
|
||||
@@ -17,14 +15,12 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cake_wallet/zano/zano.dart';
|
||||
import 'package:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
@@ -50,7 +46,7 @@ abstract class OutputBase with Store {
|
||||
note = '',
|
||||
memo = "",
|
||||
extractedAddress = '',
|
||||
estimatedFee = '0.0',
|
||||
estimatedFee = Money.zero(cryptoCurrencyHandler()),
|
||||
parsedAddress = ParsedAddress(addresses: []) {
|
||||
autorun((_) {
|
||||
final status = _wallet.syncStatus;
|
||||
@@ -64,6 +60,9 @@ abstract class OutputBase with Store {
|
||||
|
||||
bool get useSatoshi => _appStore.amountParsingProxy.useSatoshi(cryptoCurrencyHandler());
|
||||
|
||||
@observable
|
||||
bool isFiatEntry = false;
|
||||
|
||||
@observable
|
||||
String fiatAmount;
|
||||
|
||||
@@ -74,8 +73,7 @@ abstract class OutputBase with Store {
|
||||
String? displayName;
|
||||
|
||||
@computed
|
||||
String get displayCryptoAmount =>
|
||||
_appStore.amountParsingProxy.getDisplayCryptoAmount(cryptoAmount, cryptoCurrencyHandler());
|
||||
String get displayCryptoAmount => _appStore.amountParsingProxy.asDisplayString(cryptoAmountMoney);
|
||||
|
||||
@observable
|
||||
String cryptoFullBalance;
|
||||
@@ -106,9 +104,8 @@ abstract class OutputBase with Store {
|
||||
String roundedCryptoAmount(int digits) => displayCryptoAmount.withMaxDecimals(digits);
|
||||
|
||||
String roundedFiatAmount(int digits) {
|
||||
if (fiatAmount.split(".").last.length <= digits) {
|
||||
return fiatAmount;
|
||||
}
|
||||
if (fiatAmount.split(".").last.length <= digits) return fiatAmount;
|
||||
|
||||
return double.tryParse(fiatAmount.replaceAll(",", ""))?.toStringAsPrecision(digits) ?? '0.0';
|
||||
}
|
||||
|
||||
@@ -116,65 +113,18 @@ abstract class OutputBase with Store {
|
||||
String? stealthAddress;
|
||||
|
||||
@computed
|
||||
int get formattedCryptoAmount {
|
||||
int amount = 0;
|
||||
Money get cryptoAmountMoney {
|
||||
if (cryptoAmount.isEmpty) return Money.zero(cryptoCurrencyHandler());
|
||||
|
||||
try {
|
||||
if (cryptoAmount.isNotEmpty) {
|
||||
final _cryptoAmount = cryptoAmount.replaceAll(',', '.');
|
||||
int _amount = 0;
|
||||
switch (walletType) {
|
||||
case WalletType.monero:
|
||||
_amount = monero!.formatterMoneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.dogecoin:
|
||||
_amount = cryptoCurrencyHandler().parseAmount(_cryptoAmount).toInt();
|
||||
break;
|
||||
case WalletType.decred:
|
||||
_amount = decred!.formatterStringDoubleToDecredAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.bsc:
|
||||
_amount = evm!.formatterEVMParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
_amount = wownero!.formatterWowneroParseAmount(amount: _cryptoAmount);
|
||||
break;
|
||||
case WalletType.zano:
|
||||
_amount = zano!
|
||||
.formatterParseAmount(amount: _cryptoAmount, currency: cryptoCurrencyHandler());
|
||||
break;
|
||||
case WalletType.zcash:
|
||||
_amount = zcash!.formatterZcashParseAmount(_cryptoAmount);
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_amount > 0) {
|
||||
amount = _amount;
|
||||
}
|
||||
}
|
||||
return cryptoCurrencyHandler().parseAmount(cryptoAmount.sanitized());
|
||||
} catch (e) {
|
||||
amount = 0;
|
||||
return Money.zero(cryptoCurrencyHandler());
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@observable
|
||||
String estimatedFee;
|
||||
Money estimatedFee;
|
||||
|
||||
@action
|
||||
Future<void> calculateEstimatedFee() async {
|
||||
@@ -188,7 +138,7 @@ abstract class OutputBase with Store {
|
||||
if (_settingsStore.getPriority(_wallet.type, chainId: _wallet.chainId) != null) {
|
||||
fee = _wallet.calculateEstimatedFee(
|
||||
_settingsStore.getPriority(_wallet.type, chainId: _wallet.chainId)!,
|
||||
formattedCryptoAmount,
|
||||
cryptoAmountMoney.amount.toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,41 +149,35 @@ abstract class OutputBase with Store {
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.dogecoin:
|
||||
case WalletType.decred:
|
||||
estimatedFee = walletTypeToCryptoCurrency(_wallet.type).formatAmount(BigInt.from(fee));
|
||||
case WalletType.zano:
|
||||
estimatedFee = Money.fromInt(fee, walletTypeToCryptoCurrency(_wallet.type));
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
if (cryptoCurrencyHandler() == CryptoCurrency.btcln) {
|
||||
estimatedFee =
|
||||
_appStore.amountParsingProxy.getDisplayCryptoString(10, cryptoCurrencyHandler());
|
||||
estimatedFee = Money.fromInt(10, cryptoCurrencyHandler());
|
||||
break;
|
||||
}
|
||||
if (_settingsStore.getPriority(_wallet.type) ==
|
||||
bitcoin!.getBitcoinTransactionPriorityCustom()) {
|
||||
fee = bitcoin!.getEstimatedFeeWithFeeRate(
|
||||
_wallet, _settingsStore.customBitcoinFeeRate, formattedCryptoAmount);
|
||||
_wallet, _settingsStore.customBitcoinFeeRate, cryptoAmountMoney.amount.toInt());
|
||||
}
|
||||
|
||||
estimatedFee = _appStore.amountParsingProxy.getDisplayCryptoString(fee, cryptoCurrencyHandler());
|
||||
estimatedFee = Money.fromInt(fee, cryptoCurrencyHandler());
|
||||
break;
|
||||
case WalletType.solana:
|
||||
estimatedFee = solana!.getEstimateFees(_wallet).toString();
|
||||
break;
|
||||
case WalletType.zano:
|
||||
estimatedFee = zano!
|
||||
.formatterIntAmountToDouble(
|
||||
amount: fee, currency: cryptoCurrencyHandler(), forFee: true)
|
||||
.toString();
|
||||
estimatedFee = solana!.getEstimateFees(_wallet) ?? Money.zero(CryptoCurrency.sol);
|
||||
break;
|
||||
case WalletType.tron:
|
||||
if (cryptoCurrencyHandler() == CryptoCurrency.trx) {
|
||||
estimatedFee = tron!.getTronNativeEstimatedFee(_wallet).toString();
|
||||
estimatedFee = tron!.getTronNativeEstimatedFee(_wallet) ?? Money.zero(CryptoCurrency.trx);
|
||||
} else {
|
||||
estimatedFee = tron!.getTronTRC20EstimatedFee(_wallet).toString();
|
||||
estimatedFee = tron!.getTronTRC20EstimatedFee(_wallet) ?? Money.zero(CryptoCurrency.trx);
|
||||
}
|
||||
break;
|
||||
|
||||
case WalletType.zcash:
|
||||
estimatedFee = zcash!.formatterZcashAmountToDouble(amount: BigInt.from(fee)).toString();
|
||||
estimatedFee = Money.fromInt(fee, cryptoCurrencyHandler());
|
||||
break;
|
||||
|
||||
/// EVMs
|
||||
@@ -242,16 +186,19 @@ abstract class OutputBase with Store {
|
||||
case WalletType.base:
|
||||
case WalletType.arbitrum:
|
||||
case WalletType.bsc:
|
||||
final isNative = cryptoCurrencyHandler() == CryptoCurrency.eth ||
|
||||
cryptoCurrencyHandler() == CryptoCurrency.maticpoly ||
|
||||
cryptoCurrencyHandler() == CryptoCurrency.baseEth ||
|
||||
cryptoCurrencyHandler() == CryptoCurrency.arbEth ||
|
||||
cryptoCurrencyHandler() == CryptoCurrency.bnb;
|
||||
String? fee = isNative
|
||||
final isNative = [
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.maticpoly,
|
||||
CryptoCurrency.baseEth,
|
||||
CryptoCurrency.arbEth,
|
||||
CryptoCurrency.bnb
|
||||
].contains(cryptoCurrencyHandler());
|
||||
|
||||
final fee = isNative
|
||||
? evm!.getEVMNativeEstimatedFee(_wallet)
|
||||
: evm!.getEVMERC20EstimatedFee(_wallet);
|
||||
|
||||
estimatedFee = formatFixed(BigInt.parse(fee ?? '0.0'), 18, fractionalDigits: 12);
|
||||
estimatedFee = Money(BigInt.parse(fee ?? '0.0'), walletTypeToCryptoCurrency(_wallet.type));
|
||||
break;
|
||||
|
||||
/// end EVMs
|
||||
@@ -275,12 +222,12 @@ abstract class OutputBase with Store {
|
||||
|
||||
try {
|
||||
final currency = (isEVMCompatibleChain(_wallet.type) ||
|
||||
[WalletType.solana, WalletType.tron].contains(_wallet.type))
|
||||
[WalletType.solana, WalletType.tron].contains(_wallet.type)) ||
|
||||
cryptoCurrencyHandler() == CryptoCurrency.btcln
|
||||
? _wallet.currency
|
||||
: cryptoCurrencyHandler();
|
||||
|
||||
final cryptoAmount =
|
||||
double.parse(_appStore.amountParsingProxy.getCanonicalCryptoAmount(estimatedFee, currency));
|
||||
final cryptoAmount = double.parse(estimatedFee.toString());
|
||||
|
||||
return calculateFiatAmountRaw(
|
||||
price: _fiatConversationStore.prices[currency]!, cryptoAmount: cryptoAmount);
|
||||
@@ -294,7 +241,7 @@ abstract class OutputBase with Store {
|
||||
|
||||
WalletType get walletType => _wallet.type;
|
||||
|
||||
final CryptoCurrency Function() cryptoCurrencyHandler;
|
||||
final CryptoCurrency Function([CryptoCurrency?]) cryptoCurrencyHandler;
|
||||
final FiatConversionStore _fiatConversationStore;
|
||||
final AppStore _appStore;
|
||||
|
||||
@@ -401,6 +348,10 @@ abstract class OutputBase with Store {
|
||||
if(parsedAddress.name.isNotEmpty) {
|
||||
displayName = parsedAddress.name;
|
||||
}
|
||||
|
||||
if (parsedAddress.parseFrom == ParseFrom.lnurlpay) {
|
||||
cryptoCurrencyHandler(CryptoCurrency.btcln);
|
||||
}
|
||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||
note = parsedAddress.description;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,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:cake_wallet/zcash/zcash.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
@@ -121,7 +122,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
fiatFromSettings = _appStore.settingsStore.fiatCurrency,
|
||||
fiatCurrencies = FiatCurrency.all,
|
||||
super(appStore: _appStore) {
|
||||
outputs.add(Output(wallet, _appStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
outputs.add(Output(wallet, _appStore, _fiatConversationStore, _outputCryptoCurrencyHandler));
|
||||
|
||||
unspentCoinsListViewModel
|
||||
.initialSetup()
|
||||
@@ -162,12 +163,19 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
bool get isEVMWallet => isEVMCompatibleChain(walletType);
|
||||
|
||||
@action
|
||||
CryptoCurrency _outputCryptoCurrencyHandler([CryptoCurrency? override]) {
|
||||
if (override != null && override != selectedCryptoCurrency) selectedCryptoCurrency = override;
|
||||
|
||||
return selectedCryptoCurrency;
|
||||
}
|
||||
|
||||
@action
|
||||
void setShowAddressBookPopup(bool value) => _settingsStore.showAddressBookPopupEnabled = value;
|
||||
|
||||
@action
|
||||
void addOutput() =>
|
||||
outputs.add(Output(wallet, _appStore, _fiatConversationStore, () => selectedCryptoCurrency));
|
||||
outputs.add(Output(wallet, _appStore, _fiatConversationStore, _outputCryptoCurrencyHandler));
|
||||
|
||||
@action
|
||||
void removeOutput(Output output) {
|
||||
@@ -310,10 +318,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// Handle case where balance might not be available yet (e.g., during chain switch)
|
||||
final balanceForCurrency = wallet.balance[selectedCryptoCurrency];
|
||||
if (balanceForCurrency == null) {
|
||||
return _appStore.amountParsingProxy.getDisplayCryptoString(0, selectedCryptoCurrency);
|
||||
return _appStore.amountParsingProxy.asDisplayString(Money.zero(selectedCryptoCurrency));
|
||||
}
|
||||
return _appStore.amountParsingProxy.getDisplayCryptoStringFromBigInt(
|
||||
wallet.balance[selectedCryptoCurrency]!.fullAvailableBalance, selectedCryptoCurrency);
|
||||
return _appStore.amountParsingProxy
|
||||
.asDisplayString(wallet.balance[selectedCryptoCurrency]!.available);
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -340,7 +348,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// only for electrum, monero, wownero, decred wallets atm:
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
if (coinTypeToSpendFrom == UnspentCoinType.lightning) return balance;
|
||||
if (selectedCryptoCurrency == CryptoCurrency.btcln) return balance;
|
||||
return _appStore.amountParsingProxy.getDisplayCryptoString(
|
||||
await unspentCoinsListViewModel.getSendingBalance(coinTypeToSpendFrom),
|
||||
walletTypeToCryptoCurrency(walletType));
|
||||
@@ -420,6 +428,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
String get selectedCryptoCurrencySymbol =>
|
||||
amountParsingProxy.getCryptoSymbol(selectedCryptoCurrency);
|
||||
|
||||
@computed
|
||||
bool get useBaseUnits => amountParsingProxy.useSatoshi(selectedCryptoCurrency);
|
||||
|
||||
List<CryptoCurrency> currencies;
|
||||
|
||||
WalletType get walletType => wallet.type;
|
||||
@@ -681,7 +692,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
BigInt.zero,
|
||||
Money.zero(wallet.currency),
|
||||
priority,
|
||||
useBlinkProtection: canSupportBlinkProtection(selectedChainId)
|
||||
? _settingsStore.useBlinkProtection
|
||||
@@ -804,7 +815,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
wallet,
|
||||
routerTo,
|
||||
routerData,
|
||||
routerValueWei,
|
||||
Money(routerValueWei, wallet.currency),
|
||||
priority,
|
||||
sourceTokenAddress: tokenContract,
|
||||
sourceTokenAmount: requiredAmount,
|
||||
@@ -842,7 +853,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// Fallback to estimate if not available
|
||||
final fee = actualFee > 0 ? actualFee : 0.0005;
|
||||
|
||||
final amount = double.tryParse(trade.amount) ?? 0.0;
|
||||
final fromCurrency = trade.from ?? CryptoCurrency.sol;
|
||||
final amount = Money.tryParse(trade.amount, fromCurrency) ?? Money.zero(fromCurrency);
|
||||
|
||||
pendingTransaction = await solana!.signAndPrepareJupiterSwapTransaction(
|
||||
wallet,
|
||||
@@ -850,7 +862,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
requestId!,
|
||||
trade.payoutAddress ?? '',
|
||||
amount,
|
||||
fee,
|
||||
Money.tryParse(fee.toString(), CryptoCurrency.sol) ?? Money.zero(CryptoCurrency.sol),
|
||||
);
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
@@ -1104,41 +1116,41 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
// instead, they rely on the tx to eventually get fetched at sync time, which can take a while
|
||||
if(isEVMWallet) {
|
||||
wallet.transactionHistory.addOne(evm!.getTransactionInfo(
|
||||
id: pendingTransaction!.evmTxHashFromRawHex!,
|
||||
height: 0,
|
||||
// FIXME(malik) this is even more critical given all the issues we had with decimal point parsing. with money it'll be easy
|
||||
ethAmount: selectedCryptoCurrency.parseAmount(outputs.first.cryptoAmount),
|
||||
ethFee: CryptoCurrency.eth.parseAmount(pendingTransaction!.feeFormattedValue),
|
||||
tokenSymbol: selectedCryptoCurrency.title,
|
||||
direction: TransactionDirection.outgoing,
|
||||
isPending: true,
|
||||
date: DateTime.now(),
|
||||
confirmations: 0,
|
||||
chainId: wallet.chainId ?? 0));
|
||||
id: pendingTransaction!.evmTxHashFromRawHex!,
|
||||
height: 0,
|
||||
amount: outputs.first.cryptoAmountMoney,
|
||||
fee: pendingTransaction!.fee,
|
||||
tokenSymbol: selectedCryptoCurrency.title,
|
||||
direction: TransactionDirection.outgoing,
|
||||
isPending: true,
|
||||
date: DateTime.now(),
|
||||
confirmations: 0,
|
||||
chainId: wallet.chainId ?? 0,
|
||||
));
|
||||
}
|
||||
|
||||
if(walletType == WalletType.solana) {
|
||||
wallet.transactionHistory.addOne(solana!.getTransactionInfo(
|
||||
id: pendingTransaction!.id,
|
||||
blockTime: DateTime.now(),
|
||||
tokenSymbol: selectedCryptoCurrency.title,
|
||||
to: "",
|
||||
from: "",
|
||||
direction: TransactionDirection.outgoing,
|
||||
solAmount: solana!.getPendingTransactionAmount(pendingTransaction!),
|
||||
isPending: true,
|
||||
txFee: solana!.getPendingTransactionFee(pendingTransaction!)));
|
||||
id: pendingTransaction!.id,
|
||||
blockTime: DateTime.now(),
|
||||
to: "",
|
||||
from: "",
|
||||
direction: TransactionDirection.outgoing,
|
||||
amount: pendingTransaction!.amount,
|
||||
isPending: true,
|
||||
fee: pendingTransaction!.fee,
|
||||
));
|
||||
}
|
||||
|
||||
if (walletType == WalletType.tron) {
|
||||
wallet.transactionHistory.addOne(tron!.getTransactionInfo(
|
||||
id: pendingTransaction!.id,
|
||||
blockTime: DateTime.now(),
|
||||
tokenSymbol: selectedCryptoCurrency.title,
|
||||
direction: TransactionDirection.outgoing,
|
||||
tronAmount: selectedCryptoCurrency.parseAmount(outputs.first.cryptoAmount),
|
||||
isPending: true,
|
||||
txFee: CryptoCurrency.trx.parseAmount(outputs.first.estimatedFee).toInt()));
|
||||
id: pendingTransaction!.id,
|
||||
blockTime: DateTime.now(),
|
||||
direction: TransactionDirection.outgoing,
|
||||
amount: outputs.first.cryptoAmountMoney,
|
||||
isPending: true,
|
||||
fee: outputs.first.estimatedFee,
|
||||
));
|
||||
}
|
||||
|
||||
if (pendingTransaction!.id.isNotEmpty) {
|
||||
@@ -1419,9 +1431,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
|
||||
return await evm!.createTokenApproval(
|
||||
wallet,
|
||||
requiredAmount,
|
||||
Money(requiredAmount, erc20Token),
|
||||
spender,
|
||||
erc20Token,
|
||||
priority,
|
||||
useBlinkProtection:
|
||||
canSupportBlinkProtection(selectedChainId) ? _settingsStore.useBlinkProtection : false,
|
||||
|
||||
@@ -23,8 +23,8 @@ abstract class TemplateViewModelBase with Store {
|
||||
_appStore = appStore,
|
||||
_fiatConversationStore = fiatConversationStore,
|
||||
_currency = wallet.currency,
|
||||
output = Output(wallet, appStore, fiatConversationStore, () => wallet.currency) {
|
||||
output = Output(_wallet, _appStore, _fiatConversationStore, () => _currency);
|
||||
output = Output(wallet, appStore, fiatConversationStore, ([_]) => wallet.currency) {
|
||||
output = Output(_wallet, _appStore, _fiatConversationStore, _outputCryptoCurrencyHandler);
|
||||
}
|
||||
|
||||
@observable
|
||||
@@ -53,15 +53,20 @@ abstract class TemplateViewModelBase with Store {
|
||||
output.reset();
|
||||
}
|
||||
|
||||
Template toTemplate({required String cryptoCurrency, required String fiatCurrency}) {
|
||||
return Template(
|
||||
Template toTemplate({required String cryptoCurrency, required String fiatCurrency}) => Template(
|
||||
isCurrencySelectedRaw: isCryptoSelected,
|
||||
nameRaw: name,
|
||||
addressRaw: address,
|
||||
cryptoCurrencyRaw: cryptoCurrency,
|
||||
fiatCurrencyRaw: fiatCurrency,
|
||||
amountRaw: output.cryptoAmount,
|
||||
amountFiatRaw: output.fiatAmount);
|
||||
amountFiatRaw: output.fiatAmount,
|
||||
);
|
||||
|
||||
CryptoCurrency _outputCryptoCurrencyHandler([CryptoCurrency? override]) {
|
||||
if (override != null && _currency != override) changeSelectedCurrency(override);
|
||||
|
||||
return _currency;
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -56,11 +56,11 @@ abstract class SupportViewModelBase with Store {
|
||||
icon: 'assets/images/change_now.png',
|
||||
linkTitle: 'support@changenow.io',
|
||||
link: 'mailto:support@changenow.io'),
|
||||
LinkListItem(
|
||||
title: 'SideShift',
|
||||
icon: 'assets/images/sideshift.png',
|
||||
linkTitle: 'help.sideshift.ai',
|
||||
link: 'https://help.sideshift.ai/en/'),
|
||||
// LinkListItem(
|
||||
// title: 'SideShift',
|
||||
// icon: 'assets/images/sideshift.png',
|
||||
// linkTitle: 'help.sideshift.ai',
|
||||
// link: 'https://help.sideshift.ai/en/'),
|
||||
LinkListItem(
|
||||
title: 'SimpleSwap',
|
||||
icon: 'assets/images/simpleSwap.png',
|
||||
|
||||
@@ -79,22 +79,22 @@ class TxDetailRowDefinition {
|
||||
title: S.current.transaction_details_date,
|
||||
valueGetter: (vm) => DateFormatter.withCurrentLocal().format(vm.transactionInfo.date)),
|
||||
|
||||
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_height_key",
|
||||
title: S.current.transaction_details_height,
|
||||
valueGetter: (vm) => vm.transactionInfo.height?.toString() ?? "",
|
||||
applicable: (vm) => !([WalletType.solana, WalletType.tron].contains(vm.wallet.type) &&
|
||||
!isLightning(vm.transactionInfo))),
|
||||
|
||||
keyString: "standard_list_item_transaction_details_height_key",
|
||||
title: S.current.transaction_details_height,
|
||||
valueGetter: (vm) => vm.transactionInfo.height?.toString() ?? "",
|
||||
applicable: (vm) =>
|
||||
![WalletType.solana, WalletType.tron].contains(vm.wallet.type) ||
|
||||
!isLightning(vm.transactionInfo),
|
||||
),
|
||||
|
||||
TxDetailRowDefinition(
|
||||
keyString: "standard_list_item_transaction_details_fee_key",
|
||||
title: S.current.transaction_details_fee,
|
||||
valueGetter: (vm) => vm.transactionInfo.feeFormatted()!,
|
||||
valueGetter: (vm) => vm.transactionInfo.fee!.toStringWithSymbol(),
|
||||
applicable: (vm) =>
|
||||
vm.wallet.type != WalletType.nano &&
|
||||
(vm.transactionInfo.feeFormatted() ?? "").isNotEmpty),
|
||||
(vm.transactionInfo.fee?.toStringWithSymbol() ?? "").isNotEmpty),
|
||||
|
||||
|
||||
TxDetailRowDefinition(
|
||||
@@ -496,14 +496,15 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
RBFListItems.add(
|
||||
StandartListItem(
|
||||
title: S.current.old_fee,
|
||||
value: tx.feeFormatted() ?? '0.0',
|
||||
value: tx.fee?.toStringWithSymbol() ?? '0.0',
|
||||
key: ValueKey('standard_list_item_rbf_old_fee_key'),
|
||||
),
|
||||
);
|
||||
|
||||
if (transactionInfo.fee != null && rawTransaction.isNotEmpty) {
|
||||
final size = bitcoin!.getTransactionVSize(wallet, rawTransaction);
|
||||
final recommendedRate = (transactionInfo.fee! / size).round() + 1;
|
||||
final recommendedRate = (transactionInfo.fee! / BigInt.from(size)) +
|
||||
transactionInfo.fee!.copyWith(amount: BigInt.one);
|
||||
|
||||
RBFListItems.add(
|
||||
StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
|
||||
@@ -609,14 +610,14 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
final crypto = isLightning(transactionInfo) ? CryptoCurrency.btcln : CryptoCurrency.btc;
|
||||
final amount = _appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(transactionInfo.amount, crypto)
|
||||
.asDisplayString(transactionInfo.amount)
|
||||
.withMaxDecimals(8)
|
||||
.withLocalSeperator(_appStore.settingsStore.languageCode);
|
||||
|
||||
return '$amount ${_appStore.amountParsingProxy.getCryptoSymbol(crypto)}';
|
||||
}
|
||||
|
||||
return transactionInfo.amountFormatted();
|
||||
return transactionInfo.amount.toStringWithSymbol();
|
||||
}
|
||||
|
||||
void replaceByFee(String newFee) => sendViewModel.replaceByFee(transactionInfo, newFee);
|
||||
|
||||
@@ -614,11 +614,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
void setAddress(WalletAddressListItem address) =>
|
||||
wallet.walletAddresses.address = address.address;
|
||||
|
||||
@observable
|
||||
bool isRotatingAddress = false;
|
||||
|
||||
@action
|
||||
Future<void> rotateAddress() async {
|
||||
await createNewAddress(wallet, "");
|
||||
if (isElectrumWallet) {
|
||||
if(isRotatingAddress) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isRotatingAddress = true;
|
||||
await createNewAddress(wallet, "");
|
||||
wallet.walletAddresses.address = addressList.whereType<WalletAddressListItem>().last.address;
|
||||
} finally {
|
||||
isRotatingAddress = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -267,14 +267,14 @@ class CWWownero extends Wownero {
|
||||
WowneroTransactionCreationCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
))
|
||||
.toList(),
|
||||
priority: priority as MoneroTransactionPriority);
|
||||
|
||||
|
||||
+19
-17
@@ -70,23 +70,25 @@ class CWZano extends Zano {
|
||||
}
|
||||
|
||||
@override
|
||||
Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency}) {
|
||||
return ZanoTransactionCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as MoneroTransactionPriority,
|
||||
currency: currency,
|
||||
);
|
||||
}
|
||||
Object createZanoTransactionCredentials(
|
||||
{required List<Output> outputs,
|
||||
required TransactionPriority priority,
|
||||
required CryptoCurrency currency}) =>
|
||||
ZanoTransactionCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
))
|
||||
.toList(),
|
||||
priority: priority as MoneroTransactionPriority,
|
||||
currency: currency,
|
||||
);
|
||||
|
||||
@override
|
||||
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee}) {
|
||||
|
||||
+13
-15
@@ -43,23 +43,21 @@ class CWZcash extends Zcash {
|
||||
|
||||
@override
|
||||
Object createZcashTransactionCredentials(List<Output> outputs,
|
||||
{required CryptoCurrency currency,
|
||||
int? feeRate,
|
||||
TransactionPriority? priority}) {
|
||||
final txPriority = (priority ?? getZcashTransactionPriorityAutomatic())
|
||||
as MoneroTransactionPriority;
|
||||
{required CryptoCurrency currency, int? feeRate, TransactionPriority? priority}) {
|
||||
final txPriority =
|
||||
(priority ?? getZcashTransactionPriorityAutomatic()) as MoneroTransactionPriority;
|
||||
return ZcashTransactionCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
memo: out.memo,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmountMoney,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
memo: out.memo,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
))
|
||||
.toList(),
|
||||
priority: txPriority,
|
||||
currency: currency,
|
||||
@@ -90,7 +88,7 @@ class CWZcash extends Zcash {
|
||||
|
||||
@override
|
||||
int formatterZcashParseAmount(String amount) {
|
||||
return CryptoCurrency.zec.parseAmount(amount).toInt();
|
||||
return CryptoCurrency.zec.parseAmount(amount).amount.toInt();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user