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
Contributor

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:

  • @metamask/messenger@1.2.0
  • @metamask/approval-controller@9.0.2
  • @metamask/snaps-utils@12.2.0
  • @metamask/permission-controller@12.3.0
  • ses@1.15.0
  • @metamask/base-controller@9.1.0
  • @metamask/json-rpc-engine@10.5.0
  • @metamask/controller-utils@11.20.0

View full report

@jeremytsng

Copy link
Copy Markdown
Contributor 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
Comment thread packages/snap/src/services/wallet/Wallet.ts Outdated
Comment thread packages/snap/CHANGELOG.md Outdated
@jeremytsng
jeremytsng force-pushed the feat/stellar-keyring-v2 branch from c267be1 to 99030f1 Compare July 3, 2026 06:14
Comment thread packages/snap/src/services/wallet/Wallet.test.ts Outdated
Comment thread packages/snap/src/handlers/keyring/keyring.ts Outdated
Comment thread packages/snap/src/handlers/keyring/api.ts Outdated
Comment thread packages/snap/src/handlers/keyring/keyring.ts Outdated
Comment thread package.json
@jeremytsng
jeremytsng force-pushed the feat/stellar-keyring-v2 branch 3 times, most recently from 74127c4 to cd42af5 Compare July 31, 2026 06:06
@jeremytsng
jeremytsng force-pushed the feat/stellar-keyring-v2 branch from cd42af5 to d2f45f1 Compare July 31, 2026 06:24
Comment thread package.json
},
"resolutions": {
"@metamask/snaps-sdk": "11.1.1",
"@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.

this is not a must for keyring API

return this.listAccounts();
}

async createAccount(options?: CreateAccountOptions): Promise<KeyringAccount> {

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 longer needed

can also remove account service create()


if (options.type === AccountCreationType.Bip44DeriveIndex) {
if (options.type === AccountCreationType.Bip44Discover) {
const discovered = await this.discoverAccounts(

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.

let do it in that way


let range;
// For discovery, only create the account if it has on-chain activity. No
// activity means we've reached the end of the discoverable accounts, so we
// return nothing and the client stops discovering.
if (
  options.type === AccountCreationType.Bip44Discover && 
   !await this.#hasOnChainActivity(
     [AppConfig.selectedNetwork],
      options.entropySource,
      options.groupIndex,
    );
) {
   return []
}

if (options.type === AccountCreationType.Bip44DeriveIndexRange) {
  range = options.range;
} else {
  // Bip44DeriveIndex | Bip44Discover — a single group index. Ranges are
  // inclusive, so `from` and `to` are the same.
  range = { from: options.groupIndex, to: options.groupIndex };
}

const createdAccounts = await this.#accountService.batchCreate({
  entropySource: options.entropySource,
  fromIndex: options.range.from,
  toIndex: options.range.to,
});

accounts = createdAccounts.map((account) =>
  this.#toKeyringAccount(account),
);

change discoverAccounts to #hasOnChainActivity


async getAccountBalances(
accountId: string,
assets: KnownCaip19AssetIdOrSlip44Id[],

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.

seem this change is no need?

async resolveAccountAddress(
scope: KnownCaip2ChainId,
request: ResolveAccountAddressJsonRpcRequest,
scope: CaipChainId,

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.

lets keep the type

}

/**
* Emits the account-created event to the wallet.

@stanleyyconsensys stanleyyconsensys Jul 31, 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.

change #handleSubmitRequest to
async submitRequest(request: KeyringRequest): Promise<KeyringResponse>

and remove the old submitRequest

Comment on lines -128 to -132
this.#logger.debug('Keyring request handled', {
origin,
method: request.method,
result: keyringRequestResult,
});

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.

i need this debug

}

async getAccounts(): Promise<KeyringAccount[]> {
return this.listAccounts();

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.

dont use the org listAccounts, just move the listAccounts logic here

@@ -39,6 +43,9 @@ const metamaskPermissions = new Set([
KeyringRpcMethod.ListAccountAssets,
KeyringRpcMethod.ResolveAccountAddress,
KeyringRpcMethod.SetSelectedAccounts,

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.

"allowedOrigins": ["https://portfolio.metamask.io"]
"allowedOrigins": ["https://portfolio.metamask.io"],
"capabilities": {
"scopes": ["stellar:pubnet", "stellar:testnet"],

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 testnet enable

/**
* Emits the account-created event to the wallet.
* This triggers the wallet to prompt the user to add the account.
* If the user accepts, the account is added; if the user rejects, an error is thrown.

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.

account delete no longer have emitEvent

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