restore method
RESTORE an existing wallet from its BIP39 recovery phrase — the ONE
sanctioned INBOUND key crossing (spec §3.3; counterpart to the outbound
revealMnemonic). mnemonicWords are the recovery words in order.
THE LOWERCASE CONTRACT (a HARD money-reliability requirement): the words
MUST already be lowercased + trimmed — the SDK keeps the audited bip39
parser whole and does NOT case-fold, so a mis-cased/space-padded word is
rejected as a typed invalidMnemonic carrying its INDEX and a CORRECT
backup would fail to restore. The OnboardingController enforces this at
its startRestore chokepoint via normalizeMnemonicInput; this method
trusts that contract (it does not re-normalize — re-casing key material is
the controller's single responsibility, kept off the device adapter).
approximateCreationTime is the OPTIONAL wallet-creation time used to floor
the restore scan (faster than a full history scan). The adapter converts it
to a birthday HEIGHT via the SDK's conservative estimateBirthday and the
configured network, then rides it on config.birthdayHeight (the ONE source
of truth — create/open read the same field). null means "I don't know":
the scan floors to Sapling activation — a full, slower, but money-SAFE scan
that never silently skips older funds (NEVER ~tip).
LOCAL-ONLY, same BOUNDEDNESS CONTRACT as createGenerated: BIP39
validation + seed seal + SQLCipher provision are disk + keychain; the first
chain round-trip is the sync engine's job. NON-CLOBBER: throws (typed)
walletAlreadyExists over a completed wallet (the sealed seed is NEVER
overwritten) and seedMismatch if a DIFFERENT phrase is restored over an
interrupted-create remnant — both are structural in the SDK, so the boot
fork (restore offered only from Welcome, i.e. walletExists() == false)
rarely sees them.
KEY RESIDUE (§10): the inbound word-list copy lives in Dart memory (which
cannot be zeroized) only until Rust owns it as a SeedSource; the same
minimal, documented residue as the outbound reveal — minimised, not
eliminated. No BIP39 passphrase ("25th word") is exposed here: the
reference app restores standard phrases (matching its passphrase-free
create path); a passphrase wallet is an expert case, deferred
(manager-flagged).
Implementation
@override
Future<WalletSession> restore(
List<String> mnemonicWords, {
DateTime? approximateCreationTime,
}) async {
restoreCount++;
lastRestoreWords = mnemonicWords;
lastRestoreCreationTime = approximateCreationTime;
if (holdRestore != null) await holdRestore!.future;
if (failRestore != null) throw failRestore!;
exists = true; // a restored wallet now exists on disk
return session;
}