Skip to content

feat: add signer capability architecture - #326

Merged
El-swaggerito merged 1 commit into
Axionvera:mainfrom
AndreyDelgado:feat--add-signer-capability-architecture
Jul 27, 2026
Merged

feat: add signer capability architecture#326
El-swaggerito merged 1 commit into
Axionvera:mainfrom
AndreyDelgado:feat--add-signer-capability-architecture

Conversation

@AndreyDelgado

Copy link
Copy Markdown
Contributor

Closes #269
feat: add signer capability architecture

Separate read-only accounts, local secret-bearing wallets, signing
capability, and transaction submission, and check that capability
before signing instead of discovering it via try/catch.

src/account/:

  • AccountAbstraction is now the discriminated union
    ReadOnlyAccount | SigningAccount, on the literal canSign field, so
    canSignTransaction(account) narrows signer from Signer | undefined
    to exactly Signer at compile time.
  • ExternalSignerAdapter: typed extension point for future hardware/
    mobile/browser signers (contract only, no adapter implemented).
    Reuses the SDK's existing capability standard (assertCapability,
    SDK_CAPABILITIES['signer.remote']) for "not supported yet" instead
    of a new error code.
  • ReadOnlyAccount.sign() now rejects with a typed PocketPayError
    (TX_SIGNER_MISSING) instead of a generic Error.

src/transactions/offline-preparation.ts:

  • signWithAccount() / safeSignWithAccount(): capability-checked entry
    point that verifies canSignTransaction(account) and that the signer
    matches the transaction's source account BEFORE calling sign().
    Delegates to the existing signTransactionWithSigner(), which is
    otherwise unchanged; signTransaction() and the raw-secret flow
    (sendXLM/sendAsset) are untouched.

src/errors/codes.ts:

Security fix (src/account/signer.ts):

  • LocalSigner no longer leaks its wrapped Keypair's raw secret bytes
    via JSON.stringify() or Node's console.log()/util.inspect() — both
    now surface only { publicKey }. Found while verifying the secret
    boundary for this change; predates it.

Tests (tests/signer-capability.test.ts, 20 tests):

  • Read-only account rejects signing with a typed error, checked before
    any signer is touched.
  • Local signer produces byte-identical output to the pre-existing
    signTransaction()/signTransactionWithSigner() paths (compatibility
    regression).
  • Wrong signer (public key mismatch) is rejected with a typed error.
  • Unsupported external adapter propagates UnsupportedFeatureError
    through signWithAccount() unchanged.
  • Read-only and signing accounts never expose secret material via
    properties or serialization.

Docs:

  • docs/signing-boundaries.md: which type carries secrets, how
    capability is checked before signing, what the model does and does
    NOT guarantee.
  • docs/adr/0004-signer-capability-architecture.md: design decisions,
    alternatives considered, and reconciliation with the concurrently
    merged capability error standard.
  • Updated docs/account-abstraction.md, docs/security.md,
    docs/adr/README.md, CHANGELOG.md.

No breaking changes: full existing test suite (706 tests) passes
unchanged on top of this change, rebased onto origin/main @ ebd75d1.

@El-swaggerito

Copy link
Copy Markdown
Contributor
\nThis PR is currently blocked by merge conflicts.\n\nPlease update the branch with the latest main branch and resolve the conflicts before it can be merged.

@AndreyDelgado
AndreyDelgado force-pushed the feat--add-signer-capability-architecture branch from d0a1377 to 268349c Compare July 27, 2026 23:17
@El-swaggerito
El-swaggerito merged commit 5a2b25e into Axionvera:main Jul 27, 2026
1 check passed
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.

Implement SDK signer capability and external signer architecture

2 participants