Merge branch 'cake-tech:dev' into dev

This commit is contained in:
BSN ∞/21M
2026-07-21 07:40:26 +02:00
committed by GitHub
22 changed files with 1312 additions and 1143 deletions
+1 -1
View File
@@ -1,6 +1,5 @@
-
uri: api.trongrid.io
isDefault: true
useSSL: true
isEnabledForAutoSwitching: true
label: "TronGrid"
@@ -10,6 +9,7 @@
label: "PublicNode"
-
uri: trx.nownodes.io
isDefault: true
useSSL: true
isEnabledForAutoSwitching: true
label: "NOWNodes"
+1 -1
View File
@@ -124,7 +124,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
Future<PaymentURI> getPaymentRequestUri(String amount) async {
if (addressPageType is LightningAddressType && lightningWallet != null) {
final amountSats = amount.isNotEmpty ? parseFixed(amount, 8) : null;
final amountSats = amount.isNotEmpty ? tryParseFixed(amount, 8) : null;
final lnUrl = getLnurlOfLightningAddress(address);
if (amountSats == null) {
return LightningPaymentRequest(address: address, lnURL: lnUrl, amount: amount);
+3 -2
View File
@@ -1536,7 +1536,7 @@ abstract class ElectrumWalletBase
network: network,
memo: estimatedTx.memo,
inputOrdering: BitcoinOrdering.shuffle,
outputOrdering: BitcoinOrdering.none,
outputOrdering: BitcoinOrdering.shuffle,
enableRBF: !estimatedTx.spendsUnconfirmedTX,
);
}
@@ -1594,6 +1594,7 @@ abstract class ElectrumWalletBase
isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: hasTaprootInputs,
utxos: estimatedTx.utxos,
derivedOutputs: updatedOutputs,
publicKeys: estimatedTx.publicKeys,
isViewOnly: keys.privateKey.isEmpty,
)..addListener((transaction) async {
@@ -2297,7 +2298,7 @@ abstract class ElectrumWalletBase
network: network,
memo: memo,
inputOrdering: BitcoinOrdering.shuffle,
outputOrdering: BitcoinOrdering.none,
outputOrdering: BitcoinOrdering.shuffle,
enableRBF: true,
);
@@ -31,6 +31,7 @@ class PendingBitcoinTransaction with PendingTransaction {
this.hasTaprootInputs = false,
this.isMweb = false,
this.utxos = const [],
this.derivedOutputs = const [],
this.publicKeys,
this.commitOverride,
this.unsignedPsbt,
@@ -47,6 +48,9 @@ class PendingBitcoinTransaction with PendingTransaction {
final bool hasTaprootInputs;
final bool isViewOnly;
List<UtxoWithAddress> utxos;
final List<BitcoinOutput> derivedOutputs;
bool isMweb;
String? changeAddressOverride;
String? idOverride;
@@ -79,6 +83,13 @@ class PendingBitcoinTransaction with PendingTransaction {
bool get hasSilentPayment => _tx.hasSilentPayment;
List<String?> get stealthAddresses => derivedOutputs
.where((out) => !out.isChange)
.map((out) => out.isSilentPayment
? out.address.toAddress(network ?? BitcoinNetwork.mainnet)
: null)
.toList();
PendingChange? get change {
try {
final change = _tx.outputs.firstWhere((out) => out.isChange);
+119 -181
View File
@@ -1,4 +1,4 @@
import 'package:cw_core/format_fixed.dart';
import "package:cw_core/format_fixed.dart";
abstract class PaymentURI {
PaymentURI({required this.amount, required this.address});
@@ -19,7 +19,7 @@ class MoneroURI extends PaymentURI {
@override
String toString() {
var base = 'monero:$address';
var base = "monero:$address";
if (amount.isNotEmpty) {
base += '?tx_amount=${amount.replaceAll(',', '.')}';
@@ -34,7 +34,7 @@ class HavenURI extends PaymentURI {
@override
String toString() {
var base = 'haven:$address';
var base = "haven:$address";
if (amount.isNotEmpty) {
base += '?tx_amount=${amount.replaceAll(',', '.')}';
@@ -45,7 +45,7 @@ class HavenURI extends PaymentURI {
}
class BitcoinURI extends PaymentURI {
BitcoinURI({required super.amount, required super.address, this.pjUri = ''});
BitcoinURI({required super.amount, required super.address, this.pjUri = ""});
final String pjUri;
@@ -53,13 +53,16 @@ class BitcoinURI extends PaymentURI {
String toString() {
final qp = <String, String>{};
if (amount.isNotEmpty) qp['amount'] = amount.replaceAll(',', '.');
if (pjUri.isNotEmpty && !address.startsWith("sp")) {
qp['pjos'] = '0';
qp['pj'] = pjUri;
if (amount.isNotEmpty) {
qp["amount"] = amount.replaceAll(",", ".");
}
return Uri(scheme: 'bitcoin', path: address, queryParameters: qp).toString();
if (pjUri.isNotEmpty && !address.startsWith("sp")) {
qp["pjos"] = "0";
qp["pj"] = pjUri;
}
return Uri(scheme: "bitcoin", path: address, queryParameters: qp).toString();
}
}
@@ -68,7 +71,7 @@ class LitecoinURI extends PaymentURI {
@override
String toString() {
var base = 'litecoin:$address';
var base = "litecoin:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -83,52 +86,7 @@ class EthereumURI extends PaymentURI {
@override
String toString() {
var base = 'ethereum:$address';
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
class BaseURI extends PaymentURI {
BaseURI({required super.amount, required super.address});
@override
String toString() {
var base = 'base:$address';
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
class ArbitrumURI extends PaymentURI {
ArbitrumURI({required super.amount, required super.address});
@override
String toString() {
var base = 'arbitrum:$address';
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
class BSCURI extends PaymentURI {
BSCURI({required super.amount, required super.address});
@override
String toString() {
var base = 'bsc:$address';
var base = "ethereum:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -158,22 +116,7 @@ class NanoURI extends PaymentURI {
@override
String toString() {
var base = 'nano:$address';
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
return base;
}
}
class PolygonURI extends PaymentURI {
PolygonURI({required super.amount, required super.address});
@override
String toString() {
var base = 'polygon:$address';
var base = "nano:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
}
@@ -189,14 +132,14 @@ class SolanaURI extends PaymentURI {
@override
String toString() {
var base = 'solana:$address';
var base = "solana:$address";
final params = <String>[];
if (amount.isNotEmpty) {
params.add('amount=${amount.replaceAll(',', '.')}');
}
if (contractAddress != null && contractAddress!.isNotEmpty) {
params.add('spl-token=$contractAddress');
params.add("spl-token=$contractAddress");
}
if (params.isNotEmpty) {
base += '?${params.join('&')}';
@@ -213,14 +156,14 @@ class TronURI extends PaymentURI {
@override
String toString() {
var base = 'tron:$address';
var base = "tron:$address";
final params = <String>[];
if (amount.isNotEmpty) {
params.add('amount=${amount.replaceAll(',', '.')}');
}
if (contractAddress != null && contractAddress!.isNotEmpty) {
params.add('token=$contractAddress');
params.add("token=$contractAddress");
}
if (params.isNotEmpty) {
base += '?${params.join('&')}';
@@ -235,7 +178,7 @@ class WowneroURI extends PaymentURI {
@override
String toString() {
var base = 'wownero:$address';
var base = "wownero:$address";
if (amount.isNotEmpty) {
base += '?tx_amount=${amount.replaceAll(',', '.')}';
@@ -246,12 +189,11 @@ class WowneroURI extends PaymentURI {
}
class ZanoURI extends PaymentURI {
ZanoURI({required String amount, required String address})
: super(amount: amount, address: address);
ZanoURI({required super.amount, required super.address});
@override
String toString() {
var base = 'zano:' + address;
var base = "zano:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -262,12 +204,11 @@ class ZanoURI extends PaymentURI {
}
class DecredURI extends PaymentURI {
DecredURI({required String amount, required String address})
: super(amount: amount, address: address);
DecredURI({required super.amount, required super.address});
@override
String toString() {
var base = 'decred:' + address;
var base = "decred:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -278,12 +219,11 @@ class DecredURI extends PaymentURI {
}
class DogeURI extends PaymentURI {
DogeURI({required String amount, required String address})
: super(amount: amount, address: address);
DogeURI({required super.amount, required super.address});
@override
String toString() {
var base = 'doge:' + address;
var base = "doge:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -298,7 +238,7 @@ class ZcashURI extends PaymentURI {
@override
String toString() {
var base = 'zcash:$address';
var base = "zcash:$address";
if (amount.isNotEmpty) {
base += '?amount=${amount.replaceAll(',', '.')}';
@@ -309,9 +249,6 @@ class ZcashURI extends PaymentURI {
}
class ERC681URI extends PaymentURI {
final int chainId;
final String? contractAddress;
ERC681URI({
required this.chainId,
required super.address,
@@ -319,74 +256,11 @@ class ERC681URI extends PaymentURI {
required this.contractAddress,
});
@override
String toString() {
var uri = 'ethereum:';
final targetAddress = contractAddress ?? address;
uri += targetAddress;
if (chainId != 1) {
uri += '@$chainId';
}
if (contractAddress != null) {
uri += '/transfer';
}
final params = <String, String>{};
if (contractAddress != null) {
params['address'] = address;
if (amount.isNotEmpty) {
params['uint256'] = _formatAmountForERC20(amount);
}
} else {
if (amount.isNotEmpty) {
params['value'] = _formatAmountForNative(amount);
}
}
if (params.isNotEmpty) {
uri += '?';
uri += params.entries.map((e) => '${e.key}=${e.value}').join('&');
}
return uri;
}
/// Formats amount for ERC-20 token transfers (in atomic units)
String _formatAmountForERC20(String amount) {
try {
// Convert decimal amount to BigInt (assuming 18 decimals)
final amountDouble = double.parse(amount.replaceAll(',', '.'));
final amountBigInt = BigInt.from(amountDouble * 1e18);
return amountBigInt.toString();
} catch (e) {
// Fallback to original amount if parsing fails
return amount.replaceAll(',', '.');
}
}
/// Formats amount for native ETH payments (in wei using scientific notation)
String _formatAmountForNative(String amount) {
try {
// Convert decimal amount to double for scientific notation
final amountDouble = double.parse(amount.replaceAll(',', '.'));
// Use scientific notation as recommended by ERC-681
return '${amountDouble}e18';
} catch (e) {
// Fallback to original amount if parsing fails
return amount.replaceAll(',', '.');
}
}
factory ERC681URI.fromUri(Uri uri) {
final (isContract, targetAddress) = _getTargetAddress(uri.path);
final chainId = _getChainID(uri.path);
final address = isContract ? uri.queryParameters["address"] ?? '' : targetAddress;
final address = isContract ? uri.queryParameters["address"] ?? "" : targetAddress;
final amountParam = isContract ? uri.queryParameters["uint256"] : uri.queryParameters["value"];
var formatedAmount = "";
@@ -406,16 +280,78 @@ class ERC681URI extends PaymentURI {
);
}
static int _getChainID(String path) {
return int.parse(RegExp(
r'@\d*',
).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
"1");
final int chainId;
final String? contractAddress;
@override
String toString() {
var uri = "ethereum:";
final targetAddress = contractAddress ?? address;
uri += targetAddress;
if (chainId != 1) {
uri += "@$chainId";
}
if (contractAddress != null) {
uri += "/transfer";
}
final params = <String, String>{};
if (contractAddress != null) {
params["address"] = address;
if (amount.isNotEmpty) {
params["uint256"] = _formatAmountForERC20(amount);
}
} else {
if (amount.isNotEmpty) {
params["value"] = _formatAmountForNative(amount);
}
}
if (params.isNotEmpty) {
uri += "?${params.entries.map((e) => "${e.key}=${e.value}").join("&")}";
}
return uri;
}
/// Formats amount for ERC-20 token transfers (in atomic units)
String _formatAmountForERC20(String amount) {
try {
// Convert decimal amount to BigInt (assuming 18 decimals)
final amountDouble = double.parse(amount.replaceAll(",", "."));
final amountBigInt = BigInt.from(amountDouble * 1e18);
return amountBigInt.toString();
} catch (e) {
// Fallback to original amount if parsing fails
return amount.replaceAll(",", ".");
}
}
/// Formats amount for native ETH payments (in wei using scientific notation)
String _formatAmountForNative(String amount) {
try {
// Convert decimal amount to double for scientific notation
final amountDouble = double.parse(amount.replaceAll(",", "."));
// Use scientific notation as recommended by ERC-681
return "${amountDouble}e18";
} catch (e) {
// Fallback to original amount if parsing fails
return amount.replaceAll(",", ".");
}
}
static int _getChainID(String path) => int.parse(
RegExp(r"@\d*").firstMatch(path)?.group(0)?.replaceAll("@", "") ?? "1",
);
static (bool, String) _getTargetAddress(String path) {
final targetAddress =
RegExp(r'^(0x)?[0-9a-f]{40}', caseSensitive: false).firstMatch(path)!.group(0)!;
RegExp(r"^(0x)?[0-9a-f]{40}", caseSensitive: false).firstMatch(path)!.group(0)!;
return (path.contains("/"), targetAddress);
}
@@ -426,25 +362,27 @@ class ERC681URI extends PaymentURI {
/// - Scientific notation: "0.123e18", "1e6" → expanded to integer
/// - Decimal ETH: "0.123456" → shifted by 18 decimals
static String _normalizeToIntegerWei(String input) {
final raw = input.replaceAll(',', '.').trim();
final raw = input.replaceAll(",", ".").trim();
// First we check if it's already a plain integer (basically just a number with no dot, no exponent)
try {
final isPlainInteger = RegExp(r'^[+-]?\d+$').hasMatch(raw) &&
!raw.contains('.') &&
!raw.toLowerCase().contains('e');
if (isPlainInteger) return raw.replaceFirst(RegExp(r'^\+'), '');
final isPlainInteger = RegExp(r"^[+-]?\d+$").hasMatch(raw) &&
!raw.contains(".") &&
!raw.toLowerCase().contains("e");
if (isPlainInteger) {
return raw.replaceFirst(RegExp(r"^\+"), "");
}
// Then we check if it's a scientific notation
final sci = RegExp(r'^[+-]?(\d+\.?\d*|\d*\.?\d+)[eE][+-]?\d+$');
final sci = RegExp(r"^[+-]?(\d+\.?\d*|\d*\.?\d+)[eE][+-]?\d+$");
if (sci.hasMatch(raw)) {
final mantissaStr = raw.toLowerCase().split('e')[0];
final exp = int.parse(raw.toLowerCase().split('e')[1]);
final mantissaStr = raw.toLowerCase().split("e")[0];
final exp = int.parse(raw.toLowerCase().split("e")[1]);
return _expandDecimal(mantissaStr, exp);
}
// Lastly, we check if it's a fixed decimal ETH amount, here we shift by 18 to get wei for the amount
if (raw.contains('.')) {
if (raw.contains(".")) {
return _expandDecimal(raw, 18);
}
return raw;
@@ -462,34 +400,34 @@ class ERC681URI extends PaymentURI {
/// _expandDecimal('1.2', 3) -> '1200'
static String _expandDecimal(String decimalStr, int expShift) {
var s = decimalStr.trim();
var sign = '';
if (s.startsWith('-') || s.startsWith('+')) {
sign = s[0] == '-' ? '-' : '';
var sign = "";
if (s.startsWith("-") || s.startsWith("+")) {
sign = s[0] == "-" ? "-" : "";
s = s.substring(1);
}
// First we split the integer and fractional parts
final parts = s.split('.');
final intPart = parts[0].isEmpty ? '0' : parts[0];
final fracPart = parts.length > 1 ? parts[1] : '';
final digits = (intPart + fracPart).replaceFirst(RegExp(r'^0+'), '');
final parts = s.split(".");
final intPart = parts[0].isEmpty ? "0" : parts[0];
final fracPart = parts.length > 1 ? parts[1] : "";
final digits = (intPart + fracPart).replaceFirst(RegExp(r"^0+"), "");
final fracLen = fracPart.length;
// Then we calculate the effective shift = desired shift minus existing fractional digits
final shift = expShift - fracLen;
if (shift >= 0) {
final head = digits.isEmpty ? '0' : digits;
final zeros = List.filled(shift, '0').join();
final head = digits.isEmpty ? "0" : digits;
final zeros = List.filled(shift, "0").join();
final res = head + zeros;
return sign + (res.isEmpty ? '0' : res);
return sign + (res.isEmpty ? "0" : res);
} else {
// Need to insert a decimal point within digits; return integer by truncating
final cut = digits.length + shift;
if (cut <= 0) {
return '0';
return "0";
}
final res = digits.substring(0, cut);
return sign + (res.isEmpty ? '0' : res);
return sign + (res.isEmpty ? "0" : res);
}
}
}
+13 -26
View File
@@ -1,18 +1,15 @@
import 'dart:developer';
import "dart:developer";
import 'package:cw_core/payment_uris.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:mobx/mobx.dart';
import "package:cw_core/payment_uris.dart";
import "package:cw_core/wallet_addresses.dart";
import "package:mobx/mobx.dart";
part 'evm_chain_wallet_addresses.g.dart';
part "evm_chain_wallet_addresses.g.dart";
class EVMChainWalletAddresses = EVMChainWalletAddressesBase with _$EVMChainWalletAddresses;
abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
EVMChainWalletAddressesBase(WalletInfo walletInfo, this._selectedChainId)
: address = '',
super(walletInfo);
EVMChainWalletAddressesBase(super.walletInfo, this._selectedChainId) : address = "";
@override
@observable
@@ -33,7 +30,7 @@ abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
Future<void> updateAddressesInBox() async {
try {
addressesMap.clear();
addressesMap[address] = '';
addressesMap[address] = "";
await saveAddressesInBox();
} catch (e) {
log(e.toString());
@@ -41,20 +38,10 @@ abstract class EVMChainWalletAddressesBase extends WalletAddresses with Store {
}
@override
PaymentURI getPaymentUri(String amount) {
switch (_selectedChainId) {
case 1:
return EthereumURI(amount: amount, address: address);
case 137:
return PolygonURI(amount: amount, address: address);
case 8453:
return BaseURI(amount: amount, address: address);
case 42161:
return ArbitrumURI(amount: amount, address: address);
case 56:
return BSCURI(amount: amount, address: address);
default:
return EthereumURI(amount: amount, address: address);
}
}
PaymentURI getPaymentUri(String amount) => ERC681URI(
address: address,
amount: amount,
chainId: _selectedChainId,
contractAddress: null,
);
}
+2 -2
View File
@@ -90,7 +90,7 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
addressesMap.clear();
addressInfos.clear();
accountList.accounts.forEach((account) async {
for (final account in accountList.accounts) {
await _subaddressList.update(accountIndex: account.id);
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
@@ -102,7 +102,7 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
address: subaddress.address,
label: subaddress.label));
});
});
}
await saveAddressesInBox();
} catch (e) {
+95 -6
View File
@@ -1,12 +1,101 @@
# Security Policy
## Reporting a Vulnerability
Cake Wallet is developed by **Cake Labs LLC**. We take the security and privacy of
our users seriously and welcome reports from security researchers.
If you need to report a vulnerability, please either:
> **Cake Wallet is not affiliated with CAKE.com, Clockify, or security.cake.com.**
> Those services belong to unrelated companies. The only official vulnerability
> disclosure channels for Cake Wallet are the ones listed on this page and at
> https://cakewallet.com/security.
* Open a security advisory: https://github.com/cake-tech/cake_wallet/security/advisories/new
* Send an email to `dev@cakewallet.com` with details on the vulnerability
## Reporting a vulnerability
## Supported Versions
**Please do not open a public issue, pull request, or social-media post for a
security vulnerability.** Public disclosure before a fix is available puts users'
funds and privacy at risk. Use one of the private channels below and we will
coordinate a fix and disclosure with you.
As we don't maintain previous versions of the app, only the latest release for each platform is supported and any updates will bump the version number.
1. **GitHub private security advisory (preferred).**
[Report a vulnerability](https://github.com/cake-tech/cake_wallet/security/advisories/new).
This gives you a private, structured thread with the maintainers and is the
fastest way to reach us.
2. **Encrypted email.** Send details to **security@cakewallet.com**. For sensitive
reports, please encrypt with our PGP key:
- Key: https://cakewallet.com/.well-known/cakewallet-security.asc
- Fingerprint: `DC91 6520 0271 AC6A 0533 3D3C BFE7 D9A5 0E4D 3A0A`
Both channels are monitored and automatically raise an alert in our internal
security channel, so reports will not be missed.
### What to include
- A clear description of the issue and its security impact.
- Step-by-step reproduction, ideally with a proof of concept.
- Affected platforms (iOS, Android, macOS, Linux, Windows) and app version.
- Affected wallet types / chains, if applicable.
- Any relevant logs, addresses, or transaction IDs (for on-chain issues).
If you used AI tooling to find or write up the report, please say so.
## Our commitment (safe harbor)
We consider security research conducted in good faith under this policy to be
authorized. We will not pursue or support legal action against researchers who:
- make a good-faith effort to avoid privacy violations, data destruction, and
interruption or degradation of our services;
- only interact with accounts they own or have explicit permission to access; and
- give us a reasonable opportunity to fix an issue before disclosing it publicly.
If in doubt about whether an action is authorized, ask us first at
security@cakewallet.com.
## What to expect
- **Acknowledgement:** within **2 business days**.
- **Triage and initial assessment:** within **7 business days**.
- **Coordinated disclosure:** we aim to ship a fix and coordinate public
disclosure within **90 days** of the report. We will keep you updated on
progress and agree on a disclosure date with you.
- **Credit:** with your permission, we are happy to publicly credit you for the
report once a fix is released.
## Scope
**In scope:** the Cake Wallet and Monero.com applications and the code in this
repository and its sibling `cake-tech` repositories — anything that could lead to
loss of funds, exposure of keys or seeds, a privacy leak, or a
failed/incorrect transaction.
**Out of scope:** issues in third-party services, exchange/swap providers, or
nodes we do not operate; reports generated solely by automated scanners without a
demonstrated impact; low-severity or informational issues on our marketing and
landing websites (for example reflected or self-XSS, missing security headers,
clickjacking on pages with no sensitive actions, or SPF/DMARC and cookie-flag
nitpicks) that do not affect the app or user funds; and social-engineering or
physical attacks.
## Rewards
At our **sole discretion**, we may offer a reward for a valid report. To be
eligible, a report must:
- be submitted **privately** through one of the channels above (a GitHub private
security advisory or `security@cakewallet.com`) — anything disclosed publicly or
sent through other channels is not eligible; and
- identify a genuine vulnerability with real impact on users — typically loss of
funds, exposure of keys or seeds, a privacy leak, or a failed or incorrect
transaction.
Trivial or low-impact findings are **not** eligible — for example, reflected XSS
or other low-severity issues on our marketing websites, missing security headers,
hardening or best-practice suggestions, automated-scanner output without a working
proof of concept, or already-known issues. There is no fixed bounty and no
guaranteed payout; whether a report qualifies, and any amount, are determined
solely by Cake Labs LLC.
## Supported versions
We do not maintain previous releases. Only the **latest release for each platform**
is supported; security fixes are delivered in new versions. Please keep Cake Wallet
up to date.
+18 -23
View File
@@ -188,8 +188,6 @@ PODS:
- Flutter
- wakelock_plus (0.0.1):
- Flutter
- warp_api (0.0.1):
- Flutter
- YttriumWrapper (0.8.35)
DEPENDENCIES:
@@ -236,7 +234,6 @@ DEPENDENCIES:
- universal_ble (from `.symlinks/plugins/universal_ble/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
- warp_api (from `.symlinks/plugins/warp_api/ios`)
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
@@ -341,8 +338,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
warp_api:
:path: ".symlinks/plugins/warp_api/ios"
SPEC CHECKSUMS:
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
@@ -351,17 +346,17 @@ SPEC CHECKSUMS:
breez_sdk_spark_flutter: ed3b6709b8ce9b40309d8728640eade5b19e8a0d
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a
cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926
cw_decred: a02cf30175a46971c1e2fa22c48407534541edc6
cw_mweb: 3aea2fb35b2bd04d8b2d21b83216f3b8fb768d85
device_display_brightness: 04374ebd653619292c1d996f00f42877ea19f17f
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
devicelocale: bd64aa714485a8afdaded0892c1e7d5b7f680cf8
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
dnssec_proof: d461cac7bd3301eb7447f87936745a0c1ae0a67e
fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
@@ -374,32 +369,32 @@ SPEC CHECKSUMS:
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
in_app_review: 7dd1ea365263f834b8464673f9df72c80c17c937
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
payjoin_flutter: d9d4c8aa16bd5dfedb9b21d0edc8199e0187d96e
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
quick_actions_ios: 4b07fb49d8d8f3518d7565fbb7a91014067a7d82
quick_actions_ios: 500fcc11711d9f646739093395c4ae8eec25f779
reown_yttrium: cee334ade64725b1d83f7b34c706a6aae2696d58
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
rlz: 3f4022361f540a5c26d9d2474fea1d2455b69d8d
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
sensitive_clipboard: 161e9abc3d56b3131309d8a321eb4690a803c16b
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sp_scanner: b1bc9321690980bdb44bba7ec85d5543e716d1b5
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
sqlite3_flutter_libs: 86f82662868ee26ff3451f73cac9c5fc2a1f57fa
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
torch_dart: f4620705d10f05492fab047f2fa1c3a600e7d17d
uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152
universal_ble: 45519b2aeafe62761e2c6309f8927edb5288b914
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
universal_ble: a322ebabee64f0ec27a313e89a5dd6967f37a60f
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
+10 -18
View File
@@ -687,28 +687,20 @@ class CWBitcoin extends Bitcoin {
}
List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs) {
if (pendingTransaction is PendingLightningTransaction) return outputs;
final pendingTx = pendingTransaction as PendingBitcoinTransaction;
if (!pendingTx.hasSilentPayment) {
if (pendingTransaction is! PendingBitcoinTransaction || !pendingTransaction.hasSilentPayment) {
return outputs;
}
final updatedOutputs = outputs.map((output) {
try {
final pendingOut = pendingTx.outputs[outputs.indexOf(output)];
final updatedOutput = output;
final stealthAddresses = pendingTransaction.stealthAddresses;
if (stealthAddresses.length != outputs.length) {
printV("well, that shouldn't happen");
return outputs;
}
updatedOutput.stealthAddress = P2trAddress.fromScriptPubkey(script: pendingOut.scriptPubKey)
.toAddress(BitcoinNetwork.mainnet);
return updatedOutput;
} catch (_) {}
return output;
}).toList();
return updatedOutputs;
for (var i = 0; i < outputs.length; i++) {
outputs[i].stealthAddress = stealthAddresses[i];
}
return outputs;
}
@override
+24 -3
View File
@@ -10,7 +10,6 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/fs_migration.dart';
import 'package:cake_wallet/entities/haven_seed_store.dart';
import 'package:cake_wallet/entities/node_list.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/monero/monero.dart';
@@ -46,7 +45,7 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
const nanoDefaultNodeUri = 'rpc.nano.to';
const nanoDefaultPowNodeUri = 'rpc.nano.to';
const solanaDefaultNodeUri = 'solana-mainnet.core.chainstack.com';
const tronDefaultNodeUri = 'api.trongrid.io';
const tronDefaultNodeUri = 'trx.nownodes.io';
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
const zanoDefaultNodeUri = '37.27.100.59:10500';
@@ -607,6 +606,28 @@ Future<void> defaultSettingsMigration(
enabled: true,
);
break;
case 68:
await _changeDefaultNode(
sharedPreferences: sharedPreferences,
type: WalletType.tron,
newDefaultUri: tronDefaultNodeUri,
currentNodePreferenceKey: PreferencesKey.currentTronNodeIdKey,
useSSL: true,
oldUri: [
'tron-rpc.publicnode.com:443',
'api.trongrid.io',
],
);
await _changeDefaultNode(
sharedPreferences: sharedPreferences,
type: WalletType.monero,
newDefaultUri: newCakeWalletMoneroUri,
currentNodePreferenceKey: PreferencesKey.currentNodeIdKey,
useSSL: true,
trusted: true,
oldUri: ['nodes.hashvault.pro:18081'],
);
break;
default:
break;
}
@@ -663,7 +684,7 @@ Future<void> _changeDefaultNode({
} else {
final currentNode = nodes.firstWhereOrNull((node) => node.id == currentNodeId);
shouldReplace =
currentNode == null || (oldUri?.any((e) => currentNode!.uriRaw.contains(e)) ?? true);
currentNode == null || (oldUri?.any((e) => currentNode.uriRaw.contains(e)) ?? true);
}
if (shouldReplace) {
@@ -539,7 +539,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
}
Future<Uri> _getUri(String path, Map<String, String> queryParams) async {
final uri = Uri.http(onionApiAuthority, path, queryParams);
final uri = Uri.https(onionApiAuthority, path, queryParams);
if (useTorOnly) return uri;
+1 -1
View File
@@ -315,7 +315,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
payjoinSessionSource: payjoinSessionSource,
anonpayInvoiceInfo: anonpayInvoiceInfo,
havenSeedStore: havenSeedStore,
initialMigrationVersion: 67,
initialMigrationVersion: 68,
);
}
File diff suppressed because it is too large Load Diff
@@ -1,22 +1,10 @@
import 'dart:io';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/new-ui/widgets/copy_wrapper.dart';
import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/new-ui/widgets/copy_wrapper.dart";
import "package:cake_wallet/new-ui/widgets/modern_button.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
class ReceiveBottomButtons extends StatefulWidget {
final bool largeQrMode;
final ClipboardData? copyData;
final VoidCallback onCopyButtonPressed;
final VoidCallback onAmountButtonPressed;
final VoidCallback onLabelButtonPressed;
final VoidCallback onAccountsButtonPressed;
final bool showLabelButton;
final bool showAccountsButton;
const ReceiveBottomButtons({
required this.largeQrMode,
required this.onCopyButtonPressed,
@@ -29,6 +17,15 @@ class ReceiveBottomButtons extends StatefulWidget {
super.key,
});
final bool largeQrMode;
final ClipboardData? copyData;
final VoidCallback onCopyButtonPressed;
final VoidCallback onAmountButtonPressed;
final VoidCallback onLabelButtonPressed;
final VoidCallback onAccountsButtonPressed;
final bool showLabelButton;
final bool showAccountsButton;
@override
State<ReceiveBottomButtons> createState() => _ReceiveBottomButtonsState();
}
@@ -49,7 +46,7 @@ class _ReceiveBottomButtonsState extends State<ReceiveBottomButtons> {
opacity: targetOpacity,
curve: Curves.easeOut,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
@@ -57,12 +54,13 @@ class _ReceiveBottomButtonsState extends State<ReceiveBottomButtons> {
children: [
GestureDetector(
onTap: widget.copyData == null ? widget.onCopyButtonPressed : null,
behavior: HitTestBehavior.opaque,
child: IgnorePointer(
ignoring: widget.copyData == null,
child: CopyWrapper(
data: widget.copyData,
builder: (context, copied) => AnimatedSwitcher(
duration: Duration(milliseconds: 200),
duration: const Duration(milliseconds: 200),
child: IgnorePointer(
child: ModernButton.svg(
key: ValueKey(copied),
@@ -84,25 +82,28 @@ class _ReceiveBottomButtonsState extends State<ReceiveBottomButtons> {
),
),
ModernButton.svg(
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/set-amount.svg",
onPressed: widget.onAmountButtonPressed,
label: S.of(context).set_amount),
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/set-amount.svg",
onPressed: widget.onAmountButtonPressed,
label: S.of(context).set_amount,
),
if (widget.showLabelButton)
ModernButton.svg(
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/add-label.svg",
onPressed: widget.onLabelButtonPressed,
label: S.of(context).label),
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/add-label.svg",
onPressed: widget.onLabelButtonPressed,
label: S.of(context).label,
),
if (widget.showAccountsButton)
ModernButton.svg(
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/addr-book.svg",
onPressed: widget.onAccountsButtonPressed,
label: S.of(context).addresses),
size: 60,
iconSize: 32,
svgPath: "assets/new-ui/addr-book.svg",
onPressed: widget.onAccountsButtonPressed,
label: S.of(context).addresses,
),
],
),
),
@@ -1,23 +1,24 @@
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: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";
class FiatAmountBar extends StatelessWidget {
const FiatAmountBar(
{super.key,
required this.fiatInputMode,
required this.onSwitchButtonPressed,
this.onAllButtonPressed,
required this.cryptoAmount,
required this.fiatAmount,
required this.cryptoCurrencySymbol,
required this.fiatCurrencySymbol,
this.allAmount,
this.foregroundElementColor,
this.textColor,
this.allAmountColor,
this.allAmountTextColor});
const FiatAmountBar({
required this.fiatInputMode,
required this.onSwitchButtonPressed,
required this.cryptoAmount,
required this.fiatAmount,
required this.cryptoCurrencySymbol,
required this.fiatCurrencySymbol,
super.key,
this.onAllButtonPressed,
this.allAmount,
this.foregroundElementColor,
this.textColor,
this.allAmountColor,
this.allAmountTextColor,
});
final bool fiatInputMode;
final VoidCallback onSwitchButtonPressed;
@@ -34,42 +35,41 @@ class FiatAmountBar extends StatelessWidget {
final Color? allAmountTextColor;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
spacing: 8,
children: [
ModernButton.svg(
backgroundColor: foregroundElementColor,
size: 28,
svgPath: "assets/new-ui/switch.svg",
iconSize: 18,
onPressed: onSwitchButtonPressed,
),
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)
Widget build(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
spacing: 8,
children: [
Text(
"${S.of(context).max}.",
style: TextStyle(color: textColor ?? Theme.of(context).colorScheme.onSurface),
ModernButton.svg(
backgroundColor: foregroundElementColor,
size: 28,
svgPath: "assets/new-ui/switch.svg",
iconSize: 18,
onPressed: onSwitchButtonPressed,
),
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(999999)),
child: Material(
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)
Row(
spacing: 8,
children: [
Text(
"${S.of(context).max}.",
style: TextStyle(color: textColor ?? Theme.of(context).colorScheme.onSurface),
),
Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(999999)),
child: Material(
color: allAmountColor ??
foregroundElementColor ??
Theme.of(context).colorScheme.surfaceContainer,
@@ -78,22 +78,23 @@ class FiatAmountBar extends StatelessWidget {
borderRadius: BorderRadius.circular(99999),
onTap: onAllButtonPressed,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Text(
formatAmount(allAmount!),
_formatAmount(allAmount!),
style: TextStyle(
color: allAmountTextColor ?? Theme.of(context).colorScheme.primary),
color: allAmountTextColor ?? Theme.of(context).colorScheme.primary,
),
),
),
)),
)
],
)
],
);
}
),
),
),
],
),
],
);
String formatAmount(String amount) {
String _formatAmount(String amount) {
try {
return double.parse(amount).toStringAsPrecision(8).replaceFirst(RegExp(r"\.?0+$"), "");
} catch (e) {
@@ -1,20 +1,20 @@
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';
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";
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,
super.key,
this.validator,
});
@@ -41,27 +41,28 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
}
@override
Widget build(BuildContext context) {
return FormField<String>(
key: formFieldKey,
initialValue: widget.amountController.text,
validator: widget.validator,
builder: (state) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
Widget build(BuildContext context) => FormField<String>(
key: formFieldKey,
initialValue: widget.amountController.text,
validator: widget.validator,
builder: (state) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: widget.hasPicker
? Theme.of(context).colorScheme.surfaceContainerHigh
: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(18)),
color: widget.hasPicker
? Theme.of(context).colorScheme.surfaceContainerHigh
: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(18),
),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.horizontal(left: Radius.circular(18))),
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: const BorderRadius.horizontal(left: Radius.circular(18)),
),
child: Row(
mainAxisSize: MainAxisSize.max,
spacing: 8,
@@ -79,82 +80,87 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
],
controller: widget.amountController,
decoration: InputDecoration(
hintText: widget.maxDecimals > 0 ? "0" : "0.00",
hintText: widget.maxDecimals == 0 ? "0" : "0.00",
errorMaxLines: 3,
),
onChanged: state.didChange,
),
),
FloatingIconButton(
iconPath: "assets/new-ui/paste.svg",
onPressed: () async {
final data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
final text = data.text!;
widget.amountController.value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
);
}
}),
iconPath: "assets/new-ui/paste.svg",
onPressed: () async {
final data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
final text = data.text!;
widget.amountController.value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
);
}
},
),
],
),
),
),
IntrinsicWidth(
child: Observer(
builder: (_) {
return GestureDetector(
onTap: widget.onPickerClicked,
child: Container(
alignment: Alignment.center,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(18),
bottomRight: Radius.circular(18)),
color: widget.hasPicker
? Theme.of(context).colorScheme.surfaceContainerHigh
: Theme.of(context).colorScheme.surfaceContainer,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
spacing: 8,
children: [
if (widget.hasPicker && widget.currencyIconPath.isNotEmpty)
TokenImageWidget(
imageUrl: widget.currencyIconPath, size: 24),
Text(widget.currency),
if (widget.hasPicker)
CakeImageWidget(
imageUrl: "assets/new-ui/chooser.svg",
width: 12,
height: 12,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary,
BlendMode.srcIn),
)
],
))),
);
},
builder: (_) => GestureDetector(
onTap: widget.onPickerClicked,
child: Container(
alignment: Alignment.center,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(18),
bottomRight: Radius.circular(18),
),
color: widget.hasPicker
? Theme.of(context).colorScheme.surfaceContainerHigh
: Theme.of(context).colorScheme.surfaceContainer,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
spacing: 8,
children: [
if (widget.hasPicker && widget.currencyIconPath.isNotEmpty)
TokenImageWidget(
imageUrl: widget.currencyIconPath,
size: 24,
),
Text(widget.currency),
if (widget.hasPicker)
CakeImageWidget(
imageUrl: "assets/new-ui/chooser.svg",
width: 12,
height: 12,
colorFilter: ColorFilter.mode(
Theme.of(context).colorScheme.primary,
BlendMode.srcIn,
),
),
],
),
),
),
),
),
),
],
)),
if (state.hasError)
Padding(
padding: EdgeInsets.only(top: 6, left: 8),
child: Text(
state.errorText!,
style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.error),
),
)
],
),
);
}
),
if (state.hasError)
Padding(
padding: const EdgeInsets.only(top: 6, left: 8),
child: Text(
state.errorText!,
style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.error),
),
),
],
),
);
}
@@ -1,4 +1,5 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/new-ui/widgets/copy_wrapper.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/new-ui/widgets/swap_page/swap_modal_header.dart';
@@ -9,6 +10,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/payment_uris.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SwapSendExternalModal extends StatefulWidget {
const SwapSendExternalModal(
@@ -166,10 +168,59 @@ class _SwapSendExternalModalState extends State<SwapSendExternalModal> {
),
)),
if (widget.exchangeTradeViewModel.trade.extraId != null)
Text(
"${S.of(context).destination_tag} ${widget.exchangeTradeViewModel.trade.extraId}",
style: TextStyle(
fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant),
CopyWrapper(
data: ClipboardData(
text: widget.exchangeTradeViewModel.trade.extraId!),
builder: (context, copied) => Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 8,
children: [
Text(
S.of(context).destination_tag,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurfaceVariant),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
Flexible(
child: Text(
widget.exchangeTradeViewModel.trade.extraId!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.primary),
),
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: Icon(
copied ? Icons.check : Icons.copy,
key: ValueKey(copied),
size: 18,
color: Theme.of(context).colorScheme.primary,
),
),
],
),
],
),
),
),
),
Row(
spacing: 8,
@@ -34,6 +34,8 @@ class MoneroHardwareWalletPassphraseInputModal extends StatelessWidget {
BaseTextFormField(
controller: controller,
hintText: S.of(context).passphrase_raw,
autocorrect: false,
enableSuggestions: false,
),
const SizedBox(),
NewPrimaryButton(
@@ -442,16 +442,11 @@ abstract class ExchangeTradeViewModelBase with Store {
case WalletType.dogecoin:
return DogeURI(address: inputAddress, amount: amount);
case WalletType.ethereum:
return _createERC681URI(fromCurrency, inputAddress, amount);
// TODO: Expand ERC681URI support to Polygon(modify decoding flow for QRs, pay anything, and deep link handling)
case WalletType.polygon:
return PolygonURI(amount: amount, address: inputAddress);
case WalletType.base:
return BaseURI(amount: amount, address: inputAddress);
case WalletType.arbitrum:
return ArbitrumURI(amount: amount, address: inputAddress);
case WalletType.bsc:
return BSCURI(amount: amount, address: inputAddress);
return _createERC681URI(fromCurrency, inputAddress, amount);
case WalletType.solana:
return SolanaURI(amount: amount, address: inputAddress);
case WalletType.tron:
+4 -4
View File
@@ -14,15 +14,15 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="6.2.1"
MONERO_COM_BUILD_NUMBER=4161
MONERO_COM_VERSION="6.3.0"
MONERO_COM_BUILD_NUMBER=4162
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="6.2.1"
CAKEWALLET_BUILD_NUMBER=4413
CAKEWALLET_VERSION="6.3.0"
CAKEWALLET_BUILD_NUMBER=4414
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"
+4 -4
View File
@@ -12,13 +12,13 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="6.2.1"
MONERO_COM_BUILD_NUMBER=167
MONERO_COM_VERSION="6.3.0"
MONERO_COM_BUILD_NUMBER=168
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="6.2.1"
CAKEWALLET_BUILD_NUMBER=423
CAKEWALLET_VERSION="6.3.0"
CAKEWALLET_BUILD_NUMBER=424
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"