Skip to content

Commit ab58df0

Browse files
authored
feat!: move SnapPlatformWatcher in SnapAccountService + add :ensureReady and use it in MultichainAccountService (MetaMask#8715)
## Explanation Now that we have a central place for Snap accounts logic, we can move the `SnapPlatformWatcher` so we can use it (with an action) on the `MultichainAccountService`. Also adding a new `:ensureReady(snapId)` action that will be improved in the future to make sure everything is ready when interacting with an account management Snap (migration, platform is ready, keyring is ready, etc...) ## 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](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Introduces breaking messenger/API changes around Snap readiness (new required `SnapAccountService:ensureReady`, removed `MultichainAccountService:ensureCanUseSnapPlatform`), which can break consumers and affect Snap-based account operations if integration is incomplete. > > **Overview** > **Moves Snap platform readiness checks out of `@metamask/multichain-account-service` and into `@metamask/snap-account-service`.** `SnapAccountService` now owns `SnapPlatformWatcher`, adds a new `SnapAccountService:ensureReady(snapId)` messenger action, and exposes config for onboarding gating and Snap-keyring wait timeout. > > **Updates multichain Snap providers to use the new readiness flow.** `MultichainAccountService` drops its `ensureCanUseSnapPlatform` method/action and related config/types, and `SnapAccountProvider.ensureCanUseSnapPlatform()` is renamed to `ensureReady()` and now calls `SnapAccountService:ensureReady`. Tests, messenger policies, dependency graph, and package deps/tsconfig references are updated accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c41e954. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent a83b774 commit ab58df0

30 files changed

Lines changed: 445 additions & 240 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ linkStyle default opacity:0.5
388388
multichain_account_service --> base_controller;
389389
multichain_account_service --> keyring_controller;
390390
multichain_account_service --> messenger;
391+
multichain_account_service --> snap_account_service;
391392
multichain_account_service --> controller_utils;
392393
multichain_api_middleware --> accounts_controller;
393394
multichain_api_middleware --> chain_agnostic_permission;
@@ -507,6 +508,7 @@ linkStyle default opacity:0.5
507508
signature_controller --> logging_controller;
508509
signature_controller --> messenger;
509510
signature_controller --> network_controller;
511+
snap_account_service --> keyring_controller;
510512
snap_account_service --> messenger;
511513
social_controllers --> base_controller;
512514
social_controllers --> base_data_service;

eslint-suppressions.json

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,19 +1299,14 @@
12991299
"count": 2
13001300
}
13011301
},
1302-
"packages/multichain-account-service/src/snaps/SnapPlatformWatcher.ts": {
1303-
"no-restricted-syntax": {
1304-
"count": 2
1305-
}
1306-
},
13071302
"packages/multichain-account-service/src/tests/accounts.ts": {
13081303
"@typescript-eslint/explicit-function-return-type": {
13091304
"count": 7
13101305
}
13111306
},
13121307
"packages/multichain-account-service/src/tests/messenger.ts": {
13131308
"no-restricted-syntax": {
1314-
"count": 2
1309+
"count": 1
13151310
}
13161311
},
13171312
"packages/multichain-api-middleware/src/handlers/types.ts": {
@@ -2172,6 +2167,21 @@
21722167
"count": 2
21732168
}
21742169
},
2170+
"packages/snap-account-service/src/SnapAccountService.test.ts": {
2171+
"no-restricted-syntax": {
2172+
"count": 2
2173+
}
2174+
},
2175+
"packages/snap-account-service/src/SnapPlatformWatcher.test.ts": {
2176+
"no-restricted-syntax": {
2177+
"count": 2
2178+
}
2179+
},
2180+
"packages/snap-account-service/src/SnapPlatformWatcher.ts": {
2181+
"no-restricted-syntax": {
2182+
"count": 2
2183+
}
2184+
},
21752185
"packages/subscription-controller/src/SubscriptionController.test.ts": {
21762186
"no-restricted-syntax": {
21772187
"count": 2

packages/multichain-account-service/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Changed
11+
12+
- **BREAKING:** The service messenger now requires the `SnapAccountService:ensureReady` action to be declared ([#8715](https://github.com/MetaMask/core/pull/8715))
13+
- **BREAKING:** Delegate Snap platform readiness to `@metamask/snap-account-service` ([#8715](https://github.com/MetaMask/core/pull/8715))
14+
- Removed `MultichainAccountService.ensureCanUseSnapPlatform()` method and the corresponding `MultichainAccountService:ensureCanUseSnapPlatform` messenger action.
15+
- Removed the `MultichainAccountServiceEnsureCanUseSnapPlatformAction` type export.
16+
- Removed `MultichainAccountServiceOptions.ensureOnboardingComplete`. Configure it via `SnapAccountService`'s `config.snapPlatformWatcher.ensureOnboardingComplete` instead.
17+
- Removed `MultichainAccountServiceConfig.snapPlatformWatcher` and the `SnapPlatformWatcherConfig` type export. Configure the keyring-wait timeout via `SnapAccountService`'s `config.snapPlatformWatcher.snapKeyringWaitTimeoutMs` instead.
18+
- The service messenger no longer needs `SnapController:getState` or `SnapController:stateChange`.
19+
- **BREAKING:** Rename `SnapAccountProvider.ensureCanUseSnapPlatform()` to `ensureReady()` ([#8715](https://github.com/MetaMask/core/pull/8715))
20+
1021
## [9.0.0]
1122

1223
### Added

packages/multichain-account-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"@metamask/keyring-snap-client": "^9.0.2",
6565
"@metamask/keyring-utils": "^3.2.1",
6666
"@metamask/messenger": "^1.2.0",
67+
"@metamask/snap-account-service": "^0.0.0",
6768
"@metamask/snaps-controllers": "^19.0.0",
6869
"@metamask/snaps-sdk": "^11.0.0",
6970
"@metamask/snaps-utils": "^12.1.2",

packages/multichain-account-service/src/MultichainAccountService-method-action-types.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ export type MultichainAccountServiceResyncAccountsAction = {
3434
handler: MultichainAccountService['resyncAccounts'];
3535
};
3636

37-
export type MultichainAccountServiceEnsureCanUseSnapPlatformAction = {
38-
type: `MultichainAccountService:ensureCanUseSnapPlatform`;
39-
handler: MultichainAccountService['ensureCanUseSnapPlatform'];
40-
};
41-
4237
/**
4338
* Gets a reference to the multichain account wallet matching this entropy source.
4439
*
@@ -201,7 +196,6 @@ export type MultichainAccountServiceAlignWalletAction = {
201196
export type MultichainAccountServiceMethodActions =
202197
| MultichainAccountServiceInitAction
203198
| MultichainAccountServiceResyncAccountsAction
204-
| MultichainAccountServiceEnsureCanUseSnapPlatformAction
205199
| MultichainAccountServiceGetMultichainAccountWalletAction
206200
| MultichainAccountServiceGetMultichainAccountWalletsAction
207201
| MultichainAccountServiceCreateMultichainAccountWalletAction

packages/multichain-account-service/src/MultichainAccountService.test.ts

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import {
3838
TRX_ACCOUNT_PROVIDER_NAME,
3939
TrxAccountProvider,
4040
} from './providers/TrxAccountProvider';
41-
import { SnapPlatformWatcher } from './snaps/SnapPlatformWatcher';
4241
import type { RootMessenger, MockAccountProvider } from './tests';
4342
import {
4443
MOCK_HARDWARE_ACCOUNT_1,
@@ -53,7 +52,6 @@ import {
5352
import {
5453
MOCK_HD_KEYRING_1,
5554
MOCK_HD_KEYRING_2,
56-
MOCK_SNAP_KEYRING,
5755
getMultichainAccountServiceMessenger,
5856
getRootMessenger,
5957
makeMockAccountProvider,
@@ -114,10 +112,6 @@ type Mocks = {
114112
captureException: jest.Mock;
115113
};
116114
// eslint-disable-next-line @typescript-eslint/naming-convention
117-
SnapController: {
118-
getState: jest.Mock;
119-
};
120-
// eslint-disable-next-line @typescript-eslint/naming-convention
121115
EvmAccountProvider: MockAccountProvider;
122116
// eslint-disable-next-line @typescript-eslint/naming-convention
123117
SolAccountProvider: MockAccountProvider;
@@ -127,13 +121,6 @@ type Mocks = {
127121
TrxAccountProvider: MockAccountProvider;
128122
};
129123

130-
type Spies = {
131-
// eslint-disable-next-line @typescript-eslint/naming-convention
132-
SnapPlatformWatcher: {
133-
ensureCanUseSnapPlatform: jest.SpyInstance;
134-
};
135-
};
136-
137124
function mockAccountProvider<Provider extends Bip44AccountProvider>(
138125
providerClass: new (messenger: MultichainAccountServiceMessenger) => Provider,
139126
mocks: MockAccountProvider,
@@ -193,7 +180,6 @@ async function setup({
193180
rootMessenger: RootMessenger;
194181
messenger: MultichainAccountServiceMessenger;
195182
mocks: Mocks;
196-
spies: Spies;
197183
}> {
198184
const mocks: Mocks = {
199185
KeyringController: {
@@ -212,36 +198,15 @@ async function setup({
212198
ErrorReportingService: {
213199
captureException: jest.fn(),
214200
},
215-
SnapController: {
216-
getState: jest.fn(),
217-
},
218201
EvmAccountProvider: makeMockAccountProvider(),
219202
SolAccountProvider: makeMockAccountProvider(),
220203
BtcAccountProvider: makeMockAccountProvider(),
221204
TrxAccountProvider: makeMockAccountProvider(),
222205
};
223206

224-
const spies: Spies = {
225-
SnapPlatformWatcher: {
226-
ensureCanUseSnapPlatform: jest.spyOn(
227-
SnapPlatformWatcher.prototype,
228-
'ensureCanUseSnapPlatform',
229-
),
230-
},
231-
};
232-
233207
// Required for the `assert` on `MultichainAccountWallet.createMultichainAccountGroup`.
234208
Object.setPrototypeOf(mocks.EvmAccountProvider, EvmAccountProvider.prototype);
235209

236-
mocks.SnapController.getState.mockImplementation(() => ({
237-
isReady: true,
238-
}));
239-
240-
rootMessenger.registerActionHandler(
241-
'SnapController:getState',
242-
mocks.SnapController.getState,
243-
);
244-
245210
mocks.KeyringController.getState.mockImplementation(() => ({
246211
isUnlocked: true,
247212
keyrings: mocks.KeyringController.keyrings,
@@ -339,7 +304,6 @@ async function setup({
339304
rootMessenger,
340305
messenger,
341306
mocks,
342-
spies,
343307
};
344308
}
345309

@@ -1223,20 +1187,6 @@ describe('MultichainAccountService', () => {
12231187
MOCK_HD_ACCOUNT_1.address,
12241188
);
12251189
});
1226-
1227-
it('checks for Snap platform readiness with MultichainAccountService:ensureCanUseSnapPlatform', async () => {
1228-
const { rootMessenger, spies } = await setup({
1229-
accounts: [],
1230-
keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2, MOCK_SNAP_KEYRING],
1231-
});
1232-
1233-
await rootMessenger.call(
1234-
'MultichainAccountService:ensureCanUseSnapPlatform',
1235-
);
1236-
expect(
1237-
spies.SnapPlatformWatcher.ensureCanUseSnapPlatform,
1238-
).toHaveBeenCalled();
1239-
});
12401190
});
12411191

12421192
describe('resyncAccounts', () => {
@@ -1608,19 +1558,4 @@ describe('MultichainAccountService', () => {
16081558
});
16091559
});
16101560
});
1611-
1612-
describe('ensureCanUseSnapPlatform', () => {
1613-
it('delegates Snap platform readiness check to SnapPlatformWatcher (method)', async () => {
1614-
const { service, spies } = await setup({
1615-
accounts: [],
1616-
keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2, MOCK_SNAP_KEYRING],
1617-
});
1618-
1619-
await service.ensureCanUseSnapPlatform();
1620-
1621-
expect(
1622-
spies.SnapPlatformWatcher.ensureCanUseSnapPlatform,
1623-
).toHaveBeenCalledTimes(1);
1624-
});
1625-
});
16261561
});

packages/multichain-account-service/src/MultichainAccountService.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import {
4040
SOL_ACCOUNT_PROVIDER_NAME,
4141
SolAccountProviderConfig,
4242
} from './providers/SolAccountProvider';
43-
import { SnapPlatformWatcher } from './snaps/SnapPlatformWatcher';
4443
import type {
4544
MultichainAccountServiceConfig,
4645
MultichainAccountServiceMessenger,
@@ -61,10 +60,6 @@ export type MultichainAccountServiceOptions = {
6160
[TRX_ACCOUNT_PROVIDER_NAME]?: TrxAccountProviderConfig;
6261
};
6362
config?: MultichainAccountServiceConfig;
64-
/**
65-
* When provided, used to prevent using Snap platform before onboarding completion.
66-
*/
67-
ensureOnboardingComplete?: () => Promise<void>;
6863
};
6964

7065
/**
@@ -116,7 +111,6 @@ const MESSENGER_EXPOSED_METHODS = [
116111
'createMultichainAccountWallet',
117112
'resyncAccounts',
118113
'removeMultichainAccountWallet',
119-
'ensureCanUseSnapPlatform',
120114
'init',
121115
] as const;
122116

@@ -126,8 +120,6 @@ const MESSENGER_EXPOSED_METHODS = [
126120
export class MultichainAccountService {
127121
readonly #messenger: MultichainAccountServiceMessenger;
128122

129-
readonly #watcher: SnapPlatformWatcher;
130-
131123
readonly #providers: Bip44AccountProvider[];
132124

133125
readonly #trace: TraceCallback;
@@ -151,15 +143,12 @@ export class MultichainAccountService {
151143
* @param options.providers - Optional list of account
152144
* @param options.providerConfigs - Optional provider configs
153145
* @param options.config - Optional config.
154-
* @param options.ensureOnboardingComplete - Optional callback to ensure
155-
* onboarding is completed before using the Snap platform.
156146
*/
157147
constructor({
158148
messenger,
159149
providers = [],
160150
providerConfigs,
161151
config,
162-
ensureOnboardingComplete,
163152
}: MultichainAccountServiceOptions) {
164153
this.#messenger = messenger;
165154
this.#wallets = new Map();
@@ -211,11 +200,6 @@ export class MultichainAccountService {
211200
...providers,
212201
];
213202

214-
this.#watcher = new SnapPlatformWatcher(messenger, {
215-
ensureOnboardingComplete,
216-
snapKeyringWaitTimeoutMs: config?.snapPlatformWatcher?.timeoutMs,
217-
});
218-
219203
this.#messenger.registerMethodActionHandlers(
220204
this,
221205
MESSENGER_EXPOSED_METHODS,
@@ -349,10 +333,6 @@ export class MultichainAccountService {
349333
log('Providers got re-synced!');
350334
}
351335

352-
ensureCanUseSnapPlatform(): Promise<void> {
353-
return this.#watcher.ensureCanUseSnapPlatform();
354-
}
355-
356336
/**
357337
* Get the wallet matching the given entropy source.
358338
*

packages/multichain-account-service/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export type {
88
} from './types';
99
export type {
1010
MultichainAccountServiceResyncAccountsAction,
11-
MultichainAccountServiceEnsureCanUseSnapPlatformAction,
1211
MultichainAccountServiceGetMultichainAccountWalletAction,
1312
MultichainAccountServiceGetMultichainAccountWalletsAction,
1413
MultichainAccountServiceCreateMultichainAccountWalletAction,

packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class MockBtcKeyring {
141141
}
142142

143143
class MockBtcAccountProvider extends BtcAccountProvider {
144-
override async ensureCanUseSnapPlatform(): Promise<void> {
144+
override async ensureReady(): Promise<void> {
145145
// Override to avoid waiting during tests.
146146
}
147147
}

packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ const setup = ({
173173
},
174174
handleRequest: jest.fn(),
175175
},
176-
MultichainAccountService: {
177-
ensureCanUseSnapPlatform: jest.fn(),
176+
SnapAccountService: {
177+
ensureReady: jest.fn(),
178178
},
179179
};
180180

@@ -185,13 +185,11 @@ const setup = ({
185185
mocks.AccountsController.listMultichainAccounts.mockReturnValue(accounts);
186186

187187
messenger.registerActionHandler(
188-
'MultichainAccountService:ensureCanUseSnapPlatform',
189-
mocks.MultichainAccountService.ensureCanUseSnapPlatform,
188+
'SnapAccountService:ensureReady',
189+
mocks.SnapAccountService.ensureReady,
190190
);
191191
// Make the platform ready right away (having a resolved promise is enough).
192-
mocks.MultichainAccountService.ensureCanUseSnapPlatform.mockResolvedValue(
193-
undefined,
194-
);
192+
mocks.SnapAccountService.ensureReady.mockResolvedValue(undefined);
195193

196194
messenger.registerActionHandler(
197195
'SnapController:handleRequest',
@@ -239,10 +237,7 @@ const setup = ({
239237
),
240238
);
241239

242-
const serviceMessenger = getMultichainAccountServiceMessenger(messenger, {
243-
// We need this extra action to be able to mock it.
244-
actions: ['MultichainAccountService:ensureCanUseSnapPlatform'],
245-
});
240+
const serviceMessenger = getMultichainAccountServiceMessenger(messenger);
246241
const config = deepmerge(
247242
DEFAULT_TEST_CONFIG,
248243
configOverride as SnapAccountProviderConfig,
@@ -954,15 +949,13 @@ describe('SnapAccountProvider', () => {
954949
});
955950
});
956951

957-
describe('ensureCanUseSnapPlatform', () => {
958-
it('delegates Snap platform readiness check to SnapPlatformWatcher', async () => {
952+
describe('ensureReady', () => {
953+
it('delegates Snap platform readiness check to SnapAccountService:ensureReady', async () => {
959954
const { provider, mocks } = setup();
960955

961-
await provider.ensureCanUseSnapPlatform();
956+
await provider.ensureReady();
962957

963-
expect(
964-
mocks.MultichainAccountService.ensureCanUseSnapPlatform,
965-
).toHaveBeenCalledTimes(1);
958+
expect(mocks.SnapAccountService.ensureReady).toHaveBeenCalledTimes(1);
966959
});
967960
});
968961
});

0 commit comments

Comments
 (0)