improve address rotation button ui + fix bug when autogenerate is disabled (#3334)
What changed, and why it matters
This commit fixes a UI bug in Cake Wallet's receive page. When the user had turned off automatic address generation, the manual 'rotate address' button could still be shown and pressed, but it would not actually update the displayed receiving address. The fix makes the button's visibility depend on whether rotation is truly available, shows a loading spinner while a new address is being created, and ensures the displayed address is updated after rotation even for non-Electrum wallets. There is no direct evidence of a security vulnerability such as funds being stolen; the issue is primarily a usability/consistency bug.
Treat as a normal bug-fix commit. Reviewers may want to verify that rotateAddress() correctly handles errors from createNewAddress and that wallet.walletAddresses.address is always the intended new address for all wallet types. No urgent security response is indicated by the diff alone.
Security signals we found
UI state desynchronization between displayed receive address and actual wallet address
Missing guard against concurrent/re-entrant address rotation
Address rotation behavior conditional on wallet type (isElectrumWallet) rather than feature availability
No input validation or cryptographic changes
Evidence from the diff
The patch changes two files. In receive_page.dart, the top-bar trailing icon is replaced with a ModernButton wrapped in Observer and AnimatedSwitcher. The button now only appears when _largeQrMode is true or addressListViewModel.hasAddressRotation is true, and it displays a CupertinoActivityIndicator while isRotatingAddress is true. In wallet_address_list_view_model.dart, an isRotatingAddress observable is added, and rotateAddress() now guards against re-entrant calls, sets the flag, awaits createNewAddress, then assigns wallet.walletAddresses.address to the last address in addressList inside a try/finally block. Previously the address assignment only happened for Electrum wallets (isElectrumWallet), which is removed. This means the UI and wallet state stay consistent after a manual rotation regardless of wallet type.
Changed components
lib/new-ui/pages/receive_page.dartlib/view_model/wallet_address_list/wallet_address_list_view_model.dartInspect captured patch +41 / −22
diff --git a/lib/new-ui/pages/receive_page.dart b/lib/new-ui/pages/receive_page.dart
index 9ad2d7d2..84d1e935 100644
--- a/lib/new-ui/pages/receive_page.dart
+++ b/lib/new-ui/pages/receive_page.dart
@@ -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(
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index f097e5d1..b69558bf 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -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;
}
}
Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.