@metamask/snaps-controllers 11.1.0 → 11.2.1
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 +28 -1
- package/dist/cronjob/CronjobController.cjs +2 -2
- package/dist/cronjob/CronjobController.cjs.map +1 -1
- package/dist/cronjob/CronjobController.mjs +2 -2
- package/dist/cronjob/CronjobController.mjs.map +1 -1
- package/dist/insights/SnapInsightsController.cjs +1 -1
- package/dist/insights/SnapInsightsController.cjs.map +1 -1
- package/dist/insights/SnapInsightsController.mjs +1 -1
- package/dist/insights/SnapInsightsController.mjs.map +1 -1
- package/dist/interface/SnapInterfaceController.cjs +1 -8
- package/dist/interface/SnapInterfaceController.cjs.map +1 -1
- package/dist/interface/SnapInterfaceController.d.cts +2 -2
- package/dist/interface/SnapInterfaceController.d.cts.map +1 -1
- package/dist/interface/SnapInterfaceController.d.mts +2 -2
- package/dist/interface/SnapInterfaceController.d.mts.map +1 -1
- package/dist/interface/SnapInterfaceController.mjs +1 -8
- package/dist/interface/SnapInterfaceController.mjs.map +1 -1
- package/dist/interface/utils.cjs +2 -2
- package/dist/interface/utils.cjs.map +1 -1
- package/dist/interface/utils.d.cts +2 -2
- package/dist/interface/utils.d.mts +2 -2
- package/dist/interface/utils.mjs +2 -2
- package/dist/interface/utils.mjs.map +1 -1
- package/dist/multichain/MultichainRouter.cjs +7 -3
- package/dist/multichain/MultichainRouter.cjs.map +1 -1
- package/dist/multichain/MultichainRouter.d.cts +3 -0
- package/dist/multichain/MultichainRouter.d.cts.map +1 -1
- package/dist/multichain/MultichainRouter.d.mts +3 -0
- package/dist/multichain/MultichainRouter.d.mts.map +1 -1
- package/dist/multichain/MultichainRouter.mjs +7 -3
- package/dist/multichain/MultichainRouter.mjs.map +1 -1
- package/dist/snaps/SnapController.cjs +69 -54
- package/dist/snaps/SnapController.cjs.map +1 -1
- package/dist/snaps/SnapController.d.cts +2 -1
- package/dist/snaps/SnapController.d.cts.map +1 -1
- package/dist/snaps/SnapController.d.mts +2 -1
- package/dist/snaps/SnapController.d.mts.map +1 -1
- package/dist/snaps/SnapController.mjs +72 -57
- package/dist/snaps/SnapController.mjs.map +1 -1
- package/dist/snaps/constants.cjs +5 -1
- package/dist/snaps/constants.cjs.map +1 -1
- package/dist/snaps/constants.d.cts +4 -0
- package/dist/snaps/constants.d.cts.map +1 -1
- package/dist/snaps/constants.d.mts +4 -0
- package/dist/snaps/constants.d.mts.map +1 -1
- package/dist/snaps/constants.mjs +4 -0
- package/dist/snaps/constants.mjs.map +1 -1
- package/dist/utils.cjs +29 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +20 -4
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts +20 -4
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +27 -0
- package/dist/utils.mjs.map +1 -1
- package/package.json +14 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../../src/interface/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from '@metamask/snaps-sdk';\nimport type {\n FormState,\n InterfaceState,\n ComponentOrElement,\n InterfaceContext,\n State,\n FungibleAssetMetadata,\n AssetSelectorState,\n CaipChainId,\n} from '@metamask/snaps-sdk';\nimport type {\n DropdownElement,\n InputElement,\n JSXElement,\n OptionElement,\n FileInputElement,\n CheckboxElement,\n RadioGroupElement,\n RadioElement,\n SelectorElement,\n SelectorOptionElement,\n AssetSelectorElement,\n AddressInputElement,\n} from '@metamask/snaps-sdk/jsx';\nimport { isJSXElementUnsafe } from '@metamask/snaps-sdk/jsx';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport {\n getJsonSizeUnsafe,\n getJsxChildren,\n getJsxElementFromComponent,\n walkJsx,\n} from '@metamask/snaps-utils';\nimport {\n type CaipAssetType,\n type CaipAccountId,\n parseCaipAccountId,\n parseCaipAssetType,\n toCaipAccountId,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/**\n * A list of stateful component types.\n */\nconst STATEFUL_COMPONENT_TYPES = [\n 'Input',\n 'Dropdown',\n 'RadioGroup',\n 'FileInput',\n 'Checkbox',\n 'Selector',\n 'AssetSelector',\n 'AddressInput',\n] as const;\n\n/**\n * Type for stateful component types.\n */\ntype StatefulComponentType = (typeof STATEFUL_COMPONENT_TYPES)[number];\n\n/**\n * Check if a component is a stateful component.\n *\n * @param component - The component to check.\n * @param component.type - The type of the component.\n *\n * @returns Whether the component is a stateful component.\n */\nexport function isStatefulComponent(component: { type: string }): component is {\n type: StatefulComponentType;\n} {\n return STATEFUL_COMPONENT_TYPES.includes(\n component.type as StatefulComponentType,\n );\n}\n\n/**\n * A function to get the MultichainAssetController state.\n *\n * @returns The MultichainAssetController state.\n */\ntype GetAssetsState = () => {\n assetsMetadata: {\n [asset: CaipAssetType]: FungibleAssetMetadata;\n };\n accountsAssets: { [account: string]: CaipAssetType[] };\n};\n\n/**\n * A function to get an account by its address.\n *\n * @param address - The account address.\n * @returns The account or undefined if not found.\n */\ntype GetAccountByAddress = (\n address: CaipAccountId,\n) => InternalAccount | undefined;\n\n/**\n * Data getters for elements.\n * This is used to get data from elements that is not directly accessible from the element itself.\n *\n * @param getAssetState - A function to get the MultichainAssetController state.\n * @param getAccountByAddress - A function to get an account by its address.\n */\ntype ElementDataGetters = {\n getAssetsState: GetAssetsState;\n getAccountByAddress: GetAccountByAddress;\n};\n\n/**\n * Get a JSX element from a component or JSX element. If the component is a\n * JSX element, it is returned as is. Otherwise, the component is converted to\n * a JSX element.\n *\n * @param component - The component to convert.\n * @returns The JSX element.\n */\nexport function getJsxInterface(component: ComponentOrElement): JSXElement {\n if (isJSXElementUnsafe(component)) {\n return component;\n }\n\n return getJsxElementFromComponent(component);\n}\n\n/**\n * Assert that the component name is unique in state.\n *\n * @param state - The interface state to verify against.\n * @param name - The component name to verify.\n */\nexport function assertNameIsUnique(state: InterfaceState, name: string) {\n assert(\n state[name] === undefined,\n `Duplicate component names are not allowed, found multiple instances of: \"${name}\".`,\n );\n}\n\n/**\n * Get a default asset for a given address.\n *\n * @param addresses - The account addresses.\n * @param chainIds - The chain IDs to filter the assets.\n * @param elementDataGetters - Data getters for the element.\n * @param elementDataGetters.getAccountByAddress - A function to get an account by its address.\n * @param elementDataGetters.getAssetsState - A function to get the MultichainAssetController state.\n *\n * @returns The default asset for the account or undefined if not found.\n */\nexport function getDefaultAsset(\n addresses: CaipAccountId[],\n chainIds: CaipChainId[] | undefined,\n { getAccountByAddress, getAssetsState }: ElementDataGetters,\n) {\n const { assetsMetadata, accountsAssets } = getAssetsState();\n\n const parsedAccounts = addresses.map((address) =>\n parseCaipAccountId(address),\n );\n\n const accountChainIds = parsedAccounts.map(({ chainId }) => chainId);\n\n const filteredChainIds =\n chainIds && chainIds.length > 0\n ? accountChainIds.filter((accountChainId) =>\n chainIds.includes(accountChainId),\n )\n : accountChainIds;\n\n const accountId = getAccountByAddress(addresses[0])?.id;\n\n // We should never fail on this assertion as the address is already validated.\n assert(accountId, `Account not found for address: ${addresses[0]}.`);\n\n const accountAssets = accountsAssets[accountId];\n\n // The AssetSelector component in the UI will be disabled if there is no asset available for the account\n // and networks provided. In this case, we return null to indicate that there is no default selected asset.\n if (accountAssets.length === 0) {\n return null;\n }\n\n const nativeAsset = accountAssets.find((asset) => {\n const { chainId, assetNamespace } = parseCaipAssetType(asset);\n\n return filteredChainIds.includes(chainId) && assetNamespace === 'slip44';\n });\n\n if (nativeAsset) {\n return {\n asset: nativeAsset,\n name: assetsMetadata[nativeAsset].name,\n symbol: assetsMetadata[nativeAsset].symbol,\n };\n }\n\n return {\n asset: accountAssets[0],\n name: assetsMetadata[accountAssets[0]].name,\n symbol: assetsMetadata[accountAssets[0]].symbol,\n };\n}\n\n/**\n * Construct default state for a component.\n *\n * This function is meant to be used inside constructInputState to account\n * for component specific defaults and will not override the component value or existing form state.\n *\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n *\n * @returns The default state for the specific component, if any.\n */\nfunction constructComponentSpecificDefaultState(\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n elementDataGetters: ElementDataGetters,\n) {\n switch (element.type) {\n case 'Dropdown': {\n const children = getJsxChildren(element) as OptionElement[];\n return children[0]?.props.value;\n }\n\n case 'RadioGroup': {\n const children = getJsxChildren(element) as RadioElement[];\n return children[0]?.props.value;\n }\n\n case 'Selector': {\n const children = getJsxChildren(element) as SelectorOptionElement[];\n return children[0]?.props.value;\n }\n\n case 'Checkbox':\n return false;\n\n case 'AssetSelector':\n return getDefaultAsset(\n element.props.addresses,\n element.props.chainIds,\n elementDataGetters,\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get the state value for an asset selector.\n *\n * @param value - The asset selector value.\n * @param getAssetState - A function to get the MultichainAssetController state.\n * @returns The state value for the asset selector or null.\n */\nexport function getAssetSelectorStateValue(\n value: CaipAssetType | undefined,\n getAssetState: GetAssetsState,\n): AssetSelectorState | null {\n if (!value) {\n return null;\n }\n\n const { assetsMetadata } = getAssetState();\n const asset = assetsMetadata[value];\n\n if (!asset) {\n return null;\n }\n\n return {\n asset: value,\n name: asset.name,\n symbol: asset.symbol,\n };\n}\n\n/**\n * Get the state value for a stateful component.\n *\n * Most components store the state value as a `value` prop.\n * This function exists to account for components where that isn't the case.\n *\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n * @param elementDataGetters.getAssetsState - A function to get the MultichainAssetController state.\n * @returns The state value for a given component.\n */\nfunction getComponentStateValue(\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n { getAssetsState }: ElementDataGetters,\n) {\n switch (element.type) {\n case 'Checkbox':\n return element.props.checked;\n\n case 'AssetSelector':\n return getAssetSelectorStateValue(element.props.value, getAssetsState);\n\n case 'AddressInput': {\n if (!element.props.value) {\n return null;\n }\n\n // Construct CAIP-10 Id\n const { namespace, reference } = parseCaipChainId(element.props.chainId);\n return toCaipAccountId(namespace, reference, element.props.value);\n }\n default:\n return element.props.value;\n }\n}\n\n/**\n * Construct the state for an input field.\n *\n * @param oldState - The previous state.\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n * @param form - An optional form that the input is enclosed in.\n * @returns The input state.\n */\nfunction constructInputState(\n oldState: InterfaceState,\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | FileInputElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n elementDataGetters: ElementDataGetters,\n form?: string,\n) {\n const oldStateUnwrapped = form ? (oldState[form] as FormState) : oldState;\n const oldInputState = oldStateUnwrapped?.[element.props.name] as State;\n\n if (element.type === 'FileInput') {\n return oldInputState ?? null;\n }\n\n return (\n getComponentStateValue(element, elementDataGetters) ??\n oldInputState ??\n constructComponentSpecificDefaultState(element, elementDataGetters) ??\n null\n );\n}\n\n/**\n * Construct the interface state for a given component tree.\n *\n * @param oldState - The previous state.\n * @param rootComponent - The UI component to construct state from.\n * @param elementDataGetters - Data getters for the elements.\n * @returns The interface state of the passed component.\n */\nexport function constructState(\n oldState: InterfaceState,\n rootComponent: JSXElement,\n elementDataGetters: ElementDataGetters,\n): InterfaceState {\n const newState: InterfaceState = {};\n\n // Stack containing the forms we have visited and at which depth\n const formStack: { name: string; depth: number }[] = [];\n\n walkJsx(rootComponent, (component, depth) => {\n let currentForm = formStack[formStack.length - 1];\n\n // Pop the current form of the stack once we leave its depth.\n if (currentForm && depth <= currentForm.depth) {\n formStack.pop();\n currentForm = formStack[formStack.length - 1];\n }\n\n if (component.type === 'Form') {\n assertNameIsUnique(newState, component.props.name);\n formStack.push({ name: component.props.name, depth });\n newState[component.props.name] = {};\n return;\n }\n\n // Stateful components inside a form\n if (currentForm && isStatefulComponent(component)) {\n const formState = newState[currentForm.name] as FormState;\n assertNameIsUnique(formState, component.props.name);\n formState[component.props.name] = constructInputState(\n oldState,\n component,\n elementDataGetters,\n currentForm.name,\n );\n return;\n }\n\n // Stateful components outside a form\n if (isStatefulComponent(component)) {\n assertNameIsUnique(newState, component.props.name);\n newState[component.props.name] = constructInputState(\n oldState,\n component,\n elementDataGetters,\n );\n }\n });\n\n return newState;\n}\n\nconst MAX_CONTEXT_SIZE = 5_000_000; // 5 mb\n\n/**\n * Validate a JSON blob to be used as the interface context.\n *\n * @param context - The JSON blob.\n * @throws If the JSON blob is too large.\n */\nexport function validateInterfaceContext(context?: InterfaceContext) {\n if (!context) {\n return;\n }\n\n // We assume the validity of this JSON to be validated by the caller.\n // E.g., in the RPC method implementation.\n const size = getJsonSizeUnsafe(context);\n assert(\n size <= MAX_CONTEXT_SIZE,\n `A Snap interface context may not be larger than ${\n MAX_CONTEXT_SIZE / 1000000\n } MB.`,\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../../src/interface/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from '@metamask/snaps-sdk';\nimport type {\n FormState,\n InterfaceState,\n ComponentOrElement,\n InterfaceContext,\n State,\n FungibleAssetMetadata,\n AssetSelectorState,\n CaipChainId,\n} from '@metamask/snaps-sdk';\nimport type {\n DropdownElement,\n InputElement,\n JSXElement,\n OptionElement,\n FileInputElement,\n CheckboxElement,\n RadioGroupElement,\n RadioElement,\n SelectorElement,\n SelectorOptionElement,\n AssetSelectorElement,\n AddressInputElement,\n} from '@metamask/snaps-sdk/jsx';\nimport { isJSXElementUnsafe } from '@metamask/snaps-sdk/jsx';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport {\n getJsonSizeUnsafe,\n getJsxChildren,\n getJsxElementFromComponent,\n walkJsx,\n} from '@metamask/snaps-utils';\nimport {\n type CaipAssetType,\n type CaipAccountId,\n parseCaipAccountId,\n parseCaipAssetType,\n toCaipAccountId,\n parseCaipChainId,\n} from '@metamask/utils';\n\n/**\n * A list of stateful component types.\n */\nconst STATEFUL_COMPONENT_TYPES = [\n 'Input',\n 'Dropdown',\n 'RadioGroup',\n 'FileInput',\n 'Checkbox',\n 'Selector',\n 'AssetSelector',\n 'AddressInput',\n] as const;\n\n/**\n * Type for stateful component types.\n */\ntype StatefulComponentType = (typeof STATEFUL_COMPONENT_TYPES)[number];\n\n/**\n * Check if a component is a stateful component.\n *\n * @param component - The component to check.\n * @param component.type - The type of the component.\n *\n * @returns Whether the component is a stateful component.\n */\nexport function isStatefulComponent(component: { type: string }): component is {\n type: StatefulComponentType;\n} {\n return STATEFUL_COMPONENT_TYPES.includes(\n component.type as StatefulComponentType,\n );\n}\n\n/**\n * A function to get the MultichainAssetController state.\n *\n * @returns The MultichainAssetController state.\n */\ntype GetAssetsState = () => {\n assetsMetadata: {\n [asset: CaipAssetType]: FungibleAssetMetadata;\n };\n accountsAssets: { [account: string]: CaipAssetType[] };\n};\n\n/**\n * A function to get an account by its address.\n *\n * @param address - The account address.\n * @returns The account or undefined if not found.\n */\ntype GetAccountByAddress = (\n address: CaipAccountId,\n) => InternalAccount | undefined;\n\n/**\n * Data getters for elements.\n * This is used to get data from elements that is not directly accessible from the element itself.\n *\n * @param getAssetState - A function to get the MultichainAssetController state.\n * @param getAccountByAddress - A function to get an account by its address.\n */\ntype ElementDataGetters = {\n getAssetsState: GetAssetsState;\n getAccountByAddress: GetAccountByAddress;\n};\n\n/**\n * Get a JSX element from a component or JSX element. If the component is a\n * JSX element, it is returned as is. Otherwise, the component is converted to\n * a JSX element.\n *\n * @param component - The component to convert.\n * @returns The JSX element.\n */\nexport function getJsxInterface(component: ComponentOrElement): JSXElement {\n if (isJSXElementUnsafe(component)) {\n return component;\n }\n\n return getJsxElementFromComponent(component);\n}\n\n/**\n * Assert that the component name is unique in state.\n *\n * @param state - The interface state to verify against.\n * @param name - The component name to verify.\n */\nexport function assertNameIsUnique(state: InterfaceState, name: string) {\n assert(\n state[name] === undefined,\n `Duplicate component names are not allowed, found multiple instances of: \"${name}\".`,\n );\n}\n\n/**\n * Get a default asset for a given address.\n *\n * @param addresses - The account addresses.\n * @param chainIds - The chain IDs to filter the assets.\n * @param elementDataGetters - Data getters for the element.\n * @param elementDataGetters.getAccountByAddress - A function to get an account by its address.\n * @param elementDataGetters.getAssetsState - A function to get the MultichainAssetController state.\n *\n * @returns The default asset for the account or undefined if not found.\n */\nexport function getDefaultAsset(\n addresses: CaipAccountId[],\n chainIds: CaipChainId[] | undefined,\n { getAccountByAddress, getAssetsState }: ElementDataGetters,\n) {\n const { assetsMetadata, accountsAssets } = getAssetsState();\n\n const parsedAccounts = addresses.map((address) =>\n parseCaipAccountId(address),\n );\n\n const accountChainIds = parsedAccounts.map(({ chainId }) => chainId);\n\n const filteredChainIds =\n chainIds && chainIds.length > 0\n ? accountChainIds.filter((accountChainId) =>\n chainIds.includes(accountChainId),\n )\n : accountChainIds;\n\n const accountId = getAccountByAddress(addresses[0])?.id;\n\n // We should never fail on this assertion as the address is already validated.\n assert(accountId, `Account not found for address: ${addresses[0]}.`);\n\n const accountAssets = accountsAssets[accountId];\n\n // The AssetSelector component in the UI will be disabled if there is no asset available for the account\n // and networks provided. In this case, we return null to indicate that there is no default selected asset.\n if (accountAssets.length === 0) {\n return null;\n }\n\n const nativeAsset = accountAssets.find((asset) => {\n const { chainId, assetNamespace } = parseCaipAssetType(asset);\n\n return filteredChainIds.includes(chainId) && assetNamespace === 'slip44';\n });\n\n if (nativeAsset) {\n return {\n asset: nativeAsset,\n name: assetsMetadata[nativeAsset].name,\n symbol: assetsMetadata[nativeAsset].symbol,\n };\n }\n\n return {\n asset: accountAssets[0],\n name: assetsMetadata[accountAssets[0]].name,\n symbol: assetsMetadata[accountAssets[0]].symbol,\n };\n}\n\n/**\n * Construct default state for a component.\n *\n * This function is meant to be used inside constructInputState to account\n * for component specific defaults and will not override the component value or existing form state.\n *\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n *\n * @returns The default state for the specific component, if any.\n */\nfunction constructComponentSpecificDefaultState(\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n elementDataGetters: ElementDataGetters,\n) {\n switch (element.type) {\n case 'Dropdown': {\n const children = getJsxChildren(element) as OptionElement[];\n return children[0]?.props.value;\n }\n\n case 'RadioGroup': {\n const children = getJsxChildren(element) as RadioElement[];\n return children[0]?.props.value;\n }\n\n case 'Selector': {\n const children = getJsxChildren(element) as SelectorOptionElement[];\n return children[0]?.props.value;\n }\n\n case 'Checkbox':\n return false;\n\n case 'AssetSelector':\n return getDefaultAsset(\n element.props.addresses,\n element.props.chainIds,\n elementDataGetters,\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get the state value for an asset selector.\n *\n * @param value - The asset selector value.\n * @param getAssetState - A function to get the MultichainAssetController state.\n * @returns The state value for the asset selector or null.\n */\nexport function getAssetSelectorStateValue(\n value: CaipAssetType | undefined,\n getAssetState: GetAssetsState,\n): AssetSelectorState | null {\n if (!value) {\n return null;\n }\n\n const { assetsMetadata } = getAssetState();\n const asset = assetsMetadata[value];\n\n if (!asset) {\n return null;\n }\n\n return {\n asset: value,\n name: asset.name ?? asset.symbol ?? 'Unknown',\n symbol: asset.symbol ?? 'Unknown',\n };\n}\n\n/**\n * Get the state value for a stateful component.\n *\n * Most components store the state value as a `value` prop.\n * This function exists to account for components where that isn't the case.\n *\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n * @param elementDataGetters.getAssetsState - A function to get the MultichainAssetController state.\n * @returns The state value for a given component.\n */\nfunction getComponentStateValue(\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n { getAssetsState }: ElementDataGetters,\n) {\n switch (element.type) {\n case 'Checkbox':\n return element.props.checked;\n\n case 'AssetSelector':\n return getAssetSelectorStateValue(element.props.value, getAssetsState);\n\n case 'AddressInput': {\n if (!element.props.value) {\n return null;\n }\n\n // Construct CAIP-10 Id\n const { namespace, reference } = parseCaipChainId(element.props.chainId);\n return toCaipAccountId(namespace, reference, element.props.value);\n }\n default:\n return element.props.value;\n }\n}\n\n/**\n * Construct the state for an input field.\n *\n * @param oldState - The previous state.\n * @param element - The input element.\n * @param elementDataGetters - Data getters for the element.\n * @param form - An optional form that the input is enclosed in.\n * @returns The input state.\n */\nfunction constructInputState(\n oldState: InterfaceState,\n element:\n | InputElement\n | DropdownElement\n | RadioGroupElement\n | FileInputElement\n | CheckboxElement\n | SelectorElement\n | AssetSelectorElement\n | AddressInputElement,\n elementDataGetters: ElementDataGetters,\n form?: string,\n) {\n const oldStateUnwrapped = form ? (oldState[form] as FormState) : oldState;\n const oldInputState = oldStateUnwrapped?.[element.props.name] as State;\n\n if (element.type === 'FileInput') {\n return oldInputState ?? null;\n }\n\n return (\n getComponentStateValue(element, elementDataGetters) ??\n oldInputState ??\n constructComponentSpecificDefaultState(element, elementDataGetters) ??\n null\n );\n}\n\n/**\n * Construct the interface state for a given component tree.\n *\n * @param oldState - The previous state.\n * @param rootComponent - The UI component to construct state from.\n * @param elementDataGetters - Data getters for the elements.\n * @returns The interface state of the passed component.\n */\nexport function constructState(\n oldState: InterfaceState,\n rootComponent: JSXElement,\n elementDataGetters: ElementDataGetters,\n): InterfaceState {\n const newState: InterfaceState = {};\n\n // Stack containing the forms we have visited and at which depth\n const formStack: { name: string; depth: number }[] = [];\n\n walkJsx(rootComponent, (component, depth) => {\n let currentForm = formStack[formStack.length - 1];\n\n // Pop the current form of the stack once we leave its depth.\n if (currentForm && depth <= currentForm.depth) {\n formStack.pop();\n currentForm = formStack[formStack.length - 1];\n }\n\n if (component.type === 'Form') {\n assertNameIsUnique(newState, component.props.name);\n formStack.push({ name: component.props.name, depth });\n newState[component.props.name] = {};\n return;\n }\n\n // Stateful components inside a form\n if (currentForm && isStatefulComponent(component)) {\n const formState = newState[currentForm.name] as FormState;\n assertNameIsUnique(formState, component.props.name);\n formState[component.props.name] = constructInputState(\n oldState,\n component,\n elementDataGetters,\n currentForm.name,\n );\n return;\n }\n\n // Stateful components outside a form\n if (isStatefulComponent(component)) {\n assertNameIsUnique(newState, component.props.name);\n newState[component.props.name] = constructInputState(\n oldState,\n component,\n elementDataGetters,\n );\n }\n });\n\n return newState;\n}\n\nconst MAX_CONTEXT_SIZE = 5_000_000; // 5 mb\n\n/**\n * Validate a JSON blob to be used as the interface context.\n *\n * @param context - The JSON blob.\n * @throws If the JSON blob is too large.\n */\nexport function validateInterfaceContext(context?: InterfaceContext) {\n if (!context) {\n return;\n }\n\n // We assume the validity of this JSON to be validated by the caller.\n // E.g., in the RPC method implementation.\n const size = getJsonSizeUnsafe(context);\n assert(\n size <= MAX_CONTEXT_SIZE,\n `A Snap interface context may not be larger than ${\n MAX_CONTEXT_SIZE / 1000000\n } MB.`,\n );\n}\n"]}
|
|
@@ -38,6 +38,9 @@ class MultichainRouter {
|
|
|
38
38
|
* Handle an incoming JSON-RPC request tied to a specific scope by routing
|
|
39
39
|
* to either a procotol Snap or an account Snap.
|
|
40
40
|
*
|
|
41
|
+
* Note: Addresses are considered case sensitive by the MultichainRouter as
|
|
42
|
+
* not all non-EVM chains are case insensitive.
|
|
43
|
+
*
|
|
41
44
|
* @param options - An options bag.
|
|
42
45
|
* @param options.connectedAddresses - Addresses currently connected to the origin.
|
|
43
46
|
* @param options.origin - The origin of the RPC request.
|
|
@@ -117,10 +120,11 @@ class MultichainRouter {
|
|
|
117
120
|
* @returns True if the router can service the scope, otherwise false.
|
|
118
121
|
*/
|
|
119
122
|
isSupportedScope(scope) {
|
|
120
|
-
|
|
121
|
-
return __classPrivateFieldGet(this, _MultichainRouter_messenger, "f")
|
|
123
|
+
const hasAccountSnap = __classPrivateFieldGet(this, _MultichainRouter_messenger, "f")
|
|
122
124
|
.call('AccountsController:listMultichainAccounts', scope)
|
|
123
125
|
.some((account) => account.metadata.snap?.enabled);
|
|
126
|
+
// We currently assume here that if one Snap exists that service the scope, we can service the scope generally.
|
|
127
|
+
return hasAccountSnap || __classPrivateFieldGet(this, _MultichainRouter_instances, "m", _MultichainRouter_getProtocolSnaps).call(this, scope).length > 0;
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
exports.MultichainRouter = MultichainRouter;
|
|
@@ -177,7 +181,7 @@ async function _MultichainRouter_getSnapAccountId(connectedAddresses, scope, req
|
|
|
177
181
|
// otherwise, default to one of the connected accounts.
|
|
178
182
|
// TODO: Eventually let the user choose if we have more than one option for the account.
|
|
179
183
|
const selectedAccount = accounts.find((account) => parsedConnectedAddresses.includes(account.address) &&
|
|
180
|
-
(!address || account.address
|
|
184
|
+
(!address || account.address === address));
|
|
181
185
|
if (!selectedAccount) {
|
|
182
186
|
throw rpc_errors_1.rpcErrors.invalidParams({
|
|
183
187
|
message: 'No available account found for request.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainRouter.cjs","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport { getRunnableSnaps } from '../snaps';\nimport type { GetAllSnaps, HandleSnapRequest } from '../snaps';\n\nexport type MultichainRouterHandleRequestAction = {\n type: `${typeof name}:handleRequest`;\n handler: MultichainRouter['handleRequest'];\n};\n\nexport type MultichainRouterGetSupportedMethodsAction = {\n type: `${typeof name}:getSupportedMethods`;\n handler: MultichainRouter['getSupportedMethods'];\n};\n\nexport type MultichainRouterGetSupportedAccountsAction = {\n type: `${typeof name}:getSupportedAccounts`;\n handler: MultichainRouter['getSupportedAccounts'];\n};\n\nexport type MultichainRouterIsSupportedScopeAction = {\n type: `${typeof name}:isSupportedScope`;\n handler: MultichainRouter['isSupportedScope'];\n};\n\ntype SnapKeyring = {\n submitRequest: (request: {\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n resolveAccountAddress: (\n snapId: SnapId,\n scope: CaipChainId,\n request: Json,\n ) => Promise<{ address: CaipAccountId } | null>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\ntype WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRouterActions =\n | MultichainRouterHandleRequestAction\n | MultichainRouterGetSupportedMethodsAction\n | MultichainRouterGetSupportedAccountsAction\n | MultichainRouterIsSupportedScopeAction;\n\nexport type MultichainRouterAllowedActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRouterEvents = never;\n\nexport type MultichainRouterMessenger = RestrictedMessenger<\n typeof name,\n MultichainRouterActions | MultichainRouterAllowedActions,\n never,\n MultichainRouterAllowedActions['type'],\n MultichainRouterEvents['type']\n>;\n\nexport type MultichainRouterArgs = {\n messenger: MultichainRouterMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRouter';\n\nexport class MultichainRouter {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRouterMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRouterArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerActionHandler(\n `${name}:handleRequest`,\n async (...args) => this.handleRequest(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedMethods`,\n (...args) => this.getSupportedMethods(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedAccounts`,\n (...args) => this.getSupportedAccounts(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:isSupportedScope`,\n (...args) => this.isSupportedScope(...args),\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to to an account Snap via the SnapKeyring that will attempt this resolution.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#withSnapKeyring(async ({ keyring }) =>\n keyring.resolveAccountAddress(snapId, scope, request),\n );\n const address = result?.address;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the requesting origin.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const resolutionSnapId = accounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = accounts.find(\n (account) =>\n parsedConnectedAddresses.includes(account.address) &&\n (!address || account.address.toLowerCase() === address.toLowerCase()),\n );\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const allSnaps = this.#messenger.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(allSnaps);\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a procotol Snap or an account Snap.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the origin.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n params: rawRequest.params,\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MultichainRouter.cjs","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAEA,qDAAiD;AACjD,mEAGqC;AAGrC,uDAAoD;AAMpD,2CAKyB;AACzB,mCAAgC;AAEhC,8CAA4C;AA+E5C,MAAM,IAAI,GAAG,kBAAkB,CAAC;AAEhC,MAAa,gBAAgB;IAS3B,YAAY,EAAE,SAAS,EAAE,eAAe,EAAwB;;QARhE,SAAI,GAAgB,IAAI,CAAC;QAEzB,UAAK,GAAG,IAAI,CAAC;QAEJ,8CAAsC;QAEtC,oDAA0C;QAGjD,uBAAA,IAAI,+BAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,qCAAoB,eAAe,MAAA,CAAC;QAExC,uBAAA,IAAI,mCAAW,CAAC,qBAAqB,CACnC,GAAG,IAAI,gBAAgB,EACvB,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,CAC/C,CAAC;QAEF,uBAAA,IAAI,mCAAW,CAAC,qBAAqB,CACnC,GAAG,IAAI,sBAAsB,EAC7B,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC,CAC/C,CAAC;QAEF,uBAAA,IAAI,mCAAW,CAAC,qBAAqB,CACnC,GAAG,IAAI,uBAAuB,EAC9B,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC,CAChD,CAAC;QAEF,uBAAA,IAAI,mCAAW,CAAC,qBAAqB,CACnC,GAAG,IAAI,mBAAmB,EAC1B,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,CAC5C,CAAC;IACJ,CAAC;IAkID;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,aAAa,CAAC,EAClB,kBAAkB,EAClB,MAAM,EACN,KAAK,EACL,OAAO,EAAE,UAAU,GAMpB;QACC,6CAA6C;QAC7C,IAAA,cAAM,EACJ,CAAC,KAAK,CAAC,UAAU,CAAC,0BAAkB,CAAC,MAAM,CAAC;YAC1C,CAAC,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CACrC,CAAC;QAEF,2GAA2G;QAC3G,MAAM,OAAO,GAAG;YACd,OAAO,EAAE,KAAc;YACvB,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,IAAA,eAAM,GAAE;YAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC;QAEF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAEnC,yEAAyE;QACzE,MAAM,SAAS,GAAG,MAAM,uBAAA,IAAI,uEAAkB,MAAtB,IAAI,EAC1B,kBAAkB,EAClB,KAAK,EACL,OAAO,CACR,CAAC;QAEF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,uBAAA,IAAI,yCAAiB,MAArB,IAAI,EAAkB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CACjD,OAAO,CAAC,aAAa,CAAC;gBACpB,OAAO,EAAE,SAAS;gBAClB,KAAK;gBACL,MAAM;gBACN,MAAM,EAAE,MAAuB;aAChC,CAAC,CACH,CAAC;QACJ,CAAC;QAED,4DAA4D;QAC5D,qDAAqD;QACrD,MAAM,aAAa,GAAG,uBAAA,IAAI,uEAAkB,MAAtB,IAAI,EAAmB,KAAK,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC9B,CAAC;QAEF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,uBAAA,IAAI,mCAAW,CAAC,IAAI,CAAC,8BAA8B,EAAE;gBAC1D,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,MAAM;gBACN,OAAO,EAAE;oBACP,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE;wBACN,OAAO;wBACP,KAAK;qBACN;iBACF;gBACD,OAAO,EAAE,yBAAW,CAAC,iBAAiB;aACvC,CAAkB,CAAC;QACtB,CAAC;QAED,gEAAgE;QAChE,MAAM,sBAAS,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;IAcD;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAkB;QACpC,MAAM,cAAc,GAAG,uBAAA,IAAI,mFAA8B,MAAlC,IAAI,EAA+B,KAAK,CAAC,CAAC,OAAO,CACtE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAC7B,CAAC;QAEF,MAAM,eAAe,GAAG,uBAAA,IAAI,uEAAkB,MAAtB,IAAI,EAAmB,KAAK,CAAC,CAAC,OAAO,CAC3D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CACvB,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,KAAkB;QACrC,OAAO,uBAAA,IAAI,mFAA8B,MAAlC,IAAI,EAA+B,KAAK,CAAC,CAAC,GAAG,CAClD,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAC3C,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,KAAkB;QACjC,MAAM,cAAc,GAAG,uBAAA,IAAI,mCAAW;aACnC,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;aACxD,IAAI,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACtE,+GAA+G;QAC/G,OAAO,cAAc,IAAI,uBAAA,IAAI,uEAAkB,MAAtB,IAAI,EAAmB,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpE,CAAC;CACF;AAhTD,4CAgTC;;AA9QC;;;;;;;;;;GAUG;AACH,KAAK,kDACH,MAAc,EACd,KAAkB,EAClB,OAAuB;IAEvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,yCAAiB,MAArB,IAAI,EAAkB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAC/D,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CACtD,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC;QAChC,OAAO,OAAO,CAAC,CAAC,CAAC,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,sBAAS,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,KAAK,6CACH,kBAAmC,EACnC,KAAkB,EAClB,OAAuB;IAEvB,MAAM,QAAQ,GAAG,uBAAA,IAAI,mCAAW;SAC7B,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;SACxD,MAAM,CACL,CACE,OAAwB,EAGxB,EAAE,CACF,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;QACvC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAC3C,CAAC;IAEJ,uDAAuD;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,gBAAgB,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IAEtD,kEAAkE;IAClE,MAAM,OAAO,GAAG,MAAM,uBAAA,IAAI,4EAAuB,MAA3B,IAAI,EACxB,gBAAgB,EAChB,KAAK,EACL,OAAO,CACR,CAAC;IAEF,MAAM,wBAAwB,GAAG,kBAAkB,CAAC,GAAG,CACrD,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAA,0BAAkB,EAAC,gBAAgB,CAAC,CAAC,OAAO,CACnE,CAAC;IAEF,gFAAgF;IAChF,uDAAuD;IACvD,wFAAwF;IACxF,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CACnC,CAAC,OAAO,EAAE,EAAE,CACV,wBAAwB,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;QAClD,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,CAC5C,CAAC;IAEF,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,sBAAS,CAAC,aAAa,CAAC;YAC5B,OAAO,EAAE,yCAAyC;SACnD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,eAAe,CAAC,EAAE,CAAC;AAC5B,CAAC,mFAWiB,KAAkB;IAClC,MAAM,QAAQ,GAAG,uBAAA,IAAI,mCAAW,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC/D,MAAM,aAAa,GAAG,IAAA,wBAAgB,EAAC,QAAQ,CAAC,CAAC;IAEjD,OAAO,aAAa,CAAC,MAAM,CAAiB,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE;QAChE,MAAM,WAAW,GAAG,uBAAA,IAAI,mCAAW,CAAC,IAAI,CACtC,qCAAqC,EACrC,IAAI,CAAC,EAAE,CACR,CAAC;QAEF,IAAI,WAAW,IAAI,IAAA,mBAAW,EAAC,WAAW,EAAE,kCAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrE,MAAM,UAAU,GAAG,WAAW,CAAC,kCAAc,CAAC,QAAQ,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAA,2CAAuB,EAAC,UAAU,CAAC,CAAC;YACnD,IAAI,MAAM,IAAI,IAAA,mBAAW,EAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;gBACzC,WAAW,CAAC,IAAI,CAAC;oBACf,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC,2GA8F6B,KAAkB;IAC9C,OAAO,uBAAA,IAAI,mCAAW;SACnB,IAAI,CAAC,2CAA2C,EAAE,KAAK,CAAC;SACxD,MAAM,CAAC,CAAC,OAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport { getRunnableSnaps } from '../snaps';\nimport type { GetAllSnaps, HandleSnapRequest } from '../snaps';\n\nexport type MultichainRouterHandleRequestAction = {\n type: `${typeof name}:handleRequest`;\n handler: MultichainRouter['handleRequest'];\n};\n\nexport type MultichainRouterGetSupportedMethodsAction = {\n type: `${typeof name}:getSupportedMethods`;\n handler: MultichainRouter['getSupportedMethods'];\n};\n\nexport type MultichainRouterGetSupportedAccountsAction = {\n type: `${typeof name}:getSupportedAccounts`;\n handler: MultichainRouter['getSupportedAccounts'];\n};\n\nexport type MultichainRouterIsSupportedScopeAction = {\n type: `${typeof name}:isSupportedScope`;\n handler: MultichainRouter['isSupportedScope'];\n};\n\ntype SnapKeyring = {\n submitRequest: (request: {\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n resolveAccountAddress: (\n snapId: SnapId,\n scope: CaipChainId,\n request: Json,\n ) => Promise<{ address: CaipAccountId } | null>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\ntype WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRouterActions =\n | MultichainRouterHandleRequestAction\n | MultichainRouterGetSupportedMethodsAction\n | MultichainRouterGetSupportedAccountsAction\n | MultichainRouterIsSupportedScopeAction;\n\nexport type MultichainRouterAllowedActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRouterEvents = never;\n\nexport type MultichainRouterMessenger = RestrictedMessenger<\n typeof name,\n MultichainRouterActions | MultichainRouterAllowedActions,\n never,\n MultichainRouterAllowedActions['type'],\n MultichainRouterEvents['type']\n>;\n\nexport type MultichainRouterArgs = {\n messenger: MultichainRouterMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRouter';\n\nexport class MultichainRouter {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRouterMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRouterArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerActionHandler(\n `${name}:handleRequest`,\n async (...args) => this.handleRequest(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedMethods`,\n (...args) => this.getSupportedMethods(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedAccounts`,\n (...args) => this.getSupportedAccounts(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:isSupportedScope`,\n (...args) => this.isSupportedScope(...args),\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to to an account Snap via the SnapKeyring that will attempt this resolution.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#withSnapKeyring(async ({ keyring }) =>\n keyring.resolveAccountAddress(snapId, scope, request),\n );\n const address = result?.address;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the requesting origin.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const resolutionSnapId = accounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = accounts.find(\n (account) =>\n parsedConnectedAddresses.includes(account.address) &&\n (!address || account.address === address),\n );\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const allSnaps = this.#messenger.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(allSnaps);\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a procotol Snap or an account Snap.\n *\n * Note: Addresses are considered case sensitive by the MultichainRouter as\n * not all non-EVM chains are case insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the origin.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n params: rawRequest.params,\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n const hasAccountSnap = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0;\n }\n}\n"]}
|
|
@@ -56,6 +56,9 @@ export declare class MultichainRouter {
|
|
|
56
56
|
* Handle an incoming JSON-RPC request tied to a specific scope by routing
|
|
57
57
|
* to either a procotol Snap or an account Snap.
|
|
58
58
|
*
|
|
59
|
+
* Note: Addresses are considered case sensitive by the MultichainRouter as
|
|
60
|
+
* not all non-EVM chains are case insensitive.
|
|
61
|
+
*
|
|
59
62
|
* @param options - An options bag.
|
|
60
63
|
* @param options.connectedAddresses - Addresses currently connected to the origin.
|
|
61
64
|
* @param options.origin - The origin of the RPC request.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainRouter.d.cts","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"MultichainRouter.d.cts","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,6 +56,9 @@ export declare class MultichainRouter {
|
|
|
56
56
|
* Handle an incoming JSON-RPC request tied to a specific scope by routing
|
|
57
57
|
* to either a procotol Snap or an account Snap.
|
|
58
58
|
*
|
|
59
|
+
* Note: Addresses are considered case sensitive by the MultichainRouter as
|
|
60
|
+
* not all non-EVM chains are case insensitive.
|
|
61
|
+
*
|
|
59
62
|
* @param options - An options bag.
|
|
60
63
|
* @param options.connectedAddresses - Addresses currently connected to the origin.
|
|
61
64
|
* @param options.origin - The origin of the RPC request.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainRouter.d.mts","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,wCAAwC;AAMtE,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,4BAA4B;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,8BAA8B;AAE7D,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,wBAAwB;AAUzB,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,2BAAiB;AAE/D,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,GAAG,OAAO,IAAI,gBAAgB,CAAC;IACrC,OAAO,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,yCAAyC,GAAG;IACtD,IAAI,EAAE,GAAG,OAAO,IAAI,sBAAsB,CAAC;IAC3C,OAAO,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,0CAA0C,GAAG;IACvD,IAAI,EAAE,GAAG,OAAO,IAAI,uBAAuB,CAAC;IAC5C,OAAO,EAAE,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,sCAAsC,GAAG;IACnD,IAAI,EAAE,GAAG,OAAO,IAAI,mBAAmB,CAAC;IACxC,OAAO,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;CAC/C,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,KAAK,EAAE,WAAW,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,qBAAqB,EAAE,CACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,IAAI,KACV,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACjD,CAAC;AAGF,KAAK,uBAAuB,GAAG,CAAC,UAAU,EACxC,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,UAAU,CAAC,KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,MAAM,MAAM,8CAA8C,GAAG;IAC3D,IAAI,EAAE,2CAA2C,CAAC;IAClD,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,eAAe,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,mCAAmC,GACnC,yCAAyC,GACzC,0CAA0C,GAC1C,sCAAsC,CAAC;AAE3C,MAAM,MAAM,8BAA8B,GACtC,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,8CAA8C,CAAC;AAEnD,MAAM,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE3C,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,CACzD,OAAO,IAAI,EACX,uBAAuB,GAAG,8BAA8B,EACxD,KAAK,EACL,8BAA8B,CAAC,MAAM,CAAC,EACtC,sBAAsB,CAAC,MAAM,CAAC,CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,yBAAyB,CAAC;IACrC,eAAe,EAAE,uBAAuB,CAAC;CAC1C,CAAC;AAOF,QAAA,MAAM,IAAI,qBAAqB,CAAC;AAEhC,qBAAa,gBAAgB;;IAC3B,IAAI,EAAE,OAAO,IAAI,CAAQ;IAEzB,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,oBAAoB;IAyJhE
|
|
1
|
+
{"version":3,"file":"MultichainRouter.d.mts","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,6 +35,9 @@ export class MultichainRouter {
|
|
|
35
35
|
* Handle an incoming JSON-RPC request tied to a specific scope by routing
|
|
36
36
|
* to either a procotol Snap or an account Snap.
|
|
37
37
|
*
|
|
38
|
+
* Note: Addresses are considered case sensitive by the MultichainRouter as
|
|
39
|
+
* not all non-EVM chains are case insensitive.
|
|
40
|
+
*
|
|
38
41
|
* @param options - An options bag.
|
|
39
42
|
* @param options.connectedAddresses - Addresses currently connected to the origin.
|
|
40
43
|
* @param options.origin - The origin of the RPC request.
|
|
@@ -114,10 +117,11 @@ export class MultichainRouter {
|
|
|
114
117
|
* @returns True if the router can service the scope, otherwise false.
|
|
115
118
|
*/
|
|
116
119
|
isSupportedScope(scope) {
|
|
117
|
-
|
|
118
|
-
return __classPrivateFieldGet(this, _MultichainRouter_messenger, "f")
|
|
120
|
+
const hasAccountSnap = __classPrivateFieldGet(this, _MultichainRouter_messenger, "f")
|
|
119
121
|
.call('AccountsController:listMultichainAccounts', scope)
|
|
120
122
|
.some((account) => account.metadata.snap?.enabled);
|
|
123
|
+
// We currently assume here that if one Snap exists that service the scope, we can service the scope generally.
|
|
124
|
+
return hasAccountSnap || __classPrivateFieldGet(this, _MultichainRouter_instances, "m", _MultichainRouter_getProtocolSnaps).call(this, scope).length > 0;
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
_MultichainRouter_messenger = new WeakMap(), _MultichainRouter_withSnapKeyring = new WeakMap(), _MultichainRouter_instances = new WeakSet(), _MultichainRouter_resolveRequestAddress =
|
|
@@ -173,7 +177,7 @@ async function _MultichainRouter_getSnapAccountId(connectedAddresses, scope, req
|
|
|
173
177
|
// otherwise, default to one of the connected accounts.
|
|
174
178
|
// TODO: Eventually let the user choose if we have more than one option for the account.
|
|
175
179
|
const selectedAccount = accounts.find((account) => parsedConnectedAddresses.includes(account.address) &&
|
|
176
|
-
(!address || account.address
|
|
180
|
+
(!address || account.address === address));
|
|
177
181
|
if (!selectedAccount) {
|
|
178
182
|
throw rpcErrors.invalidParams({
|
|
179
183
|
message: 'No available account found for request.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultichainRouter.mjs","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport { getRunnableSnaps } from '../snaps';\nimport type { GetAllSnaps, HandleSnapRequest } from '../snaps';\n\nexport type MultichainRouterHandleRequestAction = {\n type: `${typeof name}:handleRequest`;\n handler: MultichainRouter['handleRequest'];\n};\n\nexport type MultichainRouterGetSupportedMethodsAction = {\n type: `${typeof name}:getSupportedMethods`;\n handler: MultichainRouter['getSupportedMethods'];\n};\n\nexport type MultichainRouterGetSupportedAccountsAction = {\n type: `${typeof name}:getSupportedAccounts`;\n handler: MultichainRouter['getSupportedAccounts'];\n};\n\nexport type MultichainRouterIsSupportedScopeAction = {\n type: `${typeof name}:isSupportedScope`;\n handler: MultichainRouter['isSupportedScope'];\n};\n\ntype SnapKeyring = {\n submitRequest: (request: {\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n resolveAccountAddress: (\n snapId: SnapId,\n scope: CaipChainId,\n request: Json,\n ) => Promise<{ address: CaipAccountId } | null>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\ntype WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRouterActions =\n | MultichainRouterHandleRequestAction\n | MultichainRouterGetSupportedMethodsAction\n | MultichainRouterGetSupportedAccountsAction\n | MultichainRouterIsSupportedScopeAction;\n\nexport type MultichainRouterAllowedActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRouterEvents = never;\n\nexport type MultichainRouterMessenger = RestrictedMessenger<\n typeof name,\n MultichainRouterActions | MultichainRouterAllowedActions,\n never,\n MultichainRouterAllowedActions['type'],\n MultichainRouterEvents['type']\n>;\n\nexport type MultichainRouterArgs = {\n messenger: MultichainRouterMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRouter';\n\nexport class MultichainRouter {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRouterMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRouterArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerActionHandler(\n `${name}:handleRequest`,\n async (...args) => this.handleRequest(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedMethods`,\n (...args) => this.getSupportedMethods(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedAccounts`,\n (...args) => this.getSupportedAccounts(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:isSupportedScope`,\n (...args) => this.isSupportedScope(...args),\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to to an account Snap via the SnapKeyring that will attempt this resolution.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#withSnapKeyring(async ({ keyring }) =>\n keyring.resolveAccountAddress(snapId, scope, request),\n );\n const address = result?.address;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the requesting origin.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const resolutionSnapId = accounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = accounts.find(\n (account) =>\n parsedConnectedAddresses.includes(account.address) &&\n (!address || account.address.toLowerCase() === address.toLowerCase()),\n );\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const allSnaps = this.#messenger.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(allSnaps);\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a procotol Snap or an account Snap.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the origin.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n params: rawRequest.params,\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MultichainRouter.mjs","sourceRoot":"","sources":["../../src/multichain/MultichainRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport {\n getProtocolCaveatScopes,\n SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport type { InternalAccount } from '@metamask/snaps-utils';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type {\n CaipAccountId,\n CaipChainId,\n JsonRpcParams,\n} from '@metamask/utils';\nimport {\n assert,\n hasProperty,\n KnownCaipNamespace,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport { getRunnableSnaps } from '../snaps';\nimport type { GetAllSnaps, HandleSnapRequest } from '../snaps';\n\nexport type MultichainRouterHandleRequestAction = {\n type: `${typeof name}:handleRequest`;\n handler: MultichainRouter['handleRequest'];\n};\n\nexport type MultichainRouterGetSupportedMethodsAction = {\n type: `${typeof name}:getSupportedMethods`;\n handler: MultichainRouter['getSupportedMethods'];\n};\n\nexport type MultichainRouterGetSupportedAccountsAction = {\n type: `${typeof name}:getSupportedAccounts`;\n handler: MultichainRouter['getSupportedAccounts'];\n};\n\nexport type MultichainRouterIsSupportedScopeAction = {\n type: `${typeof name}:isSupportedScope`;\n handler: MultichainRouter['isSupportedScope'];\n};\n\ntype SnapKeyring = {\n submitRequest: (request: {\n account: string;\n method: string;\n params?: Json[] | Record<string, Json>;\n scope: CaipChainId;\n }) => Promise<Json>;\n resolveAccountAddress: (\n snapId: SnapId,\n scope: CaipChainId,\n request: Json,\n ) => Promise<{ address: CaipAccountId } | null>;\n};\n\n// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring\ntype WithSnapKeyringFunction = <ReturnType>(\n operation: ({ keyring }: { keyring: SnapKeyring }) => Promise<ReturnType>,\n) => Promise<ReturnType>;\n\nexport type AccountsControllerListMultichainAccountsAction = {\n type: `AccountsController:listMultichainAccounts`;\n handler: (chainId?: CaipChainId) => InternalAccount[];\n};\n\nexport type MultichainRouterActions =\n | MultichainRouterHandleRequestAction\n | MultichainRouterGetSupportedMethodsAction\n | MultichainRouterGetSupportedAccountsAction\n | MultichainRouterIsSupportedScopeAction;\n\nexport type MultichainRouterAllowedActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions\n | AccountsControllerListMultichainAccountsAction;\n\nexport type MultichainRouterEvents = never;\n\nexport type MultichainRouterMessenger = RestrictedMessenger<\n typeof name,\n MultichainRouterActions | MultichainRouterAllowedActions,\n never,\n MultichainRouterAllowedActions['type'],\n MultichainRouterEvents['type']\n>;\n\nexport type MultichainRouterArgs = {\n messenger: MultichainRouterMessenger;\n withSnapKeyring: WithSnapKeyringFunction;\n};\n\ntype ProtocolSnap = {\n snapId: SnapId;\n methods: string[];\n};\n\nconst name = 'MultichainRouter';\n\nexport class MultichainRouter {\n name: typeof name = name;\n\n state = null;\n\n readonly #messenger: MultichainRouterMessenger;\n\n readonly #withSnapKeyring: WithSnapKeyringFunction;\n\n constructor({ messenger, withSnapKeyring }: MultichainRouterArgs) {\n this.#messenger = messenger;\n this.#withSnapKeyring = withSnapKeyring;\n\n this.#messenger.registerActionHandler(\n `${name}:handleRequest`,\n async (...args) => this.handleRequest(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedMethods`,\n (...args) => this.getSupportedMethods(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:getSupportedAccounts`,\n (...args) => this.getSupportedAccounts(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${name}:isSupportedScope`,\n (...args) => this.isSupportedScope(...args),\n );\n }\n\n /**\n * Attempts to resolve the account address to use for a given request by inspecting the request itself.\n *\n * The request is sent to to an account Snap via the SnapKeyring that will attempt this resolution.\n *\n * @param snapId - The ID of the Snap to send the request to.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns The resolved address if found, otherwise null.\n * @throws If the invocation of the SnapKeyring fails.\n */\n async #resolveRequestAddress(\n snapId: SnapId,\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n try {\n const result = await this.#withSnapKeyring(async ({ keyring }) =>\n keyring.resolveAccountAddress(snapId, scope, request),\n );\n const address = result?.address;\n return address ? parseCaipAccountId(address).address : null;\n } catch {\n throw rpcErrors.internal();\n }\n }\n\n /**\n * Get the account ID of the account that should service the RPC request via an account Snap.\n *\n * This function checks whether any accounts exist that can service a given request by\n * using a combination of the resolveAccountAddress functionality and the connected accounts.\n *\n * If an account is expected to service this request but none is found, the function will throw.\n *\n * @param connectedAddresses - The CAIP-10 addresses connected to the requesting origin.\n * @param scope - The CAIP-2 scope for the request.\n * @param request - The JSON-RPC request.\n * @returns An account ID if found, otherwise null.\n * @throws If no account is found, but the accounts exist that could service the request.\n */\n async #getSnapAccountId(\n connectedAddresses: CaipAccountId[],\n scope: CaipChainId,\n request: JsonRpcRequest,\n ) {\n const accounts = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter(\n (\n account: InternalAccount,\n ): account is InternalAccount & {\n metadata: Required<InternalAccount['metadata']>;\n } =>\n Boolean(account.metadata.snap?.enabled) &&\n account.methods.includes(request.method),\n );\n\n // If no accounts can service the request, return null.\n if (accounts.length === 0) {\n return null;\n }\n\n const resolutionSnapId = accounts[0].metadata.snap.id;\n\n // Attempt to resolve the address that should be used for signing.\n const address = await this.#resolveRequestAddress(\n resolutionSnapId,\n scope,\n request,\n );\n\n const parsedConnectedAddresses = connectedAddresses.map(\n (connectedAddress) => parseCaipAccountId(connectedAddress).address,\n );\n\n // If we have a resolved address, try to find the selected account based on that\n // otherwise, default to one of the connected accounts.\n // TODO: Eventually let the user choose if we have more than one option for the account.\n const selectedAccount = accounts.find(\n (account) =>\n parsedConnectedAddresses.includes(account.address) &&\n (!address || account.address === address),\n );\n\n if (!selectedAccount) {\n throw rpcErrors.invalidParams({\n message: 'No available account found for request.',\n });\n }\n\n return selectedAccount.id;\n }\n\n /**\n * Get all protocol Snaps that can service a given CAIP-2 scope.\n *\n * Protocol Snaps are deemed fit to service a scope if they are runnable\n * and have the proper permissions set for the scope.\n *\n * @param scope - A CAIP-2 scope.\n * @returns A list of all the protocol Snaps available and their RPC methods.\n */\n #getProtocolSnaps(scope: CaipChainId) {\n const allSnaps = this.#messenger.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(allSnaps);\n\n return filteredSnaps.reduce<ProtocolSnap[]>((accumulator, snap) => {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snap.id,\n );\n\n if (permissions && hasProperty(permissions, SnapEndowments.Protocol)) {\n const permission = permissions[SnapEndowments.Protocol];\n const scopes = getProtocolCaveatScopes(permission);\n if (scopes && hasProperty(scopes, scope)) {\n accumulator.push({\n snapId: snap.id,\n methods: scopes[scope].methods,\n });\n }\n }\n\n return accumulator;\n }, []);\n }\n\n /**\n * Handle an incoming JSON-RPC request tied to a specific scope by routing\n * to either a procotol Snap or an account Snap.\n *\n * Note: Addresses are considered case sensitive by the MultichainRouter as\n * not all non-EVM chains are case insensitive.\n *\n * @param options - An options bag.\n * @param options.connectedAddresses - Addresses currently connected to the origin.\n * @param options.origin - The origin of the RPC request.\n * @param options.request - The JSON-RPC request.\n * @param options.scope - The CAIP-2 scope for the request.\n * @returns The response from the chosen Snap.\n * @throws If no handler was found.\n */\n async handleRequest({\n connectedAddresses,\n origin,\n scope,\n request: rawRequest,\n }: {\n connectedAddresses: CaipAccountId[];\n origin: string;\n scope: CaipChainId;\n request: JsonRpcRequest;\n }): Promise<Json> {\n // Explicitly block EVM scopes, just in case.\n assert(\n !scope.startsWith(KnownCaipNamespace.Eip155) &&\n !scope.startsWith('wallet:eip155'),\n );\n\n // Re-create the request to simplify and remove additional properties that may be present in MM middleware.\n const request = {\n jsonrpc: '2.0' as const,\n id: rawRequest.id ?? nanoid(),\n method: rawRequest.method,\n params: rawRequest.params,\n };\n\n const { method, params } = request;\n\n // If the RPC request can be serviced by an account Snap, route it there.\n const accountId = await this.#getSnapAccountId(\n connectedAddresses,\n scope,\n request,\n );\n\n if (accountId) {\n return this.#withSnapKeyring(async ({ keyring }) =>\n keyring.submitRequest({\n account: accountId,\n scope,\n method,\n params: params as JsonRpcParams,\n }),\n );\n }\n\n // If the RPC request cannot be serviced by an account Snap,\n // but has a protocol Snap available, route it there.\n const protocolSnaps = this.#getProtocolSnaps(scope);\n const protocolSnap = protocolSnaps.find((snap) =>\n snap.methods.includes(method),\n );\n\n if (protocolSnap) {\n return this.#messenger.call('SnapController:handleRequest', {\n snapId: protocolSnap.snapId,\n origin,\n request: {\n method: '',\n params: {\n request,\n scope,\n },\n },\n handler: HandlerType.OnProtocolRequest,\n }) as Promise<Json>;\n }\n\n // If no compatible account or protocol Snaps were found, throw.\n throw rpcErrors.methodNotFound();\n }\n\n /**\n * Get a list of metadata for supported accounts for a given scope from the client.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of metadata for the supported accounts.\n */\n #getSupportedAccountsMetadata(scope: CaipChainId): InternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .filter((account: InternalAccount) => account.metadata.snap?.enabled);\n }\n\n /**\n * Get a list of supported methods for a given scope.\n * This combines both protocol and account Snaps supported methods.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of supported methods.\n */\n getSupportedMethods(scope: CaipChainId): string[] {\n const accountMethods = this.#getSupportedAccountsMetadata(scope).flatMap(\n (account) => account.methods,\n );\n\n const protocolMethods = this.#getProtocolSnaps(scope).flatMap(\n (snap) => snap.methods,\n );\n\n return Array.from(new Set([...accountMethods, ...protocolMethods]));\n }\n\n /**\n * Get a list of supported accounts for a given scope.\n *\n * @param scope - The CAIP-2 scope.\n * @returns A list of CAIP-10 addresses.\n */\n getSupportedAccounts(scope: CaipChainId): string[] {\n return this.#getSupportedAccountsMetadata(scope).map(\n (account) => `${scope}:${account.address}`,\n );\n }\n\n /**\n * Determine whether a given CAIP-2 scope is supported by the router.\n *\n * @param scope - The CAIP-2 scope.\n * @returns True if the router can service the scope, otherwise false.\n */\n isSupportedScope(scope: CaipChainId): boolean {\n const hasAccountSnap = this.#messenger\n .call('AccountsController:listMultichainAccounts', scope)\n .some((account: InternalAccount) => account.metadata.snap?.enabled);\n // We currently assume here that if one Snap exists that service the scope, we can service the scope generally.\n return hasAccountSnap || this.#getProtocolSnaps(scope).length > 0;\n }\n}\n"]}
|