UNPKG

@metamask/snaps-rpc-methods

Version:
144 lines 5.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.get = exports.getStateHandler = void 0; const rpc_errors_1 = require("@metamask/rpc-errors"); const superstruct_1 = require("@metamask/superstruct"); const utils_1 = require("@metamask/utils"); const manageState_1 = require("../restricted/manageState.cjs"); const utils_2 = require("../utils.cjs"); const hookNames = { getUnlockPromise: true, }; /** * Get the state of the Snap, or a specific value within the state. By default, * the data is automatically encrypted using a Snap-specific key and * automatically decrypted when retrieved. You can set `encrypted` to `false` to * use unencrypted storage (available when the client is locked). * * @example * ```json name="Manifest" * { * "initialPermissions": { * "snap_manageState": {} * } * } * ``` * ```ts name="Usage" * const state = await snap.request({ * method: 'snap_getState', * params: { * key: 'some.nested.value', // Optional, defaults to entire state * encrypted: true, // Optional, defaults to `true` * }, * }); * ``` */ exports.getStateHandler = { implementation: getStateImplementation, hookNames, actionNames: [ 'PermissionController:hasPermission', 'SnapController:getSnapState', ], }; const GetStateParametersStruct = (0, superstruct_1.object)({ key: (0, superstruct_1.optional)(utils_2.StateKeyStruct), encrypted: (0, superstruct_1.optional)((0, superstruct_1.boolean)()), }); /** * The `snap_getState` method implementation. * * @param request - The JSON-RPC request object. * @param response - The JSON-RPC response object. * @param _next - The `json-rpc-engine` "next" callback. Not used by this * function. * @param end - The `json-rpc-engine` "end" callback. * @param hooks - The RPC method hooks. * @param hooks.getUnlockPromise - Wait for the extension to be unlocked. * @param messenger - The messenger used to call controller actions. * @returns Nothing. */ async function getStateImplementation(request, // `GetStateResult` is an alias for `Json` (which is the default type argument // for `PendingJsonRpcResponse`), but that may not be the case in the future. // We use `GetStateResult` here to make it clear that this is the expected // type of the result. // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments response, _next, end, { getUnlockPromise }, messenger) { const { params, origin } = request; if (!messenger.call('PermissionController:hasPermission', origin, manageState_1.manageStateBuilder.targetName)) { return end(rpc_errors_1.providerErrors.unauthorized()); } try { const validatedParams = getValidatedParams(params); const { key, encrypted = true } = validatedParams; if (encrypted) { await getUnlockPromise(true); } const state = await messenger.call('SnapController:getSnapState', origin, encrypted); response.result = get(state, key); } catch (error) { return end(error); } return end(); } /** * Validate the parameters of the `snap_getState` method. * * @param params - The parameters to validate. * @returns The validated parameters. */ function getValidatedParams(params) { try { return (0, superstruct_1.create)(params, GetStateParametersStruct); } catch (error) { if (error instanceof superstruct_1.StructError) { throw rpc_errors_1.rpcErrors.invalidParams({ message: `Invalid params: ${error.message}.`, }); } /* istanbul ignore next */ throw rpc_errors_1.rpcErrors.internal(); } } /** * Get the value of a key in an object. The key may contain Lodash-style path * syntax, e.g., `a.b.c` (with the exception of array syntax). If the key does * not exist, `null` is returned. * * This is a simplified version of Lodash's `get` function, but Lodash doesn't * seem to be maintained anymore, so we're using our own implementation. * * @param value - The object to get the key from. * @param key - The key to get. * @returns The value of the key in the object, or `null` if the key does not * exist. */ function get(value, key) { if (key === undefined) { return value; } const keys = key.split('.'); let result = value; // Intentionally using a classic for loop here for performance reasons. // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < keys.length; i++) { const currentKey = keys[i]; if (utils_2.FORBIDDEN_KEYS.includes(currentKey)) { throw rpc_errors_1.rpcErrors.invalidParams('Invalid params: Key contains forbidden characters.'); } if ((0, utils_1.isObject)(result)) { if (!(0, utils_1.hasProperty)(result, currentKey)) { return null; } result = result[currentKey]; continue; } return null; } return result; } exports.get = get; //# sourceMappingURL=getState.cjs.map