UNPKG

@metamask/snaps-rpc-methods

Version:
135 lines 5.32 kB
import { PermissionType, SubjectType } from "@metamask/permission-controller"; import { rpcErrors } from "@metamask/rpc-errors"; import { bip32entropy, Bip32PathStruct, CurveStruct, SnapCaveatType } from "@metamask/snaps-utils"; import { boolean, object, optional, string } from "@metamask/superstruct"; import { assertStruct } from "@metamask/utils"; import { getMnemonic, getMnemonicSeed, getNodeFromMnemonic, getNodeFromSeed, getValueFromEntropySource } from "../utils.mjs"; const targetName = 'snap_getBip32PublicKey'; export const Bip32PublicKeyArgsStruct = bip32entropy(object({ path: Bip32PathStruct, curve: CurveStruct, compressed: optional(boolean()), source: optional(string()), })); /** * The specification builder for the `snap_getBip32PublicKey` permission. * `snap_getBip32PublicKey` lets the Snap retrieve public keys for a particular * BIP-32 node. * * @param options - The specification builder options. * @param options.messenger - The messenger. * @param options.methodHooks - The RPC method hooks needed by the method implementation. * @returns The specification for the `snap_getBip32PublicKey` permission. */ const specificationBuilder = ({ methodHooks, messenger, }) => { return { permissionType: PermissionType.RestrictedMethod, targetName, allowedCaveats: [SnapCaveatType.PermittedDerivationPaths], methodImplementation: getBip32PublicKeyImplementation({ methodHooks, messenger, }), validator: ({ caveats }) => { if (caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.PermittedDerivationPaths) { throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.PermittedDerivationPaths}" caveat.`, }); } }, subjectTypes: [SubjectType.Snap], }; }; const methodHooks = { getUnlockPromise: true, getClientCryptography: true, }; /** * Gets the [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) * public key for the derivation path specified by the `path` parameter. Note * that this returns the public key, not the extended public key (`xpub`), or * Ethereum address. * * @example * ```json name="Manifest" * { * "initialPermissions": { * "snap_getBip32PublicKey": [ * { * "path": ["m", "44'", "3'", "0'", "0", "0"], * "curve": "secp256k1" * } * ] * } * } * ``` * ```ts name="Usage" * // This example uses Dogecoin, which has a derivation path starting with * // "m / 44' / 3'". * const dogecoinPublicKey = await snap.request({ * method: 'snap_getBip32PublicKey', * params: { * // The path and curve must be specified in the initial permissions. * path: ['m', "44'", "3'", "0'", "0", "0"], * curve: 'secp256k1', * compressed: false, * }, * }) * * // '0x...' * console.log(dogecoinPublicKey) * ``` */ export const getBip32PublicKeyBuilder = Object.freeze({ targetName, specificationBuilder, methodHooks, actionNames: ['KeyringController:withKeyringV2Unsafe'], }); /** * Builds the method implementation for `snap_getBip32PublicKey`. * * @param options - The options. * @param options.messenger - The messenger. * @param options.methodHooks - The RPC method hooks. * @param options.methodHooks.getUnlockPromise - A function that resolves once the MetaMask extension is unlocked * and prompts the user to unlock their MetaMask if it is locked. * @param options.methodHooks.getClientCryptography - A function to retrieve the cryptographic * functions to use for the client. * @returns The method implementation which returns a public key. * @throws If the params are invalid. */ export function getBip32PublicKeyImplementation({ methodHooks: { getUnlockPromise, getClientCryptography }, messenger, }) { return async function getBip32PublicKey(args) { await getUnlockPromise(true); assertStruct(args.params, Bip32PublicKeyArgsStruct, 'Invalid BIP-32 public key params', rpcErrors.invalidParams); const { params } = args; // Using the seed is much faster, but we can only do it for these specific curves. if (params.curve === 'secp256k1' || params.curve === 'ed25519') { const seed = await getValueFromEntropySource(getMnemonicSeed.bind(null, messenger), params.source); const node = await getNodeFromSeed({ curve: params.curve, path: params.path, seed, cryptographicFunctions: getClientCryptography(), }); if (params.compressed) { return node.compressedPublicKey; } return node.publicKey; } const secretRecoveryPhrase = await getValueFromEntropySource(getMnemonic.bind(null, messenger), params.source); const node = await getNodeFromMnemonic({ curve: params.curve, path: params.path, secretRecoveryPhrase, cryptographicFunctions: getClientCryptography(), }); if (params.compressed) { return node.compressedPublicKey; } return node.publicKey; }; } //# sourceMappingURL=getBip32PublicKey.mjs.map