Skip to content

feat(keyring-snap-bridge): moving v1 SnapKeyring implementation to SnapKeyringV1#507

Merged
ccharly merged 41 commits intomainfrom
cc/snap-keyring-v2
Apr 10, 2026
Merged

feat(keyring-snap-bridge): moving v1 SnapKeyring implementation to SnapKeyringV1#507
ccharly merged 41 commits intomainfrom
cc/snap-keyring-v2

Conversation

@ccharly
Copy link
Copy Markdown
Contributor

@ccharly ccharly commented Apr 10, 2026

Following-up on:

The previous PR moved the v2 implementation into a proper class (per-Snaps), to slowly extract the monolith logic into more logical piece that we will re-use when splitting the existing SnapKeyring.

This new PR now also move the "v1 logic" out of it and move it in a dedicated keyring class (per-Snaps). The SnapKeyringV2 now extends this v1, making the SnapKeyringV2 compatible with the v2 interface AND also supports the old one (allowing us to do a progressive update with the new incoming withKeyringV2).

The remaining part on the SnapKeyring acts now as a coordinator/proxy around those new keyring objects. In the future, we would want to move this in a dedicated service (e.g the SnapAccountService) and each keyrings would live as usual, on the KeyringController, see:

Screenshot 2026-04-09 at 14 49 23

Note

Medium Risk
Refactors core Snap keyring message routing and request/account lifecycle handling into new per-snap classes, which could affect account events and pending request resolution across snaps. Adds teardown behavior (destroy) that rejects in-flight async requests when a snap keyring is removed or state is reloaded.

Overview
Refactors Snap keyring implementation into per-snap classes. Introduces SnapKeyringV1 (legacy event-driven flow + request lifecycle) and makes SnapKeyringV2 extend it, so v2 batch APIs and v1 events share the same per-snap registry/client.

SnapKeyring is simplified into a coordinator/proxy: it lazily creates per-snap keyrings (only on AccountCreated), routes signing/requests to the owning snap keyring, and now throws when receiving messages for unknown snap keyrings. It also adds explicit per-snap teardown via destroy() (used on last-account removal and before deserialize) to reject pending async requests, and serializes createAccounts across snaps with a global mutex.

Tests are updated to reflect the new unknown-snap error behavior and to assert pending requests are rejected when the last account/keyring is removed; SnapKeyringV1 is exported from index.ts and the changelog is updated.

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

@ccharly ccharly force-pushed the cc/snap-keyring-v2 branch from e8850d7 to f9674dc Compare April 10, 2026 14:31
@ccharly ccharly marked this pull request as ready for review April 10, 2026 15:56
@ccharly ccharly requested a review from a team as a code owner April 10, 2026 15:56
@ccharly
Copy link
Copy Markdown
Contributor Author

ccharly commented Apr 10, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown

Preview builds have been published. See these instructions (from the core monorepo) for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-api": "1.0.1-5c366be",
  "@metamask-previews/hw-wallet-sdk": "0.8.0-5c366be",
  "@metamask-previews/keyring-api": "22.0.0-5c366be",
  "@metamask-previews/eth-hd-keyring": "13.1.1-5c366be",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.3.1-5c366be",
  "@metamask-previews/eth-money-keyring": "2.0.0-5c366be",
  "@metamask-previews/eth-qr-keyring": "1.1.0-5c366be",
  "@metamask-previews/eth-simple-keyring": "11.1.2-5c366be",
  "@metamask-previews/eth-trezor-keyring": "9.1.1-5c366be",
  "@metamask-previews/keyring-internal-api": "10.0.1-5c366be",
  "@metamask-previews/keyring-internal-snap-client": "9.0.1-5c366be",
  "@metamask-previews/keyring-sdk": "1.2.0-5c366be",
  "@metamask-previews/eth-snap-keyring": "20.0.0-5c366be",
  "@metamask-previews/keyring-snap-client": "8.2.1-5c366be",
  "@metamask-previews/keyring-snap-sdk": "8.0.0-5c366be",
  "@metamask-previews/keyring-utils": "3.2.0-5c366be"
}

@ccharly ccharly enabled auto-merge April 10, 2026 21:21
Copy link
Copy Markdown

@cursor cursor bot left a comment

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 1 potential issue.

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 32951e7. Configure here.

// error dialogs).
onceSaved.reject(error);
},
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty keyring not cleaned up after async saveState failure

Low Severity

In #handleAccountCreated, when saveState() fails asynchronously, the catch handler removes the account from the registry and calls onUnregister, but the now-empty keyring is never removed from SnapKeyring.#snapKeyrings. The old code called #deleteAccount which included #removeSnapKeyringIfEmpty. The finally block in SnapKeyring.handleKeyringSnapMessage has already executed by the time this fire-and-forget catch runs, so the empty keyring lingers until the next deserialize.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 32951e7. Configure here.

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.

saveState should never really fail. And handling the keyring removal inside the keyring itself (in this async flow) would be a real challenge.

IMO, that's acceptable, since and empty keyring has no real bad side-effects!

@ccharly ccharly added this pull request to the merge queue Apr 10, 2026
Merged via the queue into main with commit 9e96b17 Apr 10, 2026
39 checks passed
@ccharly ccharly deleted the cc/snap-keyring-v2 branch April 10, 2026 22:21
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