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:
Robert Malikowski
2026-07-01 16:53:59 +02:00
263 changed files with 6371 additions and 4762 deletions
+16 -16
View File
@@ -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;
}
+3 -8
View File
@@ -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 ?? '',
+32 -46
View File
@@ -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 &&
+1 -1
View File
@@ -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}");
+53 -4
View File
@@ -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) {
+4 -19
View File
@@ -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([
+9 -20
View File
@@ -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;
+3 -1
View File
@@ -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';
+7 -7
View File
@@ -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);
+52
View File
@@ -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;
}
+15 -1
View File
@@ -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);
}
+4 -4
View File
@@ -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;
}
+44
View File
@@ -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
View File
@@ -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;
+62
View File
@@ -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;
}
+6 -6
View File
@@ -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
View File
@@ -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();
+10 -10
View File
@@ -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 ?? '';
+62
View File
@@ -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';
+19
View File
@@ -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";
}
+19
View File
@@ -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";
}
+9 -9
View File
@@ -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);
}
+1 -7
View File
@@ -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;
+30 -20
View File
@@ -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(
+52 -45
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+77
View File
@@ -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
View File
@@ -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];
+2 -2
View File
@@ -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,
+3 -2
View File
@@ -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
View File
@@ -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;
}
+19
View File
@@ -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;
}
}
+1
View File
@@ -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 {
+8 -8
View File
@@ -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,
+22 -22
View File
@@ -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
+114 -90
View File
@@ -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:
+136 -93
View File
@@ -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();
+42 -91
View File
@@ -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;
}
+50 -39
View File
@@ -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,
+10 -5
View File
@@ -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
+5 -5
View File
@@ -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;
}
}
+8 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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