Skip to content

Implement a client-side transaction signing pipeline with hardware wallet support, signature aggregation, and structured error classification #758

Description

@Chucks1093

Summary

The current signing flow calls the wallet extension and handles all errors as a single generic failure. This issue builds a full signing pipeline that supports both software wallets (Freighter) and hardware wallets (Ledger via WebHID), aggregates multi-signature payloads where required, classifies every possible failure into a typed error with a user-actionable recovery hint, and implements retry logic with exponential backoff for transient hardware faults.

Scope

1. Signer abstraction

  • Define a Signer interface: { sign(xdr: string): Promise<string>, getPublicKey(): Promise<string>, type: 'software' | 'hardware' }
  • Implement FreighterSigner wrapping the Freighter browser extension API
  • Implement LedgerSigner wrapping the Stellar Ledger app via @ledgerhq/hw-transport-webhid
  • Expose a useSigner() hook that detects which signers are available and returns the active signer based on the user's wallet preference stored in localStorage

2. Ledger transport layer

  • Open a WebHID transport on first use and keep it alive for the session (re-open on disconnect)
  • Implement a connection health check: send a no-op APDU and verify the response before each signing request
  • If the Ledger is locked, return typed error LedgerLocked with hint 'Unlock your Ledger and open the Stellar app'
  • If the Stellar app is not open, return typed error LedgerAppNotOpen with hint 'Open the Stellar app on your Ledger'

3. Typed error classification

  • Define a discriminated union SigningError:
    • UserRejected — user dismissed the signing prompt
    • NetworkMismatch — wallet is on a different network than the app
    • LedgerLocked — Ledger device is locked
    • LedgerAppNotOpen — Stellar app not open on Ledger
    • LedgerTimeout — no response from Ledger within 30 seconds
    • TransactionTooLarge — XDR exceeds the signer's maximum size
    • SignerUnavailable — wallet extension not installed or WebHID not supported
  • Map every raw extension and WebHID error to one of the above types in a central error classifier

4. Retry pipeline with backoff

  • On LedgerTimeout: retry up to 3 times with 2s, 4s, 8s delays; show a 'Retrying…' indicator between attempts
  • On LedgerLocked or LedgerAppNotOpen: do not retry — immediately show the typed error hint and a 'Check Ledger' button that re-runs the health check
  • On UserRejected: do not retry — reset the UI immediately

5. Signing progress UI

  • Show a multi-step progress indicator during signing: 'Preparing transaction' → 'Waiting for signature' → 'Submitting to Stellar'
  • Each step transitions only when the underlying async operation completes
  • On error, the failed step turns red and displays the typed error hint inline

6. Unit tests

  • Unit tests: error classifier correctly maps every known raw error to its typed SigningError
  • Unit tests: retry logic fires correct delays and stops after 3 attempts
  • Unit tests: useSigner returns the correct signer based on localStorage preference and availability

Acceptance Criteria

  • FreighterSigner and LedgerSigner both implement the Signer interface
  • Every raw signing error mapped to a typed SigningError with a user-actionable hint
  • Ledger retried up to 3 times on timeout with exponential backoff
  • Multi-step progress indicator transitions correctly through the signing pipeline
  • LedgerLocked and LedgerAppNotOpen not retried — hint shown immediately
  • useSigner detects available signers and respects localStorage preference

ETA: 24 hours


Coordinate on Telegram

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third Campaign

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions