fix: zcash address for exchange (#3491)
* fix: zcash address for exchange * fix: honour the restore height the user entered - Make Zcash rescan work in both directions by resetting sync from the new birth height - Keep the entered height when restoring a wallet from keys - Tolerate grouping separators in typed restore heights --------- Co-authored-by: Vik Sharma <vik@cakewallet.com>
cyan committed
Aug 11, 2026 at 03:15 UTC
3b16f2fb7c9a7686bf365a0d1c88c65ff530bccc
5 files changed
+71
-2
cw_zcash/lib/src/zcash_wallet.dart
+44
-1
@@ -1021,7 +1021,50 @@ abstract class ZcashWalletBase
1021
@override
1022
@action
1023
Future<void> rescan({required final int height}) async {
1024
- await zkool_sync.rewindSync(height: height, account: accountId, c: c);
1024
+ syncStatus = StartingScanSyncStatus(height);
1025
+ try {
1026
+ await zkool_sync.cancelSync();
1027
+ isSyncing = false;
1028
+
1029
+ await runWithCoin(
1030
+ accountId: accountId,
1031
+ func: (final coin) async {
1032
+ await zkool_account.updateAccount(
1033
+ update: zkool_account.AccountUpdate(
1034
+ coin: coin.coin,
1035
+ id: accountId,
1036
+ birth: height,
1037
+ folder: 0,
1038
+ ),
1039
+ c: coin,
1040
+ );
1041
+
1042
+ final accounts = await zkool_account.listAccounts(c: coin);
1043
+ final updated =
1044
+ accounts.where((final a) => a.id == accountId).firstOrNull;
1045
+ if (updated == null) {
1046
+ throw Exception('account $accountId not found after update');
1047
+ }
1048
+ if (updated.birth != height) {
1049
+ throw Exception(
1050
+ 'birth height did not persist: wanted $height, '
1051
+ 'database still has ${updated.birth}',
1052
+ );
1053
+ }
1054
+
1055
+ await zkool_account.resetSync(id: accountId, c: coin);
1056
+ },
1057
+ );
1058
+
1059
+ lastKnownRestoreHeight = height;
1060
+ await save();
1061
+
1062
+ syncStatus = ConnectedSyncStatus();
1063
+ unawaited(startSync());
1064
+ } catch (e) {
1065
+ printV('Zcash rescan failed: $e');
1066
+ syncStatus = FailedSyncStatus(error: e.toString());
1067
+ }
1068
// try {
1069
// syncStatus = StartingScanSyncStatus(height);
1070
// printV("rescanning from: $height");
cw_zcash/lib/src/zcash_wallet_addresses.dart
+8
@@ -220,6 +220,13 @@ abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
220
};
221
await _syncRotationHiddenAddresses();
222
await _applyAddressForCurrentType();
223
+ await _persistAddressForExchange();
224
+ }
225
+
226
+ Future<void> _persistAddressForExchange() async {
227
+ final addr = addressForExchange;
228
+ if (isPlaceholderAddress(addr)) return;
229
+ await walletInfo.setAddresses({addr: 'transparent'});
230
}
231
232
Future<void> _applyAddressForCurrentType() async {
@@ -253,6 +260,7 @@ abstract class ZcashWalletAddressesBase extends WalletAddresses with Store {
260
walletInfo.address = address;
261
}
262
await walletInfo.setAddresses({});
263
+ await _persistAddressForExchange();
264
await walletInfo.setAddressInfos(addressInfos);
265
await walletInfo.setUsedAddresses(usedAddresses.toList());
266
await walletInfo.setHiddenAddresses(hiddenAddresses.toList());
lib/src/screens/restore/wallet_restore_page.dart
+4
@@ -215,6 +215,10 @@ class WalletRestorePage extends BasePage {
215
walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
216
credentials['name'] =
217
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
218
+ if (walletRestoreViewModel.hasBlockchainHeightSelector) {
219
+ credentials['height'] =
220
+ walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState?.height;
221
+ }
222
} else {
223
credentials['name'] =
224
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
lib/src/widgets/blockchain_height_widget.dart
+2
-1
@@ -65,7 +65,8 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
65
try {
66
final int _height;
67
if (restoreHeightController.text.isNotEmpty) {
68
- _height = int.tryParse(restoreHeightController.text) ?? 0;
68
+ final digits = restoreHeightController.text.replaceAll(RegExp(r'[^0-9]'), '');
69
+ _height = int.tryParse(digits) ?? 0;
70
} else {
71
_height = 0;
72
}
lib/view_model/exchange/exchange_view_model.dart
+13
@@ -1185,6 +1185,19 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1185
}
1186
}
1187
1188
+ if (receiveCurrency == CryptoCurrency.zec &&
1189
+ selectedAddressBookWallet?.type == WalletType.zcash) {
1190
+ if (wallet.type == WalletType.zcash && wallet.name == selectedAddressBookWallet!.name) {
1191
+ receiveAddress = wallet.walletAddresses.addressForExchange;
1192
+ } else {
1193
+ receiveAddress = (await selectedAddressBookWallet!.getAddresses())
1194
+ .entries
1195
+ .firstWhereOrNull((e) => e.value == 'transparent')
1196
+ ?.key ??
1197
+ receiveAddress;
1198
+ }
1199
+ }
1200
+
1201
// parse Lightning address to bolt11 invoice
1202
if (receiveAddress.contains("@")) {
1203
receiveAddress = await getBolt11FromLightingAddress(receiveAddress) ?? receiveAddress;