@dynamic-labs-sdk/metamask 1.31.0 → 1.31.2

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 (127) hide show
  1. package/dist/assertMetaMaskNetworkIsGranted/assertMetaMaskNetworkIsGranted.d.ts +37 -0
  2. package/dist/assertMetaMaskNetworkIsGranted/assertMetaMaskNetworkIsGranted.d.ts.map +1 -0
  3. package/dist/assertMetaMaskNetworkIsGranted/index.d.ts +2 -0
  4. package/dist/assertMetaMaskNetworkIsGranted/index.d.ts.map +1 -0
  5. package/dist/clearMetaMaskSessionStorage/clearMetaMaskSessionStorage.d.ts +6 -6
  6. package/dist/clearMetaMaskSessionStorage/clearMetaMaskSessionStorage.d.ts.map +1 -1
  7. package/dist/connectWithMetaMaskUri/connectWithMetaMaskUri.d.ts +3 -8
  8. package/dist/connectWithMetaMaskUri/connectWithMetaMaskUri.d.ts.map +1 -1
  9. package/dist/constants.d.ts +8 -0
  10. package/dist/constants.d.ts.map +1 -0
  11. package/dist/core.cjs +465 -0
  12. package/dist/core.cjs.map +1 -0
  13. package/dist/core.esm.js +459 -0
  14. package/dist/core.esm.js.map +1 -0
  15. package/dist/core.native.esm.js +459 -0
  16. package/dist/core.native.esm.js.map +1 -0
  17. package/dist/createMetaMaskWalletProviderEventEmitter/createMetaMaskWalletProviderEventEmitter.d.ts +44 -0
  18. package/dist/createMetaMaskWalletProviderEventEmitter/createMetaMaskWalletProviderEventEmitter.d.ts.map +1 -0
  19. package/dist/createMetaMaskWalletProviderEventEmitter/index.d.ts +3 -0
  20. package/dist/createMetaMaskWalletProviderEventEmitter/index.d.ts.map +1 -0
  21. package/dist/disconnectMetaMaskWalletProvider/disconnectMetaMaskWalletProvider.d.ts +56 -0
  22. package/dist/disconnectMetaMaskWalletProvider/disconnectMetaMaskWalletProvider.d.ts.map +1 -0
  23. package/dist/disconnectMetaMaskWalletProvider/index.d.ts +3 -0
  24. package/dist/disconnectMetaMaskWalletProvider/index.d.ts.map +1 -0
  25. package/dist/errors/MetaMaskClientNotInitializedError/MetaMaskClientNotInitializedError.d.ts +12 -0
  26. package/dist/errors/MetaMaskClientNotInitializedError/MetaMaskClientNotInitializedError.d.ts.map +1 -0
  27. package/dist/errors/MetaMaskClientNotInitializedError/index.d.ts +2 -0
  28. package/dist/errors/MetaMaskClientNotInitializedError/index.d.ts.map +1 -0
  29. package/dist/errors/MetaMaskDisplayUriMissingError/MetaMaskDisplayUriMissingError.d.ts +21 -0
  30. package/dist/errors/MetaMaskDisplayUriMissingError/MetaMaskDisplayUriMissingError.d.ts.map +1 -1
  31. package/dist/errors/MetaMaskStorageError/MetaMaskStorageError.d.ts +12 -0
  32. package/dist/errors/MetaMaskStorageError/MetaMaskStorageError.d.ts.map +1 -0
  33. package/dist/errors/MetaMaskStorageError/index.d.ts +2 -0
  34. package/dist/errors/MetaMaskStorageError/index.d.ts.map +1 -0
  35. package/dist/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.d.ts +29 -0
  36. package/dist/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.d.ts.map +1 -0
  37. package/dist/errors/MetaMaskUnscopedNetworkError/index.d.ts +2 -0
  38. package/dist/errors/MetaMaskUnscopedNetworkError/index.d.ts.map +1 -0
  39. package/dist/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.d.ts +9 -0
  40. package/dist/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.d.ts.map +1 -0
  41. package/dist/errors/UnsupportedMetaMaskChainError/index.d.ts +2 -0
  42. package/dist/errors/UnsupportedMetaMaskChainError/index.d.ts.map +1 -0
  43. package/dist/exports/core.d.ts +21 -0
  44. package/dist/exports/core.d.ts.map +1 -0
  45. package/dist/exports/index.d.ts +2 -2
  46. package/dist/exports/index.d.ts.map +1 -1
  47. package/dist/getGrantedMetaMaskScopes/getGrantedMetaMaskScopes.d.ts +40 -0
  48. package/dist/getGrantedMetaMaskScopes/getGrantedMetaMaskScopes.d.ts.map +1 -0
  49. package/dist/getGrantedMetaMaskScopes/index.d.ts +3 -0
  50. package/dist/getGrantedMetaMaskScopes/index.d.ts.map +1 -0
  51. package/dist/getMetaMaskExtensionWalletProviderKey/getMetaMaskExtensionWalletProviderKey.d.ts +1 -1
  52. package/dist/getMetaMaskScopeForNetworkId/getMetaMaskScopeForNetworkId.d.ts +39 -0
  53. package/dist/getMetaMaskScopeForNetworkId/getMetaMaskScopeForNetworkId.d.ts.map +1 -0
  54. package/dist/getMetaMaskScopeForNetworkId/index.d.ts +2 -0
  55. package/dist/getMetaMaskScopeForNetworkId/index.d.ts.map +1 -0
  56. package/dist/getMetaMaskScopes/getMetaMaskScopes.d.ts +28 -0
  57. package/dist/getMetaMaskScopes/getMetaMaskScopes.d.ts.map +1 -0
  58. package/dist/getMetaMaskScopes/index.d.ts +2 -0
  59. package/dist/getMetaMaskScopes/index.d.ts.map +1 -0
  60. package/dist/getMetaMaskSupportedNetworks/getMetaMaskSupportedNetworks.d.ts +23 -0
  61. package/dist/getMetaMaskSupportedNetworks/getMetaMaskSupportedNetworks.d.ts.map +1 -0
  62. package/dist/getMetaMaskSupportedNetworks/index.d.ts +2 -0
  63. package/dist/getMetaMaskSupportedNetworks/index.d.ts.map +1 -0
  64. package/dist/getNetworkIdFromMetaMaskScope/getNetworkIdFromMetaMaskScope.d.ts +35 -0
  65. package/dist/getNetworkIdFromMetaMaskScope/getNetworkIdFromMetaMaskScope.d.ts.map +1 -0
  66. package/dist/getNetworkIdFromMetaMaskScope/index.d.ts +2 -0
  67. package/dist/getNetworkIdFromMetaMaskScope/index.d.ts.map +1 -0
  68. package/dist/getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js +621 -0
  69. package/dist/getOrCreateMetaMaskClientInstanceManager-BLrtpngZ.esm.js.map +1 -0
  70. package/dist/getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs +708 -0
  71. package/dist/getOrCreateMetaMaskClientInstanceManager-C2FCB8yy.cjs.map +1 -0
  72. package/dist/getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js +621 -0
  73. package/dist/getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js.map +1 -0
  74. package/dist/getScopeAccountsFromMetaMaskClient/getScopeAccountsFromMetaMaskClient.d.ts +38 -0
  75. package/dist/getScopeAccountsFromMetaMaskClient/getScopeAccountsFromMetaMaskClient.d.ts.map +1 -0
  76. package/dist/getScopeAccountsFromMetaMaskClient/index.d.ts +2 -0
  77. package/dist/getScopeAccountsFromMetaMaskClient/index.d.ts.map +1 -0
  78. package/dist/index.cjs +57 -57
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.esm.js +56 -56
  81. package/dist/index.esm.js.map +1 -1
  82. package/dist/index.native.esm.js +56 -78
  83. package/dist/index.native.esm.js.map +1 -1
  84. package/dist/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/createMetaMaskClientInstanceManager.d.ts +45 -0
  85. package/dist/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/createMetaMaskClientInstanceManager.d.ts.map +1 -0
  86. package/dist/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/index.d.ts +2 -0
  87. package/dist/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/index.d.ts.map +1 -0
  88. package/dist/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager.d.ts +18 -0
  89. package/dist/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager.d.ts.map +1 -0
  90. package/dist/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/index.d.ts +2 -0
  91. package/dist/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/index.d.ts.map +1 -0
  92. package/dist/metaMaskStorage/clearMetaMaskStorage/clearMetaMaskStorage.d.ts +23 -0
  93. package/dist/metaMaskStorage/clearMetaMaskStorage/clearMetaMaskStorage.d.ts.map +1 -0
  94. package/dist/metaMaskStorage/clearMetaMaskStorage/index.d.ts +2 -0
  95. package/dist/metaMaskStorage/clearMetaMaskStorage/index.d.ts.map +1 -0
  96. package/dist/metaMaskStorage/createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue.d.ts +19 -0
  97. package/dist/metaMaskStorage/createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue.d.ts.map +1 -0
  98. package/dist/metaMaskStorage/createMetaMaskStorageClient/createMetaMaskStorageClient.d.ts +30 -0
  99. package/dist/metaMaskStorage/createMetaMaskStorageClient/createMetaMaskStorageClient.d.ts.map +1 -0
  100. package/dist/metaMaskStorage/createMetaMaskStorageClient/index.d.ts +2 -0
  101. package/dist/metaMaskStorage/createMetaMaskStorageClient/index.d.ts.map +1 -0
  102. package/dist/metaMaskStorage/metaMaskStorage.schema.d.ts +30 -0
  103. package/dist/metaMaskStorage/metaMaskStorage.schema.d.ts.map +1 -0
  104. package/dist/metaMaskStorage/queueMetaMaskStorageWrite/queueMetaMaskStorageWrite.d.ts +20 -0
  105. package/dist/metaMaskStorage/queueMetaMaskStorageWrite/queueMetaMaskStorageWrite.d.ts.map +1 -0
  106. package/dist/metamask.types.d.ts +0 -20
  107. package/dist/metamask.types.d.ts.map +1 -1
  108. package/dist/reconcileMetaMaskWalletProvider/index.d.ts +3 -0
  109. package/dist/reconcileMetaMaskWalletProvider/index.d.ts.map +1 -0
  110. package/dist/reconcileMetaMaskWalletProvider/reconcileMetaMaskWalletProvider.d.ts +60 -0
  111. package/dist/reconcileMetaMaskWalletProvider/reconcileMetaMaskWalletProvider.d.ts.map +1 -0
  112. package/dist/resolveActiveMetaMaskScope/index.d.ts +3 -0
  113. package/dist/resolveActiveMetaMaskScope/index.d.ts.map +1 -0
  114. package/dist/resolveActiveMetaMaskScope/resolveActiveMetaMaskScope.d.ts +67 -0
  115. package/dist/resolveActiveMetaMaskScope/resolveActiveMetaMaskScope.d.ts.map +1 -0
  116. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  117. package/dist/utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton.d.ts +23 -0
  118. package/dist/utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton.d.ts.map +1 -0
  119. package/dist/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.d.ts +21 -0
  120. package/dist/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.d.ts.map +1 -0
  121. package/dist/utils/createMetaMaskPreferredOpenLink/index.d.ts +2 -0
  122. package/dist/utils/createMetaMaskPreferredOpenLink/index.d.ts.map +1 -0
  123. package/package.json +11 -3
  124. package/dist/errors/MetaMaskWalletNotConnectedError/MetaMaskWalletNotConnectedError.d.ts +0 -5
  125. package/dist/errors/MetaMaskWalletNotConnectedError/MetaMaskWalletNotConnectedError.d.ts.map +0 -1
  126. package/dist/errors/MetaMaskWalletNotConnectedError/index.d.ts +0 -2
  127. package/dist/errors/MetaMaskWalletNotConnectedError/index.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.cjs","names":["getMetaMaskScopeForNetworkId","MetaMaskUnscopedNetworkError","handleDisconnected: () => void","handleNetworkChanged: (params: { networkId: string }) => void","getOrCreateMetaMaskClientInstanceManager","getMetaMaskScopes","clearMetaMaskStorage","WalletProviderPriority","UnsupportedMetaMaskChainError","SUPPORTED_METAMASK_CHAINS","getMetaMaskScopeForNetworkId","getMetaMaskScopes","name","version"],"sources":["../src/getScopeAccountsFromMetaMaskClient/getScopeAccountsFromMetaMaskClient.ts","../src/assertMetaMaskNetworkIsGranted/assertMetaMaskNetworkIsGranted.ts","../src/createMetaMaskWalletProviderEventEmitter/createMetaMaskWalletProviderEventEmitter.ts","../src/disconnectMetaMaskWalletProvider/disconnectMetaMaskWalletProvider.ts","../src/reconcileMetaMaskWalletProvider/reconcileMetaMaskWalletProvider.ts","../src/getGrantedMetaMaskScopes/getGrantedMetaMaskScopes.ts","../src/getNetworkIdFromMetaMaskScope/getNetworkIdFromMetaMaskScope.ts","../src/resolveActiveMetaMaskScope/resolveActiveMetaMaskScope.ts","../src/exports/core.ts"],"sourcesContent":["import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\ntype Session = Awaited<ReturnType<MultichainCore['provider']['getSession']>>;\ntype SessionScopes = NonNullable<Session>['sessionScopes'];\ntype ScopeObject = SessionScopes[keyof SessionScopes];\ntype ScopeAccounts = NonNullable<ScopeObject['accounts']>;\n\nexport type GetScopeAccountsFromMetaMaskClientParams = {\n metaMaskClient: MultichainCore;\n scope: Parameters<MultichainCore['connect']>[0][number];\n};\n\n/**\n * Reads the accounts MetaMask granted for a single CAIP-2 scope out of the\n * current session, replacing direct `provider.getSession()` access so chain\n * packages never touch the raw session shape. Empty when there's no session\n * or the scope wasn't granted.\n *\n * @example\n * ```ts\n * const accounts = await getScopeAccountsFromMetaMaskClient(\n * { metaMaskClient, scope: 'eip155:1' },\n * client\n * );\n * // ['eip155:1:0x...']\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so a missing/empty-scope result is always traceable to the client that\n * requested it.\n * @returns The accounts granted for `scope`, or an empty array if there's no\n * session or the scope wasn't granted.\n * @see createMetaMaskMultichainClient\n * @see getGrantedMetaMaskScopes\n * @notInstrumented\n */\nexport const getScopeAccountsFromMetaMaskClient = async (\n { metaMaskClient, scope }: GetScopeAccountsFromMetaMaskClientParams,\n client: DynamicClient\n): Promise<ScopeAccounts> => {\n const session = await metaMaskClient.provider.getSession();\n const accounts = session?.sessionScopes[scope]?.accounts ?? [];\n\n // Account addresses are PII -- log only the count, never the accounts themselves.\n getCore(client).logger.debug('[MetaMask:Scopes] Resolved scope accounts', {\n accountCount: accounts.length,\n hasSession: session != null,\n scope,\n });\n\n return accounts;\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\nimport { MetaMaskUnscopedNetworkError } from '../errors/MetaMaskUnscopedNetworkError';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\nimport { getScopeAccountsFromMetaMaskClient } from '../getScopeAccountsFromMetaMaskClient';\n\nexport type AssertMetaMaskNetworkIsGrantedParams = {\n chain: Chain;\n metaMaskClient: MultichainCore;\n networkId: string;\n};\n\n/**\n * Guards a MetaMask wallet provider's `switchActiveNetwork` against ever\n * pointing at a network MetaMask didn't actually grant an account for.\n * Every configured network is requested as an *optional* CAIP-25 scope (see\n * `getMetaMaskScopes`), so MetaMask may silently approve only a subset --\n * switching the active network to one outside that subset would leave every\n * subsequent signing/sending call targeting a scope the wallet will reject.\n *\n * @example\n * ```ts\n * await assertMetaMaskNetworkIsGranted(\n * { chain: 'SOL', metaMaskClient, networkId: '103' },\n * client\n * );\n * // Throws MetaMaskUnscopedNetworkError if MetaMask never granted an\n * // account for network \"103\" in the current session.\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against.\n * @returns A promise that resolves once the target network is confirmed granted.\n * @throws {MetaMaskUnscopedNetworkError} If `networkId` has no granted\n * account in the current session (including when it has no configured\n * MetaMask scope at all).\n * @see resolveActiveMetaMaskScope\n * @see MetaMaskUnscopedNetworkError\n * @notInstrumented\n */\nexport const assertMetaMaskNetworkIsGranted = async (\n { chain, metaMaskClient, networkId }: AssertMetaMaskNetworkIsGrantedParams,\n client: DynamicClient\n): Promise<void> => {\n const { logger } = getCore(client);\n const scope = getMetaMaskScopeForNetworkId({ chain, networkId }, client);\n const accounts = scope\n ? await getScopeAccountsFromMetaMaskClient(\n { metaMaskClient, scope },\n client\n )\n : [];\n\n logger.debug('[MetaMask:Scopes] Checked whether target network is granted', {\n accountCount: accounts.length,\n chain,\n networkId,\n scope,\n });\n\n if (accounts.length === 0) {\n logger.warn('[MetaMask:Scopes] Rejected switch to an unscoped network', {\n chain,\n networkId,\n scope,\n });\n\n throw new MetaMaskUnscopedNetworkError({\n networkId,\n scope: scope ?? 'unknown',\n });\n }\n};\n","import {\n type WalletProviderEventEmitter,\n createWalletProviderEventEmitter,\n} from '@dynamic-labs-sdk/client/core';\n\n/**\n * The `WalletProviderEventEmitter` returned by\n * `createMetaMaskWalletProviderEventEmitter`, plus `emitNetworkChanged`/\n * `emitDisconnected` methods that MetaMask's EVM/Solana wallet providers call\n * directly from their own `switchActiveNetwork`/`disconnectWalletAccount` --\n * MetaMask's multichain session never fires a live wallet event either of\n * those could otherwise bridge from.\n *\n * @example\n * ```ts\n * const metaMaskEvents = createMetaMaskWalletProviderEventEmitter();\n * metaMaskEvents.emitNetworkChanged({ networkId: '137' });\n * ```\n * @see createMetaMaskWalletProviderEventEmitter\n * @see createWalletProviderEventEmitter\n */\nexport type MetaMaskWalletProviderEventEmitter = WalletProviderEventEmitter & {\n emitDisconnected: () => void;\n emitNetworkChanged: (params: { networkId: string }) => void;\n};\n\n/**\n * MetaMask's multichain session never triggers `createWalletProviderEventEmitter`'s\n * usual external-listener flow -- there is no live wallet event to bridge, only our\n * own local actions (e.g. `switchActiveNetwork`) that need to notify subscribers\n * themselves. `setupEventListeners` here just captures the handler references so\n * they can be invoked directly, instead of from a real event source.\n *\n * @example\n * ```ts\n * const metaMaskEvents = createMetaMaskWalletProviderEventEmitter();\n * metaMaskEvents.getEventEmitter().on('networkChanged', ({ networkId }) => {});\n * metaMaskEvents.emitNetworkChanged({ networkId: '137' });\n * ```\n * @returns A `MetaMaskWalletProviderEventEmitter` ready to emit synthetic\n * `networkChanged`/`disconnected` events.\n * @see MetaMaskWalletProviderEventEmitter\n * @see createWalletProviderEventEmitter\n * @notInstrumented\n */\nexport const createMetaMaskWalletProviderEventEmitter =\n (): MetaMaskWalletProviderEventEmitter => {\n let handleDisconnected: () => void;\n let handleNetworkChanged: (params: { networkId: string }) => void;\n\n const emitter = createWalletProviderEventEmitter({\n removeEventListeners: () => {},\n setupEventListeners: (handlers) => {\n handleDisconnected = handlers.handleDisconnected;\n handleNetworkChanged = handlers.handleNetworkChanged;\n },\n // `accountsChanged` is excluded: nothing wires it yet -- MetaMask's real\n // `metamask_accountsChanged` event exists but isn't bridged here (future work).\n supportedEvents: ['disconnected', 'networkChanged'],\n });\n\n // Captures the handlers eagerly, since nothing external ever calls\n // getEventEmitter() first the way a real wallet-event bridge would.\n emitter.getEventEmitter();\n\n return {\n ...emitter,\n emitDisconnected: () => handleDisconnected(),\n emitNetworkChanged: (params) => handleNetworkChanged(params),\n };\n };\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport {\n getCore,\n getWalletProviderRegistry,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { getMetaMaskScopes } from '../getMetaMaskScopes';\nimport { getOrCreateMetaMaskClientInstanceManager } from '../metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager';\nimport { clearMetaMaskStorage } from '../metaMaskStorage/clearMetaMaskStorage';\n\n/**\n * Params for {@link disconnectMetaMaskWalletProvider}, identifying which\n * chain's scopes to drop from the shared MetaMask session and which\n * chain-specific wallet provider to unregister alongside it.\n *\n * @example\n * ```ts\n * const params: DisconnectMetaMaskWalletProviderParams = {\n * chain: 'EVM',\n * walletProviderKey: 'metamask-evm',\n * };\n * ```\n * @see disconnectMetaMaskWalletProvider\n * @see getMetaMaskScopes\n */\nexport type DisconnectMetaMaskWalletProviderParams = {\n /** Chain of the wallet account being disconnected; scopes this chain's own scopes out of the shared session. */\n chain: Chain;\n /** Key of the chain-specific wallet provider to unregister. */\n walletProviderKey: string;\n};\n\n/**\n * Shared `disconnectWalletAccount` for every chain's MetaMask wallet provider\n * (EVM, Solana, ...). `getOrCreateMetaMaskClientInstanceManager` hands every chain\n * the SAME client instance -- one multichain session shared across chains --\n * so this only asks MetaMask to drop this chain's own scopes rather than\n * tearing the whole session down under a still-connected chain.\n *\n * `disconnect`'s teardown in `@metamask/connect-multichain` is itself\n * scope-aware: it only actually ends the shared session once every chain's\n * scopes are gone, reflected by `metaMaskClient.status` flipping to\n * `'disconnected'`. Only then do we clear the persisted session and evict\n * the cached client, so a chain that's still connected is left untouched.\n *\n * @example\n * ```ts\n * await disconnectMetaMaskWalletProvider(\n * { chain: 'EVM', walletProviderKey: 'metamask-evm' },\n * client\n * );\n * ```\n * @param params.chain - Chain of the wallet account being disconnected.\n * @param params.walletProviderKey - Key of the chain-specific wallet provider to unregister.\n * @param client - The Dynamic client instance. Required (rather than\n * defaulting to the last-initialized client) since this always runs from\n * inside an already-resolved chain wallet provider, which already has one.\n * @returns A promise that resolves once local cleanup is done. Never rejects\n * on the remote signal failing -- that's logged, not surfaced, since the\n * wallet account is being removed either way.\n * @see clearMetaMaskStorage\n * @see getMetaMaskScopes\n * @notInstrumented\n */\nexport const disconnectMetaMaskWalletProvider = async (\n { chain, walletProviderKey }: DisconnectMetaMaskWalletProviderParams,\n client: DynamicClient\n): Promise<void> => {\n const { logger } = getCore(client);\n const metaMaskClient = await getOrCreateMetaMaskClientInstanceManager(\n client\n ).getMetaMaskClient();\n const scopes = getMetaMaskScopes({ chain }, client);\n\n logger.debug('[MetaMask:Disconnect] Disconnecting wallet account', {\n chain,\n scopes,\n walletProviderKey,\n });\n\n try {\n await metaMaskClient.disconnect(scopes);\n } catch (error) {\n logger.error('[MetaMask:Disconnect] Remote signal failed', {\n error,\n walletProviderKey,\n });\n }\n\n getWalletProviderRegistry(client).unregister(walletProviderKey);\n\n logger.debug('[MetaMask:Disconnect] Shared session status after disconnect', {\n status: metaMaskClient.status,\n walletProviderKey,\n });\n\n if (metaMaskClient.status === 'disconnected') {\n await clearMetaMaskStorage(client);\n }\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getWalletAccounts, onEvent } from '@dynamic-labs-sdk/client';\nimport type { WalletProvider } from '@dynamic-labs-sdk/client/core';\nimport {\n WalletProviderPriority,\n getDefaultClient,\n getWalletProviderRegistry,\n} from '@dynamic-labs-sdk/client/core';\n\n/**\n * Params for {@link reconcileMetaMaskWalletProvider}.\n *\n * @example\n * ```ts\n * const params: ReconcileMetaMaskWalletProviderParams = {\n * walletProviderBuilder: createMetaMaskEvmWalletProvider,\n * walletProviderKey: getMetaMaskEvmSdkWalletProviderKey(),\n * };\n * ```\n * @see reconcileMetaMaskWalletProvider\n * @see WalletProvider\n */\nexport type ReconcileMetaMaskWalletProviderParams = {\n /**\n * Builds the connect-less SDK placeholder wallet provider (e.g.\n * `createMetaMaskEvmWalletProvider`). Only invoked once reconciliation\n * determines the placeholder must actually be registered, so a chain\n * extension never builds it just for being added.\n */\n walletProviderBuilder: (client: DynamicClient) => WalletProvider;\n /**\n * The placeholder's deterministic wallet provider key, computed without\n * building it (e.g. `getMetaMaskEvmSdkWalletProviderKey`).\n */\n walletProviderKey: string;\n};\n\n/**\n * Keeps a chain's MetaMask SDK wallet provider registered for as\n * long as the user has a wallet account under its exact key, and\n * unregisters it once that wallet account disappears (e.g. on disconnect).\n * This is needed because always adding the MetaMask SDK wallet provider\n * would conflict with the browser extension wallet provider, so we only add\n * it once the user has a wallet account that was connected via QR code.\n * The placeholder is only built (via `walletProviderBuilder`) the first time\n * it must be registered, mirroring how WalletConnect only builds a wallet\n * provider for a session that actually exists.\n *\n * @example\n * ```ts\n * reconcileMetaMaskWalletProvider(\n * {\n * walletProviderBuilder: createMetaMaskEvmWalletProvider,\n * walletProviderKey: getMetaMaskEvmSdkWalletProviderKey(),\n * },\n * client\n * );\n * ```\n * @param params.walletProviderBuilder - Builds the SDK placeholder wallet provider once it must be registered.\n * @param params.walletProviderKey - The placeholder's deterministic wallet provider key.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns void\n * @see connectWithMetaMaskUri\n * @see WalletProviderPriority\n * @notInstrumented\n */\nexport const reconcileMetaMaskWalletProvider = (\n { walletProviderBuilder, walletProviderKey }: ReconcileMetaMaskWalletProviderParams,\n client: DynamicClient = getDefaultClient()\n): void => {\n const walletProviderRegistry = getWalletProviderRegistry(client);\n\n const reconcile = (): void => {\n const isSdkPlaceholderRegistered =\n walletProviderRegistry.getByKey(walletProviderKey) !== undefined;\n const shouldBeRegistered = getWalletAccounts(client).some(\n (walletAccount) => walletAccount.walletProviderKey === walletProviderKey\n );\n\n if (isSdkPlaceholderRegistered && !shouldBeRegistered) {\n walletProviderRegistry.unregister(walletProviderKey);\n return;\n }\n\n if (!isSdkPlaceholderRegistered && shouldBeRegistered) {\n walletProviderRegistry.register({\n priority: WalletProviderPriority.WALLET_SDK,\n walletProvider: walletProviderBuilder(client),\n });\n }\n };\n\n onEvent(\n { event: 'walletAccountsChanged', listener: () => reconcile() },\n client\n );\n\n reconcile();\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\ntype Session = Awaited<ReturnType<MultichainCore['provider']['getSession']>>;\ntype SessionScopes = NonNullable<Session>['sessionScopes'];\ntype ScopeKey = keyof SessionScopes;\ntype ScopeAccounts = NonNullable<SessionScopes[ScopeKey]['accounts']>;\n\nexport type GrantedMetaMaskScope = {\n accounts: ScopeAccounts;\n scope: ScopeKey;\n};\n\nexport type GetGrantedMetaMaskScopesParams = {\n metaMaskClient: MultichainCore;\n};\n\n/**\n * Enumerates every CAIP-2 scope in the current session that MetaMask\n * actually granted at least one account for. Every scope requested at\n * `connect()` time (see `getMetaMaskScopes`) is sent as an *optional*\n * CAIP-25 scope, never a required one, so MetaMask is free to approve only a\n * subset -- `session.sessionScopes` still has a key for every requested\n * scope, just with an empty `accounts` array for the ones that weren't\n * granted. This filters those out, replacing direct `provider.getSession()`\n * access so chain packages never touch the raw session shape.\n *\n * @example\n * ```ts\n * const granted = await getGrantedMetaMaskScopes({ metaMaskClient }, client);\n * // [{ scope: 'eip155:1', accounts: ['eip155:1:0x...'] }]\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so an empty result is always traceable to the client that requested it.\n * @returns The scopes with at least one granted account, and their accounts.\n * @see createMetaMaskMultichainClient\n * @see resolveActiveMetaMaskScope\n * @notInstrumented\n */\nexport const getGrantedMetaMaskScopes = async (\n { metaMaskClient }: GetGrantedMetaMaskScopesParams,\n client: DynamicClient\n): Promise<GrantedMetaMaskScope[]> => {\n const session = await metaMaskClient.provider.getSession();\n const sessionScopes = session?.sessionScopes ?? ({} as SessionScopes);\n\n const grantedScopes = (\n Object.entries(sessionScopes) as [ScopeKey, SessionScopes[ScopeKey]][]\n )\n .filter(([, scopeData]) => (scopeData.accounts ?? []).length > 0)\n .map(([scope, scopeData]) => ({\n accounts: scopeData.accounts ?? [],\n scope,\n }));\n\n // Granted scopes carry account addresses, which are PII -- log only counts.\n getCore(client).logger.debug('[MetaMask:Scopes] Resolved granted scopes', {\n grantedScopeCount: grantedScopes.length,\n hasSession: session != null,\n });\n\n return grantedScopes;\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\n\nexport type GetNetworkIdFromMetaMaskScopeParams = {\n chain: Chain;\n scope: string;\n};\n\n/**\n * Reverse of `getMetaMaskScopeForNetworkId`: maps a granted CAIP-2 scope\n * back to the Dynamic `networkId` it came from, for the given chain.\n *\n * Returns `undefined` -- not a throw -- when `scope` doesn't belong to\n * `chain` (e.g. an `eip155:` scope while scanning for `SOL`). For `SOL`,\n * this also covers a `solana:` scope matching no configured network. This\n * is the normal, expected outcome when scanning a session's granted scopes\n * for a single chain: a MetaMask multichain session is shared across every\n * connected chain\n *\n * @example\n * ```ts\n * const networkId = getNetworkIdFromMetaMaskScope(\n * { chain: 'EVM', scope: 'eip155:1' },\n * client\n * );\n * // '1'\n * ```\n *\n * @returns The matching Dynamic `networkId`, or `undefined` if `scope`\n * doesn't belong to `chain` (or, for `SOL`, matches no configured network).\n * @see getMetaMaskScopeForNetworkId\n * @see resolveActiveMetaMaskScope\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @notInstrumented\n */\nexport const getNetworkIdFromMetaMaskScope = (\n { chain, scope }: GetNetworkIdFromMetaMaskScopeParams,\n client: DynamicClient\n): string | undefined => {\n const [namespace, reference] = scope.split(':');\n\n if (chain === 'EVM') {\n return namespace === 'eip155' ? reference : undefined;\n }\n\n if (chain === 'SOL') {\n if (namespace !== 'solana') {\n return undefined;\n }\n\n return getNetworksData(client).find(\n (candidate) =>\n candidate.chain === 'SOL' && candidate.genesisHash === reference\n )?.networkId;\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 {\n getActiveNetworkIdFromLastKnownRegistry,\n getCore,\n getDefaultClient,\n switchActiveNetworkInLastKnownRegistry,\n} from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\nimport { getGrantedMetaMaskScopes } from '../getGrantedMetaMaskScopes';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\nimport { getMetaMaskScopes } from '../getMetaMaskScopes';\nimport { getNetworkIdFromMetaMaskScope } from '../getNetworkIdFromMetaMaskScope';\nimport { getScopeAccountsFromMetaMaskClient } from '../getScopeAccountsFromMetaMaskClient';\n\nexport type ResolveActiveMetaMaskScopeParams = {\n chain: Chain;\n metaMaskClient: MultichainCore;\n walletProviderKey: string;\n};\n\n/**\n * The network resolved as active for a MetaMask wallet provider, and the\n * CAIP-10 accounts MetaMask granted for it -- returned by\n * `resolveActiveMetaMaskScope` so callers don't need to re-derive the scope\n * or re-read the session themselves.\n *\n * @example\n * ```ts\n * const { accounts, networkId } = await resolveActiveMetaMaskScope(\n * { chain: 'SOL', metaMaskClient, walletProviderKey },\n * client\n * );\n * ```\n * @see resolveActiveMetaMaskScope\n * @see getGrantedMetaMaskScopes\n */\nexport type ResolveActiveMetaMaskScopeResult = {\n /** CAIP-10 account ids -- same shape as `metaMaskClient.getScopeAccounts` returns. */\n accounts: string[];\n networkId: string;\n scope: string | undefined;\n};\n\n/**\n * Resolves the network a MetaMask wallet provider should treat as active,\n * self-healing when the last-known-active network (see\n * `getActiveNetworkIdFromLastKnownRegistry`) turns out to have no granted\n * account.\n *\n * Every configured network is requested as an *optional* CAIP-25 scope (see\n * `getMetaMaskScopes`), so MetaMask is free to grant an account for only a\n * subset -- and the registry's own default (the first configured network for\n * the chain) has no awareness of which subset that was. If the active\n * network's scope is empty, this scans every OTHER scope this chain\n * requested, restricted to the ones MetaMask actually granted, and adopts\n * whichever is found (preferring a non-testnet network) as the new active\n * network -- persisting the correction so future calls don't repeat the\n * scan. If nothing was granted for this chain at all, the original\n * (accounts: []) result is returned unchanged, preserving the existing\n * \"no address found\" behavior when there's truly nothing to fall back to.\n *\n * @example\n * ```ts\n * const { accounts } = await resolveActiveMetaMaskScope(\n * { chain: 'SOL', metaMaskClient, walletProviderKey },\n * client\n * );\n * // accounts is non-empty even if the registry's default network wasn't\n * // the one MetaMask actually granted.\n * ```\n *\n * @param [client] - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so a fallback decision is always traceable to the client that requested it.\n * @returns The resolved active network's id, CAIP-2 scope, and granted accounts.\n * @see assertMetaMaskNetworkIsGranted\n * @see getGrantedMetaMaskScopes\n * @notInstrumented\n */\nexport const resolveActiveMetaMaskScope = async (\n {\n chain,\n metaMaskClient,\n walletProviderKey,\n }: ResolveActiveMetaMaskScopeParams,\n client: DynamicClient = getDefaultClient()\n): Promise<ResolveActiveMetaMaskScopeResult> => {\n const { logger } = getCore(client);\n\n const { networkId: activeNetworkId } =\n await getActiveNetworkIdFromLastKnownRegistry({\n client,\n walletProviderKey,\n });\n const activeScope = getMetaMaskScopeForNetworkId(\n { chain, networkId: activeNetworkId },\n client\n );\n const activeAccounts = activeScope\n ? await getScopeAccountsFromMetaMaskClient(\n { metaMaskClient, scope: activeScope },\n client\n )\n : [];\n\n if (activeAccounts.length > 0) {\n logger.debug(\n '[MetaMask:Scopes] Active network scope already has granted accounts',\n { activeNetworkId, chain, scope: activeScope }\n );\n\n return {\n accounts: activeAccounts,\n networkId: activeNetworkId,\n scope: activeScope,\n };\n }\n\n logger.debug(\n '[MetaMask:Scopes] Active network scope has no granted accounts, scanning other requested scopes for this chain',\n { activeNetworkId, chain, scope: activeScope }\n );\n\n const requestedScopes = new Set<string>(getMetaMaskScopes({ chain }, client));\n const grantedScopes = await getGrantedMetaMaskScopes(\n {\n metaMaskClient,\n },\n client\n );\n\n // Both the testnet flag and the fallback's deterministic order come from\n // Dynamic's own configured network list, never MetaMask's -- the granted\n // scopes returned by the SDK are keyed off its own session object, whose\n // enumeration order is an implementation detail, not something to rely on\n // when multiple candidates are equally eligible.\n const configuredNetworks = getNetworksData(client).filter(\n (network) => network.chain === chain\n );\n const testnetByNetworkId = new Map(\n configuredNetworks.map((network) => [network.networkId, network.testnet])\n );\n const configuredOrderByNetworkId = new Map(\n configuredNetworks.map((network, index) => [network.networkId, index])\n );\n\n const candidates = grantedScopes\n .filter(({ scope }) => requestedScopes.has(scope))\n .flatMap(({ accounts, scope }) => {\n const networkId = getNetworkIdFromMetaMaskScope({ chain, scope }, client);\n\n return networkId ? [{ accounts, networkId, scope }] : [];\n })\n .sort(\n (a, b) =>\n (configuredOrderByNetworkId.get(a.networkId) ?? 0) -\n (configuredOrderByNetworkId.get(b.networkId) ?? 0)\n );\n\n const fallback =\n candidates.find(({ networkId }) => !testnetByNetworkId.get(networkId)) ??\n candidates[0];\n\n if (fallback === undefined) {\n logger.debug(\n '[MetaMask:Scopes] No granted scope with accounts found for chain',\n { activeNetworkId, chain, requestedScopes: [...requestedScopes] }\n );\n\n return {\n accounts: activeAccounts,\n networkId: activeNetworkId,\n scope: activeScope,\n };\n }\n\n logger.debug(\n '[MetaMask:Scopes] Falling back to a granted scope after the active scope had no accounts',\n {\n activeNetworkId,\n activeScope,\n chain,\n fallbackNetworkId: fallback.networkId,\n fallbackScope: fallback.scope,\n }\n );\n\n await switchActiveNetworkInLastKnownRegistry({\n client,\n networkId: fallback.networkId,\n walletProviderKey,\n });\n\n return {\n accounts: fallback.accounts,\n networkId: fallback.networkId,\n scope: fallback.scope,\n };\n};\n","import { assertPackageVersion } from '@dynamic-labs-sdk/assert-package-version';\n\nimport { name, version } from '../../package.json';\n\nassertPackageVersion(name, version);\n\n/**\n * @categoryDescription Extension Development\n * Core utilities and functions for building MetaMask SDK extensions.\n * These APIs provide low-level access to the SDK internals and are intended\n * for advanced use cases and extension development. Most applications should\n * use the higher-level APIs exported from the main SDK package instead.\n */\n\n/** @category Extension Development */\nexport { assertMetaMaskNetworkIsGranted } from '../assertMetaMaskNetworkIsGranted';\nexport { createMetaMaskWalletProviderEventEmitter } from '../createMetaMaskWalletProviderEventEmitter';\nexport type { MetaMaskWalletProviderEventEmitter } from '../createMetaMaskWalletProviderEventEmitter';\nexport { disconnectMetaMaskWalletProvider } from '../disconnectMetaMaskWalletProvider';\nexport type { DisconnectMetaMaskWalletProviderParams } from '../disconnectMetaMaskWalletProvider';\n/** @category Extension Development */\nexport { getOrCreateMetaMaskClientInstanceManager } from '../metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager';\nexport { clearMetaMaskStorage } from '../metaMaskStorage/clearMetaMaskStorage';\nexport { reconcileMetaMaskWalletProvider } from '../reconcileMetaMaskWalletProvider';\nexport type { ReconcileMetaMaskWalletProviderParams } from '../reconcileMetaMaskWalletProvider';\nexport { resolveActiveMetaMaskScope } from '../resolveActiveMetaMaskScope';\nexport type { ResolveActiveMetaMaskScopeResult } from '../resolveActiveMetaMaskScope';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAa,qCAAqC,OAChD,EAAE,gBAAgB,SAClB,WAC2B;CAC3B,MAAM,UAAU,MAAM,eAAe,SAAS,YAAY;CAC1D,MAAM,WAAW,SAAS,cAAc,QAAQ,YAAY,EAAE;AAG9D,4CAAQ,OAAO,CAAC,OAAO,MAAM,6CAA6C;EACxE,cAAc,SAAS;EACvB,YAAY,WAAW;EACvB;EACD,CAAC;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACXT,MAAa,iCAAiC,OAC5C,EAAE,OAAO,gBAAgB,aACzB,WACkB;CAClB,MAAM,EAAE,sDAAmB,OAAO;CAClC,MAAM,QAAQA,8EAA6B;EAAE;EAAO;EAAW,EAAE,OAAO;CACxE,MAAM,WAAW,QACb,MAAM,mCACJ;EAAE;EAAgB;EAAO,EACzB,OACD,GACD,EAAE;AAEN,QAAO,MAAM,+DAA+D;EAC1E,cAAc,SAAS;EACvB;EACA;EACA;EACD,CAAC;AAEF,KAAI,SAAS,WAAW,GAAG;AACzB,SAAO,KAAK,4DAA4D;GACtE;GACA;GACA;GACD,CAAC;AAEF,QAAM,IAAIC,8EAA6B;GACrC;GACA,OAAO,SAAS;GACjB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AC3BN,MAAa,iDAC+B;CACxC,IAAIC;CACJ,IAAIC;CAEJ,MAAM,8EAA2C;EAC/C,4BAA4B;EAC5B,sBAAsB,aAAa;AACjC,wBAAqB,SAAS;AAC9B,0BAAuB,SAAS;;EAIlC,iBAAiB,CAAC,gBAAgB,iBAAiB;EACpD,CAAC;AAIF,SAAQ,iBAAiB;AAEzB,QAAO;EACL,GAAG;EACH,wBAAwB,oBAAoB;EAC5C,qBAAqB,WAAW,qBAAqB,OAAO;EAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLL,MAAa,mCAAmC,OAC9C,EAAE,OAAO,qBACT,WACkB;CAClB,MAAM,EAAE,sDAAmB,OAAO;CAClC,MAAM,iBAAiB,MAAMC,0FAC3B,OACD,CAAC,mBAAmB;CACrB,MAAM,SAASC,mEAAkB,EAAE,OAAO,EAAE,OAAO;AAEnD,QAAO,MAAM,sDAAsD;EACjE;EACA;EACA;EACD,CAAC;AAEF,KAAI;AACF,QAAM,eAAe,WAAW,OAAO;UAChC,OAAO;AACd,SAAO,MAAM,8CAA8C;GACzD;GACA;GACD,CAAC;;AAGJ,8DAA0B,OAAO,CAAC,WAAW,kBAAkB;AAE/D,QAAO,MAAM,gEAAgE;EAC3E,QAAQ,eAAe;EACvB;EACD,CAAC;AAEF,KAAI,eAAe,WAAW,eAC5B,OAAMC,sEAAqB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BtC,MAAa,mCACX,EAAE,uBAAuB,qBACzB,8DAA0C,KACjC;CACT,MAAM,sFAAmD,OAAO;CAEhE,MAAM,kBAAwB;EAC5B,MAAM,6BACJ,uBAAuB,SAAS,kBAAkB,KAAK;EACzD,MAAM,qEAAuC,OAAO,CAAC,MAClD,kBAAkB,cAAc,sBAAsB,kBACxD;AAED,MAAI,8BAA8B,CAAC,oBAAoB;AACrD,0BAAuB,WAAW,kBAAkB;AACpD;;AAGF,MAAI,CAAC,8BAA8B,mBACjC,wBAAuB,SAAS;GAC9B,UAAUC,qDAAuB;GACjC,gBAAgB,sBAAsB,OAAO;GAC9C,CAAC;;AAIN,uCACE;EAAE,OAAO;EAAyB,gBAAgB,WAAW;EAAE,EAC/D,OACD;AAED,YAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvDb,MAAa,2BAA2B,OACtC,EAAE,kBACF,WACoC;CACpC,MAAM,UAAU,MAAM,eAAe,SAAS,YAAY;CAC1D,MAAM,gBAAgB,SAAS,iBAAkB,EAAE;CAEnD,MAAM,gBACJ,OAAO,QAAQ,cAAc,CAE5B,QAAQ,GAAG,gBAAgB,UAAU,YAAY,EAAE,EAAE,SAAS,EAAE,CAChE,KAAK,CAAC,OAAO,gBAAgB;EAC5B,UAAU,UAAU,YAAY,EAAE;EAClC;EACD,EAAE;AAGL,4CAAQ,OAAO,CAAC,OAAO,MAAM,6CAA6C;EACxE,mBAAmB,cAAc;EACjC,YAAY,WAAW;EACxB,CAAC;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzBT,MAAa,iCACX,EAAE,OAAO,SACT,WACuB;CACvB,MAAM,CAAC,WAAW,aAAa,MAAM,MAAM,IAAI;AAE/C,KAAI,UAAU,MACZ,QAAO,cAAc,WAAW,YAAY;AAG9C,KAAI,UAAU,OAAO;AACnB,MAAI,cAAc,SAChB;AAGF,uDAAuB,OAAO,CAAC,MAC5B,cACC,UAAU,UAAU,SAAS,UAAU,gBAAgB,UAC1D,EAAE;;AAGL,OAAM,IAAIC,+EAA8B;EACtC;EACA,iBAAiBC;EAClB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACkBJ,MAAa,6BAA6B,OACxC,EACE,OACA,gBACA,qBAEF,8DAA0C,KACI;CAC9C,MAAM,EAAE,sDAAmB,OAAO;CAElC,MAAM,EAAE,WAAW,oBACjB,iFAA8C;EAC5C;EACA;EACD,CAAC;CACJ,MAAM,cAAcC,8EAClB;EAAE;EAAO,WAAW;EAAiB,EACrC,OACD;CACD,MAAM,iBAAiB,cACnB,MAAM,mCACJ;EAAE;EAAgB,OAAO;EAAa,EACtC,OACD,GACD,EAAE;AAEN,KAAI,eAAe,SAAS,GAAG;AAC7B,SAAO,MACL,uEACA;GAAE;GAAiB;GAAO,OAAO;GAAa,CAC/C;AAED,SAAO;GACL,UAAU;GACV,WAAW;GACX,OAAO;GACR;;AAGH,QAAO,MACL,kHACA;EAAE;EAAiB;EAAO,OAAO;EAAa,CAC/C;CAED,MAAM,kBAAkB,IAAI,IAAYC,mEAAkB,EAAE,OAAO,EAAE,OAAO,CAAC;CAC7E,MAAM,gBAAgB,MAAM,yBAC1B,EACE,gBACD,EACD,OACD;CAOD,MAAM,mEAAqC,OAAO,CAAC,QAChD,YAAY,QAAQ,UAAU,MAChC;CACD,MAAM,qBAAqB,IAAI,IAC7B,mBAAmB,KAAK,YAAY,CAAC,QAAQ,WAAW,QAAQ,QAAQ,CAAC,CAC1E;CACD,MAAM,6BAA6B,IAAI,IACrC,mBAAmB,KAAK,SAAS,UAAU,CAAC,QAAQ,WAAW,MAAM,CAAC,CACvE;CAED,MAAM,aAAa,cAChB,QAAQ,EAAE,YAAY,gBAAgB,IAAI,MAAM,CAAC,CACjD,SAAS,EAAE,UAAU,YAAY;EAChC,MAAM,YAAY,8BAA8B;GAAE;GAAO;GAAO,EAAE,OAAO;AAEzE,SAAO,YAAY,CAAC;GAAE;GAAU;GAAW;GAAO,CAAC,GAAG,EAAE;GACxD,CACD,MACE,GAAG,OACD,2BAA2B,IAAI,EAAE,UAAU,IAAI,MAC/C,2BAA2B,IAAI,EAAE,UAAU,IAAI,GACnD;CAEH,MAAM,WACJ,WAAW,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,IAAI,UAAU,CAAC,IACtE,WAAW;AAEb,KAAI,aAAa,QAAW;AAC1B,SAAO,MACL,oEACA;GAAE;GAAiB;GAAO,iBAAiB,CAAC,GAAG,gBAAgB;GAAE,CAClE;AAED,SAAO;GACL,UAAU;GACV,WAAW;GACX,OAAO;GACR;;AAGH,QAAO,MACL,4FACA;EACE;EACA;EACA;EACA,mBAAmB,SAAS;EAC5B,eAAe,SAAS;EACzB,CACF;AAED,iFAA6C;EAC3C;EACA,WAAW,SAAS;EACpB;EACD,CAAC;AAEF,QAAO;EACL,UAAU,SAAS;EACnB,WAAW,SAAS;EACpB,OAAO,SAAS;EACjB;;;;;mECnMkBC,uDAAMC,yDAAQ"}
@@ -0,0 +1,459 @@
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";
2
+ import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
3
+ import { WalletProviderPriority, createWalletProviderEventEmitter, getActiveNetworkIdFromLastKnownRegistry, getCore, getDefaultClient, getWalletProviderRegistry, switchActiveNetworkInLastKnownRegistry } from "@dynamic-labs-sdk/client/core";
4
+ import { getNetworksData, getWalletAccounts, onEvent } from "@dynamic-labs-sdk/client";
5
+
6
+ //#region src/getScopeAccountsFromMetaMaskClient/getScopeAccountsFromMetaMaskClient.ts
7
+ /**
8
+ * Reads the accounts MetaMask granted for a single CAIP-2 scope out of the
9
+ * current session, replacing direct `provider.getSession()` access so chain
10
+ * packages never touch the raw session shape. Empty when there's no session
11
+ * or the scope wasn't granted.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const accounts = await getScopeAccountsFromMetaMaskClient(
16
+ * { metaMaskClient, scope: 'eip155:1' },
17
+ * client
18
+ * );
19
+ * // ['eip155:1:0x...']
20
+ * ```
21
+ *
22
+ * @param client - The Dynamic client instance whose logger this reports
23
+ * against. Required (rather than defaulting to the last-initialized client)
24
+ * so a missing/empty-scope result is always traceable to the client that
25
+ * requested it.
26
+ * @returns The accounts granted for `scope`, or an empty array if there's no
27
+ * session or the scope wasn't granted.
28
+ * @see createMetaMaskMultichainClient
29
+ * @see getGrantedMetaMaskScopes
30
+ * @notInstrumented
31
+ */
32
+ const getScopeAccountsFromMetaMaskClient = async ({ metaMaskClient, scope }, client) => {
33
+ const session = await metaMaskClient.provider.getSession();
34
+ const accounts = session?.sessionScopes[scope]?.accounts ?? [];
35
+ getCore(client).logger.debug("[MetaMask:Scopes] Resolved scope accounts", {
36
+ accountCount: accounts.length,
37
+ hasSession: session != null,
38
+ scope
39
+ });
40
+ return accounts;
41
+ };
42
+
43
+ //#endregion
44
+ //#region src/assertMetaMaskNetworkIsGranted/assertMetaMaskNetworkIsGranted.ts
45
+ /**
46
+ * Guards a MetaMask wallet provider's `switchActiveNetwork` against ever
47
+ * pointing at a network MetaMask didn't actually grant an account for.
48
+ * Every configured network is requested as an *optional* CAIP-25 scope (see
49
+ * `getMetaMaskScopes`), so MetaMask may silently approve only a subset --
50
+ * switching the active network to one outside that subset would leave every
51
+ * subsequent signing/sending call targeting a scope the wallet will reject.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * await assertMetaMaskNetworkIsGranted(
56
+ * { chain: 'SOL', metaMaskClient, networkId: '103' },
57
+ * client
58
+ * );
59
+ * // Throws MetaMaskUnscopedNetworkError if MetaMask never granted an
60
+ * // account for network "103" in the current session.
61
+ * ```
62
+ *
63
+ * @param client - The Dynamic client instance whose logger this reports
64
+ * against.
65
+ * @returns A promise that resolves once the target network is confirmed granted.
66
+ * @throws {MetaMaskUnscopedNetworkError} If `networkId` has no granted
67
+ * account in the current session (including when it has no configured
68
+ * MetaMask scope at all).
69
+ * @see resolveActiveMetaMaskScope
70
+ * @see MetaMaskUnscopedNetworkError
71
+ * @notInstrumented
72
+ */
73
+ const assertMetaMaskNetworkIsGranted = async ({ chain, metaMaskClient, networkId }, client) => {
74
+ const { logger } = getCore(client);
75
+ const scope = getMetaMaskScopeForNetworkId({
76
+ chain,
77
+ networkId
78
+ }, client);
79
+ const accounts = scope ? await getScopeAccountsFromMetaMaskClient({
80
+ metaMaskClient,
81
+ scope
82
+ }, client) : [];
83
+ logger.debug("[MetaMask:Scopes] Checked whether target network is granted", {
84
+ accountCount: accounts.length,
85
+ chain,
86
+ networkId,
87
+ scope
88
+ });
89
+ if (accounts.length === 0) {
90
+ logger.warn("[MetaMask:Scopes] Rejected switch to an unscoped network", {
91
+ chain,
92
+ networkId,
93
+ scope
94
+ });
95
+ throw new MetaMaskUnscopedNetworkError({
96
+ networkId,
97
+ scope: scope ?? "unknown"
98
+ });
99
+ }
100
+ };
101
+
102
+ //#endregion
103
+ //#region src/createMetaMaskWalletProviderEventEmitter/createMetaMaskWalletProviderEventEmitter.ts
104
+ /**
105
+ * MetaMask's multichain session never triggers `createWalletProviderEventEmitter`'s
106
+ * usual external-listener flow -- there is no live wallet event to bridge, only our
107
+ * own local actions (e.g. `switchActiveNetwork`) that need to notify subscribers
108
+ * themselves. `setupEventListeners` here just captures the handler references so
109
+ * they can be invoked directly, instead of from a real event source.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const metaMaskEvents = createMetaMaskWalletProviderEventEmitter();
114
+ * metaMaskEvents.getEventEmitter().on('networkChanged', ({ networkId }) => {});
115
+ * metaMaskEvents.emitNetworkChanged({ networkId: '137' });
116
+ * ```
117
+ * @returns A `MetaMaskWalletProviderEventEmitter` ready to emit synthetic
118
+ * `networkChanged`/`disconnected` events.
119
+ * @see MetaMaskWalletProviderEventEmitter
120
+ * @see createWalletProviderEventEmitter
121
+ * @notInstrumented
122
+ */
123
+ const createMetaMaskWalletProviderEventEmitter = () => {
124
+ let handleDisconnected;
125
+ let handleNetworkChanged;
126
+ const emitter = createWalletProviderEventEmitter({
127
+ removeEventListeners: () => {},
128
+ setupEventListeners: (handlers) => {
129
+ handleDisconnected = handlers.handleDisconnected;
130
+ handleNetworkChanged = handlers.handleNetworkChanged;
131
+ },
132
+ supportedEvents: ["disconnected", "networkChanged"]
133
+ });
134
+ emitter.getEventEmitter();
135
+ return {
136
+ ...emitter,
137
+ emitDisconnected: () => handleDisconnected(),
138
+ emitNetworkChanged: (params) => handleNetworkChanged(params)
139
+ };
140
+ };
141
+
142
+ //#endregion
143
+ //#region src/disconnectMetaMaskWalletProvider/disconnectMetaMaskWalletProvider.ts
144
+ /**
145
+ * Shared `disconnectWalletAccount` for every chain's MetaMask wallet provider
146
+ * (EVM, Solana, ...). `getOrCreateMetaMaskClientInstanceManager` hands every chain
147
+ * the SAME client instance -- one multichain session shared across chains --
148
+ * so this only asks MetaMask to drop this chain's own scopes rather than
149
+ * tearing the whole session down under a still-connected chain.
150
+ *
151
+ * `disconnect`'s teardown in `@metamask/connect-multichain` is itself
152
+ * scope-aware: it only actually ends the shared session once every chain's
153
+ * scopes are gone, reflected by `metaMaskClient.status` flipping to
154
+ * `'disconnected'`. Only then do we clear the persisted session and evict
155
+ * the cached client, so a chain that's still connected is left untouched.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * await disconnectMetaMaskWalletProvider(
160
+ * { chain: 'EVM', walletProviderKey: 'metamask-evm' },
161
+ * client
162
+ * );
163
+ * ```
164
+ * @param params.chain - Chain of the wallet account being disconnected.
165
+ * @param params.walletProviderKey - Key of the chain-specific wallet provider to unregister.
166
+ * @param client - The Dynamic client instance. Required (rather than
167
+ * defaulting to the last-initialized client) since this always runs from
168
+ * inside an already-resolved chain wallet provider, which already has one.
169
+ * @returns A promise that resolves once local cleanup is done. Never rejects
170
+ * on the remote signal failing -- that's logged, not surfaced, since the
171
+ * wallet account is being removed either way.
172
+ * @see clearMetaMaskStorage
173
+ * @see getMetaMaskScopes
174
+ * @notInstrumented
175
+ */
176
+ const disconnectMetaMaskWalletProvider = async ({ chain, walletProviderKey }, client) => {
177
+ const { logger } = getCore(client);
178
+ const metaMaskClient = await getOrCreateMetaMaskClientInstanceManager(client).getMetaMaskClient();
179
+ const scopes = getMetaMaskScopes({ chain }, client);
180
+ logger.debug("[MetaMask:Disconnect] Disconnecting wallet account", {
181
+ chain,
182
+ scopes,
183
+ walletProviderKey
184
+ });
185
+ try {
186
+ await metaMaskClient.disconnect(scopes);
187
+ } catch (error) {
188
+ logger.error("[MetaMask:Disconnect] Remote signal failed", {
189
+ error,
190
+ walletProviderKey
191
+ });
192
+ }
193
+ getWalletProviderRegistry(client).unregister(walletProviderKey);
194
+ logger.debug("[MetaMask:Disconnect] Shared session status after disconnect", {
195
+ status: metaMaskClient.status,
196
+ walletProviderKey
197
+ });
198
+ if (metaMaskClient.status === "disconnected") await clearMetaMaskStorage(client);
199
+ };
200
+
201
+ //#endregion
202
+ //#region src/reconcileMetaMaskWalletProvider/reconcileMetaMaskWalletProvider.ts
203
+ /**
204
+ * Keeps a chain's MetaMask SDK wallet provider registered for as
205
+ * long as the user has a wallet account under its exact key, and
206
+ * unregisters it once that wallet account disappears (e.g. on disconnect).
207
+ * This is needed because always adding the MetaMask SDK wallet provider
208
+ * would conflict with the browser extension wallet provider, so we only add
209
+ * it once the user has a wallet account that was connected via QR code.
210
+ * The placeholder is only built (via `walletProviderBuilder`) the first time
211
+ * it must be registered, mirroring how WalletConnect only builds a wallet
212
+ * provider for a session that actually exists.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * reconcileMetaMaskWalletProvider(
217
+ * {
218
+ * walletProviderBuilder: createMetaMaskEvmWalletProvider,
219
+ * walletProviderKey: getMetaMaskEvmSdkWalletProviderKey(),
220
+ * },
221
+ * client
222
+ * );
223
+ * ```
224
+ * @param params.walletProviderBuilder - Builds the SDK placeholder wallet provider once it must be registered.
225
+ * @param params.walletProviderKey - The placeholder's deterministic wallet provider key.
226
+ * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
227
+ * @returns void
228
+ * @see connectWithMetaMaskUri
229
+ * @see WalletProviderPriority
230
+ * @notInstrumented
231
+ */
232
+ const reconcileMetaMaskWalletProvider = ({ walletProviderBuilder, walletProviderKey }, client = getDefaultClient()) => {
233
+ const walletProviderRegistry = getWalletProviderRegistry(client);
234
+ const reconcile = () => {
235
+ const isSdkPlaceholderRegistered = walletProviderRegistry.getByKey(walletProviderKey) !== void 0;
236
+ const shouldBeRegistered = getWalletAccounts(client).some((walletAccount) => walletAccount.walletProviderKey === walletProviderKey);
237
+ if (isSdkPlaceholderRegistered && !shouldBeRegistered) {
238
+ walletProviderRegistry.unregister(walletProviderKey);
239
+ return;
240
+ }
241
+ if (!isSdkPlaceholderRegistered && shouldBeRegistered) walletProviderRegistry.register({
242
+ priority: WalletProviderPriority.WALLET_SDK,
243
+ walletProvider: walletProviderBuilder(client)
244
+ });
245
+ };
246
+ onEvent({
247
+ event: "walletAccountsChanged",
248
+ listener: () => reconcile()
249
+ }, client);
250
+ reconcile();
251
+ };
252
+
253
+ //#endregion
254
+ //#region src/getGrantedMetaMaskScopes/getGrantedMetaMaskScopes.ts
255
+ /**
256
+ * Enumerates every CAIP-2 scope in the current session that MetaMask
257
+ * actually granted at least one account for. Every scope requested at
258
+ * `connect()` time (see `getMetaMaskScopes`) is sent as an *optional*
259
+ * CAIP-25 scope, never a required one, so MetaMask is free to approve only a
260
+ * subset -- `session.sessionScopes` still has a key for every requested
261
+ * scope, just with an empty `accounts` array for the ones that weren't
262
+ * granted. This filters those out, replacing direct `provider.getSession()`
263
+ * access so chain packages never touch the raw session shape.
264
+ *
265
+ * @example
266
+ * ```ts
267
+ * const granted = await getGrantedMetaMaskScopes({ metaMaskClient }, client);
268
+ * // [{ scope: 'eip155:1', accounts: ['eip155:1:0x...'] }]
269
+ * ```
270
+ *
271
+ * @param client - The Dynamic client instance whose logger this reports
272
+ * against. Required (rather than defaulting to the last-initialized client)
273
+ * so an empty result is always traceable to the client that requested it.
274
+ * @returns The scopes with at least one granted account, and their accounts.
275
+ * @see createMetaMaskMultichainClient
276
+ * @see resolveActiveMetaMaskScope
277
+ * @notInstrumented
278
+ */
279
+ const getGrantedMetaMaskScopes = async ({ metaMaskClient }, client) => {
280
+ const session = await metaMaskClient.provider.getSession();
281
+ const sessionScopes = session?.sessionScopes ?? {};
282
+ const grantedScopes = Object.entries(sessionScopes).filter(([, scopeData]) => (scopeData.accounts ?? []).length > 0).map(([scope, scopeData]) => ({
283
+ accounts: scopeData.accounts ?? [],
284
+ scope
285
+ }));
286
+ getCore(client).logger.debug("[MetaMask:Scopes] Resolved granted scopes", {
287
+ grantedScopeCount: grantedScopes.length,
288
+ hasSession: session != null
289
+ });
290
+ return grantedScopes;
291
+ };
292
+
293
+ //#endregion
294
+ //#region src/getNetworkIdFromMetaMaskScope/getNetworkIdFromMetaMaskScope.ts
295
+ /**
296
+ * Reverse of `getMetaMaskScopeForNetworkId`: maps a granted CAIP-2 scope
297
+ * back to the Dynamic `networkId` it came from, for the given chain.
298
+ *
299
+ * Returns `undefined` -- not a throw -- when `scope` doesn't belong to
300
+ * `chain` (e.g. an `eip155:` scope while scanning for `SOL`). For `SOL`,
301
+ * this also covers a `solana:` scope matching no configured network. This
302
+ * is the normal, expected outcome when scanning a session's granted scopes
303
+ * for a single chain: a MetaMask multichain session is shared across every
304
+ * connected chain
305
+ *
306
+ * @example
307
+ * ```ts
308
+ * const networkId = getNetworkIdFromMetaMaskScope(
309
+ * { chain: 'EVM', scope: 'eip155:1' },
310
+ * client
311
+ * );
312
+ * // '1'
313
+ * ```
314
+ *
315
+ * @returns The matching Dynamic `networkId`, or `undefined` if `scope`
316
+ * doesn't belong to `chain` (or, for `SOL`, matches no configured network).
317
+ * @see getMetaMaskScopeForNetworkId
318
+ * @see resolveActiveMetaMaskScope
319
+ * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask
320
+ * scopes can be built for.
321
+ * @notInstrumented
322
+ */
323
+ const getNetworkIdFromMetaMaskScope = ({ chain, scope }, client) => {
324
+ const [namespace, reference] = scope.split(":");
325
+ if (chain === "EVM") return namespace === "eip155" ? reference : void 0;
326
+ if (chain === "SOL") {
327
+ if (namespace !== "solana") return;
328
+ return getNetworksData(client).find((candidate) => candidate.chain === "SOL" && candidate.genesisHash === reference)?.networkId;
329
+ }
330
+ throw new UnsupportedMetaMaskChainError({
331
+ chain,
332
+ supportedChains: SUPPORTED_METAMASK_CHAINS
333
+ });
334
+ };
335
+
336
+ //#endregion
337
+ //#region src/resolveActiveMetaMaskScope/resolveActiveMetaMaskScope.ts
338
+ /**
339
+ * Resolves the network a MetaMask wallet provider should treat as active,
340
+ * self-healing when the last-known-active network (see
341
+ * `getActiveNetworkIdFromLastKnownRegistry`) turns out to have no granted
342
+ * account.
343
+ *
344
+ * Every configured network is requested as an *optional* CAIP-25 scope (see
345
+ * `getMetaMaskScopes`), so MetaMask is free to grant an account for only a
346
+ * subset -- and the registry's own default (the first configured network for
347
+ * the chain) has no awareness of which subset that was. If the active
348
+ * network's scope is empty, this scans every OTHER scope this chain
349
+ * requested, restricted to the ones MetaMask actually granted, and adopts
350
+ * whichever is found (preferring a non-testnet network) as the new active
351
+ * network -- persisting the correction so future calls don't repeat the
352
+ * scan. If nothing was granted for this chain at all, the original
353
+ * (accounts: []) result is returned unchanged, preserving the existing
354
+ * "no address found" behavior when there's truly nothing to fall back to.
355
+ *
356
+ * @example
357
+ * ```ts
358
+ * const { accounts } = await resolveActiveMetaMaskScope(
359
+ * { chain: 'SOL', metaMaskClient, walletProviderKey },
360
+ * client
361
+ * );
362
+ * // accounts is non-empty even if the registry's default network wasn't
363
+ * // the one MetaMask actually granted.
364
+ * ```
365
+ *
366
+ * @param [client] - The Dynamic client instance whose logger this reports
367
+ * against. Required (rather than defaulting to the last-initialized client)
368
+ * so a fallback decision is always traceable to the client that requested it.
369
+ * @returns The resolved active network's id, CAIP-2 scope, and granted accounts.
370
+ * @see assertMetaMaskNetworkIsGranted
371
+ * @see getGrantedMetaMaskScopes
372
+ * @notInstrumented
373
+ */
374
+ const resolveActiveMetaMaskScope = async ({ chain, metaMaskClient, walletProviderKey }, client = getDefaultClient()) => {
375
+ const { logger } = getCore(client);
376
+ const { networkId: activeNetworkId } = await getActiveNetworkIdFromLastKnownRegistry({
377
+ client,
378
+ walletProviderKey
379
+ });
380
+ const activeScope = getMetaMaskScopeForNetworkId({
381
+ chain,
382
+ networkId: activeNetworkId
383
+ }, client);
384
+ const activeAccounts = activeScope ? await getScopeAccountsFromMetaMaskClient({
385
+ metaMaskClient,
386
+ scope: activeScope
387
+ }, client) : [];
388
+ if (activeAccounts.length > 0) {
389
+ logger.debug("[MetaMask:Scopes] Active network scope already has granted accounts", {
390
+ activeNetworkId,
391
+ chain,
392
+ scope: activeScope
393
+ });
394
+ return {
395
+ accounts: activeAccounts,
396
+ networkId: activeNetworkId,
397
+ scope: activeScope
398
+ };
399
+ }
400
+ logger.debug("[MetaMask:Scopes] Active network scope has no granted accounts, scanning other requested scopes for this chain", {
401
+ activeNetworkId,
402
+ chain,
403
+ scope: activeScope
404
+ });
405
+ const requestedScopes = new Set(getMetaMaskScopes({ chain }, client));
406
+ const grantedScopes = await getGrantedMetaMaskScopes({ metaMaskClient }, client);
407
+ const configuredNetworks = getNetworksData(client).filter((network) => network.chain === chain);
408
+ const testnetByNetworkId = new Map(configuredNetworks.map((network) => [network.networkId, network.testnet]));
409
+ const configuredOrderByNetworkId = new Map(configuredNetworks.map((network, index) => [network.networkId, index]));
410
+ const candidates = grantedScopes.filter(({ scope }) => requestedScopes.has(scope)).flatMap(({ accounts, scope }) => {
411
+ const networkId = getNetworkIdFromMetaMaskScope({
412
+ chain,
413
+ scope
414
+ }, client);
415
+ return networkId ? [{
416
+ accounts,
417
+ networkId,
418
+ scope
419
+ }] : [];
420
+ }).sort((a, b) => (configuredOrderByNetworkId.get(a.networkId) ?? 0) - (configuredOrderByNetworkId.get(b.networkId) ?? 0));
421
+ const fallback = candidates.find(({ networkId }) => !testnetByNetworkId.get(networkId)) ?? candidates[0];
422
+ if (fallback === void 0) {
423
+ logger.debug("[MetaMask:Scopes] No granted scope with accounts found for chain", {
424
+ activeNetworkId,
425
+ chain,
426
+ requestedScopes: [...requestedScopes]
427
+ });
428
+ return {
429
+ accounts: activeAccounts,
430
+ networkId: activeNetworkId,
431
+ scope: activeScope
432
+ };
433
+ }
434
+ logger.debug("[MetaMask:Scopes] Falling back to a granted scope after the active scope had no accounts", {
435
+ activeNetworkId,
436
+ activeScope,
437
+ chain,
438
+ fallbackNetworkId: fallback.networkId,
439
+ fallbackScope: fallback.scope
440
+ });
441
+ await switchActiveNetworkInLastKnownRegistry({
442
+ client,
443
+ networkId: fallback.networkId,
444
+ walletProviderKey
445
+ });
446
+ return {
447
+ accounts: fallback.accounts,
448
+ networkId: fallback.networkId,
449
+ scope: fallback.scope
450
+ };
451
+ };
452
+
453
+ //#endregion
454
+ //#region src/exports/core.ts
455
+ assertPackageVersion(name, version);
456
+
457
+ //#endregion
458
+ export { assertMetaMaskNetworkIsGranted, clearMetaMaskStorage, createMetaMaskWalletProviderEventEmitter, disconnectMetaMaskWalletProvider, getOrCreateMetaMaskClientInstanceManager, reconcileMetaMaskWalletProvider, resolveActiveMetaMaskScope };
459
+ //# sourceMappingURL=core.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.esm.js","names":["handleDisconnected: () => void","handleNetworkChanged: (params: { networkId: string }) => void"],"sources":["../src/getScopeAccountsFromMetaMaskClient/getScopeAccountsFromMetaMaskClient.ts","../src/assertMetaMaskNetworkIsGranted/assertMetaMaskNetworkIsGranted.ts","../src/createMetaMaskWalletProviderEventEmitter/createMetaMaskWalletProviderEventEmitter.ts","../src/disconnectMetaMaskWalletProvider/disconnectMetaMaskWalletProvider.ts","../src/reconcileMetaMaskWalletProvider/reconcileMetaMaskWalletProvider.ts","../src/getGrantedMetaMaskScopes/getGrantedMetaMaskScopes.ts","../src/getNetworkIdFromMetaMaskScope/getNetworkIdFromMetaMaskScope.ts","../src/resolveActiveMetaMaskScope/resolveActiveMetaMaskScope.ts","../src/exports/core.ts"],"sourcesContent":["import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\ntype Session = Awaited<ReturnType<MultichainCore['provider']['getSession']>>;\ntype SessionScopes = NonNullable<Session>['sessionScopes'];\ntype ScopeObject = SessionScopes[keyof SessionScopes];\ntype ScopeAccounts = NonNullable<ScopeObject['accounts']>;\n\nexport type GetScopeAccountsFromMetaMaskClientParams = {\n metaMaskClient: MultichainCore;\n scope: Parameters<MultichainCore['connect']>[0][number];\n};\n\n/**\n * Reads the accounts MetaMask granted for a single CAIP-2 scope out of the\n * current session, replacing direct `provider.getSession()` access so chain\n * packages never touch the raw session shape. Empty when there's no session\n * or the scope wasn't granted.\n *\n * @example\n * ```ts\n * const accounts = await getScopeAccountsFromMetaMaskClient(\n * { metaMaskClient, scope: 'eip155:1' },\n * client\n * );\n * // ['eip155:1:0x...']\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so a missing/empty-scope result is always traceable to the client that\n * requested it.\n * @returns The accounts granted for `scope`, or an empty array if there's no\n * session or the scope wasn't granted.\n * @see createMetaMaskMultichainClient\n * @see getGrantedMetaMaskScopes\n * @notInstrumented\n */\nexport const getScopeAccountsFromMetaMaskClient = async (\n { metaMaskClient, scope }: GetScopeAccountsFromMetaMaskClientParams,\n client: DynamicClient\n): Promise<ScopeAccounts> => {\n const session = await metaMaskClient.provider.getSession();\n const accounts = session?.sessionScopes[scope]?.accounts ?? [];\n\n // Account addresses are PII -- log only the count, never the accounts themselves.\n getCore(client).logger.debug('[MetaMask:Scopes] Resolved scope accounts', {\n accountCount: accounts.length,\n hasSession: session != null,\n scope,\n });\n\n return accounts;\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\nimport { MetaMaskUnscopedNetworkError } from '../errors/MetaMaskUnscopedNetworkError';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\nimport { getScopeAccountsFromMetaMaskClient } from '../getScopeAccountsFromMetaMaskClient';\n\nexport type AssertMetaMaskNetworkIsGrantedParams = {\n chain: Chain;\n metaMaskClient: MultichainCore;\n networkId: string;\n};\n\n/**\n * Guards a MetaMask wallet provider's `switchActiveNetwork` against ever\n * pointing at a network MetaMask didn't actually grant an account for.\n * Every configured network is requested as an *optional* CAIP-25 scope (see\n * `getMetaMaskScopes`), so MetaMask may silently approve only a subset --\n * switching the active network to one outside that subset would leave every\n * subsequent signing/sending call targeting a scope the wallet will reject.\n *\n * @example\n * ```ts\n * await assertMetaMaskNetworkIsGranted(\n * { chain: 'SOL', metaMaskClient, networkId: '103' },\n * client\n * );\n * // Throws MetaMaskUnscopedNetworkError if MetaMask never granted an\n * // account for network \"103\" in the current session.\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against.\n * @returns A promise that resolves once the target network is confirmed granted.\n * @throws {MetaMaskUnscopedNetworkError} If `networkId` has no granted\n * account in the current session (including when it has no configured\n * MetaMask scope at all).\n * @see resolveActiveMetaMaskScope\n * @see MetaMaskUnscopedNetworkError\n * @notInstrumented\n */\nexport const assertMetaMaskNetworkIsGranted = async (\n { chain, metaMaskClient, networkId }: AssertMetaMaskNetworkIsGrantedParams,\n client: DynamicClient\n): Promise<void> => {\n const { logger } = getCore(client);\n const scope = getMetaMaskScopeForNetworkId({ chain, networkId }, client);\n const accounts = scope\n ? await getScopeAccountsFromMetaMaskClient(\n { metaMaskClient, scope },\n client\n )\n : [];\n\n logger.debug('[MetaMask:Scopes] Checked whether target network is granted', {\n accountCount: accounts.length,\n chain,\n networkId,\n scope,\n });\n\n if (accounts.length === 0) {\n logger.warn('[MetaMask:Scopes] Rejected switch to an unscoped network', {\n chain,\n networkId,\n scope,\n });\n\n throw new MetaMaskUnscopedNetworkError({\n networkId,\n scope: scope ?? 'unknown',\n });\n }\n};\n","import {\n type WalletProviderEventEmitter,\n createWalletProviderEventEmitter,\n} from '@dynamic-labs-sdk/client/core';\n\n/**\n * The `WalletProviderEventEmitter` returned by\n * `createMetaMaskWalletProviderEventEmitter`, plus `emitNetworkChanged`/\n * `emitDisconnected` methods that MetaMask's EVM/Solana wallet providers call\n * directly from their own `switchActiveNetwork`/`disconnectWalletAccount` --\n * MetaMask's multichain session never fires a live wallet event either of\n * those could otherwise bridge from.\n *\n * @example\n * ```ts\n * const metaMaskEvents = createMetaMaskWalletProviderEventEmitter();\n * metaMaskEvents.emitNetworkChanged({ networkId: '137' });\n * ```\n * @see createMetaMaskWalletProviderEventEmitter\n * @see createWalletProviderEventEmitter\n */\nexport type MetaMaskWalletProviderEventEmitter = WalletProviderEventEmitter & {\n emitDisconnected: () => void;\n emitNetworkChanged: (params: { networkId: string }) => void;\n};\n\n/**\n * MetaMask's multichain session never triggers `createWalletProviderEventEmitter`'s\n * usual external-listener flow -- there is no live wallet event to bridge, only our\n * own local actions (e.g. `switchActiveNetwork`) that need to notify subscribers\n * themselves. `setupEventListeners` here just captures the handler references so\n * they can be invoked directly, instead of from a real event source.\n *\n * @example\n * ```ts\n * const metaMaskEvents = createMetaMaskWalletProviderEventEmitter();\n * metaMaskEvents.getEventEmitter().on('networkChanged', ({ networkId }) => {});\n * metaMaskEvents.emitNetworkChanged({ networkId: '137' });\n * ```\n * @returns A `MetaMaskWalletProviderEventEmitter` ready to emit synthetic\n * `networkChanged`/`disconnected` events.\n * @see MetaMaskWalletProviderEventEmitter\n * @see createWalletProviderEventEmitter\n * @notInstrumented\n */\nexport const createMetaMaskWalletProviderEventEmitter =\n (): MetaMaskWalletProviderEventEmitter => {\n let handleDisconnected: () => void;\n let handleNetworkChanged: (params: { networkId: string }) => void;\n\n const emitter = createWalletProviderEventEmitter({\n removeEventListeners: () => {},\n setupEventListeners: (handlers) => {\n handleDisconnected = handlers.handleDisconnected;\n handleNetworkChanged = handlers.handleNetworkChanged;\n },\n // `accountsChanged` is excluded: nothing wires it yet -- MetaMask's real\n // `metamask_accountsChanged` event exists but isn't bridged here (future work).\n supportedEvents: ['disconnected', 'networkChanged'],\n });\n\n // Captures the handlers eagerly, since nothing external ever calls\n // getEventEmitter() first the way a real wallet-event bridge would.\n emitter.getEventEmitter();\n\n return {\n ...emitter,\n emitDisconnected: () => handleDisconnected(),\n emitNetworkChanged: (params) => handleNetworkChanged(params),\n };\n };\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport {\n getCore,\n getWalletProviderRegistry,\n} from '@dynamic-labs-sdk/client/core';\n\nimport { getMetaMaskScopes } from '../getMetaMaskScopes';\nimport { getOrCreateMetaMaskClientInstanceManager } from '../metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager';\nimport { clearMetaMaskStorage } from '../metaMaskStorage/clearMetaMaskStorage';\n\n/**\n * Params for {@link disconnectMetaMaskWalletProvider}, identifying which\n * chain's scopes to drop from the shared MetaMask session and which\n * chain-specific wallet provider to unregister alongside it.\n *\n * @example\n * ```ts\n * const params: DisconnectMetaMaskWalletProviderParams = {\n * chain: 'EVM',\n * walletProviderKey: 'metamask-evm',\n * };\n * ```\n * @see disconnectMetaMaskWalletProvider\n * @see getMetaMaskScopes\n */\nexport type DisconnectMetaMaskWalletProviderParams = {\n /** Chain of the wallet account being disconnected; scopes this chain's own scopes out of the shared session. */\n chain: Chain;\n /** Key of the chain-specific wallet provider to unregister. */\n walletProviderKey: string;\n};\n\n/**\n * Shared `disconnectWalletAccount` for every chain's MetaMask wallet provider\n * (EVM, Solana, ...). `getOrCreateMetaMaskClientInstanceManager` hands every chain\n * the SAME client instance -- one multichain session shared across chains --\n * so this only asks MetaMask to drop this chain's own scopes rather than\n * tearing the whole session down under a still-connected chain.\n *\n * `disconnect`'s teardown in `@metamask/connect-multichain` is itself\n * scope-aware: it only actually ends the shared session once every chain's\n * scopes are gone, reflected by `metaMaskClient.status` flipping to\n * `'disconnected'`. Only then do we clear the persisted session and evict\n * the cached client, so a chain that's still connected is left untouched.\n *\n * @example\n * ```ts\n * await disconnectMetaMaskWalletProvider(\n * { chain: 'EVM', walletProviderKey: 'metamask-evm' },\n * client\n * );\n * ```\n * @param params.chain - Chain of the wallet account being disconnected.\n * @param params.walletProviderKey - Key of the chain-specific wallet provider to unregister.\n * @param client - The Dynamic client instance. Required (rather than\n * defaulting to the last-initialized client) since this always runs from\n * inside an already-resolved chain wallet provider, which already has one.\n * @returns A promise that resolves once local cleanup is done. Never rejects\n * on the remote signal failing -- that's logged, not surfaced, since the\n * wallet account is being removed either way.\n * @see clearMetaMaskStorage\n * @see getMetaMaskScopes\n * @notInstrumented\n */\nexport const disconnectMetaMaskWalletProvider = async (\n { chain, walletProviderKey }: DisconnectMetaMaskWalletProviderParams,\n client: DynamicClient\n): Promise<void> => {\n const { logger } = getCore(client);\n const metaMaskClient = await getOrCreateMetaMaskClientInstanceManager(\n client\n ).getMetaMaskClient();\n const scopes = getMetaMaskScopes({ chain }, client);\n\n logger.debug('[MetaMask:Disconnect] Disconnecting wallet account', {\n chain,\n scopes,\n walletProviderKey,\n });\n\n try {\n await metaMaskClient.disconnect(scopes);\n } catch (error) {\n logger.error('[MetaMask:Disconnect] Remote signal failed', {\n error,\n walletProviderKey,\n });\n }\n\n getWalletProviderRegistry(client).unregister(walletProviderKey);\n\n logger.debug('[MetaMask:Disconnect] Shared session status after disconnect', {\n status: metaMaskClient.status,\n walletProviderKey,\n });\n\n if (metaMaskClient.status === 'disconnected') {\n await clearMetaMaskStorage(client);\n }\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getWalletAccounts, onEvent } from '@dynamic-labs-sdk/client';\nimport type { WalletProvider } from '@dynamic-labs-sdk/client/core';\nimport {\n WalletProviderPriority,\n getDefaultClient,\n getWalletProviderRegistry,\n} from '@dynamic-labs-sdk/client/core';\n\n/**\n * Params for {@link reconcileMetaMaskWalletProvider}.\n *\n * @example\n * ```ts\n * const params: ReconcileMetaMaskWalletProviderParams = {\n * walletProviderBuilder: createMetaMaskEvmWalletProvider,\n * walletProviderKey: getMetaMaskEvmSdkWalletProviderKey(),\n * };\n * ```\n * @see reconcileMetaMaskWalletProvider\n * @see WalletProvider\n */\nexport type ReconcileMetaMaskWalletProviderParams = {\n /**\n * Builds the connect-less SDK placeholder wallet provider (e.g.\n * `createMetaMaskEvmWalletProvider`). Only invoked once reconciliation\n * determines the placeholder must actually be registered, so a chain\n * extension never builds it just for being added.\n */\n walletProviderBuilder: (client: DynamicClient) => WalletProvider;\n /**\n * The placeholder's deterministic wallet provider key, computed without\n * building it (e.g. `getMetaMaskEvmSdkWalletProviderKey`).\n */\n walletProviderKey: string;\n};\n\n/**\n * Keeps a chain's MetaMask SDK wallet provider registered for as\n * long as the user has a wallet account under its exact key, and\n * unregisters it once that wallet account disappears (e.g. on disconnect).\n * This is needed because always adding the MetaMask SDK wallet provider\n * would conflict with the browser extension wallet provider, so we only add\n * it once the user has a wallet account that was connected via QR code.\n * The placeholder is only built (via `walletProviderBuilder`) the first time\n * it must be registered, mirroring how WalletConnect only builds a wallet\n * provider for a session that actually exists.\n *\n * @example\n * ```ts\n * reconcileMetaMaskWalletProvider(\n * {\n * walletProviderBuilder: createMetaMaskEvmWalletProvider,\n * walletProviderKey: getMetaMaskEvmSdkWalletProviderKey(),\n * },\n * client\n * );\n * ```\n * @param params.walletProviderBuilder - Builds the SDK placeholder wallet provider once it must be registered.\n * @param params.walletProviderKey - The placeholder's deterministic wallet provider key.\n * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.\n * @returns void\n * @see connectWithMetaMaskUri\n * @see WalletProviderPriority\n * @notInstrumented\n */\nexport const reconcileMetaMaskWalletProvider = (\n { walletProviderBuilder, walletProviderKey }: ReconcileMetaMaskWalletProviderParams,\n client: DynamicClient = getDefaultClient()\n): void => {\n const walletProviderRegistry = getWalletProviderRegistry(client);\n\n const reconcile = (): void => {\n const isSdkPlaceholderRegistered =\n walletProviderRegistry.getByKey(walletProviderKey) !== undefined;\n const shouldBeRegistered = getWalletAccounts(client).some(\n (walletAccount) => walletAccount.walletProviderKey === walletProviderKey\n );\n\n if (isSdkPlaceholderRegistered && !shouldBeRegistered) {\n walletProviderRegistry.unregister(walletProviderKey);\n return;\n }\n\n if (!isSdkPlaceholderRegistered && shouldBeRegistered) {\n walletProviderRegistry.register({\n priority: WalletProviderPriority.WALLET_SDK,\n walletProvider: walletProviderBuilder(client),\n });\n }\n };\n\n onEvent(\n { event: 'walletAccountsChanged', listener: () => reconcile() },\n client\n );\n\n reconcile();\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\ntype Session = Awaited<ReturnType<MultichainCore['provider']['getSession']>>;\ntype SessionScopes = NonNullable<Session>['sessionScopes'];\ntype ScopeKey = keyof SessionScopes;\ntype ScopeAccounts = NonNullable<SessionScopes[ScopeKey]['accounts']>;\n\nexport type GrantedMetaMaskScope = {\n accounts: ScopeAccounts;\n scope: ScopeKey;\n};\n\nexport type GetGrantedMetaMaskScopesParams = {\n metaMaskClient: MultichainCore;\n};\n\n/**\n * Enumerates every CAIP-2 scope in the current session that MetaMask\n * actually granted at least one account for. Every scope requested at\n * `connect()` time (see `getMetaMaskScopes`) is sent as an *optional*\n * CAIP-25 scope, never a required one, so MetaMask is free to approve only a\n * subset -- `session.sessionScopes` still has a key for every requested\n * scope, just with an empty `accounts` array for the ones that weren't\n * granted. This filters those out, replacing direct `provider.getSession()`\n * access so chain packages never touch the raw session shape.\n *\n * @example\n * ```ts\n * const granted = await getGrantedMetaMaskScopes({ metaMaskClient }, client);\n * // [{ scope: 'eip155:1', accounts: ['eip155:1:0x...'] }]\n * ```\n *\n * @param client - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so an empty result is always traceable to the client that requested it.\n * @returns The scopes with at least one granted account, and their accounts.\n * @see createMetaMaskMultichainClient\n * @see resolveActiveMetaMaskScope\n * @notInstrumented\n */\nexport const getGrantedMetaMaskScopes = async (\n { metaMaskClient }: GetGrantedMetaMaskScopesParams,\n client: DynamicClient\n): Promise<GrantedMetaMaskScope[]> => {\n const session = await metaMaskClient.provider.getSession();\n const sessionScopes = session?.sessionScopes ?? ({} as SessionScopes);\n\n const grantedScopes = (\n Object.entries(sessionScopes) as [ScopeKey, SessionScopes[ScopeKey]][]\n )\n .filter(([, scopeData]) => (scopeData.accounts ?? []).length > 0)\n .map(([scope, scopeData]) => ({\n accounts: scopeData.accounts ?? [],\n scope,\n }));\n\n // Granted scopes carry account addresses, which are PII -- log only counts.\n getCore(client).logger.debug('[MetaMask:Scopes] Resolved granted scopes', {\n grantedScopeCount: grantedScopes.length,\n hasSession: session != null,\n });\n\n return grantedScopes;\n};\n","import type { Chain, DynamicClient } from '@dynamic-labs-sdk/client';\nimport { getNetworksData } from '@dynamic-labs-sdk/client';\n\nimport { SUPPORTED_METAMASK_CHAINS } from '../constants';\nimport { UnsupportedMetaMaskChainError } from '../errors/UnsupportedMetaMaskChainError';\n\nexport type GetNetworkIdFromMetaMaskScopeParams = {\n chain: Chain;\n scope: string;\n};\n\n/**\n * Reverse of `getMetaMaskScopeForNetworkId`: maps a granted CAIP-2 scope\n * back to the Dynamic `networkId` it came from, for the given chain.\n *\n * Returns `undefined` -- not a throw -- when `scope` doesn't belong to\n * `chain` (e.g. an `eip155:` scope while scanning for `SOL`). For `SOL`,\n * this also covers a `solana:` scope matching no configured network. This\n * is the normal, expected outcome when scanning a session's granted scopes\n * for a single chain: a MetaMask multichain session is shared across every\n * connected chain\n *\n * @example\n * ```ts\n * const networkId = getNetworkIdFromMetaMaskScope(\n * { chain: 'EVM', scope: 'eip155:1' },\n * client\n * );\n * // '1'\n * ```\n *\n * @returns The matching Dynamic `networkId`, or `undefined` if `scope`\n * doesn't belong to `chain` (or, for `SOL`, matches no configured network).\n * @see getMetaMaskScopeForNetworkId\n * @see resolveActiveMetaMaskScope\n * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask\n * scopes can be built for.\n * @notInstrumented\n */\nexport const getNetworkIdFromMetaMaskScope = (\n { chain, scope }: GetNetworkIdFromMetaMaskScopeParams,\n client: DynamicClient\n): string | undefined => {\n const [namespace, reference] = scope.split(':');\n\n if (chain === 'EVM') {\n return namespace === 'eip155' ? reference : undefined;\n }\n\n if (chain === 'SOL') {\n if (namespace !== 'solana') {\n return undefined;\n }\n\n return getNetworksData(client).find(\n (candidate) =>\n candidate.chain === 'SOL' && candidate.genesisHash === reference\n )?.networkId;\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 {\n getActiveNetworkIdFromLastKnownRegistry,\n getCore,\n getDefaultClient,\n switchActiveNetworkInLastKnownRegistry,\n} from '@dynamic-labs-sdk/client/core';\nimport type { MultichainCore } from '@metamask/connect-multichain';\n\nimport { getGrantedMetaMaskScopes } from '../getGrantedMetaMaskScopes';\nimport { getMetaMaskScopeForNetworkId } from '../getMetaMaskScopeForNetworkId';\nimport { getMetaMaskScopes } from '../getMetaMaskScopes';\nimport { getNetworkIdFromMetaMaskScope } from '../getNetworkIdFromMetaMaskScope';\nimport { getScopeAccountsFromMetaMaskClient } from '../getScopeAccountsFromMetaMaskClient';\n\nexport type ResolveActiveMetaMaskScopeParams = {\n chain: Chain;\n metaMaskClient: MultichainCore;\n walletProviderKey: string;\n};\n\n/**\n * The network resolved as active for a MetaMask wallet provider, and the\n * CAIP-10 accounts MetaMask granted for it -- returned by\n * `resolveActiveMetaMaskScope` so callers don't need to re-derive the scope\n * or re-read the session themselves.\n *\n * @example\n * ```ts\n * const { accounts, networkId } = await resolveActiveMetaMaskScope(\n * { chain: 'SOL', metaMaskClient, walletProviderKey },\n * client\n * );\n * ```\n * @see resolveActiveMetaMaskScope\n * @see getGrantedMetaMaskScopes\n */\nexport type ResolveActiveMetaMaskScopeResult = {\n /** CAIP-10 account ids -- same shape as `metaMaskClient.getScopeAccounts` returns. */\n accounts: string[];\n networkId: string;\n scope: string | undefined;\n};\n\n/**\n * Resolves the network a MetaMask wallet provider should treat as active,\n * self-healing when the last-known-active network (see\n * `getActiveNetworkIdFromLastKnownRegistry`) turns out to have no granted\n * account.\n *\n * Every configured network is requested as an *optional* CAIP-25 scope (see\n * `getMetaMaskScopes`), so MetaMask is free to grant an account for only a\n * subset -- and the registry's own default (the first configured network for\n * the chain) has no awareness of which subset that was. If the active\n * network's scope is empty, this scans every OTHER scope this chain\n * requested, restricted to the ones MetaMask actually granted, and adopts\n * whichever is found (preferring a non-testnet network) as the new active\n * network -- persisting the correction so future calls don't repeat the\n * scan. If nothing was granted for this chain at all, the original\n * (accounts: []) result is returned unchanged, preserving the existing\n * \"no address found\" behavior when there's truly nothing to fall back to.\n *\n * @example\n * ```ts\n * const { accounts } = await resolveActiveMetaMaskScope(\n * { chain: 'SOL', metaMaskClient, walletProviderKey },\n * client\n * );\n * // accounts is non-empty even if the registry's default network wasn't\n * // the one MetaMask actually granted.\n * ```\n *\n * @param [client] - The Dynamic client instance whose logger this reports\n * against. Required (rather than defaulting to the last-initialized client)\n * so a fallback decision is always traceable to the client that requested it.\n * @returns The resolved active network's id, CAIP-2 scope, and granted accounts.\n * @see assertMetaMaskNetworkIsGranted\n * @see getGrantedMetaMaskScopes\n * @notInstrumented\n */\nexport const resolveActiveMetaMaskScope = async (\n {\n chain,\n metaMaskClient,\n walletProviderKey,\n }: ResolveActiveMetaMaskScopeParams,\n client: DynamicClient = getDefaultClient()\n): Promise<ResolveActiveMetaMaskScopeResult> => {\n const { logger } = getCore(client);\n\n const { networkId: activeNetworkId } =\n await getActiveNetworkIdFromLastKnownRegistry({\n client,\n walletProviderKey,\n });\n const activeScope = getMetaMaskScopeForNetworkId(\n { chain, networkId: activeNetworkId },\n client\n );\n const activeAccounts = activeScope\n ? await getScopeAccountsFromMetaMaskClient(\n { metaMaskClient, scope: activeScope },\n client\n )\n : [];\n\n if (activeAccounts.length > 0) {\n logger.debug(\n '[MetaMask:Scopes] Active network scope already has granted accounts',\n { activeNetworkId, chain, scope: activeScope }\n );\n\n return {\n accounts: activeAccounts,\n networkId: activeNetworkId,\n scope: activeScope,\n };\n }\n\n logger.debug(\n '[MetaMask:Scopes] Active network scope has no granted accounts, scanning other requested scopes for this chain',\n { activeNetworkId, chain, scope: activeScope }\n );\n\n const requestedScopes = new Set<string>(getMetaMaskScopes({ chain }, client));\n const grantedScopes = await getGrantedMetaMaskScopes(\n {\n metaMaskClient,\n },\n client\n );\n\n // Both the testnet flag and the fallback's deterministic order come from\n // Dynamic's own configured network list, never MetaMask's -- the granted\n // scopes returned by the SDK are keyed off its own session object, whose\n // enumeration order is an implementation detail, not something to rely on\n // when multiple candidates are equally eligible.\n const configuredNetworks = getNetworksData(client).filter(\n (network) => network.chain === chain\n );\n const testnetByNetworkId = new Map(\n configuredNetworks.map((network) => [network.networkId, network.testnet])\n );\n const configuredOrderByNetworkId = new Map(\n configuredNetworks.map((network, index) => [network.networkId, index])\n );\n\n const candidates = grantedScopes\n .filter(({ scope }) => requestedScopes.has(scope))\n .flatMap(({ accounts, scope }) => {\n const networkId = getNetworkIdFromMetaMaskScope({ chain, scope }, client);\n\n return networkId ? [{ accounts, networkId, scope }] : [];\n })\n .sort(\n (a, b) =>\n (configuredOrderByNetworkId.get(a.networkId) ?? 0) -\n (configuredOrderByNetworkId.get(b.networkId) ?? 0)\n );\n\n const fallback =\n candidates.find(({ networkId }) => !testnetByNetworkId.get(networkId)) ??\n candidates[0];\n\n if (fallback === undefined) {\n logger.debug(\n '[MetaMask:Scopes] No granted scope with accounts found for chain',\n { activeNetworkId, chain, requestedScopes: [...requestedScopes] }\n );\n\n return {\n accounts: activeAccounts,\n networkId: activeNetworkId,\n scope: activeScope,\n };\n }\n\n logger.debug(\n '[MetaMask:Scopes] Falling back to a granted scope after the active scope had no accounts',\n {\n activeNetworkId,\n activeScope,\n chain,\n fallbackNetworkId: fallback.networkId,\n fallbackScope: fallback.scope,\n }\n );\n\n await switchActiveNetworkInLastKnownRegistry({\n client,\n networkId: fallback.networkId,\n walletProviderKey,\n });\n\n return {\n accounts: fallback.accounts,\n networkId: fallback.networkId,\n scope: fallback.scope,\n };\n};\n","import { assertPackageVersion } from '@dynamic-labs-sdk/assert-package-version';\n\nimport { name, version } from '../../package.json';\n\nassertPackageVersion(name, version);\n\n/**\n * @categoryDescription Extension Development\n * Core utilities and functions for building MetaMask SDK extensions.\n * These APIs provide low-level access to the SDK internals and are intended\n * for advanced use cases and extension development. Most applications should\n * use the higher-level APIs exported from the main SDK package instead.\n */\n\n/** @category Extension Development */\nexport { assertMetaMaskNetworkIsGranted } from '../assertMetaMaskNetworkIsGranted';\nexport { createMetaMaskWalletProviderEventEmitter } from '../createMetaMaskWalletProviderEventEmitter';\nexport type { MetaMaskWalletProviderEventEmitter } from '../createMetaMaskWalletProviderEventEmitter';\nexport { disconnectMetaMaskWalletProvider } from '../disconnectMetaMaskWalletProvider';\nexport type { DisconnectMetaMaskWalletProviderParams } from '../disconnectMetaMaskWalletProvider';\n/** @category Extension Development */\nexport { getOrCreateMetaMaskClientInstanceManager } from '../metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager';\nexport { clearMetaMaskStorage } from '../metaMaskStorage/clearMetaMaskStorage';\nexport { reconcileMetaMaskWalletProvider } from '../reconcileMetaMaskWalletProvider';\nexport type { ReconcileMetaMaskWalletProviderParams } from '../reconcileMetaMaskWalletProvider';\nexport { resolveActiveMetaMaskScope } from '../resolveActiveMetaMaskScope';\nexport type { ResolveActiveMetaMaskScopeResult } from '../resolveActiveMetaMaskScope';\n"],"mappings":"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"}