resolveWalletDbDir function
The wallet onboarding COMPOSITION ROOT (spec §3.2g iii-B-2-b): the one place
that wires the production adapters into the onboarding seams. Building the
provisioner, the store, and the screen-security adapter TOGETHER (with the
co-wiring assertion below) is what makes them impossible to wire apart — a
refactor cannot ship the seed-revealing provisioner without the screenshot
protection on a platform that supports it (crypto audit H2).
Resolve the wallet data directory to a PLAIN, already-created path — call
this in main(), fold it into the host's WalletConfig (the reference
buildWalletConfig does this), and pass that config to
walletOnboardingOverrides BEFORE building the ProviderScope. This is a
REFERENCE helper (it picks a per-network leaf under the app support dir and
wires iOS backup-exclusion); a host with its own data-dir convention
resolves its own path and puts it on its WalletConfig.dbDir. It must NOT
be recomputed inside a provider
build(): an async re-resolve on every rebuild would spawn a fresh
provisioner mid-flight and risk a double-create (the footgun the port and
controller both warn about).
Uses the app SUPPORT directory (not Documents — on iOS Documents is
user-visible in the Files app). Backup-exclusion is wired on BOTH platforms:
Android off app-wide (allowBackup=false), iOS via BackupExclusion on this
dir (set below). Excluding the DB matters because its key is a ThisDeviceOnly
Secure-Enclave key that never migrates — a backed-up DB restored onto a new
device can't be opened (the needsRecovery trap), so excluding it routes a
device-restore to the clean Welcome → Restore flow instead.
Implementation
/// Resolve the wallet data directory to a PLAIN, already-created path — call
/// this in `main()`, fold it into the host's [WalletConfig] (the reference
/// [buildWalletConfig] does this), and pass that config to
/// [walletOnboardingOverrides] BEFORE building the `ProviderScope`. This is a
/// REFERENCE helper (it picks a per-network leaf under the app support dir and
/// wires iOS backup-exclusion); a host with its own data-dir convention
/// resolves its own path and puts it on its `WalletConfig.dbDir`. It must NOT
/// be recomputed inside a provider
/// `build()`: an async re-resolve on every rebuild would spawn a fresh
/// provisioner mid-flight and risk a double-create (the footgun the port and
/// controller both warn about).
///
/// Uses the app SUPPORT directory (not Documents — on iOS Documents is
/// user-visible in the Files app). Backup-exclusion is wired on BOTH platforms:
/// Android off app-wide (`allowBackup=false`), iOS via [BackupExclusion] on this
/// dir (set below). Excluding the DB matters because its key is a `ThisDeviceOnly`
/// Secure-Enclave key that never migrates — a backed-up DB restored onto a new
/// device can't be opened (the needsRecovery trap), so excluding it routes a
/// device-restore to the clean Welcome → Restore flow instead.
Future<String> resolveWalletDbDir() async {
final support = await getApplicationSupportDirectory();
// Per-network leaf (`zec_wallet` mainnet / `zec_wallet_testnet` testnet): a
// v-5c testnet-proof build lives BESIDE a mainnet wallet, never over it — the
// SDK would refuse the mismatched DB anyway (NetworkMismatch), but the leaf
// makes the define switch non-destructive in both directions.
final dir = Directory('${support.path}/${referenceDbDirLeaf()}');
// The lock-free existence PROBE does not create the dir (a missing dir just
// reads "no wallet"); create/open DO create it at lock acquisition. Still
// pre-create it here so the backup-exclusion flag below lands on a real
// directory before any wallet files exist inside it.
await dir.create(recursive: true);
// iOS: exclude the wallet DB from device/iCloud backup (best-effort; never blocks
// boot). Set AFTER the dir exists; the flag applies to the whole subtree, so all
// DB files created inside it later are excluded too. (Android: allowBackup=false.)
await const BackupExclusion().exclude(dir.path);
return dir.path;
}