@metamask/snaps-rpc-methods 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/LICENSE +18 -0
- package/README.md +3 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/permitted/common/snapInstallation.js +33 -0
- package/dist/cjs/permitted/common/snapInstallation.js.map +1 -0
- package/dist/cjs/permitted/getFile.js +63 -0
- package/dist/cjs/permitted/getFile.js.map +1 -0
- package/dist/cjs/permitted/getSnaps.js +39 -0
- package/dist/cjs/permitted/getSnaps.js.map +1 -0
- package/dist/cjs/permitted/handlers.js +33 -0
- package/dist/cjs/permitted/handlers.js.map +1 -0
- package/dist/cjs/permitted/index.js +21 -0
- package/dist/cjs/permitted/index.js.map +1 -0
- package/dist/cjs/permitted/invokeKeyring.js +88 -0
- package/dist/cjs/permitted/invokeKeyring.js.map +1 -0
- package/dist/cjs/permitted/invokeSnapSugar.js +62 -0
- package/dist/cjs/permitted/invokeSnapSugar.js.map +1 -0
- package/dist/cjs/permitted/middleware.js +38 -0
- package/dist/cjs/permitted/middleware.js.map +1 -0
- package/dist/cjs/permitted/requestSnaps.js +122 -0
- package/dist/cjs/permitted/requestSnaps.js.map +1 -0
- package/dist/cjs/request.js +6 -0
- package/dist/cjs/request.js.map +1 -0
- package/dist/cjs/restricted/caveats/index.js +38 -0
- package/dist/cjs/restricted/caveats/index.js.map +1 -0
- package/dist/cjs/restricted/caveats/permittedCoinTypes.js +83 -0
- package/dist/cjs/restricted/caveats/permittedCoinTypes.js.map +1 -0
- package/dist/cjs/restricted/caveats/permittedDerivationPaths.js +67 -0
- package/dist/cjs/restricted/caveats/permittedDerivationPaths.js.map +1 -0
- package/dist/cjs/restricted/caveats/snapIds.js +59 -0
- package/dist/cjs/restricted/caveats/snapIds.js.map +1 -0
- package/dist/cjs/restricted/dialog.js +150 -0
- package/dist/cjs/restricted/dialog.js.map +1 -0
- package/dist/cjs/restricted/getBip32Entropy.js +76 -0
- package/dist/cjs/restricted/getBip32Entropy.js.map +1 -0
- package/dist/cjs/restricted/getBip32PublicKey.js +91 -0
- package/dist/cjs/restricted/getBip32PublicKey.js.map +1 -0
- package/dist/cjs/restricted/getBip44Entropy.js +77 -0
- package/dist/cjs/restricted/getBip44Entropy.js.map +1 -0
- package/dist/cjs/restricted/getEntropy.js +76 -0
- package/dist/cjs/restricted/getEntropy.js.map +1 -0
- package/dist/cjs/restricted/getLocale.js +49 -0
- package/dist/cjs/restricted/getLocale.js.map +1 -0
- package/dist/cjs/restricted/index.js +65 -0
- package/dist/cjs/restricted/index.js.map +1 -0
- package/dist/cjs/restricted/invokeSnap.js +99 -0
- package/dist/cjs/restricted/invokeSnap.js.map +1 -0
- package/dist/cjs/restricted/manageAccounts.js +68 -0
- package/dist/cjs/restricted/manageAccounts.js.map +1 -0
- package/dist/cjs/restricted/manageState.js +216 -0
- package/dist/cjs/restricted/manageState.js.map +1 -0
- package/dist/cjs/restricted/notify.js +94 -0
- package/dist/cjs/restricted/notify.js.map +1 -0
- package/dist/cjs/utils.js +98 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/permitted/common/snapInstallation.js +33 -0
- package/dist/esm/permitted/common/snapInstallation.js.map +1 -0
- package/dist/esm/permitted/getFile.js +45 -0
- package/dist/esm/permitted/getFile.js.map +1 -0
- package/dist/esm/permitted/getSnaps.js +31 -0
- package/dist/esm/permitted/getSnaps.js.map +1 -0
- package/dist/esm/permitted/handlers.js +15 -0
- package/dist/esm/permitted/handlers.js.map +1 -0
- package/dist/esm/permitted/index.js +4 -0
- package/dist/esm/permitted/index.js.map +1 -0
- package/dist/esm/permitted/invokeKeyring.js +80 -0
- package/dist/esm/permitted/invokeKeyring.js.map +1 -0
- package/dist/esm/permitted/invokeSnapSugar.js +60 -0
- package/dist/esm/permitted/invokeSnapSugar.js.map +1 -0
- package/dist/esm/permitted/middleware.js +34 -0
- package/dist/esm/permitted/middleware.js.map +1 -0
- package/dist/esm/permitted/requestSnaps.js +117 -0
- package/dist/esm/permitted/requestSnaps.js.map +1 -0
- package/dist/esm/request.js +3 -0
- package/dist/esm/request.js.map +1 -0
- package/dist/esm/restricted/caveats/index.js +20 -0
- package/dist/esm/restricted/caveats/index.js.map +1 -0
- package/dist/esm/restricted/caveats/permittedCoinTypes.js +77 -0
- package/dist/esm/restricted/caveats/permittedCoinTypes.js.map +1 -0
- package/dist/esm/restricted/caveats/permittedDerivationPaths.js +64 -0
- package/dist/esm/restricted/caveats/permittedDerivationPaths.js.map +1 -0
- package/dist/esm/restricted/caveats/snapIds.js +50 -0
- package/dist/esm/restricted/caveats/snapIds.js.map +1 -0
- package/dist/esm/restricted/dialog.js +137 -0
- package/dist/esm/restricted/dialog.js.map +1 -0
- package/dist/esm/restricted/getBip32Entropy.js +67 -0
- package/dist/esm/restricted/getBip32Entropy.js.map +1 -0
- package/dist/esm/restricted/getBip32PublicKey.js +79 -0
- package/dist/esm/restricted/getBip32PublicKey.js.map +1 -0
- package/dist/esm/restricted/getBip44Entropy.js +70 -0
- package/dist/esm/restricted/getBip44Entropy.js.map +1 -0
- package/dist/esm/restricted/getEntropy.js +58 -0
- package/dist/esm/restricted/getEntropy.js.map +1 -0
- package/dist/esm/restricted/getLocale.js +42 -0
- package/dist/esm/restricted/getLocale.js.map +1 -0
- package/dist/esm/restricted/index.js +29 -0
- package/dist/esm/restricted/index.js.map +1 -0
- package/dist/esm/restricted/invokeSnap.js +89 -0
- package/dist/esm/restricted/invokeSnap.js.map +1 -0
- package/dist/esm/restricted/manageAccounts.js +60 -0
- package/dist/esm/restricted/manageAccounts.js.map +1 -0
- package/dist/esm/restricted/manageState.js +221 -0
- package/dist/esm/restricted/manageState.js.map +1 -0
- package/dist/esm/restricted/notify.js +89 -0
- package/dist/esm/restricted/notify.js.map +1 -0
- package/dist/esm/utils.js +124 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/types/__fixtures__/entropy.d.ts +14 -0
- package/dist/types/__fixtures__/index.d.ts +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/permitted/common/snapInstallation.d.ts +16 -0
- package/dist/types/permitted/getFile.d.ts +14 -0
- package/dist/types/permitted/getSnaps.d.ts +13 -0
- package/dist/types/permitted/handlers.d.ts +14 -0
- package/dist/types/permitted/index.d.ts +5 -0
- package/dist/types/permitted/invokeKeyring.d.ts +17 -0
- package/dist/types/permitted/invokeSnapSugar.d.ts +32 -0
- package/dist/types/permitted/middleware.d.ts +10 -0
- package/dist/types/permitted/requestSnaps.d.ts +50 -0
- package/dist/types/request.d.ts +99 -0
- package/dist/types/restricted/caveats/index.d.ts +8 -0
- package/dist/types/restricted/caveats/permittedCoinTypes.d.ts +29 -0
- package/dist/types/restricted/caveats/permittedDerivationPaths.d.ts +32 -0
- package/dist/types/restricted/caveats/snapIds.d.ts +21 -0
- package/dist/types/restricted/dialog.d.ts +113 -0
- package/dist/types/restricted/getBip32Entropy.d.ts +45 -0
- package/dist/types/restricted/getBip32PublicKey.d.ts +60 -0
- package/dist/types/restricted/getBip44Entropy.d.ts +49 -0
- package/dist/types/restricted/getEntropy.d.ts +60 -0
- package/dist/types/restricted/getLocale.d.ts +47 -0
- package/dist/types/restricted/index.d.ts +232 -0
- package/dist/types/restricted/invokeSnap.d.ts +62 -0
- package/dist/types/restricted/manageAccounts.d.ts +63 -0
- package/dist/types/restricted/manageState.d.ts +127 -0
- package/dist/types/restricted/notify.d.ts +81 -0
- package/dist/types/utils.d.ts +94 -0
- package/package.json +88 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
methodName: function() {
|
|
13
|
+
return methodName;
|
|
14
|
+
},
|
|
15
|
+
specificationBuilder: function() {
|
|
16
|
+
return specificationBuilder;
|
|
17
|
+
},
|
|
18
|
+
manageAccountsImplementation: function() {
|
|
19
|
+
return manageAccountsImplementation;
|
|
20
|
+
},
|
|
21
|
+
manageAccountsBuilder: function() {
|
|
22
|
+
return manageAccountsBuilder;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const _permissioncontroller = require("@metamask/permission-controller");
|
|
26
|
+
const _utils = require("@metamask/utils");
|
|
27
|
+
const _superstruct = require("superstruct");
|
|
28
|
+
const SnapMessageStruct = (0, _superstruct.union)([
|
|
29
|
+
(0, _superstruct.object)({
|
|
30
|
+
method: (0, _superstruct.string)()
|
|
31
|
+
}),
|
|
32
|
+
(0, _superstruct.object)({
|
|
33
|
+
method: (0, _superstruct.string)(),
|
|
34
|
+
params: (0, _superstruct.union)([
|
|
35
|
+
(0, _superstruct.array)(_utils.JsonStruct),
|
|
36
|
+
(0, _superstruct.record)((0, _superstruct.string)(), _utils.JsonStruct)
|
|
37
|
+
])
|
|
38
|
+
})
|
|
39
|
+
]);
|
|
40
|
+
const methodName = 'snap_manageAccounts';
|
|
41
|
+
const specificationBuilder = ({ allowedCaveats = null, methodHooks })=>{
|
|
42
|
+
return {
|
|
43
|
+
permissionType: _permissioncontroller.PermissionType.RestrictedMethod,
|
|
44
|
+
targetName: methodName,
|
|
45
|
+
allowedCaveats,
|
|
46
|
+
methodImplementation: manageAccountsImplementation(methodHooks),
|
|
47
|
+
subjectTypes: [
|
|
48
|
+
_permissioncontroller.SubjectType.Snap
|
|
49
|
+
]
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
function manageAccountsImplementation({ getSnapKeyring }) {
|
|
53
|
+
return async function manageAccounts(options) {
|
|
54
|
+
const { context: { origin }, params } = options;
|
|
55
|
+
(0, _superstruct.assert)(params, SnapMessageStruct);
|
|
56
|
+
const keyring = await getSnapKeyring(origin);
|
|
57
|
+
return await keyring.handleKeyringSnapMessage(origin, params);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const manageAccountsBuilder = Object.freeze({
|
|
61
|
+
targetName: methodName,
|
|
62
|
+
specificationBuilder,
|
|
63
|
+
methodHooks: {
|
|
64
|
+
getSnapKeyring: true
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=manageAccounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/restricted/manageAccounts.ts"],"sourcesContent":["import type {\n RestrictedMethodOptions,\n ValidPermissionSpecification,\n PermissionSpecificationBuilder,\n} from '@metamask/permission-controller';\nimport { SubjectType, PermissionType } from '@metamask/permission-controller';\nimport type { Json, NonEmptyArray } from '@metamask/utils';\nimport { JsonStruct } from '@metamask/utils';\nimport type { Infer } from 'superstruct';\nimport { assert, string, object, union, array, record } from 'superstruct';\n\nconst SnapMessageStruct = union([\n object({\n method: string(),\n }),\n object({\n method: string(),\n params: union([array(JsonStruct), record(string(), JsonStruct)]),\n }),\n]);\n\ntype Message = Infer<typeof SnapMessageStruct>;\n\nexport const methodName = 'snap_manageAccounts';\n\nexport type ManageAccountsMethodHooks = {\n /**\n * Gets the snap keyring implementation.\n */\n getSnapKeyring: (snapOrigin: string) => Promise<{\n handleKeyringSnapMessage: (\n snapId: string,\n message: Message,\n ) => Promise<Json>;\n }>;\n};\n\ntype ManageAccountsSpecificationBuilderOptions = {\n allowedCaveats?: Readonly<NonEmptyArray<string>> | null;\n methodHooks: ManageAccountsMethodHooks;\n};\n\ntype ManageAccountsSpecification = ValidPermissionSpecification<{\n permissionType: PermissionType.RestrictedMethod;\n targetName: typeof methodName;\n methodImplementation: ReturnType<typeof manageAccountsImplementation>;\n allowedCaveats: Readonly<NonEmptyArray<string>> | null;\n}>;\n\n/**\n * The specification builder for the `snap_manageAccounts` permission.\n * `snap_manageAccounts` lets the Snap manage a set of accounts via a custom keyring.\n *\n * @param options - The specification builder options.\n * @param options.allowedCaveats - The optional allowed caveats for the permission.\n * @param options.methodHooks - The RPC method hooks needed by the method implementation.\n * @returns The specification for the `snap_manageAccounts` permission.\n */\nexport const specificationBuilder: PermissionSpecificationBuilder<\n PermissionType.RestrictedMethod,\n ManageAccountsSpecificationBuilderOptions,\n ManageAccountsSpecification\n> = ({\n allowedCaveats = null,\n methodHooks,\n}: ManageAccountsSpecificationBuilderOptions) => {\n return {\n permissionType: PermissionType.RestrictedMethod,\n targetName: methodName,\n allowedCaveats,\n methodImplementation: manageAccountsImplementation(methodHooks),\n subjectTypes: [SubjectType.Snap],\n };\n};\n\n/**\n * Builds the method implementation for `snap_manageAccounts`.\n *\n * @param hooks - The RPC method hooks.\n * @param hooks.getSnapKeyring - A function to get the snap keyring.\n * @returns The method implementation which either returns `null` for a\n * successful state update/deletion or returns the decrypted state.\n * @throws If the params are invalid.\n */\nexport function manageAccountsImplementation({\n getSnapKeyring,\n}: ManageAccountsMethodHooks) {\n return async function manageAccounts(\n options: RestrictedMethodOptions<Message>,\n ): Promise<Json> {\n const {\n context: { origin },\n params,\n } = options;\n\n assert(params, SnapMessageStruct);\n const keyring = await getSnapKeyring(origin);\n return await keyring.handleKeyringSnapMessage(origin, params);\n };\n}\n\nexport const manageAccountsBuilder = Object.freeze({\n targetName: methodName,\n specificationBuilder,\n methodHooks: {\n getSnapKeyring: true,\n },\n} as const);\n"],"names":["methodName","specificationBuilder","manageAccountsImplementation","manageAccountsBuilder","SnapMessageStruct","union","object","method","string","params","array","JsonStruct","record","allowedCaveats","methodHooks","permissionType","PermissionType","RestrictedMethod","targetName","methodImplementation","subjectTypes","SubjectType","Snap","getSnapKeyring","manageAccounts","options","context","origin","assert","keyring","handleKeyringSnapMessage","Object","freeze"],"mappings":";;;;;;;;;;;IAuBaA,UAAU;eAAVA;;IAmCAC,oBAAoB;eAApBA;;IA0BGC,4BAA4B;eAA5BA;;IAiBHC,qBAAqB;eAArBA;;;sCAhG+B;uBAEjB;6BAEkC;AAE7D,MAAMC,oBAAoBC,IAAAA,kBAAK,EAAC;IAC9BC,IAAAA,mBAAM,EAAC;QACLC,QAAQC,IAAAA,mBAAM;IAChB;IACAF,IAAAA,mBAAM,EAAC;QACLC,QAAQC,IAAAA,mBAAM;QACdC,QAAQJ,IAAAA,kBAAK,EAAC;YAACK,IAAAA,kBAAK,EAACC,iBAAU;YAAGC,IAAAA,mBAAM,EAACJ,IAAAA,mBAAM,KAAIG,iBAAU;SAAE;IACjE;CACD;AAIM,MAAMX,aAAa;AAmCnB,MAAMC,uBAIT,CAAC,EACHY,iBAAiB,IAAI,EACrBC,WAAW,EAC+B;IAC1C,OAAO;QACLC,gBAAgBC,oCAAc,CAACC,gBAAgB;QAC/CC,YAAYlB;QACZa;QACAM,sBAAsBjB,6BAA6BY;QACnDM,cAAc;YAACC,iCAAW,CAACC,IAAI;SAAC;IAClC;AACF;AAWO,SAASpB,6BAA6B,EAC3CqB,cAAc,EACY;IAC1B,OAAO,eAAeC,eACpBC,OAAyC;QAEzC,MAAM,EACJC,SAAS,EAAEC,MAAM,EAAE,EACnBlB,MAAM,EACP,GAAGgB;QAEJG,IAAAA,mBAAM,EAACnB,QAAQL;QACf,MAAMyB,UAAU,MAAMN,eAAeI;QACrC,OAAO,MAAME,QAAQC,wBAAwB,CAACH,QAAQlB;IACxD;AACF;AAEO,MAAMN,wBAAwB4B,OAAOC,MAAM,CAAC;IACjDd,YAAYlB;IACZC;IACAa,aAAa;QACXS,gBAAgB;IAClB;AACF"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
ManageStateOperation: function() {
|
|
13
|
+
return ManageStateOperation;
|
|
14
|
+
},
|
|
15
|
+
STATE_ENCRYPTION_SALT: function() {
|
|
16
|
+
return STATE_ENCRYPTION_SALT;
|
|
17
|
+
},
|
|
18
|
+
specificationBuilder: function() {
|
|
19
|
+
return specificationBuilder;
|
|
20
|
+
},
|
|
21
|
+
manageStateBuilder: function() {
|
|
22
|
+
return manageStateBuilder;
|
|
23
|
+
},
|
|
24
|
+
STORAGE_SIZE_LIMIT: function() {
|
|
25
|
+
return STORAGE_SIZE_LIMIT;
|
|
26
|
+
},
|
|
27
|
+
getManageStateImplementation: function() {
|
|
28
|
+
return getManageStateImplementation;
|
|
29
|
+
},
|
|
30
|
+
getValidatedParams: function() {
|
|
31
|
+
return getValidatedParams;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const _permissioncontroller = require("@metamask/permission-controller");
|
|
35
|
+
const _rpcerrors = require("@metamask/rpc-errors");
|
|
36
|
+
const _snapsutils = require("@metamask/snaps-utils");
|
|
37
|
+
const _utils = require("@metamask/utils");
|
|
38
|
+
const _utils1 = require("../utils");
|
|
39
|
+
const STATE_ENCRYPTION_SALT = 'snap_manageState encryption';
|
|
40
|
+
const methodName = 'snap_manageState';
|
|
41
|
+
const specificationBuilder = ({ allowedCaveats = null, methodHooks })=>{
|
|
42
|
+
return {
|
|
43
|
+
permissionType: _permissioncontroller.PermissionType.RestrictedMethod,
|
|
44
|
+
targetName: methodName,
|
|
45
|
+
allowedCaveats,
|
|
46
|
+
methodImplementation: getManageStateImplementation(methodHooks),
|
|
47
|
+
subjectTypes: [
|
|
48
|
+
_permissioncontroller.SubjectType.Snap
|
|
49
|
+
]
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const methodHooks = {
|
|
53
|
+
getMnemonic: true,
|
|
54
|
+
getUnlockPromise: true,
|
|
55
|
+
clearSnapState: true,
|
|
56
|
+
getSnapState: true,
|
|
57
|
+
updateSnapState: true,
|
|
58
|
+
encrypt: true,
|
|
59
|
+
decrypt: true
|
|
60
|
+
};
|
|
61
|
+
const manageStateBuilder = Object.freeze({
|
|
62
|
+
targetName: methodName,
|
|
63
|
+
specificationBuilder,
|
|
64
|
+
methodHooks
|
|
65
|
+
});
|
|
66
|
+
var ManageStateOperation;
|
|
67
|
+
(function(ManageStateOperation) {
|
|
68
|
+
ManageStateOperation["ClearState"] = 'clear';
|
|
69
|
+
ManageStateOperation["GetState"] = 'get';
|
|
70
|
+
ManageStateOperation["UpdateState"] = 'update';
|
|
71
|
+
})(ManageStateOperation || (ManageStateOperation = {}));
|
|
72
|
+
const STORAGE_SIZE_LIMIT = 104857600; // In bytes (100MB)
|
|
73
|
+
/**
|
|
74
|
+
* Get a deterministic encryption key to use for encrypting and decrypting the
|
|
75
|
+
* state.
|
|
76
|
+
*
|
|
77
|
+
* This key should only be used for state encryption using `snap_manageState`.
|
|
78
|
+
* To get other encryption keys, a different salt can be used.
|
|
79
|
+
*
|
|
80
|
+
* @param args - The encryption key args.
|
|
81
|
+
* @param args.snapId - The ID of the snap to get the encryption key for.
|
|
82
|
+
* @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key
|
|
83
|
+
* from.
|
|
84
|
+
* @returns The state encryption key.
|
|
85
|
+
*/ async function getEncryptionKey({ mnemonicPhrase, snapId }) {
|
|
86
|
+
return await (0, _utils1.deriveEntropy)({
|
|
87
|
+
mnemonicPhrase,
|
|
88
|
+
input: snapId,
|
|
89
|
+
salt: STATE_ENCRYPTION_SALT,
|
|
90
|
+
magic: _snapsutils.STATE_ENCRYPTION_MAGIC_VALUE
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Encrypt the state using a deterministic encryption algorithm, based on the
|
|
95
|
+
* snap ID and mnemonic phrase.
|
|
96
|
+
*
|
|
97
|
+
* @param args - The encryption args.
|
|
98
|
+
* @param args.state - The state to encrypt.
|
|
99
|
+
* @param args.encryptFunction - The function to use for encrypting the state.
|
|
100
|
+
* @param args.snapId - The ID of the snap to get the encryption key for.
|
|
101
|
+
* @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key
|
|
102
|
+
* from.
|
|
103
|
+
* @returns The encrypted state.
|
|
104
|
+
*/ async function encryptState({ state, encryptFunction, ...keyArgs }) {
|
|
105
|
+
const encryptionKey = await getEncryptionKey(keyArgs);
|
|
106
|
+
return await encryptFunction(encryptionKey, state);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Decrypt the state using a deterministic decryption algorithm, based on the
|
|
110
|
+
* snap ID and mnemonic phrase.
|
|
111
|
+
*
|
|
112
|
+
* @param args - The encryption args.
|
|
113
|
+
* @param args.state - The state to decrypt.
|
|
114
|
+
* @param args.decryptFunction - The function to use for decrypting the state.
|
|
115
|
+
* @param args.snapId - The ID of the snap to get the encryption key for.
|
|
116
|
+
* @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key
|
|
117
|
+
* from.
|
|
118
|
+
* @returns The encrypted state.
|
|
119
|
+
*/ async function decryptState({ state, decryptFunction, ...keyArgs }) {
|
|
120
|
+
try {
|
|
121
|
+
const encryptionKey = await getEncryptionKey(keyArgs);
|
|
122
|
+
const decryptedState = await decryptFunction(encryptionKey, state);
|
|
123
|
+
(0, _utils.assert)((0, _utils.isValidJson)(decryptedState));
|
|
124
|
+
return decryptedState;
|
|
125
|
+
} catch {
|
|
126
|
+
throw _rpcerrors.rpcErrors.internal({
|
|
127
|
+
message: 'Failed to decrypt snap state, the state must be corrupted.'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function getManageStateImplementation({ getMnemonic, getUnlockPromise, clearSnapState, getSnapState, updateSnapState, encrypt, decrypt }) {
|
|
132
|
+
return async function manageState(options) {
|
|
133
|
+
const { params = {}, method, context: { origin } } = options;
|
|
134
|
+
const { operation, newState } = getValidatedParams(params, method);
|
|
135
|
+
await getUnlockPromise(true);
|
|
136
|
+
const mnemonicPhrase = await getMnemonic();
|
|
137
|
+
switch(operation){
|
|
138
|
+
case ManageStateOperation.ClearState:
|
|
139
|
+
await clearSnapState(origin);
|
|
140
|
+
return null;
|
|
141
|
+
case ManageStateOperation.GetState:
|
|
142
|
+
{
|
|
143
|
+
const state = await getSnapState(origin);
|
|
144
|
+
if (state === null) {
|
|
145
|
+
return state;
|
|
146
|
+
}
|
|
147
|
+
return await decryptState({
|
|
148
|
+
state,
|
|
149
|
+
decryptFunction: decrypt,
|
|
150
|
+
mnemonicPhrase,
|
|
151
|
+
snapId: origin
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
case ManageStateOperation.UpdateState:
|
|
155
|
+
{
|
|
156
|
+
(0, _utils.assert)(newState);
|
|
157
|
+
const encryptedState = await encryptState({
|
|
158
|
+
state: newState,
|
|
159
|
+
encryptFunction: encrypt,
|
|
160
|
+
mnemonicPhrase,
|
|
161
|
+
snapId: origin
|
|
162
|
+
});
|
|
163
|
+
await updateSnapState(origin, encryptedState);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
default:
|
|
167
|
+
throw _rpcerrors.rpcErrors.invalidParams(`Invalid ${method} operation: "${operation}"`);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function getValidatedParams(params, method, storageSizeLimit = STORAGE_SIZE_LIMIT) {
|
|
172
|
+
if (!(0, _utils.isObject)(params)) {
|
|
173
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
174
|
+
message: 'Expected params to be a single object.'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const { operation, newState } = params;
|
|
178
|
+
if (!operation || typeof operation !== 'string' || !Object.values(ManageStateOperation).includes(operation)) {
|
|
179
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
180
|
+
message: 'Must specify a valid manage state "operation".'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (operation === ManageStateOperation.UpdateState) {
|
|
184
|
+
if (!(0, _utils.isObject)(newState)) {
|
|
185
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
186
|
+
message: `Invalid ${method} "updateState" parameter: The new state must be a plain object.`,
|
|
187
|
+
data: {
|
|
188
|
+
receivedNewState: typeof newState === 'undefined' ? 'undefined' : newState
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
let size;
|
|
193
|
+
try {
|
|
194
|
+
// `getJsonSize` will throw if the state is not JSON serializable.
|
|
195
|
+
size = (0, _utils.getJsonSize)(newState);
|
|
196
|
+
} catch {
|
|
197
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
198
|
+
message: `Invalid ${method} "updateState" parameter: The new state must be JSON serializable.`,
|
|
199
|
+
data: {
|
|
200
|
+
receivedNewState: typeof newState === 'undefined' ? 'undefined' : newState
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (size > storageSizeLimit) {
|
|
205
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
206
|
+
message: `Invalid ${method} "updateState" parameter: The new state must not exceed ${storageSizeLimit} bytes in size.`,
|
|
207
|
+
data: {
|
|
208
|
+
receivedNewState: typeof newState === 'undefined' ? 'undefined' : newState
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return params;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
//# sourceMappingURL=manageState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/restricted/manageState.ts"],"sourcesContent":["import type {\n PermissionSpecificationBuilder,\n RestrictedMethodOptions,\n ValidPermissionSpecification,\n} from '@metamask/permission-controller';\nimport { PermissionType, SubjectType } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { EnumToUnion } from '@metamask/snaps-utils';\nimport { STATE_ENCRYPTION_MAGIC_VALUE } from '@metamask/snaps-utils';\nimport type { Json, NonEmptyArray, Hex } from '@metamask/utils';\nimport { isObject, getJsonSize, assert, isValidJson } from '@metamask/utils';\n\nimport type { MethodHooksObject } from '../utils';\nimport { deriveEntropy } from '../utils';\n\n// The salt used for SIP-6-based entropy derivation.\nexport const STATE_ENCRYPTION_SALT = 'snap_manageState encryption';\n\nconst methodName = 'snap_manageState';\n\nexport type ManageStateMethodHooks = {\n /**\n * @returns The mnemonic of the user's primary keyring.\n */\n getMnemonic: () => Promise<Uint8Array>;\n\n /**\n * Waits for the extension to be unlocked.\n *\n * @returns A promise that resolves once the extension is unlocked.\n */\n getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise<void>;\n\n /**\n * A function that clears the state of the requesting Snap.\n */\n clearSnapState: (snapId: string) => Promise<void>;\n\n /**\n * A function that gets the encrypted state of the requesting Snap.\n *\n * @returns The current state of the Snap.\n */\n getSnapState: (snapId: string) => Promise<string>;\n\n /**\n * A function that updates the state of the requesting Snap.\n *\n * @param newState - The new state of the Snap.\n */\n updateSnapState: (snapId: string, newState: string) => Promise<void>;\n\n /**\n * Encrypts data with a key. This is assumed to perform symmetric encryption.\n *\n * @param key - The key to use for encryption, in hexadecimal format.\n * @param data - The JSON data to encrypt.\n * @returns The ciphertext as a string. The format for this string is\n * dependent on the implementation, but MUST be a string.\n */\n encrypt: (key: string, data: Json) => Promise<string>;\n\n /**\n * Decrypts data with a key. This is assumed to perform symmetric decryption.\n *\n * @param key - The key to use for decryption, in hexadecimal format.\n * @param cipherText - The ciphertext to decrypt. The format for this string\n * is dependent on the implementation, but MUST be a string.\n * @returns The decrypted data as a JSON object.\n */\n decrypt: (key: Hex, cipherText: string) => Promise<unknown>;\n};\n\ntype ManageStateSpecificationBuilderOptions = {\n allowedCaveats?: Readonly<NonEmptyArray<string>> | null;\n methodHooks: ManageStateMethodHooks;\n};\n\ntype ManageStateSpecification = ValidPermissionSpecification<{\n permissionType: PermissionType.RestrictedMethod;\n targetName: typeof methodName;\n methodImplementation: ReturnType<typeof getManageStateImplementation>;\n allowedCaveats: Readonly<NonEmptyArray<string>> | null;\n}>;\n\n/**\n * The specification builder for the `snap_manageState` permission.\n * `snap_manageState` lets the Snap store and manage some of its state on\n * your device.\n *\n * @param options - The specification builder options.\n * @param options.allowedCaveats - The optional allowed caveats for the permission.\n * @param options.methodHooks - The RPC method hooks needed by the method implementation.\n * @returns The specification for the `snap_manageState` permission.\n */\nexport const specificationBuilder: PermissionSpecificationBuilder<\n PermissionType.RestrictedMethod,\n ManageStateSpecificationBuilderOptions,\n ManageStateSpecification\n> = ({\n allowedCaveats = null,\n methodHooks,\n}: ManageStateSpecificationBuilderOptions) => {\n return {\n permissionType: PermissionType.RestrictedMethod,\n targetName: methodName,\n allowedCaveats,\n methodImplementation: getManageStateImplementation(methodHooks),\n subjectTypes: [SubjectType.Snap],\n };\n};\n\nconst methodHooks: MethodHooksObject<ManageStateMethodHooks> = {\n getMnemonic: true,\n getUnlockPromise: true,\n clearSnapState: true,\n getSnapState: true,\n updateSnapState: true,\n encrypt: true,\n decrypt: true,\n};\n\nexport const manageStateBuilder = Object.freeze({\n targetName: methodName,\n specificationBuilder,\n methodHooks,\n} as const);\n\nexport enum ManageStateOperation {\n ClearState = 'clear',\n GetState = 'get',\n UpdateState = 'update',\n}\n\nexport type ManageStateArgs = {\n operation: EnumToUnion<ManageStateOperation>;\n newState?: Record<string, Json>;\n};\n\nexport const STORAGE_SIZE_LIMIT = 104857600; // In bytes (100MB)\n\ntype GetEncryptionKeyArgs = {\n snapId: string;\n mnemonicPhrase: Uint8Array;\n};\n\n/**\n * Get a deterministic encryption key to use for encrypting and decrypting the\n * state.\n *\n * This key should only be used for state encryption using `snap_manageState`.\n * To get other encryption keys, a different salt can be used.\n *\n * @param args - The encryption key args.\n * @param args.snapId - The ID of the snap to get the encryption key for.\n * @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key\n * from.\n * @returns The state encryption key.\n */\nasync function getEncryptionKey({\n mnemonicPhrase,\n snapId,\n}: GetEncryptionKeyArgs) {\n return await deriveEntropy({\n mnemonicPhrase,\n input: snapId,\n salt: STATE_ENCRYPTION_SALT,\n magic: STATE_ENCRYPTION_MAGIC_VALUE,\n });\n}\n\ntype EncryptStateArgs = GetEncryptionKeyArgs & {\n state: Json;\n encryptFunction: ManageStateMethodHooks['encrypt'];\n};\n\n/**\n * Encrypt the state using a deterministic encryption algorithm, based on the\n * snap ID and mnemonic phrase.\n *\n * @param args - The encryption args.\n * @param args.state - The state to encrypt.\n * @param args.encryptFunction - The function to use for encrypting the state.\n * @param args.snapId - The ID of the snap to get the encryption key for.\n * @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key\n * from.\n * @returns The encrypted state.\n */\nasync function encryptState({\n state,\n encryptFunction,\n ...keyArgs\n}: EncryptStateArgs) {\n const encryptionKey = await getEncryptionKey(keyArgs);\n return await encryptFunction(encryptionKey, state);\n}\n\ntype DecryptStateArgs = GetEncryptionKeyArgs & {\n state: string;\n decryptFunction: ManageStateMethodHooks['decrypt'];\n};\n\n/**\n * Decrypt the state using a deterministic decryption algorithm, based on the\n * snap ID and mnemonic phrase.\n *\n * @param args - The encryption args.\n * @param args.state - The state to decrypt.\n * @param args.decryptFunction - The function to use for decrypting the state.\n * @param args.snapId - The ID of the snap to get the encryption key for.\n * @param args.mnemonicPhrase - The mnemonic phrase to derive the encryption key\n * from.\n * @returns The encrypted state.\n */\nasync function decryptState({\n state,\n decryptFunction,\n ...keyArgs\n}: DecryptStateArgs) {\n try {\n const encryptionKey = await getEncryptionKey(keyArgs);\n const decryptedState = await decryptFunction(encryptionKey, state);\n\n assert(isValidJson(decryptedState));\n\n return decryptedState as Record<string, Json>;\n } catch {\n throw rpcErrors.internal({\n message: 'Failed to decrypt snap state, the state must be corrupted.',\n });\n }\n}\n\n/**\n * Builds the method implementation for `snap_manageState`.\n *\n * @param hooks - The RPC method hooks.\n * @param hooks.clearSnapState - A function that clears the state stored for a\n * snap.\n * @param hooks.getSnapState - A function that fetches the persisted decrypted\n * state for a snap.\n * @param hooks.updateSnapState - A function that updates the state stored for a\n * snap.\n * @param hooks.getMnemonic - A function to retrieve the Secret Recovery Phrase\n * of the user.\n * @param hooks.getUnlockPromise - A function that resolves once the MetaMask\n * extension is unlocked and prompts the user to unlock their MetaMask if it is\n * locked.\n * @param hooks.encrypt - A function that encrypts the given state.\n * @param hooks.decrypt - A function that decrypts the given state.\n * @returns The method implementation which either returns `null` for a\n * successful state update/deletion or returns the decrypted state.\n * @throws If the params are invalid.\n */\nexport function getManageStateImplementation({\n getMnemonic,\n getUnlockPromise,\n clearSnapState,\n getSnapState,\n updateSnapState,\n encrypt,\n decrypt,\n}: ManageStateMethodHooks) {\n return async function manageState(\n options: RestrictedMethodOptions<ManageStateArgs>,\n ): Promise<null | Record<string, Json>> {\n const {\n params = {},\n method,\n context: { origin },\n } = options;\n const { operation, newState } = getValidatedParams(params, method);\n\n await getUnlockPromise(true);\n const mnemonicPhrase = await getMnemonic();\n\n switch (operation) {\n case ManageStateOperation.ClearState:\n await clearSnapState(origin);\n return null;\n\n case ManageStateOperation.GetState: {\n const state = await getSnapState(origin);\n if (state === null) {\n return state;\n }\n return await decryptState({\n state,\n decryptFunction: decrypt,\n mnemonicPhrase,\n snapId: origin,\n });\n }\n\n case ManageStateOperation.UpdateState: {\n assert(newState);\n\n const encryptedState = await encryptState({\n state: newState,\n encryptFunction: encrypt,\n mnemonicPhrase,\n snapId: origin,\n });\n\n await updateSnapState(origin, encryptedState);\n return null;\n }\n\n default:\n throw rpcErrors.invalidParams(\n `Invalid ${method} operation: \"${operation as string}\"`,\n );\n }\n };\n}\n\n/**\n * Validates the manageState method `params` and returns them cast to the correct\n * type. Throws if validation fails.\n *\n * @param params - The unvalidated params object from the method request.\n * @param method - RPC method name used for debugging errors.\n * @param storageSizeLimit - Maximum allowed size (in bytes) of a new state object.\n * @returns The validated method parameter object.\n */\nexport function getValidatedParams(\n params: unknown,\n method: string,\n storageSizeLimit = STORAGE_SIZE_LIMIT,\n): ManageStateArgs {\n if (!isObject(params)) {\n throw rpcErrors.invalidParams({\n message: 'Expected params to be a single object.',\n });\n }\n\n const { operation, newState } = params;\n\n if (\n !operation ||\n typeof operation !== 'string' ||\n !(Object.values(ManageStateOperation) as string[]).includes(operation)\n ) {\n throw rpcErrors.invalidParams({\n message: 'Must specify a valid manage state \"operation\".',\n });\n }\n\n if (operation === ManageStateOperation.UpdateState) {\n if (!isObject(newState)) {\n throw rpcErrors.invalidParams({\n message: `Invalid ${method} \"updateState\" parameter: The new state must be a plain object.`,\n data: {\n receivedNewState:\n typeof newState === 'undefined' ? 'undefined' : newState,\n },\n });\n }\n\n let size;\n try {\n // `getJsonSize` will throw if the state is not JSON serializable.\n size = getJsonSize(newState);\n } catch {\n throw rpcErrors.invalidParams({\n message: `Invalid ${method} \"updateState\" parameter: The new state must be JSON serializable.`,\n data: {\n receivedNewState:\n typeof newState === 'undefined' ? 'undefined' : newState,\n },\n });\n }\n\n if (size > storageSizeLimit) {\n throw rpcErrors.invalidParams({\n message: `Invalid ${method} \"updateState\" parameter: The new state must not exceed ${storageSizeLimit} bytes in size.`,\n data: {\n receivedNewState:\n typeof newState === 'undefined' ? 'undefined' : newState,\n },\n });\n }\n }\n\n return params as ManageStateArgs;\n}\n"],"names":["STATE_ENCRYPTION_SALT","specificationBuilder","manageStateBuilder","STORAGE_SIZE_LIMIT","getManageStateImplementation","getValidatedParams","methodName","allowedCaveats","methodHooks","permissionType","PermissionType","RestrictedMethod","targetName","methodImplementation","subjectTypes","SubjectType","Snap","getMnemonic","getUnlockPromise","clearSnapState","getSnapState","updateSnapState","encrypt","decrypt","Object","freeze","ManageStateOperation","ClearState","GetState","UpdateState","getEncryptionKey","mnemonicPhrase","snapId","deriveEntropy","input","salt","magic","STATE_ENCRYPTION_MAGIC_VALUE","encryptState","state","encryptFunction","keyArgs","encryptionKey","decryptState","decryptFunction","decryptedState","assert","isValidJson","rpcErrors","internal","message","manageState","options","params","method","context","origin","operation","newState","encryptedState","invalidParams","storageSizeLimit","isObject","values","includes","data","receivedNewState","size","getJsonSize"],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
NotificationType: function() {
|
|
13
|
+
return NotificationType;
|
|
14
|
+
},
|
|
15
|
+
specificationBuilder: function() {
|
|
16
|
+
return specificationBuilder;
|
|
17
|
+
},
|
|
18
|
+
notifyBuilder: function() {
|
|
19
|
+
return notifyBuilder;
|
|
20
|
+
},
|
|
21
|
+
getImplementation: function() {
|
|
22
|
+
return getImplementation;
|
|
23
|
+
},
|
|
24
|
+
getValidatedParams: function() {
|
|
25
|
+
return getValidatedParams;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const _permissioncontroller = require("@metamask/permission-controller");
|
|
29
|
+
const _rpcerrors = require("@metamask/rpc-errors");
|
|
30
|
+
const _utils = require("@metamask/utils");
|
|
31
|
+
const methodName = 'snap_notify';
|
|
32
|
+
var NotificationType;
|
|
33
|
+
(function(NotificationType) {
|
|
34
|
+
NotificationType["InApp"] = 'inApp';
|
|
35
|
+
NotificationType["Native"] = 'native';
|
|
36
|
+
})(NotificationType || (NotificationType = {}));
|
|
37
|
+
const specificationBuilder = ({ allowedCaveats = null, methodHooks })=>{
|
|
38
|
+
return {
|
|
39
|
+
permissionType: _permissioncontroller.PermissionType.RestrictedMethod,
|
|
40
|
+
targetName: methodName,
|
|
41
|
+
allowedCaveats,
|
|
42
|
+
methodImplementation: getImplementation(methodHooks),
|
|
43
|
+
subjectTypes: [
|
|
44
|
+
_permissioncontroller.SubjectType.Snap
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const methodHooks = {
|
|
49
|
+
showNativeNotification: true,
|
|
50
|
+
showInAppNotification: true
|
|
51
|
+
};
|
|
52
|
+
const notifyBuilder = Object.freeze({
|
|
53
|
+
targetName: methodName,
|
|
54
|
+
specificationBuilder,
|
|
55
|
+
methodHooks
|
|
56
|
+
});
|
|
57
|
+
function getImplementation({ showNativeNotification, showInAppNotification }) {
|
|
58
|
+
return async function implementation(args) {
|
|
59
|
+
const { params, context: { origin } } = args;
|
|
60
|
+
const validatedParams = getValidatedParams(params);
|
|
61
|
+
switch(validatedParams.type){
|
|
62
|
+
case NotificationType.Native:
|
|
63
|
+
return await showNativeNotification(origin, validatedParams);
|
|
64
|
+
case NotificationType.InApp:
|
|
65
|
+
return await showInAppNotification(origin, validatedParams);
|
|
66
|
+
default:
|
|
67
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
68
|
+
message: 'Must specify a valid notification "type".'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function getValidatedParams(params) {
|
|
74
|
+
if (!(0, _utils.isObject)(params)) {
|
|
75
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
76
|
+
message: 'Expected params to be a single object.'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const { type, message } = params;
|
|
80
|
+
if (!type || typeof type !== 'string' || !Object.values(NotificationType).includes(type)) {
|
|
81
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
82
|
+
message: 'Must specify a valid notification "type".'
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// Set to the max message length on a Mac notification for now.
|
|
86
|
+
if (!message || typeof message !== 'string' || message.length >= 50) {
|
|
87
|
+
throw _rpcerrors.rpcErrors.invalidParams({
|
|
88
|
+
message: 'Must specify a non-empty string "message" less than 50 characters long.'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return params;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/restricted/notify.ts"],"sourcesContent":["import type {\n PermissionSpecificationBuilder,\n RestrictedMethodOptions,\n ValidPermissionSpecification,\n} from '@metamask/permission-controller';\nimport { PermissionType, SubjectType } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { EnumToUnion } from '@metamask/snaps-utils';\nimport type { NonEmptyArray } from '@metamask/utils';\nimport { isObject } from '@metamask/utils';\n\nimport type { MethodHooksObject } from '../utils';\n\nconst methodName = 'snap_notify';\n\n// TODO: Move all the types to a shared place when implementing more\n// notifications.\nexport enum NotificationType {\n InApp = 'inApp',\n Native = 'native',\n}\n\nexport type NotificationArgs = {\n /**\n * Enum type to determine notification type.\n */\n type: EnumToUnion<NotificationType>;\n\n /**\n * A message to show on the notification.\n */\n message: string;\n};\n\nexport type NotifyMethodHooks = {\n /**\n * @param snapId - The ID of the Snap that created the notification.\n * @param args - The notification arguments.\n */\n showNativeNotification: (\n snapId: string,\n args: NotificationArgs,\n ) => Promise<null>;\n\n /**\n * @param snapId - The ID of the Snap that created the notification.\n * @param args - The notification arguments.\n */\n showInAppNotification: (\n snapId: string,\n args: NotificationArgs,\n ) => Promise<null>;\n};\n\ntype SpecificationBuilderOptions = {\n allowedCaveats?: Readonly<NonEmptyArray<string>> | null;\n methodHooks: NotifyMethodHooks;\n};\n\ntype Specification = ValidPermissionSpecification<{\n permissionType: PermissionType.RestrictedMethod;\n targetName: typeof methodName;\n methodImplementation: ReturnType<typeof getImplementation>;\n allowedCaveats: Readonly<NonEmptyArray<string>> | null;\n}>;\n\n/**\n * The specification builder for the `snap_notify` permission.\n * `snap_notify` allows snaps to send multiple types of notifications to its users.\n *\n * @param options - The specification builder options.\n * @param options.allowedCaveats - The optional allowed caveats for the permission.\n * @param options.methodHooks - The RPC method hooks needed by the method implementation.\n * @returns The specification for the `snap_notify` permission.\n */\nexport const specificationBuilder: PermissionSpecificationBuilder<\n PermissionType.RestrictedMethod,\n SpecificationBuilderOptions,\n Specification\n> = ({ allowedCaveats = null, methodHooks }: SpecificationBuilderOptions) => {\n return {\n permissionType: PermissionType.RestrictedMethod,\n targetName: methodName,\n allowedCaveats,\n methodImplementation: getImplementation(methodHooks),\n subjectTypes: [SubjectType.Snap],\n };\n};\n\nconst methodHooks: MethodHooksObject<NotifyMethodHooks> = {\n showNativeNotification: true,\n showInAppNotification: true,\n};\n\nexport const notifyBuilder = Object.freeze({\n targetName: methodName,\n specificationBuilder,\n methodHooks,\n} as const);\n\n/**\n * Builds the method implementation for `snap_notify`.\n *\n * @param hooks - The RPC method hooks.\n * @param hooks.showNativeNotification - A function that shows a native browser notification.\n * @param hooks.showInAppNotification - A function that shows a notification in the MetaMask UI.\n * @returns The method implementation which returns `null` on success.\n * @throws If the params are invalid.\n */\nexport function getImplementation({\n showNativeNotification,\n showInAppNotification,\n}: NotifyMethodHooks) {\n return async function implementation(\n args: RestrictedMethodOptions<NotificationArgs>,\n ): Promise<null> {\n const {\n params,\n context: { origin },\n } = args;\n\n const validatedParams = getValidatedParams(params);\n\n switch (validatedParams.type) {\n case NotificationType.Native:\n return await showNativeNotification(origin, validatedParams);\n case NotificationType.InApp:\n return await showInAppNotification(origin, validatedParams);\n default:\n throw rpcErrors.invalidParams({\n message: 'Must specify a valid notification \"type\".',\n });\n }\n };\n}\n\n/**\n * Validates the notify method `params` and returns them cast to the correct\n * type. Throws if validation fails.\n *\n * @param params - The unvalidated params object from the method request.\n * @returns The validated method parameter object.\n */\nexport function getValidatedParams(params: unknown): NotificationArgs {\n if (!isObject(params)) {\n throw rpcErrors.invalidParams({\n message: 'Expected params to be a single object.',\n });\n }\n\n const { type, message } = params;\n\n if (\n !type ||\n typeof type !== 'string' ||\n !Object.values(NotificationType).includes(type as NotificationType)\n ) {\n throw rpcErrors.invalidParams({\n message: 'Must specify a valid notification \"type\".',\n });\n }\n\n // Set to the max message length on a Mac notification for now.\n if (!message || typeof message !== 'string' || message.length >= 50) {\n throw rpcErrors.invalidParams({\n message:\n 'Must specify a non-empty string \"message\" less than 50 characters long.',\n });\n }\n\n return params as NotificationArgs;\n}\n"],"names":["specificationBuilder","notifyBuilder","getImplementation","getValidatedParams","methodName","NotificationType","InApp","Native","allowedCaveats","methodHooks","permissionType","PermissionType","RestrictedMethod","targetName","methodImplementation","subjectTypes","SubjectType","Snap","showNativeNotification","showInAppNotification","Object","freeze","implementation","args","params","context","origin","validatedParams","type","rpcErrors","invalidParams","message","isObject","values","includes","length"],"mappings":";;;;;;;;;;;;;;IA2EaA,oBAAoB;eAApBA;;IAmBAC,aAAa;eAAbA;;IAeGC,iBAAiB;eAAjBA;;IAkCAC,kBAAkB;eAAlBA;;;sCA1I4B;2BAClB;uBAGD;AAIzB,MAAMC,aAAa;IAIZ;UAAKC,gBAAgB;IAAhBA,iBACVC,WAAQ;IADED,iBAEVE,YAAS;GAFCF,qBAAAA;AA0DL,MAAML,uBAIT,CAAC,EAAEQ,iBAAiB,IAAI,EAAEC,WAAW,EAA+B;IACtE,OAAO;QACLC,gBAAgBC,oCAAc,CAACC,gBAAgB;QAC/CC,YAAYT;QACZI;QACAM,sBAAsBZ,kBAAkBO;QACxCM,cAAc;YAACC,iCAAW,CAACC,IAAI;SAAC;IAClC;AACF;AAEA,MAAMR,cAAoD;IACxDS,wBAAwB;IACxBC,uBAAuB;AACzB;AAEO,MAAMlB,gBAAgBmB,OAAOC,MAAM,CAAC;IACzCR,YAAYT;IACZJ;IACAS;AACF;AAWO,SAASP,kBAAkB,EAChCgB,sBAAsB,EACtBC,qBAAqB,EACH;IAClB,OAAO,eAAeG,eACpBC,IAA+C;QAE/C,MAAM,EACJC,MAAM,EACNC,SAAS,EAAEC,MAAM,EAAE,EACpB,GAAGH;QAEJ,MAAMI,kBAAkBxB,mBAAmBqB;QAE3C,OAAQG,gBAAgBC,IAAI;YAC1B,KAAKvB,iBAAiBE,MAAM;gBAC1B,OAAO,MAAMW,uBAAuBQ,QAAQC;YAC9C,KAAKtB,iBAAiBC,KAAK;gBACzB,OAAO,MAAMa,sBAAsBO,QAAQC;YAC7C;gBACE,MAAME,oBAAS,CAACC,aAAa,CAAC;oBAC5BC,SAAS;gBACX;QACJ;IACF;AACF;AASO,SAAS5B,mBAAmBqB,MAAe;IAChD,IAAI,CAACQ,IAAAA,eAAQ,EAACR,SAAS;QACrB,MAAMK,oBAAS,CAACC,aAAa,CAAC;YAC5BC,SAAS;QACX;IACF;IAEA,MAAM,EAAEH,IAAI,EAAEG,OAAO,EAAE,GAAGP;IAE1B,IACE,CAACI,QACD,OAAOA,SAAS,YAChB,CAACR,OAAOa,MAAM,CAAC5B,kBAAkB6B,QAAQ,CAACN,OAC1C;QACA,MAAMC,oBAAS,CAACC,aAAa,CAAC;YAC5BC,SAAS;QACX;IACF;IAEA,+DAA+D;IAC/D,IAAI,CAACA,WAAW,OAAOA,YAAY,YAAYA,QAAQI,MAAM,IAAI,IAAI;QACnE,MAAMN,oBAAS,CAACC,aAAa,CAAC;YAC5BC,SACE;QACJ;IACF;IAEA,OAAOP;AACT"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
selectHooks: function() {
|
|
13
|
+
return selectHooks;
|
|
14
|
+
},
|
|
15
|
+
deriveEntropy: function() {
|
|
16
|
+
return deriveEntropy;
|
|
17
|
+
},
|
|
18
|
+
getPathPrefix: function() {
|
|
19
|
+
return getPathPrefix;
|
|
20
|
+
},
|
|
21
|
+
getNode: function() {
|
|
22
|
+
return getNode;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const _keytree = require("@metamask/key-tree");
|
|
26
|
+
const _utils = require("@metamask/utils");
|
|
27
|
+
const _sha3 = require("@noble/hashes/sha3");
|
|
28
|
+
const HARDENED_VALUE = 0x80000000;
|
|
29
|
+
function selectHooks(hooks, hookNames) {
|
|
30
|
+
if (hookNames) {
|
|
31
|
+
return Object.keys(hookNames).reduce((hookSubset, _hookName)=>{
|
|
32
|
+
const hookName = _hookName;
|
|
33
|
+
hookSubset[hookName] = hooks[hookName];
|
|
34
|
+
return hookSubset;
|
|
35
|
+
}, {});
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get a BIP-32 derivation path array from a hash, which is compatible with
|
|
41
|
+
* `@metamask/key-tree`. The hash is assumed to be 32 bytes long.
|
|
42
|
+
*
|
|
43
|
+
* @param hash - The hash to derive indices from.
|
|
44
|
+
* @returns The derived indices as a {@link HardenedBIP32Node} array.
|
|
45
|
+
*/ function getDerivationPathArray(hash) {
|
|
46
|
+
const array = [];
|
|
47
|
+
const view = (0, _utils.createDataView)(hash);
|
|
48
|
+
for(let index = 0; index < 8; index++){
|
|
49
|
+
const uint32 = view.getUint32(index * 4);
|
|
50
|
+
// This is essentially `index | 0x80000000`. Because JavaScript numbers are
|
|
51
|
+
// signed, we use the bitwise unsigned right shift operator to ensure that
|
|
52
|
+
// the result is a positive number.
|
|
53
|
+
// eslint-disable-next-line no-bitwise
|
|
54
|
+
const pathIndex = (uint32 | HARDENED_VALUE) >>> 0;
|
|
55
|
+
array.push(`bip32:${pathIndex - HARDENED_VALUE}'`);
|
|
56
|
+
}
|
|
57
|
+
return array;
|
|
58
|
+
}
|
|
59
|
+
async function deriveEntropy({ input, salt = '', mnemonicPhrase, magic }) {
|
|
60
|
+
const inputBytes = (0, _utils.stringToBytes)(input);
|
|
61
|
+
const saltBytes = (0, _utils.stringToBytes)(salt);
|
|
62
|
+
// Get the derivation path from the snap ID.
|
|
63
|
+
const hash = (0, _sha3.keccak_256)((0, _utils.concatBytes)([
|
|
64
|
+
inputBytes,
|
|
65
|
+
(0, _sha3.keccak_256)(saltBytes)
|
|
66
|
+
]));
|
|
67
|
+
const computedDerivationPath = getDerivationPathArray(hash);
|
|
68
|
+
// Derive the private key using BIP-32.
|
|
69
|
+
const { privateKey } = await _keytree.SLIP10Node.fromDerivationPath({
|
|
70
|
+
derivationPath: [
|
|
71
|
+
mnemonicPhrase,
|
|
72
|
+
`bip32:${magic}`,
|
|
73
|
+
...computedDerivationPath
|
|
74
|
+
],
|
|
75
|
+
curve: 'secp256k1'
|
|
76
|
+
});
|
|
77
|
+
// This should never happen, but this keeps TypeScript happy.
|
|
78
|
+
(0, _utils.assert)(privateKey, 'Failed to derive the entropy.');
|
|
79
|
+
return (0, _utils.add0x)(privateKey);
|
|
80
|
+
}
|
|
81
|
+
function getPathPrefix(curve) {
|
|
82
|
+
if (curve === 'secp256k1') {
|
|
83
|
+
return 'bip32';
|
|
84
|
+
}
|
|
85
|
+
return 'slip10';
|
|
86
|
+
}
|
|
87
|
+
async function getNode({ curve, secretRecoveryPhrase, path }) {
|
|
88
|
+
const prefix = getPathPrefix(curve);
|
|
89
|
+
return await _keytree.SLIP10Node.fromDerivationPath({
|
|
90
|
+
curve,
|
|
91
|
+
derivationPath: [
|
|
92
|
+
secretRecoveryPhrase,
|
|
93
|
+
...path.slice(1).map((index)=>`${prefix}:${index}`)
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type {\n HardenedBIP32Node,\n BIP32Node,\n SLIP10PathNode,\n} from '@metamask/key-tree';\nimport { SLIP10Node } from '@metamask/key-tree';\nimport type { MagicValue } from '@metamask/snaps-utils';\nimport type { Hex } from '@metamask/utils';\nimport {\n add0x,\n assert,\n concatBytes,\n createDataView,\n stringToBytes,\n} from '@metamask/utils';\nimport { keccak_256 as keccak256 } from '@noble/hashes/sha3';\n\nconst HARDENED_VALUE = 0x80000000;\n\n/**\n * Maps an interface with method hooks to an object, using the keys of the\n * interface, and `true` as value. This ensures that the `methodHooks` object\n * has the same values as the interface.\n */\nexport type MethodHooksObject<HooksType extends Record<string, unknown>> = {\n [Key in keyof HooksType]: true;\n};\n\n/**\n * Returns the subset of the specified `hooks` that are included in the\n * `hookNames` object. This is a Principle of Least Authority (POLA) measure\n * to ensure that each RPC method implementation only has access to the\n * API \"hooks\" it needs to do its job.\n *\n * @param hooks - The hooks to select from.\n * @param hookNames - The names of the hooks to select.\n * @returns The selected hooks.\n * @template Hooks - The hooks to select from.\n * @template HookName - The names of the hooks to select.\n */\nexport function selectHooks<\n Hooks extends Record<string, unknown>,\n HookName extends keyof Hooks,\n>(\n hooks: Hooks,\n hookNames?: Record<HookName, boolean>,\n): Pick<Hooks, HookName> | undefined {\n if (hookNames) {\n return Object.keys(hookNames).reduce<Partial<Pick<Hooks, HookName>>>(\n (hookSubset, _hookName) => {\n const hookName = _hookName as HookName;\n hookSubset[hookName] = hooks[hookName];\n return hookSubset;\n },\n {},\n ) as Pick<Hooks, HookName>;\n }\n return undefined;\n}\n\n/**\n * Get a BIP-32 derivation path array from a hash, which is compatible with\n * `@metamask/key-tree`. The hash is assumed to be 32 bytes long.\n *\n * @param hash - The hash to derive indices from.\n * @returns The derived indices as a {@link HardenedBIP32Node} array.\n */\nfunction getDerivationPathArray(hash: Uint8Array): HardenedBIP32Node[] {\n const array: HardenedBIP32Node[] = [];\n const view = createDataView(hash);\n\n for (let index = 0; index < 8; index++) {\n const uint32 = view.getUint32(index * 4);\n\n // This is essentially `index | 0x80000000`. Because JavaScript numbers are\n // signed, we use the bitwise unsigned right shift operator to ensure that\n // the result is a positive number.\n // eslint-disable-next-line no-bitwise\n const pathIndex = (uint32 | HARDENED_VALUE) >>> 0;\n array.push(`bip32:${pathIndex - HARDENED_VALUE}'` as const);\n }\n\n return array;\n}\n\ntype DeriveEntropyOptions = {\n /**\n * The input value to derive entropy from.\n */\n input: string;\n\n /**\n * An optional salt to use when deriving entropy.\n */\n salt?: string;\n\n /**\n * The mnemonic phrase to use for entropy derivation.\n */\n mnemonicPhrase: Uint8Array;\n\n /**\n * A hardened BIP-32 index, which is used to derive the root key from the\n * mnemonic phrase.\n */\n magic: MagicValue;\n};\n\n/**\n * Derive entropy from the given mnemonic phrase and salt.\n *\n * This is based on the reference implementation of\n * [SIP-6](https://metamask.github.io/SIPs/SIPS/sip-6).\n *\n * @param options - The options for entropy derivation.\n * @param options.input - The input value to derive entropy from.\n * @param options.salt - An optional salt to use when deriving entropy.\n * @param options.mnemonicPhrase - The mnemonic phrase to use for entropy\n * derivation.\n * @param options.magic - A hardened BIP-32 index, which is used to derive the\n * root key from the mnemonic phrase.\n * @returns The derived entropy.\n */\nexport async function deriveEntropy({\n input,\n salt = '',\n mnemonicPhrase,\n magic,\n}: DeriveEntropyOptions): Promise<Hex> {\n const inputBytes = stringToBytes(input);\n const saltBytes = stringToBytes(salt);\n\n // Get the derivation path from the snap ID.\n const hash = keccak256(concatBytes([inputBytes, keccak256(saltBytes)]));\n const computedDerivationPath = getDerivationPathArray(hash);\n\n // Derive the private key using BIP-32.\n const { privateKey } = await SLIP10Node.fromDerivationPath({\n derivationPath: [\n mnemonicPhrase,\n `bip32:${magic}`,\n ...computedDerivationPath,\n ],\n curve: 'secp256k1',\n });\n\n // This should never happen, but this keeps TypeScript happy.\n assert(privateKey, 'Failed to derive the entropy.');\n\n return add0x(privateKey);\n}\n\n/**\n * Get the path prefix to use for key derivation in `key-tree`. This assumes the\n * following:\n *\n * - The Secp256k1 curve always use the BIP-32 specification.\n * - The Ed25519 curve always use the SLIP-10 specification.\n *\n * While this does not matter in most situations (no known case at the time of\n * writing), `key-tree` requires a specific specification to be used.\n *\n * @param curve - The curve to get the path prefix for. The curve is NOT\n * validated by this function.\n * @returns The path prefix, i.e., `secp256k1` or `ed25519`.\n */\nexport function getPathPrefix(\n curve: 'secp256k1' | 'ed25519',\n): 'bip32' | 'slip10' {\n if (curve === 'secp256k1') {\n return 'bip32';\n }\n\n return 'slip10';\n}\n\ntype GetNodeArgs = {\n curve: 'secp256k1' | 'ed25519';\n secretRecoveryPhrase: Uint8Array;\n path: string[];\n};\n\n/**\n * Get a `key-tree`-compatible node.\n *\n * Note: This function assumes that all the parameters have been validated\n * beforehand.\n *\n * @param options - The derivation options.\n * @param options.curve - The curve to use for derivation.\n * @param options.secretRecoveryPhrase - The secret recovery phrase to use for\n * derivation.\n * @param options.path - The derivation path to use as array, starting with an\n * \"m\" as the first item.\n */\nexport async function getNode({\n curve,\n secretRecoveryPhrase,\n path,\n}: GetNodeArgs) {\n const prefix = getPathPrefix(curve);\n return await SLIP10Node.fromDerivationPath({\n curve,\n derivationPath: [\n secretRecoveryPhrase,\n ...(path.slice(1).map((index) => `${prefix}:${index}`) as\n | BIP32Node[]\n | SLIP10PathNode[]),\n ],\n });\n}\n"],"names":["selectHooks","deriveEntropy","getPathPrefix","getNode","HARDENED_VALUE","hooks","hookNames","Object","keys","reduce","hookSubset","_hookName","hookName","undefined","getDerivationPathArray","hash","array","view","createDataView","index","uint32","getUint32","pathIndex","push","input","salt","mnemonicPhrase","magic","inputBytes","stringToBytes","saltBytes","keccak256","concatBytes","computedDerivationPath","privateKey","SLIP10Node","fromDerivationPath","derivationPath","curve","assert","add0x","secretRecoveryPhrase","path","prefix","slice","map"],"mappings":";;;;;;;;;;;IAwCgBA,WAAW;eAAXA;;IAmFMC,aAAa;eAAbA;;IA2CNC,aAAa;eAAbA;;IA6BMC,OAAO;eAAPA;;;yBA9LK;uBASpB;sBACiC;AAExC,MAAMC,iBAAiB;AAuBhB,SAASJ,YAIdK,KAAY,EACZC,SAAqC;IAErC,IAAIA,WAAW;QACb,OAAOC,OAAOC,IAAI,CAACF,WAAWG,MAAM,CAClC,CAACC,YAAYC;YACX,MAAMC,WAAWD;YACjBD,UAAU,CAACE,SAAS,GAAGP,KAAK,CAACO,SAAS;YACtC,OAAOF;QACT,GACA,CAAC;IAEL;IACA,OAAOG;AACT;AAEA;;;;;;CAMC,GACD,SAASC,uBAAuBC,IAAgB;IAC9C,MAAMC,QAA6B,EAAE;IACrC,MAAMC,OAAOC,IAAAA,qBAAc,EAACH;IAE5B,IAAK,IAAII,QAAQ,GAAGA,QAAQ,GAAGA,QAAS;QACtC,MAAMC,SAASH,KAAKI,SAAS,CAACF,QAAQ;QAEtC,2EAA2E;QAC3E,0EAA0E;QAC1E,mCAAmC;QACnC,sCAAsC;QACtC,MAAMG,YAAY,AAACF,CAAAA,SAAShB,cAAa,MAAO;QAChDY,MAAMO,IAAI,CAAC,CAAC,MAAM,EAAED,YAAYlB,eAAe,CAAC,CAAC;IACnD;IAEA,OAAOY;AACT;AAwCO,eAAef,cAAc,EAClCuB,KAAK,EACLC,OAAO,EAAE,EACTC,cAAc,EACdC,KAAK,EACgB;IACrB,MAAMC,aAAaC,IAAAA,oBAAa,EAACL;IACjC,MAAMM,YAAYD,IAAAA,oBAAa,EAACJ;IAEhC,4CAA4C;IAC5C,MAAMV,OAAOgB,IAAAA,gBAAS,EAACC,IAAAA,kBAAW,EAAC;QAACJ;QAAYG,IAAAA,gBAAS,EAACD;KAAW;IACrE,MAAMG,yBAAyBnB,uBAAuBC;IAEtD,uCAAuC;IACvC,MAAM,EAAEmB,UAAU,EAAE,GAAG,MAAMC,mBAAU,CAACC,kBAAkB,CAAC;QACzDC,gBAAgB;YACdX;YACA,CAAC,MAAM,EAAEC,MAAM,CAAC;eACbM;SACJ;QACDK,OAAO;IACT;IAEA,6DAA6D;IAC7DC,IAAAA,aAAM,EAACL,YAAY;IAEnB,OAAOM,IAAAA,YAAK,EAACN;AACf;AAgBO,SAAShC,cACdoC,KAA8B;IAE9B,IAAIA,UAAU,aAAa;QACzB,OAAO;IACT;IAEA,OAAO;AACT;AAqBO,eAAenC,QAAQ,EAC5BmC,KAAK,EACLG,oBAAoB,EACpBC,IAAI,EACQ;IACZ,MAAMC,SAASzC,cAAcoC;IAC7B,OAAO,MAAMH,mBAAU,CAACC,kBAAkB,CAAC;QACzCE;QACAD,gBAAgB;YACdI;eACIC,KAAKE,KAAK,CAAC,GAAGC,GAAG,CAAC,CAAC1B,QAAU,CAAC,EAAEwB,OAAO,CAAC,EAAExB,MAAM,CAAC;SAGtD;IACH;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export type { PermittedRpcMethodHooks } from './permitted';\nexport {\n handlers as permittedMethods,\n createSnapsMethodMiddleware,\n} from './permitted';\nexport * from './restricted';\nexport { SnapCaveatType } from '@metamask/snaps-utils';\nexport { selectHooks } from './utils';\nexport type { RequestFunction, SnapsGlobalObject } from './request';\n"],"names":["handlers","permittedMethods","createSnapsMethodMiddleware","SnapCaveatType","selectHooks"],"mappings":"AACA,SACEA,YAAYC,gBAAgB,EAC5BC,2BAA2B,QACtB,cAAc;AACrB,cAAc,eAAe;AAC7B,SAASC,cAAc,QAAQ,wBAAwB;AACvD,SAASC,WAAW,QAAQ,UAAU"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { rpcErrors } from '@metamask/rpc-errors';
|
|
2
|
+
import { isObject } from '@metamask/utils';
|
|
3
|
+
/**
|
|
4
|
+
* Typechecks the requested snaps and passes them to the permissions
|
|
5
|
+
* controller for installation.
|
|
6
|
+
*
|
|
7
|
+
* @param requestedSnaps - An object containing the requested snaps to be installed. The key of the
|
|
8
|
+
* object is the snap id and the value is potential extra data, i.e. version.
|
|
9
|
+
* @param installSnaps - A function that tries to install a given snap, prompting the user if
|
|
10
|
+
* necessary.
|
|
11
|
+
* @returns An object containing the installed snaps.
|
|
12
|
+
* @throws If the params are invalid or the snap installation fails.
|
|
13
|
+
*/ export async function handleInstallSnaps(requestedSnaps, installSnaps) {
|
|
14
|
+
if (!isObject(requestedSnaps)) {
|
|
15
|
+
throw rpcErrors.invalidParams({
|
|
16
|
+
message: `Invalid snap installation params.`,
|
|
17
|
+
data: {
|
|
18
|
+
requestedSnaps
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
} else if (Object.keys(requestedSnaps).length === 0) {
|
|
22
|
+
throw rpcErrors.invalidParams({
|
|
23
|
+
message: `Must specify at least one snap to install.`,
|
|
24
|
+
data: {
|
|
25
|
+
requestedSnaps
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// installSnaps is bound to the origin
|
|
30
|
+
return await installSnaps(requestedSnaps);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=snapInstallation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/permitted/common/snapInstallation.ts"],"sourcesContent":["import type { RequestedPermissions } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { InstallSnapsResult } from '@metamask/snaps-utils';\nimport { isObject } from '@metamask/utils';\n\nexport type { InstallSnapsResult } from '@metamask/snaps-utils';\n\nexport type InstallSnapsHook = (\n requestedSnaps: RequestedPermissions,\n) => Promise<InstallSnapsResult>;\n\n/**\n * Typechecks the requested snaps and passes them to the permissions\n * controller for installation.\n *\n * @param requestedSnaps - An object containing the requested snaps to be installed. The key of the\n * object is the snap id and the value is potential extra data, i.e. version.\n * @param installSnaps - A function that tries to install a given snap, prompting the user if\n * necessary.\n * @returns An object containing the installed snaps.\n * @throws If the params are invalid or the snap installation fails.\n */\nexport async function handleInstallSnaps(\n requestedSnaps: RequestedPermissions,\n installSnaps: InstallSnapsHook,\n): Promise<InstallSnapsResult> {\n if (!isObject(requestedSnaps)) {\n throw rpcErrors.invalidParams({\n message: `Invalid snap installation params.`,\n data: { requestedSnaps },\n });\n } else if (Object.keys(requestedSnaps).length === 0) {\n throw rpcErrors.invalidParams({\n message: `Must specify at least one snap to install.`,\n data: { requestedSnaps },\n });\n }\n\n // installSnaps is bound to the origin\n return await installSnaps(requestedSnaps);\n}\n"],"names":["rpcErrors","isObject","handleInstallSnaps","requestedSnaps","installSnaps","invalidParams","message","data","Object","keys","length"],"mappings":"AACA,SAASA,SAAS,QAAQ,uBAAuB;AAEjD,SAASC,QAAQ,QAAQ,kBAAkB;AAQ3C;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,mBACpBC,cAAoC,EACpCC,YAA8B;IAE9B,IAAI,CAACH,SAASE,iBAAiB;QAC7B,MAAMH,UAAUK,aAAa,CAAC;YAC5BC,SAAS,CAAC,iCAAiC,CAAC;YAC5CC,MAAM;gBAAEJ;YAAe;QACzB;IACF,OAAO,IAAIK,OAAOC,IAAI,CAACN,gBAAgBO,MAAM,KAAK,GAAG;QACnD,MAAMV,UAAUK,aAAa,CAAC;YAC5BC,SAAS,CAAC,0CAA0C,CAAC;YACrDC,MAAM;gBAAEJ;YAAe;QACzB;IACF;IAEA,sCAAsC;IACtC,OAAO,MAAMC,aAAaD;AAC5B"}
|