@dynamic-labs-sdk/metamask 1.31.2 → 1.32.0

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.
Files changed (25) hide show
  1. package/dist/constants.d.ts +10 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/core.cjs +1 -1
  4. package/dist/core.esm.js +1 -1
  5. package/dist/core.native.esm.js +1 -1
  6. package/dist/{getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js → getOrCreateMetaMaskClientInstanceManager-BvOCPGzg.native.esm.js} +68 -7
  7. package/dist/getOrCreateMetaMaskClientInstanceManager-BvOCPGzg.native.esm.js.map +1 -0
  8. package/dist/{getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js → getOrCreateMetaMaskClientInstanceManager-CLfDSJv6.esm.js} +68 -7
  9. package/dist/getOrCreateMetaMaskClientInstanceManager-CLfDSJv6.esm.js.map +1 -0
  10. package/dist/{getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs → getOrCreateMetaMaskClientInstanceManager-l9Bf9x5m.cjs} +68 -7
  11. package/dist/getOrCreateMetaMaskClientInstanceManager-l9Bf9x5m.cjs.map +1 -0
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.esm.js +1 -1
  14. package/dist/index.native.esm.js +1 -1
  15. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  16. package/dist/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.d.ts +7 -1
  17. package/dist/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.d.ts.map +1 -1
  18. package/dist/utils/toMetaMaskUniversalLink/index.d.ts +3 -0
  19. package/dist/utils/toMetaMaskUniversalLink/index.d.ts.map +1 -0
  20. package/dist/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.d.ts +27 -0
  21. package/dist/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.d.ts.map +1 -0
  22. package/package.json +3 -3
  23. package/dist/getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js.map +0 -1
  24. package/dist/getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs.map +0 -1
  25. package/dist/getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js.map +0 -1
@@ -5,4 +5,14 @@ import type { Chain } from '@dynamic-labs-sdk/client';
5
5
  * change in one place.
6
6
  */
7
7
  export declare const SUPPORTED_METAMASK_CHAINS: Chain[];
8
+ /**
9
+ * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for
10
+ * mobile wallet pairing and request hand-off.
11
+ */
12
+ export declare const METAMASK_APP_SCHEME_PREFIX = "metamask://";
13
+ /**
14
+ * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS
15
+ * resolves through MetaMask's associated domain.
16
+ */
17
+ export declare const METAMASK_UNIVERSAL_LINK_PREFIX = "https://metamask.app.link/";
8
18
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAqB,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAqB,KAAK,EAAE,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,gBAAgB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,8BAA8B,+BAA+B,CAAC"}
package/dist/core.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_getOrCreateMetaMaskClientInstanceManager = require('./getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs');
1
+ const require_getOrCreateMetaMaskClientInstanceManager = require('./getOrCreateMetaMaskClientInstanceManager-l9Bf9x5m.cjs');
2
2
  let _dynamic_labs_sdk_assert_package_version = require("@dynamic-labs-sdk/assert-package-version");
3
3
  let _dynamic_labs_sdk_client_core = require("@dynamic-labs-sdk/client/core");
4
4
  let _dynamic_labs_sdk_client = require("@dynamic-labs-sdk/client");
package/dist/core.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as UnsupportedMetaMaskChainError, c as name, i as getMetaMaskScopeForNetworkId, l as version, n as clearMetaMaskStorage, o as SUPPORTED_METAMASK_CHAINS, r as getMetaMaskScopes, s as MetaMaskUnscopedNetworkError, t as getOrCreateMetaMaskClientInstanceManager } from "./getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js";
1
+ import { a as UnsupportedMetaMaskChainError, c as name, i as getMetaMaskScopeForNetworkId, l as version, n as clearMetaMaskStorage, o as SUPPORTED_METAMASK_CHAINS, r as getMetaMaskScopes, s as MetaMaskUnscopedNetworkError, t as getOrCreateMetaMaskClientInstanceManager } from "./getOrCreateMetaMaskClientInstanceManager-CLfDSJv6.esm.js";
2
2
  import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
3
3
  import { WalletProviderPriority, createWalletProviderEventEmitter, getActiveNetworkIdFromLastKnownRegistry, getCore, getDefaultClient, getWalletProviderRegistry, switchActiveNetworkInLastKnownRegistry } from "@dynamic-labs-sdk/client/core";
4
4
  import { getNetworksData, getWalletAccounts, onEvent } from "@dynamic-labs-sdk/client";
@@ -1,4 +1,4 @@
1
- import { a as UnsupportedMetaMaskChainError, c as name, i as getMetaMaskScopeForNetworkId, l as version, n as clearMetaMaskStorage, o as SUPPORTED_METAMASK_CHAINS, r as getMetaMaskScopes, s as MetaMaskUnscopedNetworkError, t as getOrCreateMetaMaskClientInstanceManager } from "./getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js";
1
+ import { a as UnsupportedMetaMaskChainError, c as name, i as getMetaMaskScopeForNetworkId, l as version, n as clearMetaMaskStorage, o as SUPPORTED_METAMASK_CHAINS, r as getMetaMaskScopes, s as MetaMaskUnscopedNetworkError, t as getOrCreateMetaMaskClientInstanceManager } from "./getOrCreateMetaMaskClientInstanceManager-BvOCPGzg.native.esm.js";
2
2
  import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
3
3
  import { WalletProviderPriority, createWalletProviderEventEmitter, getActiveNetworkIdFromLastKnownRegistry, getCore, getDefaultClient, getWalletProviderRegistry, switchActiveNetworkInLastKnownRegistry } from "@dynamic-labs-sdk/client/core";
4
4
  import { getNetworksData, getWalletAccounts, onEvent } from "@dynamic-labs-sdk/client";
@@ -5,7 +5,7 @@ import * as z from "zod/mini";
5
5
 
6
6
  //#region package.json
7
7
  var name = "@dynamic-labs-sdk/metamask";
8
- var version = "1.31.2";
8
+ var version = "1.32.0";
9
9
 
10
10
  //#endregion
11
11
  //#region src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts
@@ -51,6 +51,16 @@ var MetaMaskUnscopedNetworkError = class extends BaseError {
51
51
  * change in one place.
52
52
  */
53
53
  const SUPPORTED_METAMASK_CHAINS = ["EVM", "SOL"];
54
+ /**
55
+ * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for
56
+ * mobile wallet pairing and request hand-off.
57
+ */
58
+ const METAMASK_APP_SCHEME_PREFIX = "metamask://";
59
+ /**
60
+ * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS
61
+ * resolves through MetaMask's associated domain.
62
+ */
63
+ const METAMASK_UNIVERSAL_LINK_PREFIX = "https://metamask.app.link/";
54
64
 
55
65
  //#endregion
56
66
  //#region src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts
@@ -471,6 +481,35 @@ const clearMetaMaskSdkSingleton = () => {
471
481
  delete globalThis[METAMASK_SDK_SINGLETON_GLOBAL_KEY];
472
482
  };
473
483
 
484
+ //#endregion
485
+ //#region src/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.ts
486
+ /**
487
+ * Rewrites a MetaMask app-scheme deeplink (`metamask://connect/...`) into its
488
+ * universal-link equivalent (`https://metamask.app.link/connect/...`).
489
+ *
490
+ * `@metamask/connect-multichain` only ever hands `preferredOpenLink` the
491
+ * app-scheme form, which iOS refuses to open whenever no installed app claims
492
+ * the `metamask` scheme. The universal link is resolved through MetaMask's
493
+ * associated domain instead, so it opens the wallet when it is installed and
494
+ * lands on a web page when it is not, rather than failing outright.
495
+ *
496
+ * @example
497
+ * ```ts
498
+ * toMetaMaskUniversalLink({ deeplink: 'metamask://connect/mwp?id=abc' });
499
+ * // 'https://metamask.app.link/connect/mwp?id=abc'
500
+ * ```
501
+ *
502
+ * @returns The universal-link form of the deeplink, or undefined when the
503
+ * deeplink does not use MetaMask's app scheme and therefore has no equivalent.
504
+ * @see createMetaMaskPreferredOpenLink
505
+ * @see METAMASK_UNIVERSAL_LINK_PREFIX
506
+ * @notInstrumented
507
+ */
508
+ const toMetaMaskUniversalLink = ({ deeplink }) => {
509
+ if (!deeplink.startsWith(METAMASK_APP_SCHEME_PREFIX)) return;
510
+ return `${METAMASK_UNIVERSAL_LINK_PREFIX}${deeplink.slice(METAMASK_APP_SCHEME_PREFIX.length)}`;
511
+ };
512
+
474
513
  //#endregion
475
514
  //#region src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts
476
515
  /**
@@ -479,10 +518,16 @@ const clearMetaMaskSdkSingleton = () => {
479
518
  * SDK's own default handling, so every platform (web, React Native, Expo)
480
519
  * opens them the same way the rest of the SDK does.
481
520
  *
521
+ * MetaMask only ever passes app-scheme deeplinks (`metamask://...`) to this
522
+ * callback, and iOS rejects those whenever it finds no installed handler for the
523
+ * scheme. When opening one fails, the universal-link equivalent is tried before
524
+ * giving up, so the wallet still comes to the foreground and the pending request
525
+ * is not left unanswered.
526
+ *
482
527
  * @example
483
528
  * ```ts
484
529
  * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);
485
- * preferredOpenLink('https://metamask.app.link/...');
530
+ * preferredOpenLink('metamask://connect/mwp?id=...');
486
531
  * ```
487
532
  *
488
533
  * @returns A callback that opens a MetaMask-issued deeplink through the
@@ -494,10 +539,26 @@ const clearMetaMaskSdkSingleton = () => {
494
539
  const createMetaMaskPreferredOpenLink = (client) => (deeplink) => {
495
540
  const { logger, openDeeplink } = getCore(client);
496
541
  logger.debug("[MetaMask:Client] Opening MetaMask deeplink", { deeplink });
497
- openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((error) => logger.error("[MetaMask:Client] Failed to open deeplink", {
498
- deeplink,
499
- error
500
- }));
542
+ openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((appSchemeError) => {
543
+ const universalLink = toMetaMaskUniversalLink({ deeplink });
544
+ if (!universalLink) {
545
+ logger.error("[MetaMask:Client] Failed to open deeplink", {
546
+ deeplink,
547
+ error: appSchemeError
548
+ });
549
+ return;
550
+ }
551
+ logger.debug("[MetaMask:Client] Retrying MetaMask deeplink as a universal link", {
552
+ error: appSchemeError,
553
+ universalLink
554
+ });
555
+ return openDeeplink(universalLink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((universalLinkError) => logger.error("[MetaMask:Client] Failed to open deeplink", {
556
+ deeplink,
557
+ error: appSchemeError,
558
+ universalLink,
559
+ universalLinkError
560
+ }));
561
+ });
501
562
  };
502
563
 
503
564
  //#endregion
@@ -618,4 +679,4 @@ const getOrCreateMetaMaskClientInstanceManager = createRuntimeServiceAccessKey(M
618
679
 
619
680
  //#endregion
620
681
  export { UnsupportedMetaMaskChainError as a, name as c, getMetaMaskScopeForNetworkId as i, version as l, clearMetaMaskStorage as n, SUPPORTED_METAMASK_CHAINS as o, getMetaMaskScopes as r, MetaMaskUnscopedNetworkError as s, getOrCreateMetaMaskClientInstanceManager as t };
621
- //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js.map
682
+ //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-BvOCPGzg.native.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOrCreateMetaMaskClientInstanceManager-BvOCPGzg.native.esm.js","names":["supportedNetworks: MetaMaskSupportedNetworks","queueMetaMaskStorageWrite: SequenceQueue","adapter: StoreAdapter","metaMaskClientPromise: Promise<MultichainCore> | null","clearEventListeners: () => void","builtClientPromise: Promise<MultichainCore>"],"sources":["../package.json","../src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts","../src/constants.ts","../src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts","../src/getMetaMaskScopeForNetworkId/getMetaMaskScopeForNetworkId.ts","../src/getMetaMaskScopes/getMetaMaskScopes.ts","../src/errors/MetaMaskClientNotInitializedError/MetaMaskClientNotInitializedError.ts","../src/getMetaMaskSupportedNetworks/getMetaMaskSupportedNetworks.ts","../src/metaMaskStorage/createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue.ts","../src/metaMaskStorage/queueMetaMaskStorageWrite/queueMetaMaskStorageWrite.ts","../src/metaMaskStorage/metaMaskStorage.schema.ts","../src/metaMaskStorage/clearMetaMaskStorage/clearMetaMaskStorage.ts","../src/errors/MetaMaskStorageError/MetaMaskStorageError.ts","../src/metaMaskStorage/createMetaMaskStorageClient/createMetaMaskStorageClient.ts","../src/utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton.ts","../src/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.ts","../src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts","../src/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/createMetaMaskClientInstanceManager.ts","../src/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when attempting to switch a MetaMask multichain wallet provider to\n * a network whose CAIP-2 scope was requested at connect time but never\n * granted an account by MetaMask in the current session (e.g. the user only\n * approved mainnet, not a preconfigured devnet, during pairing). Switching\n * to it anyway would silently break every subsequent signing/sending call\n * on that network, since MetaMask rejects those against an ungranted scope.\n *\n * @example\n * ```ts\n * try {\n * await switchActiveNetwork({ networkId: '103', walletAccount });\n * } catch (error) {\n * if (error instanceof MetaMaskUnscopedNetworkError) {\n * // Tell the user to approve this network in MetaMask, then retry.\n * }\n * }\n * ```\n * @see assertMetaMaskNetworkIsGranted\n * @see resolveActiveMetaMaskScope\n */\nexport class MetaMaskUnscopedNetworkError extends BaseError {\n constructor({ networkId, scope }: { networkId: string; scope: string }) {\n super({\n cause: null,\n code: 'metamask_unscoped_network',\n docsUrl: null,\n metaMessages: [`scope: ${scope}`],\n name: 'MetaMaskUnscopedNetworkError',\n shortMessage: `Cannot switch to network \"${networkId}\": MetaMask did not grant an account for scope \"${scope}\" in the current session.`,\n });\n }\n}\n","import type { Chain } from '@dynamic-labs-sdk/client';\n\n/**\n * Chains MetaMask's multichain client can build a CAIP-2 scope for. Shared by\n * every scope/network-mapping helper so the supported set only needs to\n * change in one place.\n */\nexport const SUPPORTED_METAMASK_CHAINS = ['EVM', 'SOL'] as Chain[];\n\n/**\n * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for\n * mobile wallet pairing and request hand-off.\n */\nexport const METAMASK_APP_SCHEME_PREFIX = 'metamask://';\n\n/**\n * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS\n * resolves through MetaMask's associated domain.\n */\nexport const METAMASK_UNIVERSAL_LINK_PREFIX = 'https://metamask.app.link/';\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/** Thrown when a chain has no MetaMask multichain scope mapping defined. */\nexport class UnsupportedMetaMaskChainError extends BaseError {\n constructor({\n chain,\n supportedChains,\n }: {\n chain: string;\n supportedChains: string[];\n }) {\n super({\n cause: null,\n code: 'metamask_unsupported_chain',\n docsUrl: null,\n name: 'UnsupportedMetaMaskChainError',\n shortMessage: `Unsupported chain \"${chain}\" for MetaMask. Supported chains: ${supportedChains.join(', ')}`,\n });\n }\n}\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport type { RPCAPI, Scope } from '@metamask/connect-multichain';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\n\nexport type GetMetaMaskScopeForNetworkIdParams = {\n chain: Chain;\n networkId: string;\n};\n\n/**\n * Maps a single Dynamic-configured network to its CAIP-2 MetaMask\n * multichain scope -- the same per-network mapping `getMetaMaskScopes`\n * applies to every configured network at once, extracted so callers that\n * already have one `networkId` (e.g. the \"active network\" registry) don't\n * need to rebuild and filter the whole configured list to find it.\n *\n * EVM's `networkId` IS the CAIP-2 reference (`eip155:<networkId>`); SOL's\n * `networkId` is an internal routing id, so the network's base58 genesis\n * hash is used instead (`solana:<genesisHash>`). Returns `undefined` when no\n * configured SOL network matches, or matches but has no genesis hash --\n * these are normal (\"this networkId isn't usable as a MetaMask scope\"), not\n * error conditions.\n *\n * @example\n * ```ts\n * const scope = getMetaMaskScopeForNetworkId(\n * { chain: 'SOL', networkId: '101' },\n * client\n * );\n * // 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'\n * ```\n *\n * @returns The CAIP-2 scope for `networkId`, or `undefined` if it can't be\n * expressed as one (e.g. a SOL network with no genesis hash configured).\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @see getMetaMaskScopes\n * @see getNetworkIdFromMetaMaskScope\n * @notInstrumented\n */\nexport const getMetaMaskScopeForNetworkId = (\n { chain, networkId }: GetMetaMaskScopeForNetworkIdParams,\n client: DynamicClient\n): Scope<RPCAPI> | undefined => {\n if (chain === 'EVM') {\n return `eip155:${networkId}` as Scope<RPCAPI>;\n }\n\n if (chain === 'SOL') {\n const network = getNetworksData(client).find(\n (candidate) => candidate.chain === 'SOL' && candidate.networkId === networkId\n );\n\n return network?.genesisHash\n ? (`solana:${network.genesisHash}` as Scope<RPCAPI>)\n : undefined;\n }\n\n throw new UnsupportedMetaMaskChainError({\n chain,\n supportedChains: SUPPORTED_METAMASK_CHAINS,\n });\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport type { RPCAPI, Scope } from '@metamask/connect-multichain';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\n\ntype GetMetaMaskScopesOptions = {\n chain: Chain;\n};\n\n/**\n * Builds the CAIP-2 scopes to request for a chain's MetaMask multichain\n * session, one per configured network. SOL scopes use the network's genesis\n * hash (not `networkId`, which is an internal routing id for SOL) since\n * that's the CAIP-2 reference MetaMask expects.\n *\n * @example\n * ```ts\n * const scopes = getMetaMaskScopes({ chain: 'EVM' }, client);\n * // ['eip155:1', 'eip155:137']\n * ```\n *\n * @returns The CAIP-2 scopes for every configured network of `chain` that\n * can be expressed as one.\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @see getMetaMaskScopeForNetworkId\n * @see getMetaMaskSupportedNetworks\n * @notInstrumented\n */\nexport const getMetaMaskScopes = (\n { chain }: GetMetaMaskScopesOptions,\n client: DynamicClient\n): Scope<RPCAPI>[] => {\n if (!chain || !SUPPORTED_METAMASK_CHAINS.includes(chain)) {\n throw new UnsupportedMetaMaskChainError({\n chain,\n supportedChains: SUPPORTED_METAMASK_CHAINS,\n });\n }\n\n const networks = getNetworksData(client).filter(\n (network) => network.chain === chain\n );\n\n return networks.flatMap((network) => {\n const scope = getMetaMaskScopeForNetworkId(\n { chain: network.chain, networkId: network.networkId },\n client\n );\n\n // Skip networks that can't be expressed as a MetaMask scope (e.g. a SOL\n // network with no genesis hash configured) rather than build an invalid\n // one -- see `getMetaMaskScopeForNetworkId`.\n return scope ? [scope] : [];\n });\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when `getMetaMaskClient()` is called but the underlying\n * `@metamask/connect-multichain` client failed to initialize -- this should\n * be unreachable in practice, since `getMetaMaskClient` always triggers\n * `refreshMetaMaskClient` first when no client promise exists yet, but it\n * guards against a future change silently returning `undefined` instead.\n */\nexport class MetaMaskClientNotInitializedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'metamask_client_not_initialized',\n docsUrl: null,\n name: 'MetaMaskClientNotInitializedError',\n shortMessage: 'MetaMask client not initialized',\n });\n }\n}\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\n\ntype MetaMaskSupportedNetworks = Record<\n `eip155:${string}` | `solana:${string}`,\n string\n>;\n\n/**\n * Builds the `api.supportedNetworks` map for MetaMask's multichain client:\n * EVM networks keyed by their CAIP-2 `eip155:<networkId>` scope, SOL networks\n * keyed by their CAIP-2 `solana:<genesisHash>` scope (SOL's `networkId` is an\n * internal routing id, not usable as a CAIP-2 reference).\n *\n * @example\n * ```ts\n * const supportedNetworks = getMetaMaskSupportedNetworks(client);\n * // { 'eip155:1': 'https://...', 'solana:5eykt4...': 'https://...' }\n * ```\n *\n * @returns The RPC URL for every configured EVM/SOL network, keyed by its\n * MetaMask-recognized network identifier.\n * @see getMetaMaskScopes\n * @see createMetaMaskClientInstanceManager\n * @notInstrumented\n */\nexport const getMetaMaskSupportedNetworks = (\n client: DynamicClient\n): MetaMaskSupportedNetworks => {\n const { logger } = getCore(client);\n\n const networks = getNetworksData(client).filter((network) =>\n SUPPORTED_METAMASK_CHAINS.includes(network.chain)\n );\n\n const supportedNetworks: MetaMaskSupportedNetworks = {};\n\n for (const network of networks) {\n if (network.chain === 'EVM') {\n supportedNetworks[`eip155:${network.networkId}`] =\n network.rpcUrls.http[0];\n }\n\n // SOL's `networkId` is an internal routing id, not the CAIP-2 reference —\n // the multichain client's `solana:` scope needs the base58 genesis hash.\n // Skip networks that haven't configured one.\n if (network.chain === 'SOL' && network.genesisHash) {\n supportedNetworks[`solana:${network.genesisHash}`] =\n network.rpcUrls.http[0];\n }\n }\n\n logger.debug('[MetaMask:Client] Resolved supported networks', {\n supportedNetworks,\n });\n\n return supportedNetworks;\n};\n","import type { MetaMaskStoreValue } from '../metaMaskStorage.schema';\n\n/**\n * Builds the persisted store blob's value for when there is nothing stored\n * yet, or after a clear. A function rather than a shared constant, so every\n * caller gets its own object and can't mutate a value other callers rely on.\n *\n * @example\n * ```ts\n * const initial = createInitialMetaMaskStoreValue();\n * // { entries: {}, schemaVersion: 1 }\n * ```\n *\n * @returns A fresh, empty `MetaMaskStoreValue`.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createInitialMetaMaskStoreValue = (): MetaMaskStoreValue => ({\n entries: {},\n schemaVersion: 1,\n});\n","import { createSequenceQueue } from '@dynamic-labs-sdk/client/core';\nimport type { SequenceQueue } from '@dynamic-labs-sdk/client/core';\n\n/**\n * Single shared queue serializing every read-modify-write against\n * `metaMaskStoreStorageKeySchema` -- `createMetaMaskStorageClient`'s adapter\n * reads/writes/anonId operations and `clearMetaMaskStorage`'s reset all go\n * through this same instance, so a write that started before a clear can't\n * land after it and resurrect the data the clear was meant to erase.\n *\n * @example\n * ```ts\n * await queueMetaMaskStorageWrite(() => storage.setItem(key, value), client);\n * ```\n *\n * @returns A `SequenceQueue` that runs one queued task at a time, in order.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const queueMetaMaskStorageWrite: SequenceQueue = createSequenceQueue();\n","import * as z from 'zod/mini';\nimport { createStorageKeySchema } from '@dynamic-labs-sdk/client/core';\n\n/**\n * Single storage key holding every value the MetaMask multichain SDK writes\n * through our `StoreAdapter` (sessions, nonces, cached accounts/chain,\n * pending pairing requests, transport ids, ...), keyed by whatever key string\n * the SDK itself hands us -- the SDK controls every key name, including ones\n * that embed an unpredictable id (`session:<id>`), so this can't be split\n * into one storage key per SDK key.\n *\n * `entries[ANON_ID_ENTRY_KEY]` also lives in here, alongside `extensionId`\n * and the other transport keys.\n *\n * `clearMetaMaskStorage` resets this key back to its initial value; nothing\n * else needs to know the individual key names the SDK is using.\n */\nexport const metaMaskStoreStorageKeySchema = createStorageKeySchema({\n key: 'metaMaskStore',\n schema: z.object({\n entries: z.record(z.string(), z.string()),\n schemaVersion: z.literal(1),\n }),\n});\n\n/**\n * The persisted store blob's shape. Exported here because it is shared by\n * every function operating on this storage key.\n */\nexport type MetaMaskStoreValue = z.infer<typeof metaMaskStoreStorageKeySchema.schema>;\n\n/**\n * Reserved `entries` key the MetaMask SDK itself never writes to, used to\n * carry the SDK's anonymous analytics id inside the same store blob.\n */\nexport const ANON_ID_ENTRY_KEY = 'anonId';\n","import { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { DynamicClient } from '@dynamic-labs-sdk/client';\n\nimport { createInitialMetaMaskStoreValue } from '../createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue';\nimport { queueMetaMaskStorageWrite } from '../queueMetaMaskStorageWrite/queueMetaMaskStorageWrite';\nimport { metaMaskStoreStorageKeySchema } from '../metaMaskStorage.schema';\n\nconst FAILED_TO_CLEAR_STORAGE_MESSAGE =\n '[MetaMask:Session] Failed to clear the persisted store; the next pairing may resume a stale session instead of showing a new one';\n\n/**\n * Resets the MetaMask SDK's persisted store blob back to its initial\n * (empty) value, so the next connect call starts a fresh pairing and emits a\n * `display_uri` instead of silently resuming a session that no longer works.\n *\n * @example\n * ```ts\n * await clearMetaMaskStorage(client);\n * // Next connectWithMetaMaskUri call will emit a fresh URI immediately.\n * ```\n *\n * @param client - The Dynamic client instance. Required (rather than\n * defaulting to the last-initialized client) so a storage-clear failure is\n * always logged against the client that actually requested it.\n * @returns A promise that resolves once the blob is reset. Never rejects -- a\n * storage failure must not take down the connect flow that depends on it.\n * @see connectWithMetaMaskUri\n * @see createMetaMaskStorageClient\n * @notInstrumented\n */\nexport const clearMetaMaskStorage = async (\n client: DynamicClient\n): Promise<void> => {\n const { logger, storage } = getCore(client);\n\n try {\n await queueMetaMaskStorageWrite(\n () =>\n storage.setItem(\n metaMaskStoreStorageKeySchema,\n createInitialMetaMaskStoreValue()\n ),\n client\n );\n\n logger.debug('[MetaMask:Session] Cleared persisted store');\n } catch (error) {\n logger.warn(FAILED_TO_CLEAR_STORAGE_MESSAGE, { error });\n }\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\ntype MetaMaskStorageErrorParams = {\n cause: unknown;\n key: string;\n operation: 'get' | 'set' | 'delete';\n};\n\n/** Thrown when the underlying storage service rejects a read/write/delete for the MetaMask multichain client's own store (e.g. quota exceeded, private-browsing restrictions). */\nexport class MetaMaskStorageError extends BaseError {\n constructor({ cause, key, operation }: MetaMaskStorageErrorParams) {\n super({\n cause: cause instanceof Error ? cause : null,\n code: 'metamask_storage_error',\n docsUrl: null,\n name: 'MetaMaskStorageError',\n shortMessage: `Failed to ${operation} MetaMask multichain client storage key \"${key}\"`,\n });\n }\n}\n","import { getCore, getPlatform } from '@dynamic-labs-sdk/client/core';\nimport type { StoreAdapter, StoreClient } from '@metamask/connect-multichain';\nimport { getTransportType } from '@metamask/connect-multichain';\nimport type { DynamicClient } from '@dynamic-labs-sdk/client';\n\nimport { MetaMaskStorageError } from '../../errors/MetaMaskStorageError';\nimport { createInitialMetaMaskStoreValue } from '../createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue';\nimport { queueMetaMaskStorageWrite } from '../queueMetaMaskStorageWrite/queueMetaMaskStorageWrite';\nimport { ANON_ID_ENTRY_KEY, metaMaskStoreStorageKeySchema } from '../metaMaskStorage.schema';\n\nconst TRANSPORT_TYPE_KEY = 'multichain-transport';\nconst EXTENSION_ID_KEY = 'extensionId';\n\n/** Maps this SDK's platform literal onto the one `@metamask/connect-multichain`'s `StoreAdapter` expects. */\nconst toStoreAdapterPlatform = (\n platform: ReturnType<typeof getPlatform>\n): StoreAdapter['platform'] => (platform === 'react-native' ? 'rn' : 'web');\n\n/**\n * Builds a storage-service-backed store for the MetaMask multichain client,\n * in place of its own default web adapter (IndexedDB) and React Native\n * adapter (raw `AsyncStorage`), so every platform persists through the same\n * core `Storage` service and `clearMetaMaskStorage` can reliably wipe it.\n *\n * The SDK controls every key name it reads/writes through `adapter`,\n * including ones that embed an unpredictable id (`session:<id>`), so every\n * key it uses is folded into a single stored blob (`metaMaskStoreStorageKeySchema`)\n * rather than one storage key per SDK key. `DEBUG` is never persisted at all,\n * answered live from the client's own logger.\n *\n * @example\n * ```ts\n * const storeClient = createMetaMaskStorageClient(client);\n * const multichainClient = createMultichainClient({ storage: storeClient });\n * ```\n *\n * @returns A `StoreClient` backed by the client's core `Storage` service.\n * @throws {MetaMaskStorageError} If the underlying `Storage` service's\n * `getItem`/`setItem` calls fail for any of the adapter's read/write/delete\n * operations.\n * @see createInitialMetaMaskStoreValue\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createMetaMaskStorageClient = (client: DynamicClient): StoreClient => {\n const { logger, storage } = getCore(client);\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.get`\n const read = async (key: string): Promise<string | null> => {\n try {\n const { entries } =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n\n return entries[key] ?? null;\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'get' });\n }\n };\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.set`\n const write = async (key: string, value: string): Promise<void> => {\n try {\n await queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: { ...current.entries, [key]: value },\n });\n }, client);\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'set' });\n }\n };\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.delete`\n const remove = async (key: string): Promise<void> => {\n try {\n await queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n const remainingEntries = Object.fromEntries(\n Object.entries(current.entries).filter(\n ([entryKey]) => entryKey !== key\n )\n );\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: remainingEntries,\n });\n }, client);\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'delete' });\n }\n };\n\n const adapter: StoreAdapter = {\n delete: remove,\n get: read,\n platform: toStoreAdapterPlatform(getPlatform()),\n set: write,\n };\n\n return {\n adapter,\n // Queued (rather than a plain read-then-write) so its check-then-create\n // can't race itself into generating two different ids for the same\n // client -- the SDK calls `getAnonId()` from multiple internal call sites.\n getAnonId: () =>\n queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n const existing = current.entries[ANON_ID_ENTRY_KEY];\n\n if (existing) {\n return existing;\n }\n\n const anonId = crypto.randomUUID();\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: { ...current.entries, [ANON_ID_ENTRY_KEY]: anonId },\n });\n\n return anonId;\n }, client),\n getDebug: () =>\n // The SDK matches this against its own debug namespaces via\n // `debugValue.includes(namespace) || debugValue.includes('metamask-sdk:*')`\n // -- a boolean-shaped string like `'true'` would never match, so this\n // has to be a namespace pattern, not a flag.\n Promise.resolve(logger.isEnabled('debug') ? 'metamask-sdk:*' : null),\n getExtensionId: () => adapter.get(EXTENSION_ID_KEY),\n getTransportType: async () => {\n const value = await adapter.get(TRANSPORT_TYPE_KEY);\n\n return value ? getTransportType(value) : null;\n },\n removeAnonId: () => remove(ANON_ID_ENTRY_KEY),\n removeExtensionId: () => adapter.delete(EXTENSION_ID_KEY),\n removeTransportType: () => adapter.delete(TRANSPORT_TYPE_KEY),\n setAnonId: (anonId) => write(ANON_ID_ENTRY_KEY, anonId),\n setExtensionId: (extensionId) => adapter.set(EXTENSION_ID_KEY, extensionId),\n setTransportType: (transportType) =>\n adapter.set(TRANSPORT_TYPE_KEY, transportType),\n };\n};\n","// Mirrors `@metamask/connect-multichain`'s own singleton key -- not exported\n// by the SDK, so it must be kept in sync by hand whenever the pinned version\n// in packages/metamask/package.json changes.\nconst METAMASK_SDK_SINGLETON_GLOBAL_KEY =\n '__METAMASK_CONNECT_MULTICHAIN_SINGLETON__';\n\n/**\n * Deletes the upstream `@metamask/connect-multichain` SDK's own cached\n * client instance off `globalThis`.\n *\n * The SDK's `createMultichainClient` reuses a private `globalThis` singleton\n * and only merges in a subset of options on repeat calls -- notably not\n * `storage` -- so it keeps talking to its original `StoreAdapter` even after\n * a fresh `clearMetaMaskStorage` call. Deleting this key forces a genuinely\n * new instance (and storage adapter) on the next `createMultichainClient` call.\n *\n * @example\n * ```ts\n * clearMetaMaskSdkSingleton();\n * // Next createMultichainClient call builds a fresh client and adapter.\n * ```\n *\n * @returns Nothing -- the singleton key is deleted synchronously.\n * @see createMetaMaskClientInstanceManager\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const clearMetaMaskSdkSingleton = (): void => {\n delete (globalThis as Record<string, unknown>)[\n METAMASK_SDK_SINGLETON_GLOBAL_KEY\n ];\n};\n","import {\n METAMASK_APP_SCHEME_PREFIX,\n METAMASK_UNIVERSAL_LINK_PREFIX,\n} from '../../constants';\n\nexport type ToMetaMaskUniversalLinkParams = {\n deeplink: string;\n};\n\n/**\n * Rewrites a MetaMask app-scheme deeplink (`metamask://connect/...`) into its\n * universal-link equivalent (`https://metamask.app.link/connect/...`).\n *\n * `@metamask/connect-multichain` only ever hands `preferredOpenLink` the\n * app-scheme form, which iOS refuses to open whenever no installed app claims\n * the `metamask` scheme. The universal link is resolved through MetaMask's\n * associated domain instead, so it opens the wallet when it is installed and\n * lands on a web page when it is not, rather than failing outright.\n *\n * @example\n * ```ts\n * toMetaMaskUniversalLink({ deeplink: 'metamask://connect/mwp?id=abc' });\n * // 'https://metamask.app.link/connect/mwp?id=abc'\n * ```\n *\n * @returns The universal-link form of the deeplink, or undefined when the\n * deeplink does not use MetaMask's app scheme and therefore has no equivalent.\n * @see createMetaMaskPreferredOpenLink\n * @see METAMASK_UNIVERSAL_LINK_PREFIX\n * @notInstrumented\n */\nexport const toMetaMaskUniversalLink = ({\n deeplink,\n}: ToMetaMaskUniversalLinkParams): string | undefined => {\n if (!deeplink.startsWith(METAMASK_APP_SCHEME_PREFIX)) {\n return undefined;\n }\n\n return `${METAMASK_UNIVERSAL_LINK_PREFIX}${deeplink.slice(\n METAMASK_APP_SCHEME_PREFIX.length\n )}`;\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { toMetaMaskUniversalLink } from '../toMetaMaskUniversalLink';\n\n/**\n * Builds the `preferredOpenLink` callback handed to `@metamask/connect-multichain`,\n * routing wallet deeplinks through the core `deeplink` service instead of the\n * SDK's own default handling, so every platform (web, React Native, Expo)\n * opens them the same way the rest of the SDK does.\n *\n * MetaMask only ever passes app-scheme deeplinks (`metamask://...`) to this\n * callback, and iOS rejects those whenever it finds no installed handler for the\n * scheme. When opening one fails, the universal-link equivalent is tried before\n * giving up, so the wallet still comes to the foreground and the pending request\n * is not left unanswered.\n *\n * @example\n * ```ts\n * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);\n * preferredOpenLink('metamask://connect/mwp?id=...');\n * ```\n *\n * @returns A callback that opens a MetaMask-issued deeplink through the\n * core `deeplink` service.\n * @see createMetaMaskClientInstanceManager\n * @see createMetaMaskStorageClient\n * @notInstrumented\n */\nexport const createMetaMaskPreferredOpenLink =\n (client: DynamicClient): ((deeplink: string) => void) =>\n (deeplink) => {\n const { logger, openDeeplink } = getCore(client);\n\n logger.debug('[MetaMask:Client] Opening MetaMask deeplink', { deeplink });\n\n void openDeeplink(deeplink)\n .then(() => logger.debug('[MetaMask:Client] Deeplink opened'))\n .catch((appSchemeError: unknown) => {\n const universalLink = toMetaMaskUniversalLink({ deeplink });\n\n if (!universalLink) {\n logger.error('[MetaMask:Client] Failed to open deeplink', {\n deeplink,\n error: appSchemeError,\n });\n\n return;\n }\n\n logger.debug(\n '[MetaMask:Client] Retrying MetaMask deeplink as a universal link',\n { error: appSchemeError, universalLink }\n );\n\n return openDeeplink(universalLink)\n .then(() => logger.debug('[MetaMask:Client] Deeplink opened'))\n .catch((universalLinkError: unknown) =>\n logger.error('[MetaMask:Client] Failed to open deeplink', {\n deeplink,\n error: appSchemeError,\n universalLink,\n universalLinkError,\n })\n );\n });\n };\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport {\n type EventEmitter,\n createEventEmitter,\n getAppMetadata,\n getCore,\n getNativeLinkScheme,\n} from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\nimport { createMultichainClient } from '@metamask/connect-multichain';\n\nimport { MetaMaskClientNotInitializedError } from '../../errors/MetaMaskClientNotInitializedError';\nimport { getMetaMaskSupportedNetworks } from '../../getMetaMaskSupportedNetworks';\nimport { clearMetaMaskStorage } from '../../metaMaskStorage/clearMetaMaskStorage';\nimport { createMetaMaskStorageClient } from '../../metaMaskStorage/createMetaMaskStorageClient';\nimport { clearMetaMaskSdkSingleton } from '../../utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton';\nimport { createMetaMaskPreferredOpenLink } from '../../utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink';\n\n/**\n * Events a `MetaMaskClientInstanceManager` forwards from the underlying\n * `@metamask/connect-multichain` client, for callers that don't hold a\n * reference to the raw SDK client.\n */\nexport type MetaMaskClientEvents = {\n display_uri: (evt: string) => void;\n};\n\n/**\n * The per-client handle returned by `createMetaMaskClientInstanceManager`.\n */\nexport type MetaMaskClientInstanceManager = {\n /** Wipes persisted MetaMask storage and forces the next build to start fresh. */\n clearMetaMaskStorageAndInstance: () => Promise<void>;\n /** Emits `MetaMaskClientEvents` forwarded from the underlying SDK client. */\n events: EventEmitter<MetaMaskClientEvents>;\n /** Resolves the shared `MultichainCore` client, building it on first access. */\n getMetaMaskClient(): Promise<MultichainCore>;\n};\n\n/**\n * Creates the per-client manager that lazily builds, tears down, and rebuilds\n * the shared `@metamask/connect-multichain` client instance, and forwards its\n * `display_uri` event to callers that don't hold a reference to the raw SDK\n * client.\n *\n * @example\n * ```ts\n * const manager = createMetaMaskClientInstanceManager(client);\n * const metaMaskClient = await manager.getMetaMaskClient();\n * ```\n *\n * @returns A `MetaMaskClientInstanceManager` scoped to `client`.\n * @throws {MetaMaskClientNotInitializedError} From `getMetaMaskClient` if a\n * build was never started -- a defensive guard, since `refreshMetaMaskClient`\n * always starts one. A build that starts but fails instead rejects\n * `getMetaMaskClient()` with its original error.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createMetaMaskClientInstanceManager = (\n client: DynamicClient\n): MetaMaskClientInstanceManager => {\n let metaMaskClientPromise: Promise<MultichainCore> | null;\n let clearEventListeners: () => void;\n const { logger } = getCore(client);\n const events = createEventEmitter<MetaMaskClientEvents>();\n\n const refreshMetaMaskClient = () => {\n // Runs synchronously up to the assignment below, so this guard makes\n // concurrent callers (e.g. two overlapping getMetaMaskClient() calls\n // before the first client exists) share the same build instead of\n // racing -- the loser's clearEventListeners would otherwise be\n // overwritten and its listener leaked. Callers that need a genuine\n // rebuild (clearMetaMaskStorageAndInstance) reset the promise to null\n // first.\n if (metaMaskClientPromise) {\n return;\n }\n\n const { appName, appLogoUrl, appUniversalLink, appNativeLink } =\n getAppMetadata(client);\n const supportedNetworks = getMetaMaskSupportedNetworks(client);\n const nativeScheme = getNativeLinkScheme({ nativeLink: appNativeLink });\n const debug = logger.isEnabled('debug');\n\n logger.debug('[MetaMask:Client] Creating MetaMask client', {\n appLogoUrl,\n appName,\n appUniversalLink,\n debug,\n nativeScheme,\n supportedNetworks,\n });\n\n if (clearEventListeners) {\n clearEventListeners();\n }\n\n clearMetaMaskSdkSingleton();\n\n const builtClientPromise: Promise<MultichainCore> = createMultichainClient({\n api: {\n supportedNetworks,\n },\n dapp: {\n iconUrl: appLogoUrl,\n name: appName,\n nativeScheme,\n url: appUniversalLink ?? '',\n },\n debug,\n mobile: {\n preferredOpenLink: createMetaMaskPreferredOpenLink(client),\n },\n // Backed by the core `Storage` service on every platform, so\n // `clearMetaMaskStorage` can reliably wipe everything the SDK writes\n // -- see `createMetaMaskStorageClient`.\n storage: createMetaMaskStorageClient(client),\n ui: { headless: true },\n }).then((metaMaskClient) => {\n const createEventMapper = <T extends keyof MetaMaskClientEvents>(\n event: T\n ) => {\n return (...args: Parameters<MetaMaskClientEvents[T]>) => {\n logger.debug(`[MetaMask:Events] ${event} event received`, { args });\n events.emit(event, ...args);\n };\n };\n\n const onDisplayUri = createEventMapper('display_uri');\n\n metaMaskClient.on('display_uri', onDisplayUri);\n\n const detachListeners = () => {\n metaMaskClient.off('display_uri', onDisplayUri);\n };\n\n // A newer build may have already replaced this one (e.g. triggered by\n // clearMetaMaskStorageAndInstance while this build was still pending).\n // Detach immediately instead of registering as the active cleanup --\n // otherwise this stale client's listener stays attached forever, or\n // clobbers (or gets clobbered by) the newer client's cleanup.\n if (metaMaskClientPromise !== builtClientPromise) {\n detachListeners();\n return metaMaskClient;\n }\n\n clearEventListeners = detachListeners;\n\n return metaMaskClient;\n });\n\n metaMaskClientPromise = builtClientPromise;\n\n // A rejected build must not stay cached -- otherwise every later\n // getMetaMaskClient() call would keep returning the same rejection\n // forever, with no way to recover short of clearMetaMaskStorageAndInstance.\n // Only clear the cache if it still points at this build: a newer build\n // (e.g. triggered by clearMetaMaskStorageAndInstance while this one was\n // still pending) may have already replaced it, and this rejection must\n // not clobber that newer, possibly-successful build.\n builtClientPromise.catch(() => {\n if (metaMaskClientPromise === builtClientPromise) {\n metaMaskClientPromise = null;\n }\n });\n };\n\n return {\n clearMetaMaskStorageAndInstance: async () => {\n await clearMetaMaskStorage(client);\n\n metaMaskClientPromise = null;\n refreshMetaMaskClient();\n },\n events,\n getMetaMaskClient: async () => {\n if (!metaMaskClientPromise) {\n refreshMetaMaskClient();\n }\n\n if (!metaMaskClientPromise) {\n throw new MetaMaskClientNotInitializedError();\n }\n\n return metaMaskClientPromise;\n },\n };\n};\n","import { createRuntimeServiceAccessKey } from '@dynamic-labs-sdk/client/core';\n\nimport { createMetaMaskClientInstanceManager } from '../createMetaMaskClientInstanceManager';\n\nconst META_MASK_CLIENT_INSTANCE_MANAGER_KEY =\n 'META_MASK_CLIENT_INSTANCE_MANAGER';\n\n/**\n * Returns this client's `MetaMaskClientInstanceManager`, creating it on first\n * access. Every chain's MetaMask wallet provider (EVM, Solana, ...) calls\n * this to reach the SAME manager -- and therefore the same shared multichain\n * session -- rather than each building its own.\n *\n * @example\n * ```ts\n * const manager = getOrCreateMetaMaskClientInstanceManager(client);\n * const metaMaskClient = await manager.getMetaMaskClient();\n * ```\n * @param client - The Dynamic client instance.\n * @returns The client's `MetaMaskClientInstanceManager`.\n * @see createMetaMaskClientInstanceManager\n * @see disconnectMetaMaskWalletProvider\n */\nexport const getOrCreateMetaMaskClientInstanceManager =\n createRuntimeServiceAccessKey(\n META_MASK_CLIENT_INSTANCE_MANAGER_KEY,\n (client) => createMetaMaskClientInstanceManager(client)\n );\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ import * as z from "zod/mini";
5
5
 
6
6
  //#region package.json
7
7
  var name = "@dynamic-labs-sdk/metamask";
8
- var version = "1.31.2";
8
+ var version = "1.32.0";
9
9
 
10
10
  //#endregion
11
11
  //#region src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts
@@ -51,6 +51,16 @@ var MetaMaskUnscopedNetworkError = class extends BaseError {
51
51
  * change in one place.
52
52
  */
53
53
  const SUPPORTED_METAMASK_CHAINS = ["EVM", "SOL"];
54
+ /**
55
+ * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for
56
+ * mobile wallet pairing and request hand-off.
57
+ */
58
+ const METAMASK_APP_SCHEME_PREFIX = "metamask://";
59
+ /**
60
+ * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS
61
+ * resolves through MetaMask's associated domain.
62
+ */
63
+ const METAMASK_UNIVERSAL_LINK_PREFIX = "https://metamask.app.link/";
54
64
 
55
65
  //#endregion
56
66
  //#region src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts
@@ -471,6 +481,35 @@ const clearMetaMaskSdkSingleton = () => {
471
481
  delete globalThis[METAMASK_SDK_SINGLETON_GLOBAL_KEY];
472
482
  };
473
483
 
484
+ //#endregion
485
+ //#region src/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.ts
486
+ /**
487
+ * Rewrites a MetaMask app-scheme deeplink (`metamask://connect/...`) into its
488
+ * universal-link equivalent (`https://metamask.app.link/connect/...`).
489
+ *
490
+ * `@metamask/connect-multichain` only ever hands `preferredOpenLink` the
491
+ * app-scheme form, which iOS refuses to open whenever no installed app claims
492
+ * the `metamask` scheme. The universal link is resolved through MetaMask's
493
+ * associated domain instead, so it opens the wallet when it is installed and
494
+ * lands on a web page when it is not, rather than failing outright.
495
+ *
496
+ * @example
497
+ * ```ts
498
+ * toMetaMaskUniversalLink({ deeplink: 'metamask://connect/mwp?id=abc' });
499
+ * // 'https://metamask.app.link/connect/mwp?id=abc'
500
+ * ```
501
+ *
502
+ * @returns The universal-link form of the deeplink, or undefined when the
503
+ * deeplink does not use MetaMask's app scheme and therefore has no equivalent.
504
+ * @see createMetaMaskPreferredOpenLink
505
+ * @see METAMASK_UNIVERSAL_LINK_PREFIX
506
+ * @notInstrumented
507
+ */
508
+ const toMetaMaskUniversalLink = ({ deeplink }) => {
509
+ if (!deeplink.startsWith(METAMASK_APP_SCHEME_PREFIX)) return;
510
+ return `${METAMASK_UNIVERSAL_LINK_PREFIX}${deeplink.slice(METAMASK_APP_SCHEME_PREFIX.length)}`;
511
+ };
512
+
474
513
  //#endregion
475
514
  //#region src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts
476
515
  /**
@@ -479,10 +518,16 @@ const clearMetaMaskSdkSingleton = () => {
479
518
  * SDK's own default handling, so every platform (web, React Native, Expo)
480
519
  * opens them the same way the rest of the SDK does.
481
520
  *
521
+ * MetaMask only ever passes app-scheme deeplinks (`metamask://...`) to this
522
+ * callback, and iOS rejects those whenever it finds no installed handler for the
523
+ * scheme. When opening one fails, the universal-link equivalent is tried before
524
+ * giving up, so the wallet still comes to the foreground and the pending request
525
+ * is not left unanswered.
526
+ *
482
527
  * @example
483
528
  * ```ts
484
529
  * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);
485
- * preferredOpenLink('https://metamask.app.link/...');
530
+ * preferredOpenLink('metamask://connect/mwp?id=...');
486
531
  * ```
487
532
  *
488
533
  * @returns A callback that opens a MetaMask-issued deeplink through the
@@ -494,10 +539,26 @@ const clearMetaMaskSdkSingleton = () => {
494
539
  const createMetaMaskPreferredOpenLink = (client) => (deeplink) => {
495
540
  const { logger, openDeeplink } = getCore(client);
496
541
  logger.debug("[MetaMask:Client] Opening MetaMask deeplink", { deeplink });
497
- openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((error) => logger.error("[MetaMask:Client] Failed to open deeplink", {
498
- deeplink,
499
- error
500
- }));
542
+ openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((appSchemeError) => {
543
+ const universalLink = toMetaMaskUniversalLink({ deeplink });
544
+ if (!universalLink) {
545
+ logger.error("[MetaMask:Client] Failed to open deeplink", {
546
+ deeplink,
547
+ error: appSchemeError
548
+ });
549
+ return;
550
+ }
551
+ logger.debug("[MetaMask:Client] Retrying MetaMask deeplink as a universal link", {
552
+ error: appSchemeError,
553
+ universalLink
554
+ });
555
+ return openDeeplink(universalLink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((universalLinkError) => logger.error("[MetaMask:Client] Failed to open deeplink", {
556
+ deeplink,
557
+ error: appSchemeError,
558
+ universalLink,
559
+ universalLinkError
560
+ }));
561
+ });
501
562
  };
502
563
 
503
564
  //#endregion
@@ -618,4 +679,4 @@ const getOrCreateMetaMaskClientInstanceManager = createRuntimeServiceAccessKey(M
618
679
 
619
680
  //#endregion
620
681
  export { UnsupportedMetaMaskChainError as a, name as c, getMetaMaskScopeForNetworkId as i, version as l, clearMetaMaskStorage as n, SUPPORTED_METAMASK_CHAINS as o, getMetaMaskScopes as r, MetaMaskUnscopedNetworkError as s, getOrCreateMetaMaskClientInstanceManager as t };
621
- //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js.map
682
+ //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-CLfDSJv6.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOrCreateMetaMaskClientInstanceManager-CLfDSJv6.esm.js","names":["supportedNetworks: MetaMaskSupportedNetworks","queueMetaMaskStorageWrite: SequenceQueue","adapter: StoreAdapter","metaMaskClientPromise: Promise<MultichainCore> | null","clearEventListeners: () => void","builtClientPromise: Promise<MultichainCore>"],"sources":["../package.json","../src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts","../src/constants.ts","../src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts","../src/getMetaMaskScopeForNetworkId/getMetaMaskScopeForNetworkId.ts","../src/getMetaMaskScopes/getMetaMaskScopes.ts","../src/errors/MetaMaskClientNotInitializedError/MetaMaskClientNotInitializedError.ts","../src/getMetaMaskSupportedNetworks/getMetaMaskSupportedNetworks.ts","../src/metaMaskStorage/createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue.ts","../src/metaMaskStorage/queueMetaMaskStorageWrite/queueMetaMaskStorageWrite.ts","../src/metaMaskStorage/metaMaskStorage.schema.ts","../src/metaMaskStorage/clearMetaMaskStorage/clearMetaMaskStorage.ts","../src/errors/MetaMaskStorageError/MetaMaskStorageError.ts","../src/metaMaskStorage/createMetaMaskStorageClient/createMetaMaskStorageClient.ts","../src/utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton.ts","../src/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.ts","../src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts","../src/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/createMetaMaskClientInstanceManager.ts","../src/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when attempting to switch a MetaMask multichain wallet provider to\n * a network whose CAIP-2 scope was requested at connect time but never\n * granted an account by MetaMask in the current session (e.g. the user only\n * approved mainnet, not a preconfigured devnet, during pairing). Switching\n * to it anyway would silently break every subsequent signing/sending call\n * on that network, since MetaMask rejects those against an ungranted scope.\n *\n * @example\n * ```ts\n * try {\n * await switchActiveNetwork({ networkId: '103', walletAccount });\n * } catch (error) {\n * if (error instanceof MetaMaskUnscopedNetworkError) {\n * // Tell the user to approve this network in MetaMask, then retry.\n * }\n * }\n * ```\n * @see assertMetaMaskNetworkIsGranted\n * @see resolveActiveMetaMaskScope\n */\nexport class MetaMaskUnscopedNetworkError extends BaseError {\n constructor({ networkId, scope }: { networkId: string; scope: string }) {\n super({\n cause: null,\n code: 'metamask_unscoped_network',\n docsUrl: null,\n metaMessages: [`scope: ${scope}`],\n name: 'MetaMaskUnscopedNetworkError',\n shortMessage: `Cannot switch to network \"${networkId}\": MetaMask did not grant an account for scope \"${scope}\" in the current session.`,\n });\n }\n}\n","import type { Chain } from '@dynamic-labs-sdk/client';\n\n/**\n * Chains MetaMask's multichain client can build a CAIP-2 scope for. Shared by\n * every scope/network-mapping helper so the supported set only needs to\n * change in one place.\n */\nexport const SUPPORTED_METAMASK_CHAINS = ['EVM', 'SOL'] as Chain[];\n\n/**\n * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for\n * mobile wallet pairing and request hand-off.\n */\nexport const METAMASK_APP_SCHEME_PREFIX = 'metamask://';\n\n/**\n * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS\n * resolves through MetaMask's associated domain.\n */\nexport const METAMASK_UNIVERSAL_LINK_PREFIX = 'https://metamask.app.link/';\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/** Thrown when a chain has no MetaMask multichain scope mapping defined. */\nexport class UnsupportedMetaMaskChainError extends BaseError {\n constructor({\n chain,\n supportedChains,\n }: {\n chain: string;\n supportedChains: string[];\n }) {\n super({\n cause: null,\n code: 'metamask_unsupported_chain',\n docsUrl: null,\n name: 'UnsupportedMetaMaskChainError',\n shortMessage: `Unsupported chain \"${chain}\" for MetaMask. Supported chains: ${supportedChains.join(', ')}`,\n });\n }\n}\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport type { RPCAPI, Scope } from '@metamask/connect-multichain';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\n\nexport type GetMetaMaskScopeForNetworkIdParams = {\n chain: Chain;\n networkId: string;\n};\n\n/**\n * Maps a single Dynamic-configured network to its CAIP-2 MetaMask\n * multichain scope -- the same per-network mapping `getMetaMaskScopes`\n * applies to every configured network at once, extracted so callers that\n * already have one `networkId` (e.g. the \"active network\" registry) don't\n * need to rebuild and filter the whole configured list to find it.\n *\n * EVM's `networkId` IS the CAIP-2 reference (`eip155:<networkId>`); SOL's\n * `networkId` is an internal routing id, so the network's base58 genesis\n * hash is used instead (`solana:<genesisHash>`). Returns `undefined` when no\n * configured SOL network matches, or matches but has no genesis hash --\n * these are normal (\"this networkId isn't usable as a MetaMask scope\"), not\n * error conditions.\n *\n * @example\n * ```ts\n * const scope = getMetaMaskScopeForNetworkId(\n * { chain: 'SOL', networkId: '101' },\n * client\n * );\n * // 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'\n * ```\n *\n * @returns The CAIP-2 scope for `networkId`, or `undefined` if it can't be\n * expressed as one (e.g. a SOL network with no genesis hash configured).\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @see getMetaMaskScopes\n * @see getNetworkIdFromMetaMaskScope\n * @notInstrumented\n */\nexport const getMetaMaskScopeForNetworkId = (\n { chain, networkId }: GetMetaMaskScopeForNetworkIdParams,\n client: DynamicClient\n): Scope<RPCAPI> | undefined => {\n if (chain === 'EVM') {\n return `eip155:${networkId}` as Scope<RPCAPI>;\n }\n\n if (chain === 'SOL') {\n const network = getNetworksData(client).find(\n (candidate) => candidate.chain === 'SOL' && candidate.networkId === networkId\n );\n\n return network?.genesisHash\n ? (`solana:${network.genesisHash}` as Scope<RPCAPI>)\n : undefined;\n }\n\n throw new UnsupportedMetaMaskChainError({\n chain,\n supportedChains: SUPPORTED_METAMASK_CHAINS,\n });\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport type { RPCAPI, Scope } from '@metamask/connect-multichain';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\n\ntype GetMetaMaskScopesOptions = {\n chain: Chain;\n};\n\n/**\n * Builds the CAIP-2 scopes to request for a chain's MetaMask multichain\n * session, one per configured network. SOL scopes use the network's genesis\n * hash (not `networkId`, which is an internal routing id for SOL) since\n * that's the CAIP-2 reference MetaMask expects.\n *\n * @example\n * ```ts\n * const scopes = getMetaMaskScopes({ chain: 'EVM' }, client);\n * // ['eip155:1', 'eip155:137']\n * ```\n *\n * @returns The CAIP-2 scopes for every configured network of `chain` that\n * can be expressed as one.\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @see getMetaMaskScopeForNetworkId\n * @see getMetaMaskSupportedNetworks\n * @notInstrumented\n */\nexport const getMetaMaskScopes = (\n { chain }: GetMetaMaskScopesOptions,\n client: DynamicClient\n): Scope<RPCAPI>[] => {\n if (!chain || !SUPPORTED_METAMASK_CHAINS.includes(chain)) {\n throw new UnsupportedMetaMaskChainError({\n chain,\n supportedChains: SUPPORTED_METAMASK_CHAINS,\n });\n }\n\n const networks = getNetworksData(client).filter(\n (network) => network.chain === chain\n );\n\n return networks.flatMap((network) => {\n const scope = getMetaMaskScopeForNetworkId(\n { chain: network.chain, networkId: network.networkId },\n client\n );\n\n // Skip networks that can't be expressed as a MetaMask scope (e.g. a SOL\n // network with no genesis hash configured) rather than build an invalid\n // one -- see `getMetaMaskScopeForNetworkId`.\n return scope ? [scope] : [];\n });\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when `getMetaMaskClient()` is called but the underlying\n * `@metamask/connect-multichain` client failed to initialize -- this should\n * be unreachable in practice, since `getMetaMaskClient` always triggers\n * `refreshMetaMaskClient` first when no client promise exists yet, but it\n * guards against a future change silently returning `undefined` instead.\n */\nexport class MetaMaskClientNotInitializedError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'metamask_client_not_initialized',\n docsUrl: null,\n name: 'MetaMaskClientNotInitializedError',\n shortMessage: 'MetaMask client not initialized',\n });\n }\n}\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\n\ntype MetaMaskSupportedNetworks = Record<\n `eip155:${string}` | `solana:${string}`,\n string\n>;\n\n/**\n * Builds the `api.supportedNetworks` map for MetaMask's multichain client:\n * EVM networks keyed by their CAIP-2 `eip155:<networkId>` scope, SOL networks\n * keyed by their CAIP-2 `solana:<genesisHash>` scope (SOL's `networkId` is an\n * internal routing id, not usable as a CAIP-2 reference).\n *\n * @example\n * ```ts\n * const supportedNetworks = getMetaMaskSupportedNetworks(client);\n * // { 'eip155:1': 'https://...', 'solana:5eykt4...': 'https://...' }\n * ```\n *\n * @returns The RPC URL for every configured EVM/SOL network, keyed by its\n * MetaMask-recognized network identifier.\n * @see getMetaMaskScopes\n * @see createMetaMaskClientInstanceManager\n * @notInstrumented\n */\nexport const getMetaMaskSupportedNetworks = (\n client: DynamicClient\n): MetaMaskSupportedNetworks => {\n const { logger } = getCore(client);\n\n const networks = getNetworksData(client).filter((network) =>\n SUPPORTED_METAMASK_CHAINS.includes(network.chain)\n );\n\n const supportedNetworks: MetaMaskSupportedNetworks = {};\n\n for (const network of networks) {\n if (network.chain === 'EVM') {\n supportedNetworks[`eip155:${network.networkId}`] =\n network.rpcUrls.http[0];\n }\n\n // SOL's `networkId` is an internal routing id, not the CAIP-2 reference —\n // the multichain client's `solana:` scope needs the base58 genesis hash.\n // Skip networks that haven't configured one.\n if (network.chain === 'SOL' && network.genesisHash) {\n supportedNetworks[`solana:${network.genesisHash}`] =\n network.rpcUrls.http[0];\n }\n }\n\n logger.debug('[MetaMask:Client] Resolved supported networks', {\n supportedNetworks,\n });\n\n return supportedNetworks;\n};\n","import type { MetaMaskStoreValue } from '../metaMaskStorage.schema';\n\n/**\n * Builds the persisted store blob's value for when there is nothing stored\n * yet, or after a clear. A function rather than a shared constant, so every\n * caller gets its own object and can't mutate a value other callers rely on.\n *\n * @example\n * ```ts\n * const initial = createInitialMetaMaskStoreValue();\n * // { entries: {}, schemaVersion: 1 }\n * ```\n *\n * @returns A fresh, empty `MetaMaskStoreValue`.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createInitialMetaMaskStoreValue = (): MetaMaskStoreValue => ({\n entries: {},\n schemaVersion: 1,\n});\n","import { createSequenceQueue } from '@dynamic-labs-sdk/client/core';\nimport type { SequenceQueue } from '@dynamic-labs-sdk/client/core';\n\n/**\n * Single shared queue serializing every read-modify-write against\n * `metaMaskStoreStorageKeySchema` -- `createMetaMaskStorageClient`'s adapter\n * reads/writes/anonId operations and `clearMetaMaskStorage`'s reset all go\n * through this same instance, so a write that started before a clear can't\n * land after it and resurrect the data the clear was meant to erase.\n *\n * @example\n * ```ts\n * await queueMetaMaskStorageWrite(() => storage.setItem(key, value), client);\n * ```\n *\n * @returns A `SequenceQueue` that runs one queued task at a time, in order.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const queueMetaMaskStorageWrite: SequenceQueue = createSequenceQueue();\n","import * as z from 'zod/mini';\nimport { createStorageKeySchema } from '@dynamic-labs-sdk/client/core';\n\n/**\n * Single storage key holding every value the MetaMask multichain SDK writes\n * through our `StoreAdapter` (sessions, nonces, cached accounts/chain,\n * pending pairing requests, transport ids, ...), keyed by whatever key string\n * the SDK itself hands us -- the SDK controls every key name, including ones\n * that embed an unpredictable id (`session:<id>`), so this can't be split\n * into one storage key per SDK key.\n *\n * `entries[ANON_ID_ENTRY_KEY]` also lives in here, alongside `extensionId`\n * and the other transport keys.\n *\n * `clearMetaMaskStorage` resets this key back to its initial value; nothing\n * else needs to know the individual key names the SDK is using.\n */\nexport const metaMaskStoreStorageKeySchema = createStorageKeySchema({\n key: 'metaMaskStore',\n schema: z.object({\n entries: z.record(z.string(), z.string()),\n schemaVersion: z.literal(1),\n }),\n});\n\n/**\n * The persisted store blob's shape. Exported here because it is shared by\n * every function operating on this storage key.\n */\nexport type MetaMaskStoreValue = z.infer<typeof metaMaskStoreStorageKeySchema.schema>;\n\n/**\n * Reserved `entries` key the MetaMask SDK itself never writes to, used to\n * carry the SDK's anonymous analytics id inside the same store blob.\n */\nexport const ANON_ID_ENTRY_KEY = 'anonId';\n","import { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { DynamicClient } from '@dynamic-labs-sdk/client';\n\nimport { createInitialMetaMaskStoreValue } from '../createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue';\nimport { queueMetaMaskStorageWrite } from '../queueMetaMaskStorageWrite/queueMetaMaskStorageWrite';\nimport { metaMaskStoreStorageKeySchema } from '../metaMaskStorage.schema';\n\nconst FAILED_TO_CLEAR_STORAGE_MESSAGE =\n '[MetaMask:Session] Failed to clear the persisted store; the next pairing may resume a stale session instead of showing a new one';\n\n/**\n * Resets the MetaMask SDK's persisted store blob back to its initial\n * (empty) value, so the next connect call starts a fresh pairing and emits a\n * `display_uri` instead of silently resuming a session that no longer works.\n *\n * @example\n * ```ts\n * await clearMetaMaskStorage(client);\n * // Next connectWithMetaMaskUri call will emit a fresh URI immediately.\n * ```\n *\n * @param client - The Dynamic client instance. Required (rather than\n * defaulting to the last-initialized client) so a storage-clear failure is\n * always logged against the client that actually requested it.\n * @returns A promise that resolves once the blob is reset. Never rejects -- a\n * storage failure must not take down the connect flow that depends on it.\n * @see connectWithMetaMaskUri\n * @see createMetaMaskStorageClient\n * @notInstrumented\n */\nexport const clearMetaMaskStorage = async (\n client: DynamicClient\n): Promise<void> => {\n const { logger, storage } = getCore(client);\n\n try {\n await queueMetaMaskStorageWrite(\n () =>\n storage.setItem(\n metaMaskStoreStorageKeySchema,\n createInitialMetaMaskStoreValue()\n ),\n client\n );\n\n logger.debug('[MetaMask:Session] Cleared persisted store');\n } catch (error) {\n logger.warn(FAILED_TO_CLEAR_STORAGE_MESSAGE, { error });\n }\n};\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\ntype MetaMaskStorageErrorParams = {\n cause: unknown;\n key: string;\n operation: 'get' | 'set' | 'delete';\n};\n\n/** Thrown when the underlying storage service rejects a read/write/delete for the MetaMask multichain client's own store (e.g. quota exceeded, private-browsing restrictions). */\nexport class MetaMaskStorageError extends BaseError {\n constructor({ cause, key, operation }: MetaMaskStorageErrorParams) {\n super({\n cause: cause instanceof Error ? cause : null,\n code: 'metamask_storage_error',\n docsUrl: null,\n name: 'MetaMaskStorageError',\n shortMessage: `Failed to ${operation} MetaMask multichain client storage key \"${key}\"`,\n });\n }\n}\n","import { getCore, getPlatform } from '@dynamic-labs-sdk/client/core';\nimport type { StoreAdapter, StoreClient } from '@metamask/connect-multichain';\nimport { getTransportType } from '@metamask/connect-multichain';\nimport type { DynamicClient } from '@dynamic-labs-sdk/client';\n\nimport { MetaMaskStorageError } from '../../errors/MetaMaskStorageError';\nimport { createInitialMetaMaskStoreValue } from '../createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue';\nimport { queueMetaMaskStorageWrite } from '../queueMetaMaskStorageWrite/queueMetaMaskStorageWrite';\nimport { ANON_ID_ENTRY_KEY, metaMaskStoreStorageKeySchema } from '../metaMaskStorage.schema';\n\nconst TRANSPORT_TYPE_KEY = 'multichain-transport';\nconst EXTENSION_ID_KEY = 'extensionId';\n\n/** Maps this SDK's platform literal onto the one `@metamask/connect-multichain`'s `StoreAdapter` expects. */\nconst toStoreAdapterPlatform = (\n platform: ReturnType<typeof getPlatform>\n): StoreAdapter['platform'] => (platform === 'react-native' ? 'rn' : 'web');\n\n/**\n * Builds a storage-service-backed store for the MetaMask multichain client,\n * in place of its own default web adapter (IndexedDB) and React Native\n * adapter (raw `AsyncStorage`), so every platform persists through the same\n * core `Storage` service and `clearMetaMaskStorage` can reliably wipe it.\n *\n * The SDK controls every key name it reads/writes through `adapter`,\n * including ones that embed an unpredictable id (`session:<id>`), so every\n * key it uses is folded into a single stored blob (`metaMaskStoreStorageKeySchema`)\n * rather than one storage key per SDK key. `DEBUG` is never persisted at all,\n * answered live from the client's own logger.\n *\n * @example\n * ```ts\n * const storeClient = createMetaMaskStorageClient(client);\n * const multichainClient = createMultichainClient({ storage: storeClient });\n * ```\n *\n * @returns A `StoreClient` backed by the client's core `Storage` service.\n * @throws {MetaMaskStorageError} If the underlying `Storage` service's\n * `getItem`/`setItem` calls fail for any of the adapter's read/write/delete\n * operations.\n * @see createInitialMetaMaskStoreValue\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createMetaMaskStorageClient = (client: DynamicClient): StoreClient => {\n const { logger, storage } = getCore(client);\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.get`\n const read = async (key: string): Promise<string | null> => {\n try {\n const { entries } =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n\n return entries[key] ?? null;\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'get' });\n }\n };\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.set`\n const write = async (key: string, value: string): Promise<void> => {\n try {\n await queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: { ...current.entries, [key]: value },\n });\n }, client);\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'set' });\n }\n };\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- signature dictated by `StoreAdapter.delete`\n const remove = async (key: string): Promise<void> => {\n try {\n await queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n const remainingEntries = Object.fromEntries(\n Object.entries(current.entries).filter(\n ([entryKey]) => entryKey !== key\n )\n );\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: remainingEntries,\n });\n }, client);\n } catch (error) {\n throw new MetaMaskStorageError({ cause: error, key, operation: 'delete' });\n }\n };\n\n const adapter: StoreAdapter = {\n delete: remove,\n get: read,\n platform: toStoreAdapterPlatform(getPlatform()),\n set: write,\n };\n\n return {\n adapter,\n // Queued (rather than a plain read-then-write) so its check-then-create\n // can't race itself into generating two different ids for the same\n // client -- the SDK calls `getAnonId()` from multiple internal call sites.\n getAnonId: () =>\n queueMetaMaskStorageWrite(async () => {\n const current =\n (await storage.getItem(metaMaskStoreStorageKeySchema)) ??\n createInitialMetaMaskStoreValue();\n const existing = current.entries[ANON_ID_ENTRY_KEY];\n\n if (existing) {\n return existing;\n }\n\n const anonId = crypto.randomUUID();\n\n await storage.setItem(metaMaskStoreStorageKeySchema, {\n ...current,\n entries: { ...current.entries, [ANON_ID_ENTRY_KEY]: anonId },\n });\n\n return anonId;\n }, client),\n getDebug: () =>\n // The SDK matches this against its own debug namespaces via\n // `debugValue.includes(namespace) || debugValue.includes('metamask-sdk:*')`\n // -- a boolean-shaped string like `'true'` would never match, so this\n // has to be a namespace pattern, not a flag.\n Promise.resolve(logger.isEnabled('debug') ? 'metamask-sdk:*' : null),\n getExtensionId: () => adapter.get(EXTENSION_ID_KEY),\n getTransportType: async () => {\n const value = await adapter.get(TRANSPORT_TYPE_KEY);\n\n return value ? getTransportType(value) : null;\n },\n removeAnonId: () => remove(ANON_ID_ENTRY_KEY),\n removeExtensionId: () => adapter.delete(EXTENSION_ID_KEY),\n removeTransportType: () => adapter.delete(TRANSPORT_TYPE_KEY),\n setAnonId: (anonId) => write(ANON_ID_ENTRY_KEY, anonId),\n setExtensionId: (extensionId) => adapter.set(EXTENSION_ID_KEY, extensionId),\n setTransportType: (transportType) =>\n adapter.set(TRANSPORT_TYPE_KEY, transportType),\n };\n};\n","// Mirrors `@metamask/connect-multichain`'s own singleton key -- not exported\n// by the SDK, so it must be kept in sync by hand whenever the pinned version\n// in packages/metamask/package.json changes.\nconst METAMASK_SDK_SINGLETON_GLOBAL_KEY =\n '__METAMASK_CONNECT_MULTICHAIN_SINGLETON__';\n\n/**\n * Deletes the upstream `@metamask/connect-multichain` SDK's own cached\n * client instance off `globalThis`.\n *\n * The SDK's `createMultichainClient` reuses a private `globalThis` singleton\n * and only merges in a subset of options on repeat calls -- notably not\n * `storage` -- so it keeps talking to its original `StoreAdapter` even after\n * a fresh `clearMetaMaskStorage` call. Deleting this key forces a genuinely\n * new instance (and storage adapter) on the next `createMultichainClient` call.\n *\n * @example\n * ```ts\n * clearMetaMaskSdkSingleton();\n * // Next createMultichainClient call builds a fresh client and adapter.\n * ```\n *\n * @returns Nothing -- the singleton key is deleted synchronously.\n * @see createMetaMaskClientInstanceManager\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const clearMetaMaskSdkSingleton = (): void => {\n delete (globalThis as Record<string, unknown>)[\n METAMASK_SDK_SINGLETON_GLOBAL_KEY\n ];\n};\n","import {\n METAMASK_APP_SCHEME_PREFIX,\n METAMASK_UNIVERSAL_LINK_PREFIX,\n} from '../../constants';\n\nexport type ToMetaMaskUniversalLinkParams = {\n deeplink: string;\n};\n\n/**\n * Rewrites a MetaMask app-scheme deeplink (`metamask://connect/...`) into its\n * universal-link equivalent (`https://metamask.app.link/connect/...`).\n *\n * `@metamask/connect-multichain` only ever hands `preferredOpenLink` the\n * app-scheme form, which iOS refuses to open whenever no installed app claims\n * the `metamask` scheme. The universal link is resolved through MetaMask's\n * associated domain instead, so it opens the wallet when it is installed and\n * lands on a web page when it is not, rather than failing outright.\n *\n * @example\n * ```ts\n * toMetaMaskUniversalLink({ deeplink: 'metamask://connect/mwp?id=abc' });\n * // 'https://metamask.app.link/connect/mwp?id=abc'\n * ```\n *\n * @returns The universal-link form of the deeplink, or undefined when the\n * deeplink does not use MetaMask's app scheme and therefore has no equivalent.\n * @see createMetaMaskPreferredOpenLink\n * @see METAMASK_UNIVERSAL_LINK_PREFIX\n * @notInstrumented\n */\nexport const toMetaMaskUniversalLink = ({\n deeplink,\n}: ToMetaMaskUniversalLinkParams): string | undefined => {\n if (!deeplink.startsWith(METAMASK_APP_SCHEME_PREFIX)) {\n return undefined;\n }\n\n return `${METAMASK_UNIVERSAL_LINK_PREFIX}${deeplink.slice(\n METAMASK_APP_SCHEME_PREFIX.length\n )}`;\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\n\nimport { toMetaMaskUniversalLink } from '../toMetaMaskUniversalLink';\n\n/**\n * Builds the `preferredOpenLink` callback handed to `@metamask/connect-multichain`,\n * routing wallet deeplinks through the core `deeplink` service instead of the\n * SDK's own default handling, so every platform (web, React Native, Expo)\n * opens them the same way the rest of the SDK does.\n *\n * MetaMask only ever passes app-scheme deeplinks (`metamask://...`) to this\n * callback, and iOS rejects those whenever it finds no installed handler for the\n * scheme. When opening one fails, the universal-link equivalent is tried before\n * giving up, so the wallet still comes to the foreground and the pending request\n * is not left unanswered.\n *\n * @example\n * ```ts\n * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);\n * preferredOpenLink('metamask://connect/mwp?id=...');\n * ```\n *\n * @returns A callback that opens a MetaMask-issued deeplink through the\n * core `deeplink` service.\n * @see createMetaMaskClientInstanceManager\n * @see createMetaMaskStorageClient\n * @notInstrumented\n */\nexport const createMetaMaskPreferredOpenLink =\n (client: DynamicClient): ((deeplink: string) => void) =>\n (deeplink) => {\n const { logger, openDeeplink } = getCore(client);\n\n logger.debug('[MetaMask:Client] Opening MetaMask deeplink', { deeplink });\n\n void openDeeplink(deeplink)\n .then(() => logger.debug('[MetaMask:Client] Deeplink opened'))\n .catch((appSchemeError: unknown) => {\n const universalLink = toMetaMaskUniversalLink({ deeplink });\n\n if (!universalLink) {\n logger.error('[MetaMask:Client] Failed to open deeplink', {\n deeplink,\n error: appSchemeError,\n });\n\n return;\n }\n\n logger.debug(\n '[MetaMask:Client] Retrying MetaMask deeplink as a universal link',\n { error: appSchemeError, universalLink }\n );\n\n return openDeeplink(universalLink)\n .then(() => logger.debug('[MetaMask:Client] Deeplink opened'))\n .catch((universalLinkError: unknown) =>\n logger.error('[MetaMask:Client] Failed to open deeplink', {\n deeplink,\n error: appSchemeError,\n universalLink,\n universalLinkError,\n })\n );\n });\n };\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport {\n type EventEmitter,\n createEventEmitter,\n getAppMetadata,\n getCore,\n getNativeLinkScheme,\n} from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\nimport { createMultichainClient } from '@metamask/connect-multichain';\n\nimport { MetaMaskClientNotInitializedError } from '../../errors/MetaMaskClientNotInitializedError';\nimport { getMetaMaskSupportedNetworks } from '../../getMetaMaskSupportedNetworks';\nimport { clearMetaMaskStorage } from '../../metaMaskStorage/clearMetaMaskStorage';\nimport { createMetaMaskStorageClient } from '../../metaMaskStorage/createMetaMaskStorageClient';\nimport { clearMetaMaskSdkSingleton } from '../../utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton';\nimport { createMetaMaskPreferredOpenLink } from '../../utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink';\n\n/**\n * Events a `MetaMaskClientInstanceManager` forwards from the underlying\n * `@metamask/connect-multichain` client, for callers that don't hold a\n * reference to the raw SDK client.\n */\nexport type MetaMaskClientEvents = {\n display_uri: (evt: string) => void;\n};\n\n/**\n * The per-client handle returned by `createMetaMaskClientInstanceManager`.\n */\nexport type MetaMaskClientInstanceManager = {\n /** Wipes persisted MetaMask storage and forces the next build to start fresh. */\n clearMetaMaskStorageAndInstance: () => Promise<void>;\n /** Emits `MetaMaskClientEvents` forwarded from the underlying SDK client. */\n events: EventEmitter<MetaMaskClientEvents>;\n /** Resolves the shared `MultichainCore` client, building it on first access. */\n getMetaMaskClient(): Promise<MultichainCore>;\n};\n\n/**\n * Creates the per-client manager that lazily builds, tears down, and rebuilds\n * the shared `@metamask/connect-multichain` client instance, and forwards its\n * `display_uri` event to callers that don't hold a reference to the raw SDK\n * client.\n *\n * @example\n * ```ts\n * const manager = createMetaMaskClientInstanceManager(client);\n * const metaMaskClient = await manager.getMetaMaskClient();\n * ```\n *\n * @returns A `MetaMaskClientInstanceManager` scoped to `client`.\n * @throws {MetaMaskClientNotInitializedError} From `getMetaMaskClient` if a\n * build was never started -- a defensive guard, since `refreshMetaMaskClient`\n * always starts one. A build that starts but fails instead rejects\n * `getMetaMaskClient()` with its original error.\n * @see createMetaMaskStorageClient\n * @see clearMetaMaskStorage\n * @notInstrumented\n */\nexport const createMetaMaskClientInstanceManager = (\n client: DynamicClient\n): MetaMaskClientInstanceManager => {\n let metaMaskClientPromise: Promise<MultichainCore> | null;\n let clearEventListeners: () => void;\n const { logger } = getCore(client);\n const events = createEventEmitter<MetaMaskClientEvents>();\n\n const refreshMetaMaskClient = () => {\n // Runs synchronously up to the assignment below, so this guard makes\n // concurrent callers (e.g. two overlapping getMetaMaskClient() calls\n // before the first client exists) share the same build instead of\n // racing -- the loser's clearEventListeners would otherwise be\n // overwritten and its listener leaked. Callers that need a genuine\n // rebuild (clearMetaMaskStorageAndInstance) reset the promise to null\n // first.\n if (metaMaskClientPromise) {\n return;\n }\n\n const { appName, appLogoUrl, appUniversalLink, appNativeLink } =\n getAppMetadata(client);\n const supportedNetworks = getMetaMaskSupportedNetworks(client);\n const nativeScheme = getNativeLinkScheme({ nativeLink: appNativeLink });\n const debug = logger.isEnabled('debug');\n\n logger.debug('[MetaMask:Client] Creating MetaMask client', {\n appLogoUrl,\n appName,\n appUniversalLink,\n debug,\n nativeScheme,\n supportedNetworks,\n });\n\n if (clearEventListeners) {\n clearEventListeners();\n }\n\n clearMetaMaskSdkSingleton();\n\n const builtClientPromise: Promise<MultichainCore> = createMultichainClient({\n api: {\n supportedNetworks,\n },\n dapp: {\n iconUrl: appLogoUrl,\n name: appName,\n nativeScheme,\n url: appUniversalLink ?? '',\n },\n debug,\n mobile: {\n preferredOpenLink: createMetaMaskPreferredOpenLink(client),\n },\n // Backed by the core `Storage` service on every platform, so\n // `clearMetaMaskStorage` can reliably wipe everything the SDK writes\n // -- see `createMetaMaskStorageClient`.\n storage: createMetaMaskStorageClient(client),\n ui: { headless: true },\n }).then((metaMaskClient) => {\n const createEventMapper = <T extends keyof MetaMaskClientEvents>(\n event: T\n ) => {\n return (...args: Parameters<MetaMaskClientEvents[T]>) => {\n logger.debug(`[MetaMask:Events] ${event} event received`, { args });\n events.emit(event, ...args);\n };\n };\n\n const onDisplayUri = createEventMapper('display_uri');\n\n metaMaskClient.on('display_uri', onDisplayUri);\n\n const detachListeners = () => {\n metaMaskClient.off('display_uri', onDisplayUri);\n };\n\n // A newer build may have already replaced this one (e.g. triggered by\n // clearMetaMaskStorageAndInstance while this build was still pending).\n // Detach immediately instead of registering as the active cleanup --\n // otherwise this stale client's listener stays attached forever, or\n // clobbers (or gets clobbered by) the newer client's cleanup.\n if (metaMaskClientPromise !== builtClientPromise) {\n detachListeners();\n return metaMaskClient;\n }\n\n clearEventListeners = detachListeners;\n\n return metaMaskClient;\n });\n\n metaMaskClientPromise = builtClientPromise;\n\n // A rejected build must not stay cached -- otherwise every later\n // getMetaMaskClient() call would keep returning the same rejection\n // forever, with no way to recover short of clearMetaMaskStorageAndInstance.\n // Only clear the cache if it still points at this build: a newer build\n // (e.g. triggered by clearMetaMaskStorageAndInstance while this one was\n // still pending) may have already replaced it, and this rejection must\n // not clobber that newer, possibly-successful build.\n builtClientPromise.catch(() => {\n if (metaMaskClientPromise === builtClientPromise) {\n metaMaskClientPromise = null;\n }\n });\n };\n\n return {\n clearMetaMaskStorageAndInstance: async () => {\n await clearMetaMaskStorage(client);\n\n metaMaskClientPromise = null;\n refreshMetaMaskClient();\n },\n events,\n getMetaMaskClient: async () => {\n if (!metaMaskClientPromise) {\n refreshMetaMaskClient();\n }\n\n if (!metaMaskClientPromise) {\n throw new MetaMaskClientNotInitializedError();\n }\n\n return metaMaskClientPromise;\n },\n };\n};\n","import { createRuntimeServiceAccessKey } from '@dynamic-labs-sdk/client/core';\n\nimport { createMetaMaskClientInstanceManager } from '../createMetaMaskClientInstanceManager';\n\nconst META_MASK_CLIENT_INSTANCE_MANAGER_KEY =\n 'META_MASK_CLIENT_INSTANCE_MANAGER';\n\n/**\n * Returns this client's `MetaMaskClientInstanceManager`, creating it on first\n * access. Every chain's MetaMask wallet provider (EVM, Solana, ...) calls\n * this to reach the SAME manager -- and therefore the same shared multichain\n * session -- rather than each building its own.\n *\n * @example\n * ```ts\n * const manager = getOrCreateMetaMaskClientInstanceManager(client);\n * const metaMaskClient = await manager.getMetaMaskClient();\n * ```\n * @param client - The Dynamic client instance.\n * @returns The client's `MetaMaskClientInstanceManager`.\n * @see createMetaMaskClientInstanceManager\n * @see disconnectMetaMaskWalletProvider\n */\nexport const getOrCreateMetaMaskClientInstanceManager =\n createRuntimeServiceAccessKey(\n META_MASK_CLIENT_INSTANCE_MANAGER_KEY,\n (client) => createMetaMaskClientInstanceManager(client)\n );\n"],"mappings":"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"}
@@ -33,7 +33,7 @@ zod_mini = __toESM(zod_mini);
33
33
 
34
34
  //#region package.json
35
35
  var name = "@dynamic-labs-sdk/metamask";
36
- var version = "1.31.2";
36
+ var version = "1.32.0";
37
37
 
38
38
  //#endregion
39
39
  //#region src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts
@@ -79,6 +79,16 @@ var MetaMaskUnscopedNetworkError = class extends _dynamic_labs_sdk_client.BaseEr
79
79
  * change in one place.
80
80
  */
81
81
  const SUPPORTED_METAMASK_CHAINS = ["EVM", "SOL"];
82
+ /**
83
+ * Prefix of the app-scheme deeplinks `@metamask/connect-multichain` builds for
84
+ * mobile wallet pairing and request hand-off.
85
+ */
86
+ const METAMASK_APP_SCHEME_PREFIX = "metamask://";
87
+ /**
88
+ * Prefix of MetaMask's universal links, the app-scheme equivalent that iOS
89
+ * resolves through MetaMask's associated domain.
90
+ */
91
+ const METAMASK_UNIVERSAL_LINK_PREFIX = "https://metamask.app.link/";
82
92
 
83
93
  //#endregion
84
94
  //#region src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts
@@ -499,6 +509,35 @@ const clearMetaMaskSdkSingleton = () => {
499
509
  delete globalThis[METAMASK_SDK_SINGLETON_GLOBAL_KEY];
500
510
  };
501
511
 
512
+ //#endregion
513
+ //#region src/utils/toMetaMaskUniversalLink/toMetaMaskUniversalLink.ts
514
+ /**
515
+ * Rewrites a MetaMask app-scheme deeplink (`metamask://connect/...`) into its
516
+ * universal-link equivalent (`https://metamask.app.link/connect/...`).
517
+ *
518
+ * `@metamask/connect-multichain` only ever hands `preferredOpenLink` the
519
+ * app-scheme form, which iOS refuses to open whenever no installed app claims
520
+ * the `metamask` scheme. The universal link is resolved through MetaMask's
521
+ * associated domain instead, so it opens the wallet when it is installed and
522
+ * lands on a web page when it is not, rather than failing outright.
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * toMetaMaskUniversalLink({ deeplink: 'metamask://connect/mwp?id=abc' });
527
+ * // 'https://metamask.app.link/connect/mwp?id=abc'
528
+ * ```
529
+ *
530
+ * @returns The universal-link form of the deeplink, or undefined when the
531
+ * deeplink does not use MetaMask's app scheme and therefore has no equivalent.
532
+ * @see createMetaMaskPreferredOpenLink
533
+ * @see METAMASK_UNIVERSAL_LINK_PREFIX
534
+ * @notInstrumented
535
+ */
536
+ const toMetaMaskUniversalLink = ({ deeplink }) => {
537
+ if (!deeplink.startsWith(METAMASK_APP_SCHEME_PREFIX)) return;
538
+ return `${METAMASK_UNIVERSAL_LINK_PREFIX}${deeplink.slice(METAMASK_APP_SCHEME_PREFIX.length)}`;
539
+ };
540
+
502
541
  //#endregion
503
542
  //#region src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts
504
543
  /**
@@ -507,10 +546,16 @@ const clearMetaMaskSdkSingleton = () => {
507
546
  * SDK's own default handling, so every platform (web, React Native, Expo)
508
547
  * opens them the same way the rest of the SDK does.
509
548
  *
549
+ * MetaMask only ever passes app-scheme deeplinks (`metamask://...`) to this
550
+ * callback, and iOS rejects those whenever it finds no installed handler for the
551
+ * scheme. When opening one fails, the universal-link equivalent is tried before
552
+ * giving up, so the wallet still comes to the foreground and the pending request
553
+ * is not left unanswered.
554
+ *
510
555
  * @example
511
556
  * ```ts
512
557
  * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);
513
- * preferredOpenLink('https://metamask.app.link/...');
558
+ * preferredOpenLink('metamask://connect/mwp?id=...');
514
559
  * ```
515
560
  *
516
561
  * @returns A callback that opens a MetaMask-issued deeplink through the
@@ -522,10 +567,26 @@ const clearMetaMaskSdkSingleton = () => {
522
567
  const createMetaMaskPreferredOpenLink = (client) => (deeplink) => {
523
568
  const { logger, openDeeplink } = (0, _dynamic_labs_sdk_client_core.getCore)(client);
524
569
  logger.debug("[MetaMask:Client] Opening MetaMask deeplink", { deeplink });
525
- openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((error) => logger.error("[MetaMask:Client] Failed to open deeplink", {
526
- deeplink,
527
- error
528
- }));
570
+ openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((appSchemeError) => {
571
+ const universalLink = toMetaMaskUniversalLink({ deeplink });
572
+ if (!universalLink) {
573
+ logger.error("[MetaMask:Client] Failed to open deeplink", {
574
+ deeplink,
575
+ error: appSchemeError
576
+ });
577
+ return;
578
+ }
579
+ logger.debug("[MetaMask:Client] Retrying MetaMask deeplink as a universal link", {
580
+ error: appSchemeError,
581
+ universalLink
582
+ });
583
+ return openDeeplink(universalLink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((universalLinkError) => logger.error("[MetaMask:Client] Failed to open deeplink", {
584
+ deeplink,
585
+ error: appSchemeError,
586
+ universalLink,
587
+ universalLinkError
588
+ }));
589
+ });
529
590
  };
530
591
 
531
592
  //#endregion
@@ -705,4 +766,4 @@ Object.defineProperty(exports, 'version', {
705
766
  return version;
706
767
  }
707
768
  });
708
- //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs.map
769
+ //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-l9Bf9x5m.cjs.map