v6.3.0 Release Candidate (#3414)

* v6.3.0 Release Candidate
- the new transaction history
- Radar in Apps screen
- Zcash update
- Flutter update
- Parts of the Refactor
  should improve how amounts are handled in the app and fix any issue with amounts
  improve handling sending to aliases

- Improvements for Cake Pay mobile
- Bug fixes

* don't use memo for note/message values

* save message in QR locally in the notes field

* Make stealth addresses generated from silent payment, not reliant on output index (#3420)

* - fix swaps showing only on primary account
- switch tron default node
- privacy fixes

* - enlarge destination tag and make it copiable
- migrate users on hashvault
- minor fix
This commit is contained in:
Omar Hatem
2026-07-21 04:54:48 +03:00
committed by GitHub
parent b336da4a9a
commit 544cbec0e0
15 changed files with 152 additions and 66 deletions
+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,
);
}
+15 -2
View File
@@ -241,7 +241,9 @@ class _NewSendPageState extends State<NewSendPage> {
widget.initialPaymentRequest!.scheme.toLowerCase()) {
_addressControllers[0].text = widget.initialPaymentRequest!.address;
_amountControllers[0].text = widget.initialPaymentRequest!.amount;
_memoControllers[0].text = widget.initialPaymentRequest!.note;
// _memoControllers[0].text = widget.initialPaymentRequest!.note;
_applyNote(widget.initialPaymentRequest!.note, 0);
final contractAddress = widget.initialPaymentRequest!.contractAddress;
if (contractAddress != null && contractAddress.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -1308,7 +1310,9 @@ class _NewSendPageState extends State<NewSendPage> {
);
} catch (e) {}
}
_memoControllers[_selectedOutput].text = paymentRequest.note;
// _memoControllers[_selectedOutput].text = paymentRequest.note;
_applyNote(paymentRequest.note, _selectedOutput);
}
Future<void> _handleSwapFlow(
@@ -1456,6 +1460,15 @@ class _NewSendPageState extends State<NewSendPage> {
String _wrapAmount(String amount, int maxChars) =>
amount.length <= maxChars ? amount : "${amount.substring(0, maxChars - 3)}...";
// TODO: make a separate variable for memo in payment request model
void _applyNote(String note, int selectedOutput) {
if (widget.sendViewModel.hasMemos && note.length <= widget.sendViewModel.maxMemoLength) {
widget.sendViewModel.outputs[selectedOutput].memo = note;
} else {
widget.sendViewModel.outputs[selectedOutput].note = note;
}
}
}
class SendHelpPage extends StatelessWidget {
@@ -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(