Skip to content

Commit dd973e4

Browse files
feat: Add isReady state property (#3778)
Adds a lazily populated `isReady` state property that is populated as a side-effect of `ensureCanUsePlatform` (renamed from `assertCanUsePlatform`). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces `isReady` to `SnapController` state, lazily populated via new `#ensureCanUsePlatform`, and updates flows/tests accordingly. > > - **State/Metadata**: > - Add `state.isReady` (default `false`); include in `includeInStateLogs` and `includeInDebugSnapshot`; not persisted/used in UI. > - **Platform readiness**: > - Replace `#assertCanUsePlatform` with `#ensureCanUsePlatform` that waits onboarding, sets `isReady` based on `disableSnaps`, and asserts availability. > - Call `#ensureCanUsePlatform` in `init()`, `updateRegistry()`, `startSnap()`, `installSnaps()`, `#updateSnap()`, and `handleRequest()`. > - Reset `isReady` to `false` in `clearState()`. > - **Tests**: > - Add test verifying `SnapController:init` sets `isReady`. > - Update expectations to include `isReady` in debug/state logs and post-`clearState` state, and minor timing adjustments. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2df985d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 24b87d3 commit dd973e4

File tree

2 files changed

+55
-8
lines changed

2 files changed

+55
-8
lines changed

packages/snaps-controllers/src/snaps/SnapController.test.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10624,6 +10624,8 @@ describe('SnapController', () => {
1062410624

1062510625
await waitForStateChange(messenger);
1062610626

10627+
await waitForStateChange(messenger);
10628+
1062710629
expect(snapController.isRunning(MOCK_SNAP_ID)).toBe(true);
1062810630

1062910631
await new Promise((resolve) => setTimeout(resolve, 100));
@@ -10647,6 +10649,7 @@ describe('SnapController', () => {
1064710649
);
1064810650

1064910651
expect(snapController.state).toStrictEqual({
10652+
isReady: false,
1065010653
snaps: {},
1065110654
snapStates: {},
1065210655
unencryptedSnapStates: {},
@@ -10769,6 +10772,23 @@ describe('SnapController', () => {
1076910772

1077010773
describe('SnapController actions', () => {
1077110774
describe('SnapController:init', () => {
10775+
it('populates `isReady`', async () => {
10776+
const rootMessenger = getControllerMessenger();
10777+
const messenger = getSnapControllerMessenger(rootMessenger);
10778+
10779+
const snapController = getSnapController(
10780+
getSnapControllerOptions({ messenger }),
10781+
);
10782+
10783+
expect(snapController.state.isReady).toBe(false);
10784+
messenger.call('SnapController:init');
10785+
10786+
await waitForStateChange(messenger);
10787+
expect(snapController.state.isReady).toBe(true);
10788+
10789+
snapController.destroy();
10790+
});
10791+
1077210792
it('calls `onStart` for all Snaps with the `endowment:lifecycle-hooks` permission', async () => {
1077310793
const rootMessenger = getControllerMessenger();
1077410794
const messenger = getSnapControllerMessenger(rootMessenger);
@@ -12997,7 +13017,11 @@ describe('SnapController', () => {
1299713017
controller.metadata,
1299813018
'includeInDebugSnapshot',
1299913019
),
13000-
).toMatchInlineSnapshot(`{}`);
13020+
).toMatchInlineSnapshot(`
13021+
{
13022+
"isReady": false,
13023+
}
13024+
`);
1300113025
});
1300213026

1300313027
describe('includeInStateLogs', () => {
@@ -13012,6 +13036,7 @@ describe('SnapController', () => {
1301213036
),
1301313037
).toMatchInlineSnapshot(`
1301413038
{
13039+
"isReady": false,
1301513040
"snaps": {},
1301613041
}
1301713042
`);

packages/snaps-controllers/src/snaps/SnapController.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ export type SnapControllerState = {
318318
snaps: StoredSnaps;
319319
snapStates: Record<SnapId, string | null>;
320320
unencryptedSnapStates: Record<SnapId, string | null>;
321+
isReady: boolean;
321322
};
322323

323324
export type PersistedSnapControllerState = SnapControllerState & {
@@ -854,6 +855,7 @@ const defaultState: SnapControllerState = {
854855
snaps: {},
855856
snapStates: {},
856857
unencryptedSnapStates: {},
858+
isReady: false,
857859
};
858860

859861
/**
@@ -965,6 +967,12 @@ export class SnapController extends BaseController<
965967
super({
966968
messenger,
967969
metadata: {
970+
isReady: {
971+
includeInStateLogs: true,
972+
includeInDebugSnapshot: true,
973+
persist: false,
974+
usedInUi: false,
975+
},
968976
snapStates: {
969977
includeInStateLogs: false,
970978
persist: true,
@@ -1327,6 +1335,9 @@ export class SnapController extends BaseController<
13271335
* runnable Snaps.
13281336
*/
13291337
init() {
1338+
// Lazily populate the `isReady` state.
1339+
this.#ensureCanUsePlatform().catch(logWarning);
1340+
13301341
this.#callLifecycleHooks(METAMASK_ORIGIN, HandlerType.OnStart);
13311342
}
13321343

@@ -1537,7 +1548,7 @@ export class SnapController extends BaseController<
15371548
* Also updates any preinstalled Snaps to the latest allowlisted version.
15381549
*/
15391550
async updateRegistry(): Promise<void> {
1540-
await this.#assertCanUsePlatform();
1551+
await this.#ensureCanUsePlatform();
15411552
await this.messenger.call('SnapsRegistry:update');
15421553

15431554
const blockedSnaps = await this.messenger.call(
@@ -1717,13 +1728,23 @@ export class SnapController extends BaseController<
17171728
/**
17181729
* Waits for onboarding and then asserts whether the Snaps platform is allowed to run.
17191730
*/
1720-
async #assertCanUsePlatform() {
1731+
async #ensureCanUsePlatform() {
17211732
// Ensure the user has onboarded before allowing access to Snaps.
17221733
await this.#ensureOnboardingComplete();
17231734

17241735
const flags = this.#getFeatureFlags();
1736+
1737+
// If the user has onboarded, the Snaps Platform is considered ready,
1738+
// if it isn't forced to be disabled via feature flags.
1739+
const isReady = flags.disableSnaps !== true;
1740+
if (this.state.isReady !== isReady) {
1741+
this.update((state) => {
1742+
state.isReady = isReady;
1743+
});
1744+
}
1745+
17251746
assert(
1726-
flags.disableSnaps !== true,
1747+
isReady,
17271748
'The Snaps platform requires basic functionality to be used. Enable basic functionality in the settings to use the Snaps platform.',
17281749
);
17291750
}
@@ -1803,7 +1824,7 @@ export class SnapController extends BaseController<
18031824
* @param snapId - The id of the Snap to start.
18041825
*/
18051826
async startSnap(snapId: SnapId): Promise<void> {
1806-
await this.#assertCanUsePlatform();
1827+
await this.#ensureCanUsePlatform();
18071828
const snap = this.state.snaps[snapId];
18081829

18091830
if (!snap.enabled) {
@@ -2387,6 +2408,7 @@ export class SnapController extends BaseController<
23872408
snapIds.forEach((snapId) => this.#revokeAllSnapPermissions(snapId));
23882409

23892410
this.update((state) => {
2411+
state.isReady = false;
23902412
state.snaps = {};
23912413
state.snapStates = {};
23922414
state.unencryptedSnapStates = {};
@@ -2703,7 +2725,7 @@ export class SnapController extends BaseController<
27032725
origin: string,
27042726
requestedSnaps: RequestSnapsParams,
27052727
): Promise<RequestSnapsResult> {
2706-
await this.#assertCanUsePlatform();
2728+
await this.#ensureCanUsePlatform();
27072729

27082730
const result: RequestSnapsResult = {};
27092731

@@ -2989,7 +3011,7 @@ export class SnapController extends BaseController<
29893011
if (!automaticUpdate) {
29903012
this.#assertCanInstallSnaps();
29913013
}
2992-
await this.#assertCanUsePlatform();
3014+
await this.#ensureCanUsePlatform();
29933015

29943016
const snap = this.getExpect(snapId);
29953017

@@ -3624,7 +3646,7 @@ export class SnapController extends BaseController<
36243646
handler: handlerType,
36253647
request: rawRequest,
36263648
}: SnapRpcHookArgs & { snapId: SnapId }): Promise<unknown> {
3627-
await this.#assertCanUsePlatform();
3649+
await this.#ensureCanUsePlatform();
36283650

36293651
const snap = this.get(snapId);
36303652

0 commit comments

Comments
 (0)