Skip to content

Commit 3c08db6

Browse files
authored
fix(keyring-snap-bridge)!: re-forward keyring Snap ID in callbacks (#520)
We need this since the `snapId` is injected through `deserialize`. While "baking" the `snapId` in `SnapKeyring.ts` is working fine for the legacy keyring, this cannot work when we use standalone `SnapKeyringV2` instances (as the `KeyringController` uses a builder to create the instance AND THEN call `deserialize` with the `snapId`, making it impossible to auto-capture the `snapId` at build-time for those). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk because it is a **breaking** change to callback signatures (`addAccount`, `removeAccount`, `redirectUser`) that downstream consumers must update; incorrect wiring could mis-associate accounts/redirects with the wrong Snap. > > **Overview** > Reintroduces forwarding of `snapId` through the parent-level callbacks used by per-snap keyrings, so standalone `SnapKeyring` v2 instances (where `snapId` is only known after `deserialize`) can still pass the correct Snap identity to host integrations. > > Updates the v2 keyring bridge in `SnapKeyring.ts` to use the `snapId` coming from `deserialize` when invoking `addAccount`/`removeAccount`/`redirectUser`, and adjusts `SnapKeyringV1Callbacks` plus call sites in `SnapKeyringV1.ts` so `redirectUser` (and the account mutation callbacks) explicitly receive `snapId`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7fe1dc1. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent deb171f commit 3c08db6

3 files changed

Lines changed: 32 additions & 9 deletions

File tree

packages/keyring-snap-bridge/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **BREAKING:** Re-add `snapId` as part of the callbacks (`SnapKeyringCallbacks`) when building `SnapKeyring` v2 keyrings ([#520](https://github.com/MetaMask/accounts/pull/520))
13+
- Since the `snapId` is now injected via `deserialize` we cannot auto-capture it at build-time.
14+
1015
### Changed
1116

1217
- **BREAKING:** No longer use `snapId` as constructor parameter for `SnapKeyring` (v2) ([#519](https://github.com/MetaMask/accounts/pull/519))

packages/keyring-snap-bridge/src/SnapKeyring.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,32 +198,48 @@ export class SnapKeyring {
198198
messenger: this.#messenger,
199199
isAnyAccountTypeAllowed: this.#isAnyAccountTypeAllowed,
200200
callbacks: {
201+
// Optional callbacks that mutate accounts are responsible for keeping
202+
// #accountIndex in sync via these callbacks.
201203
onRegister: (id: AccountId): void => {
204+
// We can safely use `snapId` here because we create the keyring
205+
// instance right away.
202206
this.#accountIndex.set(id, snapId);
203207
},
204208
onUnregister: (id: AccountId): void => {
205209
this.#accountIndex.delete(id);
206210
},
211+
// Required callbacks:
212+
// NOTE: `keyringSnapId` and `snapId` are the same value. The `keyringSnapId` is coming from
213+
// the `deserialize` call right after.
207214
addAccount: async (
208215
address,
216+
keyringSnapId,
209217
handleUserInput,
210218
onceSaved,
211219
accountNameSuggestion,
212220
internalOptions,
213221
): Promise<void> =>
214222
this.#callbacks.addAccount(
215223
address,
216-
snapId,
224+
keyringSnapId,
217225
handleUserInput,
218226
onceSaved,
219227
accountNameSuggestion,
220228
internalOptions,
221229
),
222-
removeAccount: async (address, handleUserInput): Promise<void> =>
223-
this.#callbacks.removeAccount(address, snapId, handleUserInput),
230+
removeAccount: async (
231+
address,
232+
keyringSnapId,
233+
handleUserInput,
234+
): Promise<void> =>
235+
this.#callbacks.removeAccount(
236+
address,
237+
keyringSnapId,
238+
handleUserInput,
239+
),
224240
saveState: async (): Promise<void> => this.#callbacks.saveState(),
225-
redirectUser: async (url, message): Promise<void> =>
226-
this.#callbacks.redirectUser(snapId, url, message),
241+
redirectUser: async (id, url, message): Promise<void> =>
242+
this.#callbacks.redirectUser(id, url, message),
227243
assertAccountCanBeUsed: async (account): Promise<void> =>
228244
this.#assertAccountCanBeUsed(account),
229245
withLock: async <Result>(

packages/keyring-snap-bridge/src/SnapKeyringV1.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ export type SnapKeyringV1Callbacks = {
102102
/**
103103
* Called when a new account is being created (v1 event-driven flow).
104104
* Handles the user-approval dialog and account registration in the UI.
105-
* The `snapId` is baked in at construction time.
106105
*/
107106
addAccount: (
108107
address: string,
108+
snapId: SnapId,
109109
handleUserInput: (accepted: boolean) => Promise<void>,
110110
onceSaved: Promise<AccountId>,
111111
accountNameSuggestion?: string,
@@ -115,10 +115,10 @@ export type SnapKeyringV1Callbacks = {
115115
/**
116116
* Called when an account is being deleted.
117117
* Handles the user-approval dialog and account removal in the UI.
118-
* The `snapId` is baked in at construction time.
119118
*/
120119
removeAccount: (
121120
address: string,
121+
snapId: SnapId,
122122
handleUserInput: (accepted: boolean) => Promise<void>,
123123
) => Promise<void>;
124124

@@ -130,7 +130,7 @@ export type SnapKeyringV1Callbacks = {
130130
/**
131131
* Redirect the user to a URL after an async snap request.
132132
*/
133-
redirectUser: (url: string, message: string) => Promise<void>;
133+
redirectUser: (snapId: SnapId, url: string, message: string) => Promise<void>;
134134

135135
/**
136136
* Check global uniqueness (address + ID). Throws if the account cannot be used.
@@ -776,6 +776,7 @@ export class SnapKeyringV1 {
776776
// approve the account creation first.
777777
await this.#callbacks.addAccount(
778778
address,
779+
this.snapId,
779780
// This callback is passed to the MetaMask client, it will be called whenever
780781
// the end user will accept or not the account creation.
781782
async (accepted: boolean) => {
@@ -910,6 +911,7 @@ export class SnapKeyringV1 {
910911

911912
await this.#callbacks.removeAccount(
912913
normalizeAccountAddress(account),
914+
this.snapId,
913915
async (accepted) => {
914916
if (accepted) {
915917
await this.#callbacks.saveState();
@@ -1180,7 +1182,7 @@ export class SnapKeyringV1 {
11801182
if (url) {
11811183
this.#validateRedirectUrl(url);
11821184
}
1183-
await this.#callbacks.redirectUser(url, message);
1185+
await this.#callbacks.redirectUser(this.snapId, url, message);
11841186
}
11851187

11861188
/**

0 commit comments

Comments
 (0)