Skip to content

feat: update to keyring v2#645

Open
hmalik88 wants to merge 14 commits into
mainfrom
hm/add-v2-methods
Open

feat: update to keyring v2#645
hmalik88 wants to merge 14 commits into
mainfrom
hm/add-v2-methods

Conversation

@hmalik88

@hmalik88 hmalik88 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates the Bitcoin snap to the v2 Keyring API and declares the corresponding capabilities in the manifest. Also removes all v1-only keyring methods and replaces the manual routing table with the v2 dispatcher.

  • Rewrites KeyringHandler to implement KeyringSnapRpc (from @metamask/keyring-api/v2): adds getAccounts, getAccount, createAccounts, exportAccount, getAccountBalances, getAccountAssets, getAccountTransactions, deleteAccount, submitRequest, setSelectedAccounts, and resolveAccountAddress
  • Removes v1-only flows (createAccount, discoverAccounts, filterAccountChains, updateAccount) and stops emitting AccountCreated / AccountDeleted events
  • Folds bip44:discover into createAccounts: calls AccountUseCases.discover(), deletes the account from state if no transactions are found, and returns [] to signal end-of-discovery
  • Adds exportAccount with WIF private-key export (Base58Check, version byte 0x80/0xEF); uses is() not assert() to prevent the private key from appearing in a StructError message on encoding failure
  • Reads SUPPORTED_SCOPES directly from snap.manifest.json capabilities block (same pattern as Solana snap) instead of hardcoding the network
  • Switches handleKeyringRequest to the v2 dispatcher (@metamask/keyring-snap-sdk/v2)
  • Adds endowment:keyring.capabilities to snap.manifest.json: scopes, privateKey.exportFormats, and bip44 derive/discover flags
  • Sets module: "ESNext" and moduleResolution: "bundler" in packages/snap/tsconfig.json so /v2 subpath exports resolve correctly
  • Bumps @metamask/keyring-api → ^23.5.0, keyring-snap-sdk → ^9.2.0, snaps-sdk → ^11.2.0, snaps-cli → ^8.4.1, snaps-jest → ^10.2.0
  • Updates KeyringHandler.test.ts: removes v1-only describe blocks, adds exportAccount and bip44:discover coverage, mocks wif module, adds beforeEach(() => jest.resetAllMocks()) to fix mock-call accumulation between tests

References

N/A

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

Note

High Risk
Breaking keyring RPC surface and account-creation/discovery semantics for MetaMask clients, plus new private-key export—security-sensitive changes across auth/key material handling.

Overview
Migrates the Bitcoin wallet snap to Keyring API v2, replacing the in-snap RPC router with handleKeyringRequest from @metamask/keyring-snap-sdk/v2 and v2 method names (keyring_createAccounts, keyring_getAccounts, etc.).

KeyringHandler now implements KeyringSnapRpc: account lifecycle goes through createAccounts (BIP-44 derive path/index/range and bip44:discover folded in—inactive discovered accounts are deleted and [] is returned). Removed v1-only createAccount, discoverAccounts, filterAccountChains, and updateAccount. Renamed list-style APIs to getAccounts, getAccountAssets, getAccountTransactions. Adds exportAccount with WIF (base58) export and guards so private keys do not leak in error messages.

Manifest & mapping: endowment:keyring gains capabilities (scopes, privateKey.exportFormats, bip44 derive/discover). Accounts are marked exportable: true. Supported discovery scope is read from the manifest instead of being hardcoded.

Deps & tooling: Bumps keyring-api, keyring-snap-sdk, snaps-sdk/cli/jest; snap tsconfig uses ESNext/bundler for /v2 imports. Integration/unit tests updated for plural create results, new RPC names, and v2 error shapes.

Reviewed by Cursor Bugbot for commit e698816. Bugbot is set up for automated code reviews on this repo. Configure here.

@socket-security

socket-security Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​metamask/​keyring-api@​23.5.01001001009850
Updated@​metamask/​keyring-snap-sdk@​8.0.0 ⏵ 9.2.099 +110075 +197 +2100
Updated@​metamask/​snaps-cli@​8.3.0 ⏵ 8.4.176100100 +187 +1100
Updated@​metamask/​snaps-jest@​9.8.0 ⏵ 10.2.076 +110010097 +2100
Updated@​metamask/​slip44@​4.3.0 ⏵ 4.5.0100 +110099 +192 +8100
Updatedprettier@​3.5.3 ⏵ 3.9.499 +110097 +198100

View full report

@socket-security

socket-security Bot commented Jul 10, 2026

Copy link
Copy Markdown

Warning

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Warn Low
Potential code anomaly (AI signal): npm @metamask/snaps-simulation is 62.0% likely to have a medium risk anomaly

Notes: The fragment is primarily an orchestration layer for installing/executing MetaMask Snaps and registering internal RPC/action handlers. The main high-impact risk signals are (1) direct execution of snap source code obtained from fetchSnap(), and (2) sensitive key material derivation (mnemonic/seed) from options.secretRecoveryPhrase in a clearly labeled 'withKeyringV2Unsafe' handler that passes mnemonic+seed into an externally provided operation callback. Additionally, SnapController:getSnapFile forwards caller-controlled path/encoding to getSnapFile(auxiliaryFiles, ...)—which could enable unintended file access depending on that helper’s validation. No clear malicious exfiltration or obfuscation is visible in this snippet alone.

Confidence: 0.62

Severity: 0.58

From: ?npm/@metamask/snaps-jest@10.2.0npm/@metamask/snaps-simulation@4.3.0

ℹ Read more on: This package | This alert | What is an AI-detected potential code anomaly?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: An AI system found a low-risk anomaly in this package. It may still be fine to use, but you should check that it is safe before proceeding.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@metamask/snaps-simulation@4.3.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Ignoring alerts on:

  • @ethersproject/web@5.8.0
  • @metamask/snaps-controllers@21.0.0
  • ses@2.2.0

View full report

@hmalik88

Copy link
Copy Markdown
Contributor Author

@SocketSecurity ignore npm/@metamask/snaps-controllers@21.0.0

@hmalik88

Copy link
Copy Markdown
Contributor Author

@SocketSecurity ignore npm/ses@2.2.0

@hmalik88

Copy link
Copy Markdown
Contributor Author

@SocketSecurity ignore npm/@ethersproject/web@5.8.0

@hmalik88 hmalik88 marked this pull request as ready for review July 13, 2026 08:36
@hmalik88 hmalik88 requested a review from a team as a code owner July 13, 2026 08:36

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 54d5d1f. Configure here.

Comment thread packages/snap/src/handlers/KeyringHandler.ts Outdated
Comment thread packages/snap/src/handlers/KeyringHandler.ts Outdated

expect(response).toRespondWithError({
code: -32000,
code: -32603,

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.

What is this code exactly? Would it be worth extracting into a constant seeing it's reused a lot throughout the file?
I know this wasn't in a constant before, so feel free to ignore!

accounts[expectedAddress] = response.response.result as KeyringAccount;
accounts[expectedAddress] = (
response.response.result as KeyringAccount[]
)[0]!;

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.

Is the non null assertion here "legal"? Can we maybe find a way to explicitly go around it?

response.response as { result: KeyringAccount }
).result;
const { result } = response.response as { result: KeyringAccount[] };
const account = result[0]!;

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.

same here

writeFileSync(
join(__dirname, '../locales/en.json'),
JSON.stringify(englishLocale, null, 2),
`${JSON.stringify(englishLocale, null, 2)}\n`,

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.

Why this change?

Comment on lines +8 to +9
"module": "ESNext",
"moduleResolution": "bundler",

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.

Is this related to / needed for V2 migration?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yup! needed to make a similar change on the sol snap

Comment on lines +117 to +121
if (options.type === AccountCreationType.Bip44DerivePath) {
const parts = (options.derivationPath as string).split('/');
const purpose = parts[1]?.replace("'", '');
const coinType = parts[2]?.replace("'", '');
const accountIndex = parts[3]?.replace("'", '');

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.

I think this could be a bit dangerous and brittle. We're pulling out purpose, coinType, and accountIndex purely by position, but the validation struct only guarantees the shape of each segment, not how many there are.

It seems we had extractAccountIndex / extractAddressType doing this with before, should we potentially bring those back (or a similar single, validated parser) instead of parsing inline?

// validate default address type is P2WPKH just to be sure
if (resolvedAddressType !== 'p2wpkh') {

if (coinType !== '0' && coinType !== '1') {

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.

Nit 😄 could pull the supported types into a constant to make the check read a bit nicer:

const SUPPORTED_COIN_TYPES = ['0', '1'];
if (!SUPPORTED_COIN_TYPES.includes(coinType)) {
  throw new FormatError(
    'Unsupported coin type: only coin type 0 (mainnet) and 1 (regtest) are supported',
  );
}

const traceName = 'Create Bitcoin Accounts Batch';
const traceStarted = await this.#snapClient.startTrace(traceName);
// Only mainnet is supported for v2 account creation.
const network = scopeToNetwork[SUPPORTED_SCOPES[0] ?? BtcScope.Mainnet];

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.

Can SUPPORTED_SCOPES[0] be dangerous if the manifest order changes?

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.

2 participants