Skip to content

Implement Keyring API v2#151

Open
jeremytsng wants to merge 1 commit into
mainfrom
feat/stellar-keyring-v2
Open

Implement Keyring API v2#151
jeremytsng wants to merge 1 commit into
mainfrom
feat/stellar-keyring-v2

Conversation

@jeremytsng

@jeremytsng jeremytsng commented Jul 2, 2026

Copy link
Copy Markdown

Explanation

Migrates the Stellar snap's keyring handler onto the remaining MetaMask Keyring API v2 surface and adds private-key export:

  • Adds exportAccount — exports the raw ed25519 secret seed as hexadecimal (default) or base58, resolved through the existing AccountResolver. The exported value is validated with a boolean guard rather than an asserting one so a validation failure can never leak the key into an error message; the same value is redacted before it reaches the handler's debug log.
  • Adds getAccounts (delegates to listAccounts) and makes getAccount throw for an unknown id instead of returning undefinedthis is a breaking change for any caller relying on the old undefined-on-missing behavior.
  • Switches the keyring dispatcher to @metamask/keyring-snap-sdk/v2 and implements KeyringRpc instead of Keyring.
  • Declares the v2 RPC permissions (GetAccounts, ExportAccount for the MetaMask origin only) and manifest endowment:keyring.capabilities (scopes, export formats, bip44).
  • Enables moduleResolution: bundler so the /v2 subpath types resolve. No platformVersion change was needed. One dependency-resolution bump was required: endowment:keyring.capabilities validation was only added in @metamask/snaps-utils@12.2.0, one minor above what snaps-cli's existing range resolved to, so an exact resolutions pin was added (a caret range resolves to a patch release that pulls in an unrelated major bump elsewhere in the graph, which the exact pin avoids).

Exported keys are raw 32-byte seed bytes (hex/base58), not the Stellar S… secret-seed format — that StrKey encoding isn't expressible through the v2 exportAccount contract (which only supports hexadecimal/base58 over raw bytes), so this export is not directly importable into wallets like Freighter or Lobstr. Flagging this as a known limitation rather than a gap in this PR; a StrKey-compatible export would need a separate, non-keyring-v2 mechanism.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

@jeremytsng jeremytsng force-pushed the feat/stellar-keyring-v2 branch from 529fdab to 00fcbf1 Compare July 2, 2026 11:32
@jeremytsng jeremytsng changed the title Implement Keyring API v2 (private-key export) Implement Keyring API v2 Jul 2, 2026
@jeremytsng jeremytsng marked this pull request as ready for review July 2, 2026 11:46
@jeremytsng jeremytsng requested a review from a team as a code owner July 2, 2026 11:46
@jeremytsng jeremytsng force-pushed the feat/stellar-keyring-v2 branch from 00fcbf1 to 03fdea9 Compare July 2, 2026 11:50
@socket-security

socket-security Bot commented Jul 2, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

Ignoring alerts on:

  • @nodable/entities@2.2.0
  • is-unsafe@1.0.1
  • path-expression-matcher@1.6.1
  • xml-naming@0.1.0
  • anynum@1.0.1
  • @scure/base@1.2.6
  • @metamask/messenger@1.2.0
  • @metamask/approval-controller@9.0.2
  • @metamask/snaps-utils@12.2.0
  • @metamask/permission-controller@12.3.0
  • fast-xml-parser@5.9.3
  • ses@1.15.0
  • @metamask/base-controller@9.1.0
  • @metamask/json-rpc-engine@10.5.0
  • @metamask/controller-utils@11.20.0
  • fast-xml-builder@1.2.0
  • strnum@2.4.1

View full report

@jeremytsng

Copy link
Copy Markdown
Author

@SocketSecurity ignore-all

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.

Pull request overview

This PR migrates the Stellar snap’s keyring handler to the remaining MetaMask Keyring API v2 surface, adding support for getAccounts and secure private-key export via exportAccount, along with required permissions/manifest updates.

Changes:

  • Implemented Keyring API v2 semantics in the keyring handler (KeyringRpc), including getAccounts, exportAccount, and updated getAccount missing-id behavior.
  • Added wallet-level raw seed export helpers and tests for hex/base58 export round-trips.
  • Updated snap permissions/manifest and dependency resolution to support v2 types and keyring capabilities.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
yarn.lock Updates lockfile to reflect new/updated dependency graph needed for v2 support.
packages/snap/tsconfig.json Enables moduleResolution: bundler (and module: preserve) to resolve /v2 subpath types.
packages/snap/src/services/wallet/Wallet.ts Adds exportKey for raw seed export in hex/base58.
packages/snap/src/services/wallet/Wallet.test.ts Adds tests validating exportKey output and round-trip behavior.
packages/snap/src/permissions.ts Adds v2 RPC method permissions (GetAccounts, ExportAccount) and updates selected method constants.
packages/snap/src/handlers/keyring/keyring.ts Migrates dispatcher to @metamask/keyring-snap-sdk/v2, implements KeyringRpc, adds getAccounts + exportAccount, and redacts export results from debug logs.
packages/snap/src/handlers/keyring/keyring.test.ts Updates tests for v2 dispatcher usage, new semantics, export behavior, and log redaction.
packages/snap/src/handlers/keyring/api.ts Adds Base58Struct and handler-level request struct for exportAccount.
packages/snap/src/handlers/keyring/api.test.ts Adds tests for Base58Struct.
packages/snap/src/context.ts Wires AccountResolver into KeyringHandler construction.
packages/snap/snap.manifest.json Declares keyring capabilities for scopes, private-key export formats, and bip44 features.
packages/snap/package.json Adds @scure/base dependency for base58 encoding.
packages/snap/CHANGELOG.md Documents added v2 functionality and the getAccount breaking change.
package.json Pins @metamask/snaps-utils via resolutions to support capabilities validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/snap/src/handlers/keyring/keyring.ts Outdated
@jeremytsng jeremytsng force-pushed the feat/stellar-keyring-v2 branch from 03fdea9 to c267be1 Compare July 2, 2026 17:30
* @returns The encoded raw secret seed.
*/
exportKey(encoding: PrivateKeyEncoding): string {
const rawSeed = bufferToUint8Array(this.#signer.rawSecretKey());

@stanleyyconsensys stanleyyconsensys Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

add try catch with new custom error to mask the root cause

Comment thread packages/snap/CHANGELOG.md Outdated

### Added

- Private-key export (`keyring_exportAccount`) for Stellar accounts (hex/base58).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no need to add any change log

Migrates the keyring handler onto the remaining Keyring API v2
surface and adds private-key export.

- Add exportAccount: exports the raw ed25519 secret seed as
  hexadecimal (default) or base58, resolved through the existing
  AccountResolver. The exported value is validated with a boolean
  guard rather than an asserting one so a validation failure can
  never leak the key into an error message, and the same value is
  redacted before it reaches the handler's debug log.
- Add getAccounts (delegates to listAccounts) and make getAccount
  throw for an unknown id instead of returning undefined, matching
  the v2 contract. This is a breaking change for any caller relying
  on the old undefined-on-missing behavior.
- Switch the keyring dispatcher to the v2 handleKeyringRequest and
  implement KeyringRpc instead of Keyring.
- Grant the v2 RPC permissions (GetAccounts, ExportAccount for the
  MetaMask origin only) and declare endowment:keyring.capabilities
  in the manifest (scopes, export formats, bip44).
- Enable moduleResolution: bundler so the /v2 subpath types resolve.
- Pin @metamask/snaps-utils to an exact 12.2.0 in resolutions:
  endowment:keyring.capabilities validation was only added in that
  version, one minor above what the existing snaps-cli range
  resolved to. Pinned exact rather than caret because ^12.2.0
  resolves to 12.2.1, which bumps @metamask/permission-controller to
  a 13.x major and drags in an unrelated ethersproject/
  controller-utils subtree; 12.2.0 exact avoids that while still
  satisfying snaps-cli's own ^12.1.0 range.

Exported keys are raw 32-byte seed bytes, not the Stellar S...
secret-seed format - that StrKey encoding isn't expressible through
the v2 exportAccount contract, so this export is not directly
importable into wallets like Freighter or Lobstr. A StrKey-compatible
export would need a separate, non-keyring-v2 mechanism.
@jeremytsng jeremytsng force-pushed the feat/stellar-keyring-v2 branch from c267be1 to 99030f1 Compare July 3, 2026 06:14
@@ -1,4 +1,6 @@
import { PrivateKeyEncoding } from '@metamask/keyring-api/v2';
import { hexToBytes } from '@metamask/utils';
import { base58 } from '@scure/base';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

may consider using the same package as solona if we need base58

result: keyringRequestResult,
// SECURITY: never debug-log the exportAccount result — it carries the
// raw private key. Every other method's result is safe to log as-is.
result:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

just remove this debug, as we should no longer needed

* exported private key — NEVER as an asserting validator (a StructError would
* embed the key value in its message).
*/
export const Base58Struct = define<string>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please consider the same validation as solana if we need base58

validateRequest(accountId, GetAccountRequestStruct);
const account = await this.#accountService.findById(accountId);
return account ? this.#toKeyringAccount(account) : undefined;
const { account } = await this.#accountService.resolveAccount({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

resolveAccount is slow , becoz it will compare the wallet address

Comment thread package.json
},
"resolutions": {
"@metamask/snaps-sdk": "^11.1.0",
"@metamask/snaps-utils": "12.2.0",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why we need this bump?

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.

3 participants