diff --git a/packages/snaps-execution-environments/src/common/validation.ts b/packages/snaps-execution-environments/src/common/validation.ts index 806e78dedf..fa5697bbf6 100644 --- a/packages/snaps-execution-environments/src/common/validation.ts +++ b/packages/snaps-execution-environments/src/common/validation.ts @@ -3,7 +3,7 @@ import { InterfaceContextStruct, UserInputEventStruct, } from '@metamask/snaps-sdk'; -import { ChainIdStruct, HandlerType } from '@metamask/snaps-utils'; +import { HandlerType } from '@metamask/snaps-utils'; import type { Infer, Struct } from '@metamask/superstruct'; import { any, @@ -127,7 +127,7 @@ export type RequestArguments = export const OnTransactionRequestArgumentsStruct = object({ // TODO: Improve `transaction` type. transaction: record(string(), JsonStruct), - chainId: ChainIdStruct, + chainId: CaipChainIdStruct, transactionOrigin: nullable(string()), }); @@ -182,7 +182,7 @@ export function assertIsOnSignatureRequestArguments( ); } -const baseNameLookupArgs = { chainId: ChainIdStruct }; +const baseNameLookupArgs = { chainId: CaipChainIdStruct }; const domainRequestStruct = object({ ...baseNameLookupArgs, address: string(), diff --git a/packages/snaps-rpc-methods/src/endowments/name-lookup.test.ts b/packages/snaps-rpc-methods/src/endowments/name-lookup.test.ts index 16caf5d591..71865d58d9 100644 --- a/packages/snaps-rpc-methods/src/endowments/name-lookup.test.ts +++ b/packages/snaps-rpc-methods/src/endowments/name-lookup.test.ts @@ -243,7 +243,7 @@ describe('nameLookupCaveatSpecifications', () => { value: ['eip155'], }), ).toThrow( - 'Assertion failed: At path: 0 -- Expected a Chain ID matching `/^(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32})$/` but received "eip155".', + 'Assertion failed: At path: 0 -- Expected a value of type `CaipChainId`, but received: `"eip155"`.', ); expect(() => diff --git a/packages/snaps-sdk/src/types/caip.test.ts b/packages/snaps-sdk/src/types/caip.test.ts deleted file mode 100644 index 426370cf36..0000000000 --- a/packages/snaps-sdk/src/types/caip.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { expectTypeOf } from 'expect-type'; - -import type { AccountId, ChainId } from './caip'; - -describe('ChainId', () => { - it('has a colon-separated namespace and reference', () => { - expectTypeOf<'foo:bar'>().toMatchTypeOf(); - }); -}); - -describe('AccountId', () => { - it('has a colon-separated chain ID and account address', () => { - expectTypeOf<'foo:bar:baz'>().toMatchTypeOf(); - }); -}); diff --git a/packages/snaps-sdk/src/types/caip.ts b/packages/snaps-sdk/src/types/caip.ts index 622280d5bc..ee4b0e9df8 100644 --- a/packages/snaps-sdk/src/types/caip.ts +++ b/packages/snaps-sdk/src/types/caip.ts @@ -1,13 +1,24 @@ +import type { CaipAccountId, CaipChainId } from '@metamask/utils'; + +export type { + CaipAccountId, + CaipAssetType, + CaipChainId, + CaipNamespace, +} from '@metamask/utils'; + /** * A CAIP-2 chain ID, i.e., a human-readable namespace and reference. * * @see https://chainagnostic.org/CAIPs/caip-2 + * @deprecated Use {@link CaipChainId} instead. */ -export type ChainId = `${string}:${string}`; +export type ChainId = CaipChainId; /** * A CAIP-10 account ID, i.e., a chain ID and an account address. * * @see https://chainagnostic.org/CAIPs/caip-10 + * @deprecated Use {@link CaipAccountId} instead. */ -export type AccountId = `${ChainId}:${string}`; +export type AccountId = CaipAccountId; diff --git a/packages/snaps-sdk/src/types/handlers/name-lookup.ts b/packages/snaps-sdk/src/types/handlers/name-lookup.ts index 6879ac4b05..845e8c2a03 100644 --- a/packages/snaps-sdk/src/types/handlers/name-lookup.ts +++ b/packages/snaps-sdk/src/types/handlers/name-lookup.ts @@ -1,9 +1,7 @@ -import type { NonEmptyArray } from '@metamask/utils'; - -import type { ChainId } from '../caip'; +import type { CaipChainId, NonEmptyArray } from '@metamask/utils'; type BaseOnNameLookupArgs = { - chainId: ChainId; + chainId: CaipChainId; }; /** @@ -76,7 +74,7 @@ export type AddressLookupResult = { * Note that using this handler requires the `endowment:name-lookup` permission. * * @param args - The request arguments. - * @param args.chainId - The CAIP-2 {@link ChainId} of the network the + * @param args.chainId - The CAIP-2 {@link CaipChainId} of the network the * transaction is being submitted to. * @param args.domain - The human-readable address that is to be resolved. This * is mutually exclusive with `args.address`. diff --git a/packages/snaps-sdk/src/types/handlers/transaction.ts b/packages/snaps-sdk/src/types/handlers/transaction.ts index 3d3363c1f4..1bd85554c7 100644 --- a/packages/snaps-sdk/src/types/handlers/transaction.ts +++ b/packages/snaps-sdk/src/types/handlers/transaction.ts @@ -1,6 +1,7 @@ +import type { CaipChainId } from '@metamask/utils'; + import type { ComponentOrElement } from '..'; import type { EnumToUnion } from '../../internals'; -import type { ChainId } from '../caip'; /** * The severity level of content being returned from a transaction insight. @@ -87,7 +88,7 @@ export type Transaction = EIP1559Transaction | LegacyTransaction; * @param args - The request arguments. * @param args.transaction - The transaction object, containing the address, * value, data, and other properties of the transaction. - * @param args.chainId - The CAIP-2 {@link ChainId} of the network the + * @param args.chainId - The CAIP-2 {@link CaipChainId} of the network the * transaction is being submitted to. * @param args.transactionOrigin - The origin of the transaction. This is the * URL of the website that submitted the transaction. This is only available if @@ -99,7 +100,7 @@ export type Transaction = EIP1559Transaction | LegacyTransaction; */ export type OnTransactionHandler = (args: { transaction: Transaction; - chainId: ChainId; + chainId: CaipChainId; transactionOrigin?: string; }) => Promise; diff --git a/packages/snaps-sdk/src/types/permissions.ts b/packages/snaps-sdk/src/types/permissions.ts index a591ee3957..2bf1b60113 100644 --- a/packages/snaps-sdk/src/types/permissions.ts +++ b/packages/snaps-sdk/src/types/permissions.ts @@ -1,7 +1,5 @@ import type { SupportedCurve } from '@metamask/key-tree'; -import type { JsonRpcRequest } from '@metamask/utils'; - -import type { ChainId } from './caip'; +import type { CaipChainId, JsonRpcRequest } from '@metamask/utils'; export type EmptyObject = Record; @@ -49,7 +47,7 @@ export type InitialPermissions = Partial<{ maxRequestTime?: number; }; 'endowment:name-lookup': { - chains?: ChainId[]; + chains?: CaipChainId[]; matchers?: NameLookupMatchers; maxRequestTime?: number; }; diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 16cb70ee5a..2497636a15 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { "branches": 99.74, - "functions": 98.95, - "lines": 99.47, - "statements": 96.27 + "functions": 98.92, + "lines": 99.61, + "statements": 96.91 } diff --git a/packages/snaps-utils/src/index.executionenv.ts b/packages/snaps-utils/src/index.executionenv.ts index 919513c7d6..db7dbf9123 100644 --- a/packages/snaps-utils/src/index.executionenv.ts +++ b/packages/snaps-utils/src/index.executionenv.ts @@ -4,5 +4,4 @@ export * from './handlers'; export * from './handler-types'; export * from './iframe'; export * from './logging'; -export * from './namespace'; export * from './types'; diff --git a/packages/snaps-utils/src/index.ts b/packages/snaps-utils/src/index.ts index 7cfb076bdf..b6f15aba7a 100644 --- a/packages/snaps-utils/src/index.ts +++ b/packages/snaps-utils/src/index.ts @@ -20,7 +20,6 @@ export * from './json-rpc'; export * from './localization'; export * from './logging'; export * from './manifest'; -export * from './namespace'; export * from './path'; export * from './platform-version'; export * from './snaps'; diff --git a/packages/snaps-utils/src/manifest/validation.ts b/packages/snaps-utils/src/manifest/validation.ts index 2eabc2a769..2df4e9ce5c 100644 --- a/packages/snaps-utils/src/manifest/validation.ts +++ b/packages/snaps-utils/src/manifest/validation.ts @@ -34,7 +34,6 @@ import { isEqual } from '../array'; import { CronjobSpecificationArrayStruct } from '../cronjob'; import { SIP_6_MAGIC_VALUE, STATE_ENCRYPTION_MAGIC_VALUE } from '../entropy'; import { KeyringOriginsStruct, RpcOriginsStruct } from '../json-rpc'; -import { ChainIdStruct } from '../namespace'; import { SnapIdStruct } from '../snaps'; import { mergeStructs, type InferMatching } from '../structs'; import { NameStruct, NpmSnapFileNames, uri } from '../types'; @@ -151,7 +150,7 @@ export const SnapIdsStruct = refine( export type SnapIds = Infer; -export const ChainIdsStruct = size(array(ChainIdStruct), 1, Infinity); +export const ChainIdsStruct = size(array(CaipChainIdStruct), 1, Infinity); export const LookupMatchersStruct = union([ object({ diff --git a/packages/snaps-utils/src/namespace.test.ts b/packages/snaps-utils/src/namespace.test.ts deleted file mode 100644 index b6e96ce7b3..0000000000 --- a/packages/snaps-utils/src/namespace.test.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { - isAccountId, - isAccountIdArray, - isChainId, - isNamespace, - isNamespaceId, - parseAccountId, - parseChainId, -} from './namespace'; -import { getChain, getNamespace } from './test-utils'; - -describe('parseChainId', () => { - it('parses valid chain ids', () => { - expect(parseChainId('eip155:1')).toMatchInlineSnapshot(` - { - "namespace": "eip155", - "reference": "1", - } - `); - - expect( - parseChainId('bip122:000000000019d6689c085ae165831e93'), - ).toMatchInlineSnapshot( - ` - { - "namespace": "bip122", - "reference": "000000000019d6689c085ae165831e93", - } - `, - ); - - expect(parseChainId('cosmos:cosmoshub-3')).toMatchInlineSnapshot( - ` - { - "namespace": "cosmos", - "reference": "cosmoshub-3", - } - `, - ); - - expect( - parseChainId('polkadot:b0a8d493285c2df73290dfb7e61f870f'), - ).toMatchInlineSnapshot( - ` - { - "namespace": "polkadot", - "reference": "b0a8d493285c2df73290dfb7e61f870f", - } - `, - ); - }); - - it.each([ - true, - false, - null, - undefined, - 1, - 'foo', - 'foobarbazquz:1', - 'foo:', - 'foo:foobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquz', - ])('throws for invalid input', (input) => { - expect(() => parseChainId(input as any)).toThrow('Invalid chain ID.'); - }); -}); - -describe('parseAccountId', () => { - it('parses valid account ids', () => { - expect( - parseAccountId('eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb'), - ).toMatchInlineSnapshot( - ` - { - "address": "0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb", - "chain": { - "namespace": "eip155", - "reference": "1", - }, - "chainId": "eip155:1", - } - `, - ); - - expect( - parseAccountId( - 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', - ), - ).toMatchInlineSnapshot( - ` - { - "address": "128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6", - "chain": { - "namespace": "bip122", - "reference": "000000000019d6689c085ae165831e93", - }, - "chainId": "bip122:000000000019d6689c085ae165831e93", - } - `, - ); - - expect( - parseAccountId( - 'cosmos:cosmoshub-3:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0', - ), - ).toMatchInlineSnapshot( - ` - { - "address": "cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0", - "chain": { - "namespace": "cosmos", - "reference": "cosmoshub-3", - }, - "chainId": "cosmos:cosmoshub-3", - } - `, - ); - - expect( - parseAccountId( - 'polkadot:b0a8d493285c2df73290dfb7e61f870f:5hmuyxw9xdgbpptgypokw4thfyoe3ryenebr381z9iaegmfy', - ), - ).toMatchInlineSnapshot( - ` - { - "address": "5hmuyxw9xdgbpptgypokw4thfyoe3ryenebr381z9iaegmfy", - "chain": { - "namespace": "polkadot", - "reference": "b0a8d493285c2df73290dfb7e61f870f", - }, - "chainId": "polkadot:b0a8d493285c2df73290dfb7e61f870f", - } - `, - ); - }); - - it.each([ - true, - false, - null, - undefined, - 1, - 'foo', - 'foobarbazquz:1', - 'foo:', - 'foo:foobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquz', - 'eip155:1', - 'eip155:1:', - ])('throws for invalid input', (input) => { - expect(() => parseAccountId(input as any)).toThrow('Invalid account ID.'); - }); -}); - -describe('isNamespaceId', () => { - it.each(['eip155', 'bip122'])( - 'returns true for a valid namespace id', - (id) => { - expect(isNamespaceId(id)).toBe(true); - }, - ); - - it.each([true, false, null, undefined, 1, {}, [], 'a', 'foobarbaz'])( - 'returns false for an invalid namespace id', - (id) => { - expect(isNamespaceId(id)).toBe(false); - }, - ); -}); - -describe('isChainId', () => { - it.each([ - 'eip155:1', - 'eip155:1337', - 'bip122:000000000019d6689c085ae165831e93', - ])('returns true for a valid chain id', (id) => { - expect(isChainId(id)).toBe(true); - }); - - it.each([ - true, - false, - null, - undefined, - 1, - {}, - [], - 'a', - 'eip155', - 'eip155:', - 'eip155:1:2', - 'bip122', - 'bip122:', - 'bip122:000000000019d6689c085ae165831e93:2', - ])('returns false for an invalid chain id', (id) => { - expect(isChainId(id)).toBe(false); - }); -}); - -describe('isAccountId', () => { - it.each([ - 'eip155:1:0x0000000000000000000000000000000000000000', - 'eip155:1337:0x0000000000000000000000000000000000000000', - 'bip122:000000000019d6689c085ae165831e93:0x0000000000000000000000000000000000000000', - ])('returns true for a valid account id', (id) => { - expect(isAccountId(id)).toBe(true); - }); - - it.each([ - true, - false, - null, - undefined, - 1, - {}, - [], - 'foo', - 'eip155', - 'eip155:', - 'eip155:1', - 'eip155:1:', - 'eip155:1:0x0000000000000000000000000000000000000000:2', - 'bip122', - 'bip122:', - 'bip122:000000000019d6689c085ae165831e93', - 'bip122:000000000019d6689c085ae165831e93:', - 'bip122:000000000019d6689c085ae165831e93:0x0000000000000000000000000000000000000000:2', - ])('returns false for an invalid account id', (id) => { - expect(isAccountId(id)).toBe(false); - }); -}); - -describe('isAccountIdArray', () => { - it.each([ - // `it.each` does not support nested arrays, so we nest them in objects. - { - accounts: [], - }, - { - accounts: [ - 'eip155:1:0x0000000000000000000000000000000000000000', - 'eip155:1337:0x0000000000000000000000000000000000000000', - 'bip122:000000000019d6689c085ae165831e93:0x0000000000000000000000000000000000000000', - ], - }, - { - accounts: ['eip155:1:0x0000000000000000000000000000000000000000'], - }, - ])('returns true for a valid account id array', ({ accounts }) => { - expect(isAccountIdArray(accounts)).toBe(true); - }); - - it.each([ - true, - false, - null, - undefined, - 1, - {}, - 'foo', - ['foo'], - ['eip155:1:0x0000000000000000000000000000000000000000:2'], - [ - 'bip122:000000000019d6689c085ae165831e93:0x0000000000000000000000000000000000000000:2', - ], - ])('returns false for an invalid account id array', (accounts) => { - expect(isAccountIdArray(accounts)).toBe(false); - }); -}); - -describe('isNamespace', () => { - it.each([ - getNamespace(), - { chains: [getChain()], methods: ['eth_signTransaction'] }, - { chains: [getChain()], events: ['accountsChanged'] }, - { chains: [getChain()] }, - ])('returns true for a valid namespace', (namespace) => { - expect(isNamespace(namespace)).toBe(true); - }); - - it.each([ - {}, - [], - true, - false, - null, - undefined, - 1, - 'foo', - { methods: [], events: [] }, - { chains: ['foo'] }, - ])('returns false for an invalid namespace', (namespace) => { - expect(isNamespace(namespace)).toBe(false); - }); -}); diff --git a/packages/snaps-utils/src/namespace.ts b/packages/snaps-utils/src/namespace.ts deleted file mode 100644 index b656223ad4..0000000000 --- a/packages/snaps-utils/src/namespace.ts +++ /dev/null @@ -1,181 +0,0 @@ -import type { AccountId, ChainId } from '@metamask/snaps-sdk'; -import type { Infer } from '@metamask/superstruct'; -import { - array, - define, - is, - object, - optional, - pattern, - size, - string, -} from '@metamask/superstruct'; - -import type { InferMatching } from './structs'; - -export const CHAIN_ID_REGEX = - /^(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32})$/u; - -export const ACCOUNT_ID_REGEX = - /^(?(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32})):(?[a-zA-Z0-9]{1,64})$/u; - -export const ACCOUNT_ADDRESS_REGEX = /^(?[a-zA-Z0-9]{1,64})$/u; - -/** - * Parse a chain ID string to an object containing the namespace and reference. - * This validates the chain ID before parsing it. - * - * @param chainId - The chain ID to validate and parse. - * @returns The parsed chain ID. - */ -export function parseChainId(chainId: ChainId): { - namespace: NamespaceId; - reference: string; -} { - const match = CHAIN_ID_REGEX.exec(chainId); - if (!match?.groups) { - throw new Error('Invalid chain ID.'); - } - - return { - namespace: match.groups.namespace, - reference: match.groups.reference, - }; -} - -/** - * Parse an account ID to an object containing the chain, chain ID and address. - * This validates the account ID before parsing it. - * - * @param accountId - The account ID to validate and parse. - * @returns The parsed account ID. - */ -export function parseAccountId(accountId: AccountId): { - chain: { namespace: NamespaceId; reference: string }; - chainId: ChainId; - address: string; -} { - const match = ACCOUNT_ID_REGEX.exec(accountId); - if (!match?.groups) { - throw new Error('Invalid account ID.'); - } - - return { - address: match.groups.accountAddress, - chainId: match.groups.chainId as ChainId, - chain: { - namespace: match.groups.namespace, - reference: match.groups.reference, - }, - }; -} - -/** - * A helper struct for a string with a minimum length of 1 and a maximum length - * of 40. - */ -export const LimitedString = size(string(), 1, 40); - -export const ChainIdStringStruct = define( - 'Chain ID', - string().validator, -); - -/** - * A CAIP-2 chain ID, i.e., a human-readable namespace and reference. - */ -export const ChainIdStruct = pattern( - ChainIdStringStruct, - CHAIN_ID_REGEX, -); - -export type Caip2ChainId = InferMatching; - -export const AccountIdStruct = pattern(string(), ACCOUNT_ID_REGEX); - -export const AccountIdArrayStruct = array(AccountIdStruct); -export const AccountAddressStruct = pattern(string(), ACCOUNT_ADDRESS_REGEX); -export type AccountAddress = Infer; - -/** - * A chain descriptor. - */ -export const ChainStruct = object({ - id: ChainIdStruct, - name: LimitedString, -}); -export type Chain = Infer; - -export const NamespaceStruct = object({ - /** - * A list of supported chains in the namespace. - */ - chains: array(ChainStruct), - - /** - * A list of supported RPC methods on the namespace, that a DApp can call. - */ - methods: optional(array(LimitedString)), - - /** - * A list of supported RPC events on the namespace, that a DApp can listen to. - */ - events: optional(array(LimitedString)), -}); -export type Namespace = Infer; - -/** - * A CAIP-2 namespace, i.e., the first part of a chain ID. - */ -export const NamespaceIdStruct = pattern(string(), /^[-a-z0-9]{3,8}$/u); -export type NamespaceId = Infer; - -/** - * Check if the given value is a CAIP-2 namespace ID. - * - * @param value - The value to check. - * @returns Whether the value is a CAIP-2 namespace ID. - */ -export function isNamespaceId(value: unknown): value is NamespaceId { - return is(value, NamespaceIdStruct); -} - -/** - * Check if the given value is a CAIP-2 chain ID. - * - * @param value - The value to check. - * @returns Whether the value is a CAIP-2 chain ID. - */ -export function isChainId(value: unknown): value is ChainId { - return is(value, ChainIdStruct); -} - -/** - * Check if the given value is a CAIP-10 account ID. - * - * @param value - The value to check. - * @returns Whether the value is a CAIP-10 account ID. - */ -export function isAccountId(value: unknown): value is AccountId { - return is(value, AccountIdStruct); -} - -/** - * Check if the given value is an array of CAIP-10 account IDs. - * - * @param value - The value to check. - * @returns Whether the value is an array of CAIP-10 account IDs. - */ -export function isAccountIdArray(value: unknown): value is AccountId[] { - return is(value, AccountIdArrayStruct); -} - -/** - * Check if a value is a {@link Namespace}. - * - * @param value - The value to validate. - * @returns True if the value is a valid {@link Namespace}. - */ -export function isNamespace(value: unknown): value is Namespace { - return is(value, NamespaceStruct); -} diff --git a/packages/snaps-utils/src/snaps.test.ts b/packages/snaps-utils/src/snaps.test.ts index 17ceec4d31..812c70a5ff 100644 --- a/packages/snaps-utils/src/snaps.test.ts +++ b/packages/snaps-utils/src/snaps.test.ts @@ -8,7 +8,6 @@ import { SnapCaveatType } from './caveats'; import { isSnapPermitted, HttpSnapIdStruct, - isCaipChainId, LocalSnapIdStruct, NpmSnapIdStruct, assertIsValidSnapId, @@ -95,24 +94,6 @@ describe('assertIsValidSnapId', () => { ); }); -describe('isCaipChainId', () => { - it.each([undefined, {}, null, true, 2])( - 'returns false for non-strings (#%#)', - (value) => { - expect(isCaipChainId(value)).toBe(false); - }, - ); - - it.each([ - 'eip155:1', - 'cosmos:iov-mainnet', - 'bip122:000000000019d6689c085ae165831e93', - 'cosmos:cosmoshub-2', - ])('returns true for valid IDs (#%#)', (value) => { - expect(isCaipChainId(value)).toBe(true); - }); -}); - describe('LocalSnapIdStruct', () => { it.each([ 'local:http://localhost', diff --git a/packages/snaps-utils/src/snaps.ts b/packages/snaps-utils/src/snaps.ts index 6ccd71fb97..9fefd8e011 100644 --- a/packages/snaps-utils/src/snaps.ts +++ b/packages/snaps-utils/src/snaps.ts @@ -333,21 +333,6 @@ export function assertIsValidSnapId(value: unknown): asserts value is SnapId { assertStruct(value, SnapIdStruct, 'Invalid snap ID'); } -/** - * Typeguard to ensure a chainId follows the CAIP-2 standard. - * - * @param chainId - The chainId being tested. - * @returns `true` if the value is a valid CAIP chain id, and `false` otherwise. - */ -export function isCaipChainId(chainId: unknown): chainId is string { - return ( - typeof chainId === 'string' && - /^(?[-a-z0-9]{3,8}):(?[-a-zA-Z0-9]{1,32})$/u.test( - chainId, - ) - ); -} - /** * Utility function to check if an origin has permission (and caveat) for a particular snap. *