Skip to content

Import an account by address when its key cannot be derived from the phrase #398

Closed
1 of 3 issues completed

Description

@l-henri

Problem

Split out of #378 as tier 3, and it applies equally to Ready — see #377.

Measured on Mainnet, the phrase alone finds roughly half of real accounts:

family discoverable from the phrase alone
Braavos 72/150 (48.0%)
Ready / Argent 81/150 (54%)

(Corrected 2026-09-21: the Ready row originally read 64/150 (42.7%). That run missed the
constructor shapes Argent 0.4.0 uses. See the correction on #377.)

The remainder are not recoverable from the phrase alone, because their address is not a function
of the phrase. The reason differs by family:

  • Braavos: the deploy salt was not the public key, or the signing key was rotated after
    deployment.
  • Ready: mostly a guardian. The guardian is part of the constructor calldata, so it is part of
    the address, and it differs per account. 46 of the 69 undiscoverable Ready accounts in the
    sample are guardian-ed 0.4.0 accounts that reproduce exactly once the address is known.

No set of class hashes and no index changes that.

For those accounts there is exactly one thing the user can supply that closes the gap: the
account address
. They know it — it is in their other wallet, on an explorer, in a transaction
they sent.

Proposed solution

Let a user import an account by pasting its address, and prove control rather than assume it:

  1. derive the candidate keypairs from the phrase across the scanned index range;
  2. read the account's owner/signer public key on chain;
  3. import only if a derived public key matches what the chain reports.

The match is what makes this safe. Without it a user could import an address they do not
control, and the wallet would display a balance it can never move.

Why this is not just a text field

  • The public key is read differently per family and per class. Braavos answers
    get_public_key; Argent 0.3.x answers get_owner/get_signer. Argent 0.4 answers get_owner
    with the raw Stark key when get_owner_type reports a Stark signer. The wallet already relies
    on that pair in requireCompatibleArgentSigner, and any other owner type must be refused.
    (Corrected 2026-09-21: this line originally suggested 0.4 might return a GUID rather than the
    key. It does not, for a Stark owner.)
    Each family and version needs a reader that knows what
    it is reading, and a mismatch must fail closed rather than be compared as if it were a key.
  • A public-key match proves control only for a single-signer account. With Braavos 2FA, an
    Argent guardian, or any hardware co-signer, the phrase holds one of several required keys: the
    match succeeds and signing still fails. Detection has to happen before import — see Read-only imported accounts for signer configurations the phrase cannot satisfy #395. For
    Ready this is the common case, not the edge: most accounts this issue exists to recover have a
    guardian, and the wallet refuses to sign for any Argent account with one. Without Read-only imported accounts for signer configurations the phrase cannot satisfy #395 they can
    be found, but not usefully imported.
  • Signing still needs a supported class. Recovering the address is necessary but not
    sufficient; the wallet has to build a valid signature for whatever class the account runs.
    Accept classes from the known published set, never "whatever is deployed", or we sign for a
    contract nobody vetted.

Open questions

  • What does the user paste, and how forgiving is it? An address is 64 hex characters that all
    look alike. Mistyping one is silent — the lookup simply finds nothing. Worth considering paste
    from clipboard, an explorer URL, or a QR.
  • How far do we scan for the matching index? The key could be at any HD index. The recovery
    scan already has bounds (RECOVERY_SCAN_LIMITS); reuse them rather than inventing a second
    limit.
  • What do we say when nothing matches? "This phrase does not control that account" is the
    honest message, but it is also what a user sees when they mistyped, or when the account has an
    extra signer. Those need distinguishing.

Scope

Both families in one flow. The only per-family part is how the owner key is read; the rest — the
derivation, the comparison, the refusal — is shared. Building it for one family and retrofitting
the other is how the two drift.

Target release / milestone

TBC at triage.

Activity

  1. added
    enhancementNew feature or request
    securitySecurity-sensitive changes (keys, permissions, crypto)
    phase:7Phase 7 — Migration off Braavos/Ready
    on Sep 21, 2026
  2. derek-flossman commented on Oct 26, 2026

    @derek-flossman
    Collaborator

    Status update (AI-assisted triage by Claude), posted from @derek-flossman's account.

    Proposed design (needs owner sign-off; size M–L):

    • UX: in "Find your accounts" and Settings → Import, add "Can't find it? Paste the account address" (address or explorer URL; the network is shown).
    • Matching: read the live class, require a known family/class, read the owner key with the per-family reader (Argent get_owner + owner type = Starknet; Braavos first stark signer), derive keys over both ladders within the scan limits, and import only on an exact match.
    • Refuse: undeployed addresses, unknown classes, non-Stark owners; guardian or strong-signer accounts are shown view-only with the reason (Read-only imported accounts for signer configurations the phrase cannot satisfy #395). No match → "This phrase doesn't control that account", kept separate from "address not found on this network".
    • Security review: the stored address is no longer derived from the phrase, so every signing path must re-verify the live owner (confirmed for Argent; to confirm for Braavos). Independent review on the matching and refusal logic.

    Related: #743 and #756 are likely in this bucket. #734 (Xverse) is in PR #763.

  3. derek-flossman commented on Oct 27, 2026

    @derek-flossman
    Collaborator

    Fixed by #781, merged into release/v0.10.8. Ships in v0.10.8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestphase:7Phase 7 — Migration off Braavos/ReadysecuritySecurity-sensitive changes (keys, permissions, crypto)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions