Restore a wallet
A restore rebuilds a wallet on-device from a recovery phrase the user already has, rather than generating a fresh seed. Restores are always local password wallets: the user supplies the recovery phrase plus a new password to encrypt the seed on this device. This guide covers restoring the seed, opting into server-assisted recovery to bring back funds and history, and observing that recovery run in the background so the user is not stuck on a loading screen.
Restore from a recovery phrase
Pass an existing mnemonic to useWalletRestore()’s restore (with
seedPassphrase too, if the original wallet used a BIP-39 passphrase). This
re-derives the same HD seed and encrypts it with the new password.
On its own, that rebuilds the keys but leaves the wallet empty: it does not know about any funds or past activity yet. To bring those back, opt into recovery.
Bring back balances and history
Set recoverState: true to turn on server-assisted recovery. The daemon walks
the seed’s addresses and queries the operator’s indexer to rebuild wallet state:
boarding outputs, VTXOs, and Lightning receive history. Without it, a restored
wallet starts at a zero balance even if the seed has funds.
recoveryWindow is an optional per-key-family address look-ahead: how many
unused addresses past the last used one the scan probes before giving up. Omit
it to let the daemon pick its own default, and only raise it if a wallet skipped
a large gap of addresses.
Restore in the background
The recovery scan can take minutes and reports no progress while it runs.
Rather than blocking on the whole scan, restore from useWalletRestore()
resolves (and flips restorePending back to false) as soon as the restored
wallet is usable, which happens well before the scan completes; phase from
useWallet() advances to 'restoring' while the wallet comes up and then to
'ready' at that point. The scan itself keeps running in the background,
tracked separately through useWalletRecovery(). The user lands in a working
wallet immediately while balances and history fill in behind them.
import { useWallet, useWalletRestore } from '@lightninglabs/wavelength-react';
function Restore({ mnemonic }: { mnemonic: string[] }) { const { phase } = useWallet(); const { restore, restorePending, restoreError } = useWalletRestore();
const handleRestore = async () => { // Resolves as soon as the wallet is usable; the recovery scan (if any) // keeps rebuilding balances and history in the background, tracked // through useWalletRecovery. await restore({ password: 'a-strong-password', mnemonic, recoverState: true, }); };
if (phase !== 'needsWallet') { return null; }
return ( <> <button onClick={handleRestore} disabled={restorePending}> Restore wallet </button> {restoreError && <p role="alert">{restoreError.message}</p>} </> );}Show recovery progress
useWalletRecovery() returns recovery, a discriminated union on status,
and acknowledge, which resets it to idle. Drive a banner off recovery so
the user knows the scan is still running, and report the outcome when it
settles.
import { useWalletRecovery } from '@lightninglabs/wavelength-react';
function RecoveryBanner() { const { recovery, acknowledge } = useWalletRecovery();
switch (recovery.status) { case 'idle': return null; case 'restoring': return ( <p role="status"> Restoring your balance and history. This can take a few minutes; your balance will fill in as it is found. </p> ); case 'done': return ( <p role="status"> Wallet restored. Recovered {recovery.result.recoveredVTXOs} VTXOs. <button onClick={acknowledge}>Dismiss</button> </p> ); case 'failed': return recovery.walletUsable ? ( <p role="alert"> Could not finish restoring your history, so your balance may be incomplete. The wallet is still usable. {recovery.error.message} <button onClick={acknowledge}>Dismiss</button> </p> ) : ( <p role="alert"> Restore failed: {recovery.error.message} <button onClick={acknowledge}>Dismiss</button> </p> ); }}recovery.status === 'failed' covers two distinct situations: a background
scan that errored on an already-usable wallet, or a restore that failed before
the wallet came up at all (for example the recovery phrase was rejected).
recovery.walletUsable disambiguates the two directly: true means the
background scan errored on an already-usable wallet, which works with
whatever the scan found before it failed; false means the restore itself
never got that far, so there is no wallet to fall back to and the failure
should be shown on the onboarding screen instead of a post-usability banner.
This applies even to an untracked restore (one whose returned promise you did
not await, or whose component unmounted while restorePending): the failure
still lands in recovery, so a screen that reads it after remounting picks it
back up.
The wallet is fully usable throughout the first case. Recovery only reads state, so sending and receiving during the scan is safe. Until it finishes, the balance is understated (a send may briefly report insufficient funds), while receiving is unaffected. If the scan fails, the wallet still works with whatever it found; the phrase is the source of truth, so the user can restore again later.