Skip to content

Commit 57095fe

Browse files
committed
Add StorageService and handle preinstalled snap setup in init
1 parent 1e95bcd commit 57095fe

File tree

5 files changed

+106
-24
lines changed

5 files changed

+106
-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/utils": "^11.8.1",
9899
"@xstate/fsm": "^2.0.0",
99100
"async-mutex": "^0.5.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,
@@ -669,7 +675,11 @@ export type AllowedActions =
669675
| Update
670676
| ResolveVersion
671677
| CreateInterface
672-
| GetInterface;
678+
| GetInterface
679+
| StorageServiceSetItemAction
680+
| StorageServiceGetItemAction
681+
| StorageServiceRemoveItemAction
682+
| StorageServiceClearAction;
673683

674684
export type AllowedEvents =
675685
| ExecutionServiceEvents
@@ -941,6 +951,8 @@ export class SnapController extends BaseController<
941951

942952
readonly #ensureOnboardingComplete: () => Promise<void>;
943953

954+
readonly #controllerSetup = createDeferredPromise();
955+
944956
constructor({
945957
closeAllConnections,
946958
messenger,
@@ -983,7 +995,6 @@ export class SnapController extends BaseController<
983995
return Object.values(snaps).reduce<Record<SnapId, Partial<Snap>>>(
984996
(acc, snap) => {
985997
const snapCopy: Partial<Snap> = { ...snap };
986-
delete snapCopy.sourceCode;
987998
delete snapCopy.auxiliaryFiles;
988999
acc[snap.id] = snapCopy;
9891000
return acc;
@@ -1107,10 +1118,6 @@ export class SnapController extends BaseController<
11071118
this.#setupRuntime(snap.id),
11081119
);
11091120

1110-
if (this.#preinstalledSnaps) {
1111-
this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
1112-
}
1113-
11141121
this.#trackSnapExport = throttleTracking(
11151122
(snapId: SnapId, handler: string, success: boolean, origin: string) => {
11161123
const snapMetadata = this.messenger.call(
@@ -1207,8 +1214,9 @@ export class SnapController extends BaseController<
12071214
* actions.
12081215
*/
12091216
#registerMessageHandlers(): void {
1210-
this.messenger.registerActionHandler(`${controllerName}:init`, (...args) =>
1211-
this.init(...args),
1217+
this.messenger.registerActionHandler(
1218+
`${controllerName}:init`,
1219+
async (...args) => this.init(...args),
12121220
);
12131221

12141222
this.messenger.registerActionHandler(
@@ -1323,14 +1331,20 @@ export class SnapController extends BaseController<
13231331
/**
13241332
* Initialise the SnapController.
13251333
*
1326-
* Currently this method calls the `onStart` lifecycle hook for all
1334+
* Currently this method sets up the preinstalled snaps and calls the `onStart` lifecycle hook for all
13271335
* runnable Snaps.
13281336
*/
1329-
init() {
1337+
async init() {
1338+
if (this.#preinstalledSnaps) {
1339+
await this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
1340+
}
1341+
13301342
this.#callLifecycleHooks(METAMASK_ORIGIN, HandlerType.OnStart);
1343+
1344+
this.#controllerSetup.resolve();
13311345
}
13321346

1333-
#handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) {
1347+
async #handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) {
13341348
for (const {
13351349
snapId,
13361350
manifest,
@@ -1402,7 +1416,7 @@ export class SnapController extends BaseController<
14021416
};
14031417

14041418
// Add snap to the SnapController state
1405-
this.#set({
1419+
await this.#set({
14061420
id: snapId,
14071421
origin: METAMASK_ORIGIN,
14081422
files: filesObject,
@@ -1721,6 +1735,9 @@ export class SnapController extends BaseController<
17211735
// Ensure the user has onboarded before allowing access to Snaps.
17221736
await this.#ensureOnboardingComplete();
17231737

1738+
// Ensure the controller has finished setting up.
1739+
await this.#controllerSetup.promise;
1740+
17241741
const flags = this.#getFeatureFlags();
17251742
assert(
17261743
flags.disableSnaps !== true,
@@ -1810,9 +1827,11 @@ export class SnapController extends BaseController<
18101827
throw new Error(`Snap "${snapId}" is disabled.`);
18111828
}
18121829

1830+
const sourceCode = await this.#getSourceCode(snapId);
1831+
18131832
await this.#startSnap({
18141833
snapId,
1815-
sourceCode: snap.sourceCode,
1834+
sourceCode,
18161835
});
18171836
}
18181837

@@ -2395,9 +2414,11 @@ export class SnapController extends BaseController<
23952414
this.#snapsRuntimeData.clear();
23962415
this.#rollbackSnapshots.clear();
23972416

2417+
await this.#clearStorageService();
2418+
23982419
// We want to remove all snaps & permissions, except for preinstalled snaps
23992420
if (this.#preinstalledSnaps) {
2400-
this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
2421+
await this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
24012422
}
24022423
}
24032424

@@ -2448,6 +2469,8 @@ export class SnapController extends BaseController<
24482469
delete state.unencryptedSnapStates[snapId];
24492470
});
24502471

2472+
await this.#removeSourceCode(snapId);
2473+
24512474
// If the snap has been fully installed before, also emit snapUninstalled.
24522475
if (snap.status !== SnapStatus.Installing) {
24532476
this.messenger.publish(`SnapController:snapUninstalled`, truncated);
@@ -3096,7 +3119,7 @@ export class SnapController extends BaseController<
30963119

30973120
this.#transition(snapId, SnapStatusEvents.Update);
30983121

3099-
this.#set({
3122+
await this.#set({
31003123
origin,
31013124
id: snapId,
31023125
files: newSnap,
@@ -3351,7 +3374,7 @@ export class SnapController extends BaseController<
33513374
* @param args - The add snap args.
33523375
* @returns The resulting snap object.
33533376
*/
3354-
#set(args: SetSnapArgs): PersistedSnap {
3377+
async #set(args: SetSnapArgs): Promise<PersistedSnap> {
33553378
const {
33563379
id: snapId,
33573380
origin,
@@ -3424,7 +3447,6 @@ export class SnapController extends BaseController<
34243447
initialPermissions: manifest.result.initialPermissions,
34253448
manifest: manifest.result,
34263449
status: this.#statusMachine.config.initial as StatusStates['value'],
3427-
sourceCode,
34283450
version,
34293451
versionHistory,
34303452
auxiliaryFiles,
@@ -3434,13 +3456,16 @@ export class SnapController extends BaseController<
34343456
// If the snap was blocked, it isn't any longer
34353457
delete snap.blockInformation;
34363458

3459+
await this.#setSourceCode(snapId, sourceCode);
3460+
34373461
// store the snap back in state
34383462
const { inversePatches } = this.update((state: any) => {
34393463
state.snaps[snapId] = snap;
34403464
});
34413465

34423466
// checking for isUpdate here as this function is also used in
34433467
// the install flow, we do not care to create snapshots for installs
3468+
// @TODO (guillaumerx): Find a way to add the sourceCode to the rollback snapshot
34443469
if (isUpdate) {
34453470
const rollbackSnapshot = this.#getRollbackSnapshot(snapId);
34463471
if (rollbackSnapshot !== undefined) {
@@ -4642,4 +4667,53 @@ export class SnapController extends BaseController<
46424667
runtime.state = undefined;
46434668
}
46444669
}
4670+
4671+
/**
4672+
* Retrieve the source code for a snap from storage.
4673+
*
4674+
* @param snapId - The snap ID.
4675+
* @returns The source code for the snap.
4676+
*/
4677+
async #getSourceCode(snapId: SnapId) {
4678+
const { result } = await this.messenger.call(
4679+
'StorageService:getItem',
4680+
this.name,
4681+
snapId,
4682+
);
4683+
4684+
assert(result, `Source code for snap "${snapId}" not found.`);
4685+
4686+
return result as string;
4687+
}
4688+
4689+
/**
4690+
* Store the source code for a snap in storage.
4691+
*
4692+
* @param snapId - The snap ID.
4693+
* @param sourceCode - The source code for the snap.
4694+
*/
4695+
async #setSourceCode(snapId: SnapId, sourceCode: string) {
4696+
await this.messenger.call(
4697+
'StorageService:setItem',
4698+
this.name,
4699+
snapId,
4700+
sourceCode,
4701+
);
4702+
}
4703+
4704+
/**
4705+
* Remove the source code for a snap from storage.
4706+
*
4707+
* @param snapId - The snap ID.
4708+
*/
4709+
async #removeSourceCode(snapId: SnapId) {
4710+
await this.messenger.call('StorageService:removeItem', this.name, snapId);
4711+
}
4712+
4713+
/**
4714+
* Clear all snap source code from storage.
4715+
*/
4716+
async #clearStorageService() {
4717+
await this.messenger.call('StorageService:clear', this.name);
4718+
}
46454719
}

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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4269,6 +4269,7 @@ __metadata:
42694269
"@metamask/snaps-rpc-methods": "workspace:^"
42704270
"@metamask/snaps-sdk": "workspace:^"
42714271
"@metamask/snaps-utils": "workspace:^"
4272+
"@metamask/storage-service": "npm:^0.0.1"
42724273
"@metamask/utils": "npm:^11.8.1"
42734274
"@noble/hashes": "npm:^1.7.1"
42744275
"@swc/core": "npm:1.11.31"
@@ -4681,6 +4682,16 @@ __metadata:
46814682
languageName: unknown
46824683
linkType: soft
46834684

4685+
"@metamask/storage-service@npm:^0.0.1":
4686+
version: 0.0.1
4687+
resolution: "@metamask/storage-service@npm:0.0.1"
4688+
dependencies:
4689+
"@metamask/messenger": "npm:^0.3.0"
4690+
"@metamask/utils": "npm:^11.8.1"
4691+
checksum: 10/ba2443e4bab7a4ef64bf3e0a10403ef94d50225e5ae5931a6fd32a21bfa8e276916ab6dc377d2db30c15c50acaeaee74a3c0b418a563311da9f98b9172fba300
4692+
languageName: node
4693+
linkType: hard
4694+
46844695
"@metamask/superstruct@npm:^3.1.0, @metamask/superstruct@npm:^3.2.1":
46854696
version: 3.2.1
46864697
resolution: "@metamask/superstruct@npm:3.2.1"

0 commit comments

Comments
 (0)