Skip to content

Commit 042dc48

Browse files
committed
Add StorageService and handle preinstalled snap setup in init
1 parent b58a9d1 commit 042dc48

File tree

5 files changed

+109
-24
lines changed

5 files changed

+109
-24
lines changed

packages/snaps-controllers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@metamask/snaps-rpc-methods": "workspace:^",
9595
"@metamask/snaps-sdk": "workspace:^",
9696
"@metamask/snaps-utils": "workspace:^",
97+
"@metamask/storage-service": "^0.0.1",
9798
"@metamask/superstruct": "^3.2.1",
9899
"@metamask/utils": "^11.8.1",
99100
"@xstate/fsm": "^2.0.0",

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

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ import {
111111
OnAssetsConversionResponseStruct,
112112
OnAssetsMarketDataResponseStruct,
113113
} from '@metamask/snaps-utils';
114+
import type {
115+
StorageServiceGetItemAction,
116+
StorageServiceSetItemAction,
117+
StorageServiceRemoveItemAction,
118+
StorageServiceClearAction,
119+
} from '@metamask/storage-service';
114120
import type {
115121
Json,
116122
NonEmptyArray,
@@ -670,7 +676,11 @@ export type AllowedActions =
670676
| Update
671677
| ResolveVersion
672678
| CreateInterface
673-
| GetInterface;
679+
| GetInterface
680+
| StorageServiceSetItemAction
681+
| StorageServiceGetItemAction
682+
| StorageServiceRemoveItemAction
683+
| StorageServiceClearAction;
674684

675685
export type AllowedEvents =
676686
| ExecutionServiceEvents
@@ -943,6 +953,8 @@ export class SnapController extends BaseController<
943953

944954
readonly #ensureOnboardingComplete: () => Promise<void>;
945955

956+
readonly #controllerSetup = createDeferredPromise();
957+
946958
constructor({
947959
closeAllConnections,
948960
messenger,
@@ -991,7 +1003,6 @@ export class SnapController extends BaseController<
9911003
return Object.values(snaps).reduce<Record<SnapId, Partial<Snap>>>(
9921004
(acc, snap) => {
9931005
const snapCopy: Partial<Snap> = { ...snap };
994-
delete snapCopy.sourceCode;
9951006
delete snapCopy.auxiliaryFiles;
9961007
acc[snap.id] = snapCopy;
9971008
return acc;
@@ -1115,10 +1126,6 @@ export class SnapController extends BaseController<
11151126
this.#setupRuntime(snap.id),
11161127
);
11171128

1118-
if (this.#preinstalledSnaps) {
1119-
this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
1120-
}
1121-
11221129
this.#trackSnapExport = throttleTracking(
11231130
(snapId: SnapId, handler: string, success: boolean, origin: string) => {
11241131
const snapMetadata = this.messenger.call(
@@ -1215,8 +1222,9 @@ export class SnapController extends BaseController<
12151222
* actions.
12161223
*/
12171224
#registerMessageHandlers(): void {
1218-
this.messenger.registerActionHandler(`${controllerName}:init`, (...args) =>
1219-
this.init(...args),
1225+
this.messenger.registerActionHandler(
1226+
`${controllerName}:init`,
1227+
async (...args) => this.init(...args),
12201228
);
12211229

12221230
this.messenger.registerActionHandler(
@@ -1331,17 +1339,23 @@ export class SnapController extends BaseController<
13311339
/**
13321340
* Initialise the SnapController.
13331341
*
1334-
* Currently this method calls the `onStart` lifecycle hook for all
1342+
* Currently this method sets up the preinstalled snaps and calls the `onStart` lifecycle hook for all
13351343
* runnable Snaps.
13361344
*/
1337-
init() {
1345+
async init() {
13381346
// Lazily populate the `isReady` state.
13391347
this.#ensureCanUsePlatform().catch(logWarning);
13401348

1349+
if (this.#preinstalledSnaps) {
1350+
await this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
1351+
}
1352+
13411353
this.#callLifecycleHooks(METAMASK_ORIGIN, HandlerType.OnStart);
1354+
1355+
this.#controllerSetup.resolve();
13421356
}
13431357

1344-
#handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) {
1358+
async #handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) {
13451359
for (const {
13461360
snapId,
13471361
manifest,
@@ -1413,7 +1427,7 @@ export class SnapController extends BaseController<
14131427
};
14141428

14151429
// Add snap to the SnapController state
1416-
this.#set({
1430+
await this.#set({
14171431
id: snapId,
14181432
origin: METAMASK_ORIGIN,
14191433
files: filesObject,
@@ -1732,6 +1746,9 @@ export class SnapController extends BaseController<
17321746
// Ensure the user has onboarded before allowing access to Snaps.
17331747
await this.#ensureOnboardingComplete();
17341748

1749+
// Ensure the controller has finished setting up.
1750+
await this.#controllerSetup.promise;
1751+
17351752
const flags = this.#getFeatureFlags();
17361753

17371754
// If the user has onboarded, the Snaps Platform is considered ready,
@@ -1831,9 +1848,11 @@ export class SnapController extends BaseController<
18311848
throw new Error(`Snap "${snapId}" is disabled.`);
18321849
}
18331850

1851+
const sourceCode = await this.#getSourceCode(snapId);
1852+
18341853
await this.#startSnap({
18351854
snapId,
1836-
sourceCode: snap.sourceCode,
1855+
sourceCode,
18371856
});
18381857
}
18391858

@@ -2417,9 +2436,11 @@ export class SnapController extends BaseController<
24172436
this.#snapsRuntimeData.clear();
24182437
this.#rollbackSnapshots.clear();
24192438

2439+
await this.#clearStorageService();
2440+
24202441
// We want to remove all snaps & permissions, except for preinstalled snaps
24212442
if (this.#preinstalledSnaps) {
2422-
this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
2443+
await this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
24232444
}
24242445
}
24252446

@@ -2470,6 +2491,8 @@ export class SnapController extends BaseController<
24702491
delete state.unencryptedSnapStates[snapId];
24712492
});
24722493

2494+
await this.#removeSourceCode(snapId);
2495+
24732496
// If the snap has been fully installed before, also emit snapUninstalled.
24742497
if (snap.status !== SnapStatus.Installing) {
24752498
this.messenger.publish(`SnapController:snapUninstalled`, truncated);
@@ -3118,7 +3141,7 @@ export class SnapController extends BaseController<
31183141

31193142
this.#transition(snapId, SnapStatusEvents.Update);
31203143

3121-
this.#set({
3144+
await this.#set({
31223145
origin,
31233146
id: snapId,
31243147
files: newSnap,
@@ -3373,7 +3396,7 @@ export class SnapController extends BaseController<
33733396
* @param args - The add snap args.
33743397
* @returns The resulting snap object.
33753398
*/
3376-
#set(args: SetSnapArgs): PersistedSnap {
3399+
async #set(args: SetSnapArgs): Promise<PersistedSnap> {
33773400
const {
33783401
id: snapId,
33793402
origin,
@@ -3446,7 +3469,6 @@ export class SnapController extends BaseController<
34463469
initialPermissions: manifest.result.initialPermissions,
34473470
manifest: manifest.result,
34483471
status: this.#statusMachine.config.initial as StatusStates['value'],
3449-
sourceCode,
34503472
version,
34513473
versionHistory,
34523474
auxiliaryFiles,
@@ -3456,13 +3478,16 @@ export class SnapController extends BaseController<
34563478
// If the snap was blocked, it isn't any longer
34573479
delete snap.blockInformation;
34583480

3481+
await this.#setSourceCode(snapId, sourceCode);
3482+
34593483
// store the snap back in state
34603484
const { inversePatches } = this.update((state: any) => {
34613485
state.snaps[snapId] = snap;
34623486
});
34633487

34643488
// checking for isUpdate here as this function is also used in
34653489
// the install flow, we do not care to create snapshots for installs
3490+
// @TODO (guillaumerx): Find a way to add the sourceCode to the rollback snapshot
34663491
if (isUpdate) {
34673492
const rollbackSnapshot = this.#getRollbackSnapshot(snapId);
34683493
if (rollbackSnapshot !== undefined) {
@@ -4664,4 +4689,53 @@ export class SnapController extends BaseController<
46644689
runtime.state = undefined;
46654690
}
46664691
}
4692+
4693+
/**
4694+
* Retrieve the source code for a snap from storage.
4695+
*
4696+
* @param snapId - The snap ID.
4697+
* @returns The source code for the snap.
4698+
*/
4699+
async #getSourceCode(snapId: SnapId) {
4700+
const { result } = await this.messenger.call(
4701+
'StorageService:getItem',
4702+
this.name,
4703+
snapId,
4704+
);
4705+
4706+
assert(result, `Source code for snap "${snapId}" not found.`);
4707+
4708+
return result as string;
4709+
}
4710+
4711+
/**
4712+
* Store the source code for a snap in storage.
4713+
*
4714+
* @param snapId - The snap ID.
4715+
* @param sourceCode - The source code for the snap.
4716+
*/
4717+
async #setSourceCode(snapId: SnapId, sourceCode: string) {
4718+
await this.messenger.call(
4719+
'StorageService:setItem',
4720+
this.name,
4721+
snapId,
4722+
sourceCode,
4723+
);
4724+
}
4725+
4726+
/**
4727+
* Remove the source code for a snap from storage.
4728+
*
4729+
* @param snapId - The snap ID.
4730+
*/
4731+
async #removeSourceCode(snapId: SnapId) {
4732+
await this.messenger.call('StorageService:removeItem', this.name, snapId);
4733+
}
4734+
4735+
/**
4736+
* Clear all snap source code from storage.
4737+
*/
4738+
async #clearStorageService() {
4739+
await this.messenger.call('StorageService:clear', this.name);
4740+
}
46674741
}

packages/snaps-simulation/src/methods/hooks/get-snap.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export function getGetSnapImplementation(preinstalled: boolean = true) {
2323
status: SnapStatus.Running,
2424
versionHistory: [],
2525
initialPermissions: {},
26-
sourceCode: '',
2726
manifest: {
2827
version: '0.1.0' as SemVerVersion,
2928
proposedName: 'Test Snap',

packages/snaps-utils/src/snaps.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ export type SnapAuxiliaryFile = {
9191
*/
9292
export type SnapAuxilaryFile = SnapAuxiliaryFile;
9393

94-
export type PersistedSnap = Snap;
94+
export type PersistedSnap = Snap & {
95+
sourceCode: string;
96+
};
9597

9698
/**
9799
* A Snap as it exists in {@link SnapController} state.
@@ -107,11 +109,6 @@ export type Snap = TruncatedSnap & {
107109
*/
108110
initialPermissions: SnapPermissions;
109111

110-
/**
111-
* The source code of the Snap.
112-
*/
113-
sourceCode: string;
114-
115112
/**
116113
* The Snap's manifest file.
117114
*/

yarn.lock

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4269,7 +4269,11 @@ __metadata:
42694269
"@metamask/snaps-rpc-methods": "workspace:^"
42704270
"@metamask/snaps-sdk": "workspace:^"
42714271
"@metamask/snaps-utils": "workspace:^"
4272+
<<<<<<< HEAD
42724273
"@metamask/superstruct": "npm:^3.2.1"
4274+
=======
4275+
"@metamask/storage-service": "npm:^0.0.1"
4276+
>>>>>>> 57095fea (Add `StorageService` and handle preinstalled snap setup in `init`)
42734277
"@metamask/utils": "npm:^11.8.1"
42744278
"@noble/hashes": "npm:^1.7.1"
42754279
"@swc/core": "npm:1.11.31"
@@ -4682,6 +4686,16 @@ __metadata:
46824686
languageName: unknown
46834687
linkType: soft
46844688

4689+
"@metamask/storage-service@npm:^0.0.1":
4690+
version: 0.0.1
4691+
resolution: "@metamask/storage-service@npm:0.0.1"
4692+
dependencies:
4693+
"@metamask/messenger": "npm:^0.3.0"
4694+
"@metamask/utils": "npm:^11.8.1"
4695+
checksum: 10/ba2443e4bab7a4ef64bf3e0a10403ef94d50225e5ae5931a6fd32a21bfa8e276916ab6dc377d2db30c15c50acaeaee74a3c0b418a563311da9f98b9172fba300
4696+
languageName: node
4697+
linkType: hard
4698+
46854699
"@metamask/superstruct@npm:^3.1.0, @metamask/superstruct@npm:^3.2.1":
46864700
version: 3.2.1
46874701
resolution: "@metamask/superstruct@npm:3.2.1"

0 commit comments

Comments
 (0)