Skip to content

feat(onboarding): import a wallet from a recovery phrase or secret key - #3091

Draft
aristidesstaffieri wants to merge 3 commits into
feature/import-additional-recovery-phrasefrom
feature/onboard-secret-key
Draft

aristidesstaffieri wants to merge 3 commits into
feature/import-additional-recovery-phrasefrom
feature/onboard-secret-key

Conversation

@aristidesstaffieri

@aristidesstaffieri aristidesstaffieri commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Onboarding "Import wallet" now takes a single input and works out whether it holds a 12 or 24-word recovery phrase or a Stellar secret key before asking for a password.

A secret key goes through a new RECOVER_ACCOUNT_FROM_SECRET_KEY message, which checks the key before wiping anything and stores one imported account with a secret-key source and no phrase. It runs behind the account-mutation lock. If storing fails after the wipe, the handler wipes again and clears APPLICATION_ID and KEY_ID, so the install is left fresh and the user can retry. The wipe and finish steps move out of recoverAccount into shared helpers. The phrase path keeps its handler and order, but the popup now lowercases the phrase, collapses whitespace and checks the checksum before sending it.

"Show recovery phrase" and its route are hidden for a wallet whose sources hold no phrase. Secret-key onboarding is counted as account_recovery.completed and account_recovery.failed with recovery_method: "secret_key". The e2e login and onboarding helpers follow the new screen order, and a new spec onboards with a secret key.

Stacked on #3084.

Why

Onboarding could only import a recovery phrase, and importing a secret key from Add account needs an existing wallet, so someone with only a secret key had no way to set up Freighter. The designs fold both imports into one screen.

Closes stellar/freighter-private#53

Screenshots / video

Before After

Test plan

  • Background tests for RECOVER_ACCOUNT_FROM_SECRET_KEY: one imported account with no phrase, invalid key leaves storage untouched, overwrite clears the old wallet, a failed store or source registration leaves a fresh install, lock membership
  • recoverAccount tests pass unchanged after the helper extraction
  • Popup tests for the import screen: phrase and key routing, invalid input stops before the password step, masking, double submit, stale history entry after reload, failure returns to the input with the error
  • Security hides "Show recovery phrase" and the backup phrase route redirects for a key-only wallet
  • tsc --noEmit clean, yarn test:ci has no failing tests
  • E2E: onboarding, login, accountSwitcher and translations-pt pass (Chrome)
  • Manual check in Firefox
  • CI green

Checklist

  • I have read CONTRIBUTING.md and this PR meets its requirements.
  • This PR has narrow scope and does not mix refactoring with feature changes.
  • This PR adds tests for the new functionality or fixes.
  • These changes have been tested in both Chrome and Firefox.
  • This PR includes relevant before and after screenshots for UI changes.
  • I took the time to review my own PR.

  Onboarding import takes a single input and detects whether it is a
  12/24-word recovery phrase or a Stellar secret key. A secret key creates
  a wallet with one imported account, a secret-key source and no recovery
  phrase, through a new RECOVER_ACCOUNT_FROM_SECRET_KEY handler that shares
  its wipe and finish steps with recoverAccount.

  "Show recovery phrase" is hidden for wallets with no phrase source. The
  e2e login and onboarding helpers follow the new screen order.
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-9455dadb65eb0b7b8850
Backend: sandbox (aristidesstaffieri). SDF collaborators only — install instructions in the release description.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed recovery can leave orphaned keystores, successful imports can preserve stale onboarding state that returns users to wallet creation, and pending imports can trigger navigation after unmount.

3 open issues
What changed in this PR

Adds secret-key onboarding alongside recovery-phrase import, using the existing account-source model introduced in #3084. Both import methods now share a single input screen before password creation.

Changes:

  • Adds validated, masked import input with password, importing, and ready steps.
  • Introduces a locked background secret-key recovery handler and shared recovery lifecycle helpers.
  • Hides recovery-phrase access for key-only wallets and updates metrics, translations, and tests.
File Description
extension/​src/​popup/​views/​Security/​index.tsx Hides phrase access for key-only wallets.
extension/​src/​popup/​views/​RecoverAccount/​styles.scss Styles unified input and status screens.
extension/​src/​popup/​views/​RecoverAccount/​index.tsx Implements the new import flow.
extension/​src/​popup/​views/​RecoverAccount/​ImportStatus.tsx Displays importing and ready states.
extension/​src/​popup/​views/​DisplayBackupPhrase/​index.tsx Redirects wallets without a phrase.
extension/​src/​popup/​views/​__tests__/​Security.test.tsx Tests phrase-access visibility and routing.
extension/​src/​popup/​views/​__tests__/​RecoverAccount.test.tsx Tests validation, navigation, submission, and errors.
extension/​src/​popup/​metrics/​authServices.ts Records secret-key recovery outcomes.
extension/​src/​popup/​metrics/​__tests__/​authServices.test.ts Tests recovery metric payloads.
extension/​src/​popup/​locales/​pt/​translation.json Adds Portuguese import text.
extension/​src/​popup/​locales/​en/​translation.json Adds English import text.
extension/​src/​popup/​helpers/​parseImportSecret.ts Classifies and validates import input.
extension/​src/​popup/​helpers/​hasRecoveryPhrase.ts Checks source types for phrase availability.
extension/​src/​popup/​helpers/​__tests__/​parseImportSecret.test.ts Tests phrase and key classification.
extension/​src/​popup/​helpers/​__tests__/​hasRecoveryPhrase.test.ts Tests phrase-availability checks.
extension/​src/​popup/​ducks/​accountServices.ts Adds secret-key recovery state handling.
extension/​src/​background/​messageListener/​popupMessageListener.ts Routes the new recovery message.
extension/​src/​background/​messageListener/​helpers/​recovery-lifecycle.ts Shares cleanup, activation, and response helpers.
extension/​src/​background/​messageListener/​handlers/​recoverAccountFromSecretKey.ts Implements key validation, storage, and rollback.
extension/​src/​background/​messageListener/​handlers/​recoverAccount.ts Uses shared recovery lifecycle helpers.
extension/​src/​background/​messageListener/​__tests__/​recoverAccountFromSecretKey.test.ts Tests key recovery and failure cleanup.
extension/​src/​background/​helpers/​account-mutation-lock.ts Serializes secret-key recovery with account mutations.
extension/​e2e-tests/​translations-pt.test.ts Updates Portuguese import-flow assertions.
extension/​e2e-tests/​onboarding.test.ts Covers the revised flow and secret-key onboarding.
extension/​e2e-tests/​helpers/​onboarding.ts Provides helpers for the new screen order.
extension/​e2e-tests/​helpers/​login.ts Uses the revised import helpers.
extension/​e2e-tests/​helpers/​login-pt.ts Updates Portuguese login sequencing.
@shared/​constants/​services.ts Defines the secret-key recovery message.
@shared/​api/​types/​message-request.ts Types the new recovery request.
@shared/​api/​internal.ts Adds the internal secret-key recovery API.

Review effort: Balanced

Comment on lines +252 to +254
readyTimer.current = setTimeout(() => {
navigateTo(ROUTES.recoverAccountSuccess, navigate);
}, READY_SCREEN_MS);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants