feat: prefill rescan height with the saved Monero and Zcash restore height (#3669)
What changed, and why it matters
This commit changes the wallet's rescan screen so that, for Monero and Zcash wallets, the starting block height is automatically filled in with the wallet's saved restore/birth height. This is a convenience feature that helps users avoid typing the wrong height when rescanning the blockchain. There is no direct security bug in the code, but it slightly reduces the chance that a user accidentally rescans from block 0 (which would be slower and expose more transaction history/metadata) or enters an incorrect height.
No security action required. As a normal code-quality step, reviewers may want to confirm that `getBirthHeight()` handles cases where `zkool_account.listAccounts` returns null or empty gracefully, and that the prefill does not override a user-entered value.
Security signals we found
UI convenience change, no cryptographic or network code modified
No input validation changes; prefill only occurs when field is empty and height > 0
Reduces likelihood of user error (e.g., rescanning from genesis or an incorrect height)
No evidence of malicious intent or vulnerability introduction in the diff
Evidence from the diff
The patch adds a savedRestoreHeight getter in RescanViewModel that returns the saved restore height for Monero wallets (falling back to monero.getRestoreHeight(wallet)) and the account birth height for Zcash wallets via a new getBirthHeight() method. The rescan page’s initState() then prefills the height text field with this value if it is positive and the field is empty. The change is purely UI/UX prefill logic and does not alter validation, authentication, cryptography, or network behavior.
Changed components
Rescan page UIRescan view modelZcash wallet birth-height retrievalZcash platform interfaceInspect captured patch +32 / −0
### cw_zcash/lib/src/zcash_wallet.dart
@@ -999,6 +999,14 @@ abstract class ZcashWalletBase
static int zashiAnnouncedBlockHeight = 2419420;
+ Future<int?> getBirthHeight() async => runWithCoin(
+ accountId: accountId,
+ func: (final coin) async => (await zkool_account.listAccounts(c: coin))
+ .where((final a) => a.id == accountId)
+ .firstOrNull
+ ?.birth,
+ );
+
Future<dynamic> _getAddressesForAccount(final int id) async {
return runWithCoin(
accountId: id,
### lib/src/screens/rescan/rescan_page.dart
@@ -25,6 +25,16 @@ class RescanPage extends StatefulWidget {
class _RescanPageState extends State<RescanPage> {
final TextEditingController _heightController = TextEditingController();
+ @override
+ void initState() {
+ super.initState();
+ widget._rescanViewModel.savedRestoreHeight.then((height) {
+ if (mounted && (height ?? 0) > 0 && _heightController.text.isEmpty) {
+ _heightController.text = '$height';
+ }
+ });
+ }
+
@override
Widget build(BuildContext context) {
Widget child;
### lib/view_model/rescan_view_model.dart
@@ -1,4 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
+import 'package:cake_wallet/monero/monero.dart';
+import 'package:cake_wallet/zcash/zcash.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
@@ -32,6 +34,14 @@ abstract class RescanViewModelBase with Store {
@computed
bool get isMwebScan => wallet.type == WalletType.litecoin;
+ Future<int?> get savedRestoreHeight async => switch (wallet.type) {
+ WalletType.monero => wallet.walletInfo.restoreHeight != 0
+ ? wallet.walletInfo.restoreHeight
+ : monero!.getRestoreHeight(wallet),
+ WalletType.zcash => await zcash!.getBirthHeight(wallet),
+ _ => null,
+ };
+
Future<bool> get isBitcoinMempoolAPIEnabled async =>
wallet.type == WalletType.bitcoin && await bitcoin!.checkIfMempoolAPIIsEnabled(wallet);
### lib/zcash/cw_zcash.dart
@@ -119,6 +119,9 @@ class CWZcash extends Zcash {
return getAddress(wallet);
}
+ @override
+ Future<int?> getBirthHeight(Object wallet) => (wallet as ZcashWallet).getBirthHeight();
+
@override
Map<String, String> getKeys(Object wallet) {
final zcashWallet = wallet as ZcashWallet;
### tool/configure.dart
@@ -1778,6 +1778,7 @@ abstract class Zcash {
String getPrivateKey(WalletBase wallet);
String getPublicKey(WalletBase wallet);
Map<String, String> getKeys(Object wallet);
+ Future<int?> getBirthHeight(Object wallet);
Object createZcashTransactionCredentials(
List<Output> outputs, {Why this scored 19/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.