@metamask/snaps-utils
Version:
A collection of utilities for MetaMask Snaps
197 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSnapManifest = exports.assertIsSnapManifest = exports.isSnapManifest = exports.SnapManifestStruct = exports.InitialConnectionsStruct = exports.SnapAuxilaryFilesStruct = exports.PermissionsStruct = exports.EmptyObjectStruct = exports.HandlerCaveatsStruct = exports.ProtocolScopesStruct = exports.MaxRequestTimeStruct = exports.MAXIMUM_REQUEST_TIMEOUT = exports.MINIMUM_REQUEST_TIMEOUT = exports.LookupMatchersStruct = exports.ChainIdsStruct = exports.SnapIdsStruct = exports.SemVerRangeStruct = exports.SnapGetBip32EntropyPermissionsStruct = exports.Bip32EntropyStruct = exports.CurveStruct = exports.bip32entropy = exports.Bip32PathStruct = exports.FORBIDDEN_COIN_TYPES = void 0;
const key_tree_1 = require("@metamask/key-tree");
const superstruct_1 = require("@metamask/superstruct");
const utils_1 = require("@metamask/utils");
const array_1 = require("../array.cjs");
const cronjob_1 = require("../cronjob.cjs");
const entropy_1 = require("../entropy.cjs");
const json_rpc_1 = require("../json-rpc.cjs");
const snaps_1 = require("../snaps.cjs");
const structs_1 = require("../structs.cjs");
const types_1 = require("../types.cjs");
// BIP-43 purposes that cannot be used for entropy derivation. These are in the
// string form, ending with `'`.
const FORBIDDEN_PURPOSES = [
entropy_1.SIP_6_MAGIC_VALUE,
entropy_1.STATE_ENCRYPTION_MAGIC_VALUE,
];
exports.FORBIDDEN_COIN_TYPES = [60];
const FORBIDDEN_PATHS = exports.FORBIDDEN_COIN_TYPES.map((coinType) => [
'm',
"44'",
`${coinType}'`,
]);
exports.Bip32PathStruct = (0, superstruct_1.refine)((0, superstruct_1.array)((0, superstruct_1.string)()), 'BIP-32 path', (path) => {
if (path.length === 0) {
return 'Path must be a non-empty BIP-32 derivation path array';
}
if (path[0] !== 'm') {
return 'Path must start with "m".';
}
if (path.length < 3) {
return 'Paths must have a length of at least three.';
}
if (path.slice(1).some((part) => !(0, key_tree_1.isValidBIP32PathSegment)(part))) {
return 'Path must be a valid BIP-32 derivation path array.';
}
if (FORBIDDEN_PURPOSES.includes(path[1])) {
return `The purpose "${path[1]}" is not allowed for entropy derivation.`;
}
if (FORBIDDEN_PATHS.some((forbiddenPath) => (0, array_1.isEqual)(path.slice(0, forbiddenPath.length), forbiddenPath))) {
return `The path "${path.join('/')}" is not allowed for entropy derivation.`;
}
return true;
});
const bip32entropy = (struct) => (0, superstruct_1.refine)(struct, 'BIP-32 entropy', (value) => {
if (value.curve === 'ed25519' &&
value.path.slice(1).some((part) => !part.endsWith("'"))) {
return 'Ed25519 does not support unhardened paths.';
}
return true;
});
exports.bip32entropy = bip32entropy;
exports.CurveStruct = (0, superstruct_1.enums)([
'ed25519',
'secp256k1',
'ed25519Bip32',
]);
// Used outside @metamask/snap-utils
exports.Bip32EntropyStruct = (0, exports.bip32entropy)((0, superstruct_1.type)({
path: exports.Bip32PathStruct,
curve: exports.CurveStruct,
}));
exports.SnapGetBip32EntropyPermissionsStruct = (0, superstruct_1.size)((0, superstruct_1.array)(exports.Bip32EntropyStruct), 1, Infinity);
exports.SemVerRangeStruct = (0, superstruct_1.refine)((0, superstruct_1.string)(), 'SemVer range', (value) => {
if ((0, utils_1.isValidSemVerRange)(value)) {
return true;
}
return 'Expected a valid SemVer range.';
});
exports.SnapIdsStruct = (0, superstruct_1.refine)((0, superstruct_1.record)(snaps_1.SnapIdStruct, (0, superstruct_1.object)({ version: (0, superstruct_1.optional)(exports.SemVerRangeStruct) })), 'SnapIds', (value) => {
if (Object.keys(value).length === 0) {
return false;
}
return true;
});
exports.ChainIdsStruct = (0, superstruct_1.size)((0, superstruct_1.array)(utils_1.CaipChainIdStruct), 1, Infinity);
exports.LookupMatchersStruct = (0, superstruct_1.union)([
(0, superstruct_1.object)({
tlds: (0, superstruct_1.size)((0, superstruct_1.array)((0, superstruct_1.string)()), 1, Infinity),
}),
(0, superstruct_1.object)({
schemes: (0, superstruct_1.size)((0, superstruct_1.array)((0, superstruct_1.string)()), 1, Infinity),
}),
(0, superstruct_1.object)({
tlds: (0, superstruct_1.size)((0, superstruct_1.array)((0, superstruct_1.string)()), 1, Infinity),
schemes: (0, superstruct_1.size)((0, superstruct_1.array)((0, superstruct_1.string)()), 1, Infinity),
}),
]);
exports.MINIMUM_REQUEST_TIMEOUT = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second);
exports.MAXIMUM_REQUEST_TIMEOUT = (0, utils_1.inMilliseconds)(3, utils_1.Duration.Minute);
exports.MaxRequestTimeStruct = (0, superstruct_1.size)((0, superstruct_1.integer)(), exports.MINIMUM_REQUEST_TIMEOUT, exports.MAXIMUM_REQUEST_TIMEOUT);
exports.ProtocolScopesStruct = (0, superstruct_1.record)(utils_1.CaipChainIdStruct, (0, superstruct_1.object)({ methods: (0, superstruct_1.array)((0, superstruct_1.string)()) }));
// Utility type to union with for all handler structs
exports.HandlerCaveatsStruct = (0, superstruct_1.object)({
maxRequestTime: (0, superstruct_1.optional)(exports.MaxRequestTimeStruct),
});
exports.EmptyObjectStruct = (0, superstruct_1.object)({});
/* eslint-disable @typescript-eslint/naming-convention */
exports.PermissionsStruct = (0, superstruct_1.type)({
'endowment:assets': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({ scopes: exports.ChainIdsStruct }))),
'endowment:cronjob': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({ jobs: (0, superstruct_1.optional)(cronjob_1.CronjobSpecificationArrayStruct) }))),
'endowment:ethereum-provider': (0, superstruct_1.optional)(exports.EmptyObjectStruct),
'endowment:keyring': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, json_rpc_1.KeyringOriginsStruct)),
'endowment:protocol': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({ scopes: exports.ProtocolScopesStruct }))),
'endowment:lifecycle-hooks': (0, superstruct_1.optional)(exports.HandlerCaveatsStruct),
'endowment:name-lookup': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({
chains: (0, superstruct_1.optional)(exports.ChainIdsStruct),
matchers: (0, superstruct_1.optional)(exports.LookupMatchersStruct),
}))),
'endowment:network-access': (0, superstruct_1.optional)(exports.EmptyObjectStruct),
'endowment:page-home': (0, superstruct_1.optional)(exports.HandlerCaveatsStruct),
'endowment:rpc': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, json_rpc_1.RpcOriginsStruct)),
'endowment:signature-insight': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({
allowSignatureOrigin: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
}))),
'endowment:transaction-insight': (0, superstruct_1.optional)((0, structs_1.mergeStructs)(exports.HandlerCaveatsStruct, (0, superstruct_1.object)({
allowTransactionOrigin: (0, superstruct_1.optional)((0, superstruct_1.boolean)()),
}))),
'endowment:webassembly': (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_dialog: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_manageState: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_manageAccounts: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_notify: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_getBip32Entropy: (0, superstruct_1.optional)(exports.SnapGetBip32EntropyPermissionsStruct),
snap_getBip32PublicKey: (0, superstruct_1.optional)(exports.SnapGetBip32EntropyPermissionsStruct),
snap_getBip44Entropy: (0, superstruct_1.optional)((0, superstruct_1.size)((0, superstruct_1.array)((0, superstruct_1.object)({ coinType: (0, superstruct_1.size)((0, superstruct_1.integer)(), 0, 2 ** 32 - 1) })), 1, Infinity)),
snap_getEntropy: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
snap_getLocale: (0, superstruct_1.optional)(exports.EmptyObjectStruct),
wallet_snap: (0, superstruct_1.optional)(exports.SnapIdsStruct),
});
exports.SnapAuxilaryFilesStruct = (0, superstruct_1.array)((0, superstruct_1.string)());
exports.InitialConnectionsStruct = (0, superstruct_1.record)((0, superstruct_1.intersection)([(0, superstruct_1.string)(), (0, types_1.uri)()]), (0, superstruct_1.object)({}));
exports.SnapManifestStruct = (0, superstruct_1.object)({
version: utils_1.VersionStruct,
description: (0, superstruct_1.size)((0, superstruct_1.string)(), 1, 280),
proposedName: (0, superstruct_1.size)((0, superstruct_1.string)(), 1, 214),
repository: (0, superstruct_1.optional)((0, superstruct_1.type)({
type: (0, superstruct_1.size)((0, superstruct_1.string)(), 1, Infinity),
url: (0, superstruct_1.size)((0, superstruct_1.string)(), 1, Infinity),
})),
source: (0, superstruct_1.object)({
shasum: utils_1.ChecksumStruct,
location: (0, superstruct_1.object)({
npm: (0, superstruct_1.object)({
filePath: (0, superstruct_1.size)((0, superstruct_1.string)(), 1, Infinity),
iconPath: (0, superstruct_1.optional)((0, superstruct_1.size)((0, superstruct_1.string)(), 1, Infinity)),
packageName: types_1.NameStruct,
registry: (0, superstruct_1.union)([
(0, superstruct_1.literal)('https://registry.npmjs.org'),
(0, superstruct_1.literal)('https://registry.npmjs.org/'),
]),
}),
}),
files: (0, superstruct_1.optional)(exports.SnapAuxilaryFilesStruct),
locales: (0, superstruct_1.optional)(exports.SnapAuxilaryFilesStruct),
}),
initialConnections: (0, superstruct_1.optional)(exports.InitialConnectionsStruct),
initialPermissions: exports.PermissionsStruct,
manifestVersion: (0, superstruct_1.literal)('0.1'),
platformVersion: (0, superstruct_1.optional)(utils_1.VersionStruct),
$schema: (0, superstruct_1.optional)((0, superstruct_1.string)()), // enables JSON-Schema linting in VSC and other IDEs
});
/**
* Check if the given value is a valid {@link SnapManifest} object.
*
* @param value - The value to check.
* @returns Whether the value is a valid {@link SnapManifest} object.
*/
function isSnapManifest(value) {
return (0, superstruct_1.is)(value, exports.SnapManifestStruct);
}
exports.isSnapManifest = isSnapManifest;
/**
* Assert that the given value is a valid {@link SnapManifest} object.
*
* @param value - The value to check.
* @throws If the value is not a valid {@link SnapManifest} object.
*/
function assertIsSnapManifest(value) {
(0, utils_1.assertStruct)(value, exports.SnapManifestStruct, `"${types_1.NpmSnapFileNames.Manifest}" is invalid`);
}
exports.assertIsSnapManifest = assertIsSnapManifest;
/**
* Creates a {@link SnapManifest} object from JSON.
*
* @param value - The value to check.
* @throws If the value cannot be coerced to a {@link SnapManifest} object.
* @returns The created {@link SnapManifest} object.
*/
function createSnapManifest(value) {
// TODO: Add a utility to prefix these errors similar to assertStruct
return (0, superstruct_1.create)(value, exports.SnapManifestStruct);
}
exports.createSnapManifest = createSnapManifest;
//# sourceMappingURL=validation.cjs.map