Restore a wallet

3 min read
Guides
Web SDK

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.

Restore.tsx
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.

RecoveryBanner.tsx
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.