@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,621 @@
1
+ import { createEventEmitter, createRuntimeServiceAccessKey, createSequenceQueue, createStorageKeySchema, getAppMetadata, getCore, getNativeLinkScheme, getPlatform } from "@dynamic-labs-sdk/client/core";
2
+ import { BaseError, getNetworksData } from "@dynamic-labs-sdk/client";
3
+ import { createMultichainClient, getTransportType } from "@metamask/connect-multichain";
4
+ import * as z from "zod/mini";
5
+
6
+ //#region package.json
7
+ var name = "@dynamic-labs-sdk/metamask";
8
+ var version = "1.31.2";
9
+
10
+ //#endregion
11
+ //#region src/errors/MetaMaskUnscopedNetworkError/MetaMaskUnscopedNetworkError.ts
12
+ /**
13
+ * Thrown when attempting to switch a MetaMask multichain wallet provider to
14
+ * a network whose CAIP-2 scope was requested at connect time but never
15
+ * granted an account by MetaMask in the current session (e.g. the user only
16
+ * approved mainnet, not a preconfigured devnet, during pairing). Switching
17
+ * to it anyway would silently break every subsequent signing/sending call
18
+ * on that network, since MetaMask rejects those against an ungranted scope.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * try {
23
+ * await switchActiveNetwork({ networkId: '103', walletAccount });
24
+ * } catch (error) {
25
+ * if (error instanceof MetaMaskUnscopedNetworkError) {
26
+ * // Tell the user to approve this network in MetaMask, then retry.
27
+ * }
28
+ * }
29
+ * ```
30
+ * @see assertMetaMaskNetworkIsGranted
31
+ * @see resolveActiveMetaMaskScope
32
+ */
33
+ var MetaMaskUnscopedNetworkError = class extends BaseError {
34
+ constructor({ networkId, scope }) {
35
+ super({
36
+ cause: null,
37
+ code: "metamask_unscoped_network",
38
+ docsUrl: null,
39
+ metaMessages: [`scope: ${scope}`],
40
+ name: "MetaMaskUnscopedNetworkError",
41
+ shortMessage: `Cannot switch to network "${networkId}": MetaMask did not grant an account for scope "${scope}" in the current session.`
42
+ });
43
+ }
44
+ };
45
+
46
+ //#endregion
47
+ //#region src/constants.ts
48
+ /**
49
+ * Chains MetaMask's multichain client can build a CAIP-2 scope for. Shared by
50
+ * every scope/network-mapping helper so the supported set only needs to
51
+ * change in one place.
52
+ */
53
+ const SUPPORTED_METAMASK_CHAINS = ["EVM", "SOL"];
54
+
55
+ //#endregion
56
+ //#region src/errors/UnsupportedMetaMaskChainError/UnsupportedMetaMaskChainError.ts
57
+ /** Thrown when a chain has no MetaMask multichain scope mapping defined. */
58
+ var UnsupportedMetaMaskChainError = class extends BaseError {
59
+ constructor({ chain, supportedChains }) {
60
+ super({
61
+ cause: null,
62
+ code: "metamask_unsupported_chain",
63
+ docsUrl: null,
64
+ name: "UnsupportedMetaMaskChainError",
65
+ shortMessage: `Unsupported chain "${chain}" for MetaMask. Supported chains: ${supportedChains.join(", ")}`
66
+ });
67
+ }
68
+ };
69
+
70
+ //#endregion
71
+ //#region src/getMetaMaskScopeForNetworkId/getMetaMaskScopeForNetworkId.ts
72
+ /**
73
+ * Maps a single Dynamic-configured network to its CAIP-2 MetaMask
74
+ * multichain scope -- the same per-network mapping `getMetaMaskScopes`
75
+ * applies to every configured network at once, extracted so callers that
76
+ * already have one `networkId` (e.g. the "active network" registry) don't
77
+ * need to rebuild and filter the whole configured list to find it.
78
+ *
79
+ * EVM's `networkId` IS the CAIP-2 reference (`eip155:<networkId>`); SOL's
80
+ * `networkId` is an internal routing id, so the network's base58 genesis
81
+ * hash is used instead (`solana:<genesisHash>`). Returns `undefined` when no
82
+ * configured SOL network matches, or matches but has no genesis hash --
83
+ * these are normal ("this networkId isn't usable as a MetaMask scope"), not
84
+ * error conditions.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * const scope = getMetaMaskScopeForNetworkId(
89
+ * { chain: 'SOL', networkId: '101' },
90
+ * client
91
+ * );
92
+ * // 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
93
+ * ```
94
+ *
95
+ * @returns The CAIP-2 scope for `networkId`, or `undefined` if it can't be
96
+ * expressed as one (e.g. a SOL network with no genesis hash configured).
97
+ * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask
98
+ * scopes can be built for.
99
+ * @see getMetaMaskScopes
100
+ * @see getNetworkIdFromMetaMaskScope
101
+ * @notInstrumented
102
+ */
103
+ const getMetaMaskScopeForNetworkId = ({ chain, networkId }, client) => {
104
+ if (chain === "EVM") return `eip155:${networkId}`;
105
+ if (chain === "SOL") {
106
+ const network = getNetworksData(client).find((candidate) => candidate.chain === "SOL" && candidate.networkId === networkId);
107
+ return network?.genesisHash ? `solana:${network.genesisHash}` : void 0;
108
+ }
109
+ throw new UnsupportedMetaMaskChainError({
110
+ chain,
111
+ supportedChains: SUPPORTED_METAMASK_CHAINS
112
+ });
113
+ };
114
+
115
+ //#endregion
116
+ //#region src/getMetaMaskScopes/getMetaMaskScopes.ts
117
+ /**
118
+ * Builds the CAIP-2 scopes to request for a chain's MetaMask multichain
119
+ * session, one per configured network. SOL scopes use the network's genesis
120
+ * hash (not `networkId`, which is an internal routing id for SOL) since
121
+ * that's the CAIP-2 reference MetaMask expects.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const scopes = getMetaMaskScopes({ chain: 'EVM' }, client);
126
+ * // ['eip155:1', 'eip155:137']
127
+ * ```
128
+ *
129
+ * @returns The CAIP-2 scopes for every configured network of `chain` that
130
+ * can be expressed as one.
131
+ * @throws {UnsupportedMetaMaskChainError} If `chain` isn't a chain MetaMask
132
+ * scopes can be built for.
133
+ * @see getMetaMaskScopeForNetworkId
134
+ * @see getMetaMaskSupportedNetworks
135
+ * @notInstrumented
136
+ */
137
+ const getMetaMaskScopes = ({ chain }, client) => {
138
+ if (!chain || !SUPPORTED_METAMASK_CHAINS.includes(chain)) throw new UnsupportedMetaMaskChainError({
139
+ chain,
140
+ supportedChains: SUPPORTED_METAMASK_CHAINS
141
+ });
142
+ return getNetworksData(client).filter((network) => network.chain === chain).flatMap((network) => {
143
+ const scope = getMetaMaskScopeForNetworkId({
144
+ chain: network.chain,
145
+ networkId: network.networkId
146
+ }, client);
147
+ return scope ? [scope] : [];
148
+ });
149
+ };
150
+
151
+ //#endregion
152
+ //#region src/errors/MetaMaskClientNotInitializedError/MetaMaskClientNotInitializedError.ts
153
+ /**
154
+ * Thrown when `getMetaMaskClient()` is called but the underlying
155
+ * `@metamask/connect-multichain` client failed to initialize -- this should
156
+ * be unreachable in practice, since `getMetaMaskClient` always triggers
157
+ * `refreshMetaMaskClient` first when no client promise exists yet, but it
158
+ * guards against a future change silently returning `undefined` instead.
159
+ */
160
+ var MetaMaskClientNotInitializedError = class extends BaseError {
161
+ constructor() {
162
+ super({
163
+ cause: null,
164
+ code: "metamask_client_not_initialized",
165
+ docsUrl: null,
166
+ name: "MetaMaskClientNotInitializedError",
167
+ shortMessage: "MetaMask client not initialized"
168
+ });
169
+ }
170
+ };
171
+
172
+ //#endregion
173
+ //#region src/getMetaMaskSupportedNetworks/getMetaMaskSupportedNetworks.ts
174
+ /**
175
+ * Builds the `api.supportedNetworks` map for MetaMask's multichain client:
176
+ * EVM networks keyed by their CAIP-2 `eip155:<networkId>` scope, SOL networks
177
+ * keyed by their CAIP-2 `solana:<genesisHash>` scope (SOL's `networkId` is an
178
+ * internal routing id, not usable as a CAIP-2 reference).
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * const supportedNetworks = getMetaMaskSupportedNetworks(client);
183
+ * // { 'eip155:1': 'https://...', 'solana:5eykt4...': 'https://...' }
184
+ * ```
185
+ *
186
+ * @returns The RPC URL for every configured EVM/SOL network, keyed by its
187
+ * MetaMask-recognized network identifier.
188
+ * @see getMetaMaskScopes
189
+ * @see createMetaMaskClientInstanceManager
190
+ * @notInstrumented
191
+ */
192
+ const getMetaMaskSupportedNetworks = (client) => {
193
+ const { logger } = getCore(client);
194
+ const networks = getNetworksData(client).filter((network) => SUPPORTED_METAMASK_CHAINS.includes(network.chain));
195
+ const supportedNetworks = {};
196
+ for (const network of networks) {
197
+ if (network.chain === "EVM") supportedNetworks[`eip155:${network.networkId}`] = network.rpcUrls.http[0];
198
+ if (network.chain === "SOL" && network.genesisHash) supportedNetworks[`solana:${network.genesisHash}`] = network.rpcUrls.http[0];
199
+ }
200
+ logger.debug("[MetaMask:Client] Resolved supported networks", { supportedNetworks });
201
+ return supportedNetworks;
202
+ };
203
+
204
+ //#endregion
205
+ //#region src/metaMaskStorage/createInitialMetaMaskStoreValue/createInitialMetaMaskStoreValue.ts
206
+ /**
207
+ * Builds the persisted store blob's value for when there is nothing stored
208
+ * yet, or after a clear. A function rather than a shared constant, so every
209
+ * caller gets its own object and can't mutate a value other callers rely on.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const initial = createInitialMetaMaskStoreValue();
214
+ * // { entries: {}, schemaVersion: 1 }
215
+ * ```
216
+ *
217
+ * @returns A fresh, empty `MetaMaskStoreValue`.
218
+ * @see createMetaMaskStorageClient
219
+ * @see clearMetaMaskStorage
220
+ * @notInstrumented
221
+ */
222
+ const createInitialMetaMaskStoreValue = () => ({
223
+ entries: {},
224
+ schemaVersion: 1
225
+ });
226
+
227
+ //#endregion
228
+ //#region src/metaMaskStorage/queueMetaMaskStorageWrite/queueMetaMaskStorageWrite.ts
229
+ /**
230
+ * Single shared queue serializing every read-modify-write against
231
+ * `metaMaskStoreStorageKeySchema` -- `createMetaMaskStorageClient`'s adapter
232
+ * reads/writes/anonId operations and `clearMetaMaskStorage`'s reset all go
233
+ * through this same instance, so a write that started before a clear can't
234
+ * land after it and resurrect the data the clear was meant to erase.
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * await queueMetaMaskStorageWrite(() => storage.setItem(key, value), client);
239
+ * ```
240
+ *
241
+ * @returns A `SequenceQueue` that runs one queued task at a time, in order.
242
+ * @see createMetaMaskStorageClient
243
+ * @see clearMetaMaskStorage
244
+ * @notInstrumented
245
+ */
246
+ const queueMetaMaskStorageWrite = createSequenceQueue();
247
+
248
+ //#endregion
249
+ //#region src/metaMaskStorage/metaMaskStorage.schema.ts
250
+ /**
251
+ * Single storage key holding every value the MetaMask multichain SDK writes
252
+ * through our `StoreAdapter` (sessions, nonces, cached accounts/chain,
253
+ * pending pairing requests, transport ids, ...), keyed by whatever key string
254
+ * the SDK itself hands us -- the SDK controls every key name, including ones
255
+ * that embed an unpredictable id (`session:<id>`), so this can't be split
256
+ * into one storage key per SDK key.
257
+ *
258
+ * `entries[ANON_ID_ENTRY_KEY]` also lives in here, alongside `extensionId`
259
+ * and the other transport keys.
260
+ *
261
+ * `clearMetaMaskStorage` resets this key back to its initial value; nothing
262
+ * else needs to know the individual key names the SDK is using.
263
+ */
264
+ const metaMaskStoreStorageKeySchema = createStorageKeySchema({
265
+ key: "metaMaskStore",
266
+ schema: z.object({
267
+ entries: z.record(z.string(), z.string()),
268
+ schemaVersion: z.literal(1)
269
+ })
270
+ });
271
+ /**
272
+ * Reserved `entries` key the MetaMask SDK itself never writes to, used to
273
+ * carry the SDK's anonymous analytics id inside the same store blob.
274
+ */
275
+ const ANON_ID_ENTRY_KEY = "anonId";
276
+
277
+ //#endregion
278
+ //#region src/metaMaskStorage/clearMetaMaskStorage/clearMetaMaskStorage.ts
279
+ const FAILED_TO_CLEAR_STORAGE_MESSAGE = "[MetaMask:Session] Failed to clear the persisted store; the next pairing may resume a stale session instead of showing a new one";
280
+ /**
281
+ * Resets the MetaMask SDK's persisted store blob back to its initial
282
+ * (empty) value, so the next connect call starts a fresh pairing and emits a
283
+ * `display_uri` instead of silently resuming a session that no longer works.
284
+ *
285
+ * @example
286
+ * ```ts
287
+ * await clearMetaMaskStorage(client);
288
+ * // Next connectWithMetaMaskUri call will emit a fresh URI immediately.
289
+ * ```
290
+ *
291
+ * @param client - The Dynamic client instance. Required (rather than
292
+ * defaulting to the last-initialized client) so a storage-clear failure is
293
+ * always logged against the client that actually requested it.
294
+ * @returns A promise that resolves once the blob is reset. Never rejects -- a
295
+ * storage failure must not take down the connect flow that depends on it.
296
+ * @see connectWithMetaMaskUri
297
+ * @see createMetaMaskStorageClient
298
+ * @notInstrumented
299
+ */
300
+ const clearMetaMaskStorage = async (client) => {
301
+ const { logger, storage } = getCore(client);
302
+ try {
303
+ await queueMetaMaskStorageWrite(() => storage.setItem(metaMaskStoreStorageKeySchema, createInitialMetaMaskStoreValue()), client);
304
+ logger.debug("[MetaMask:Session] Cleared persisted store");
305
+ } catch (error) {
306
+ logger.warn(FAILED_TO_CLEAR_STORAGE_MESSAGE, { error });
307
+ }
308
+ };
309
+
310
+ //#endregion
311
+ //#region src/errors/MetaMaskStorageError/MetaMaskStorageError.ts
312
+ /** Thrown when the underlying storage service rejects a read/write/delete for the MetaMask multichain client's own store (e.g. quota exceeded, private-browsing restrictions). */
313
+ var MetaMaskStorageError = class extends BaseError {
314
+ constructor({ cause, key, operation }) {
315
+ super({
316
+ cause: cause instanceof Error ? cause : null,
317
+ code: "metamask_storage_error",
318
+ docsUrl: null,
319
+ name: "MetaMaskStorageError",
320
+ shortMessage: `Failed to ${operation} MetaMask multichain client storage key "${key}"`
321
+ });
322
+ }
323
+ };
324
+
325
+ //#endregion
326
+ //#region src/metaMaskStorage/createMetaMaskStorageClient/createMetaMaskStorageClient.ts
327
+ const TRANSPORT_TYPE_KEY = "multichain-transport";
328
+ const EXTENSION_ID_KEY = "extensionId";
329
+ /** Maps this SDK's platform literal onto the one `@metamask/connect-multichain`'s `StoreAdapter` expects. */
330
+ const toStoreAdapterPlatform = (platform) => platform === "react-native" ? "rn" : "web";
331
+ /**
332
+ * Builds a storage-service-backed store for the MetaMask multichain client,
333
+ * in place of its own default web adapter (IndexedDB) and React Native
334
+ * adapter (raw `AsyncStorage`), so every platform persists through the same
335
+ * core `Storage` service and `clearMetaMaskStorage` can reliably wipe it.
336
+ *
337
+ * The SDK controls every key name it reads/writes through `adapter`,
338
+ * including ones that embed an unpredictable id (`session:<id>`), so every
339
+ * key it uses is folded into a single stored blob (`metaMaskStoreStorageKeySchema`)
340
+ * rather than one storage key per SDK key. `DEBUG` is never persisted at all,
341
+ * answered live from the client's own logger.
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * const storeClient = createMetaMaskStorageClient(client);
346
+ * const multichainClient = createMultichainClient({ storage: storeClient });
347
+ * ```
348
+ *
349
+ * @returns A `StoreClient` backed by the client's core `Storage` service.
350
+ * @throws {MetaMaskStorageError} If the underlying `Storage` service's
351
+ * `getItem`/`setItem` calls fail for any of the adapter's read/write/delete
352
+ * operations.
353
+ * @see createInitialMetaMaskStoreValue
354
+ * @see clearMetaMaskStorage
355
+ * @notInstrumented
356
+ */
357
+ const createMetaMaskStorageClient = (client) => {
358
+ const { logger, storage } = getCore(client);
359
+ const read = async (key) => {
360
+ try {
361
+ const { entries } = await storage.getItem(metaMaskStoreStorageKeySchema) ?? createInitialMetaMaskStoreValue();
362
+ return entries[key] ?? null;
363
+ } catch (error) {
364
+ throw new MetaMaskStorageError({
365
+ cause: error,
366
+ key,
367
+ operation: "get"
368
+ });
369
+ }
370
+ };
371
+ const write = async (key, value) => {
372
+ try {
373
+ await queueMetaMaskStorageWrite(async () => {
374
+ const current = await storage.getItem(metaMaskStoreStorageKeySchema) ?? createInitialMetaMaskStoreValue();
375
+ await storage.setItem(metaMaskStoreStorageKeySchema, {
376
+ ...current,
377
+ entries: {
378
+ ...current.entries,
379
+ [key]: value
380
+ }
381
+ });
382
+ }, client);
383
+ } catch (error) {
384
+ throw new MetaMaskStorageError({
385
+ cause: error,
386
+ key,
387
+ operation: "set"
388
+ });
389
+ }
390
+ };
391
+ const remove = async (key) => {
392
+ try {
393
+ await queueMetaMaskStorageWrite(async () => {
394
+ const current = await storage.getItem(metaMaskStoreStorageKeySchema) ?? createInitialMetaMaskStoreValue();
395
+ const remainingEntries = Object.fromEntries(Object.entries(current.entries).filter(([entryKey]) => entryKey !== key));
396
+ await storage.setItem(metaMaskStoreStorageKeySchema, {
397
+ ...current,
398
+ entries: remainingEntries
399
+ });
400
+ }, client);
401
+ } catch (error) {
402
+ throw new MetaMaskStorageError({
403
+ cause: error,
404
+ key,
405
+ operation: "delete"
406
+ });
407
+ }
408
+ };
409
+ const adapter = {
410
+ delete: remove,
411
+ get: read,
412
+ platform: toStoreAdapterPlatform(getPlatform()),
413
+ set: write
414
+ };
415
+ return {
416
+ adapter,
417
+ getAnonId: () => queueMetaMaskStorageWrite(async () => {
418
+ const current = await storage.getItem(metaMaskStoreStorageKeySchema) ?? createInitialMetaMaskStoreValue();
419
+ const existing = current.entries[ANON_ID_ENTRY_KEY];
420
+ if (existing) return existing;
421
+ const anonId = crypto.randomUUID();
422
+ await storage.setItem(metaMaskStoreStorageKeySchema, {
423
+ ...current,
424
+ entries: {
425
+ ...current.entries,
426
+ [ANON_ID_ENTRY_KEY]: anonId
427
+ }
428
+ });
429
+ return anonId;
430
+ }, client),
431
+ getDebug: () => Promise.resolve(logger.isEnabled("debug") ? "metamask-sdk:*" : null),
432
+ getExtensionId: () => adapter.get(EXTENSION_ID_KEY),
433
+ getTransportType: async () => {
434
+ const value = await adapter.get(TRANSPORT_TYPE_KEY);
435
+ return value ? getTransportType(value) : null;
436
+ },
437
+ removeAnonId: () => remove(ANON_ID_ENTRY_KEY),
438
+ removeExtensionId: () => adapter.delete(EXTENSION_ID_KEY),
439
+ removeTransportType: () => adapter.delete(TRANSPORT_TYPE_KEY),
440
+ setAnonId: (anonId) => write(ANON_ID_ENTRY_KEY, anonId),
441
+ setExtensionId: (extensionId) => adapter.set(EXTENSION_ID_KEY, extensionId),
442
+ setTransportType: (transportType) => adapter.set(TRANSPORT_TYPE_KEY, transportType)
443
+ };
444
+ };
445
+
446
+ //#endregion
447
+ //#region src/utils/clearMetaMaskSdkSingleton/clearMetaMaskSdkSingleton.ts
448
+ const METAMASK_SDK_SINGLETON_GLOBAL_KEY = "__METAMASK_CONNECT_MULTICHAIN_SINGLETON__";
449
+ /**
450
+ * Deletes the upstream `@metamask/connect-multichain` SDK's own cached
451
+ * client instance off `globalThis`.
452
+ *
453
+ * The SDK's `createMultichainClient` reuses a private `globalThis` singleton
454
+ * and only merges in a subset of options on repeat calls -- notably not
455
+ * `storage` -- so it keeps talking to its original `StoreAdapter` even after
456
+ * a fresh `clearMetaMaskStorage` call. Deleting this key forces a genuinely
457
+ * new instance (and storage adapter) on the next `createMultichainClient` call.
458
+ *
459
+ * @example
460
+ * ```ts
461
+ * clearMetaMaskSdkSingleton();
462
+ * // Next createMultichainClient call builds a fresh client and adapter.
463
+ * ```
464
+ *
465
+ * @returns Nothing -- the singleton key is deleted synchronously.
466
+ * @see createMetaMaskClientInstanceManager
467
+ * @see clearMetaMaskStorage
468
+ * @notInstrumented
469
+ */
470
+ const clearMetaMaskSdkSingleton = () => {
471
+ delete globalThis[METAMASK_SDK_SINGLETON_GLOBAL_KEY];
472
+ };
473
+
474
+ //#endregion
475
+ //#region src/utils/createMetaMaskPreferredOpenLink/createMetaMaskPreferredOpenLink.ts
476
+ /**
477
+ * Builds the `preferredOpenLink` callback handed to `@metamask/connect-multichain`,
478
+ * routing wallet deeplinks through the core `deeplink` service instead of the
479
+ * SDK's own default handling, so every platform (web, React Native, Expo)
480
+ * opens them the same way the rest of the SDK does.
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * const preferredOpenLink = createMetaMaskPreferredOpenLink(client);
485
+ * preferredOpenLink('https://metamask.app.link/...');
486
+ * ```
487
+ *
488
+ * @returns A callback that opens a MetaMask-issued deeplink through the
489
+ * core `deeplink` service.
490
+ * @see createMetaMaskClientInstanceManager
491
+ * @see createMetaMaskStorageClient
492
+ * @notInstrumented
493
+ */
494
+ const createMetaMaskPreferredOpenLink = (client) => (deeplink) => {
495
+ const { logger, openDeeplink } = getCore(client);
496
+ logger.debug("[MetaMask:Client] Opening MetaMask deeplink", { deeplink });
497
+ openDeeplink(deeplink).then(() => logger.debug("[MetaMask:Client] Deeplink opened")).catch((error) => logger.error("[MetaMask:Client] Failed to open deeplink", {
498
+ deeplink,
499
+ error
500
+ }));
501
+ };
502
+
503
+ //#endregion
504
+ //#region src/metaMaskClientInstanceManager/createMetaMaskClientInstanceManager/createMetaMaskClientInstanceManager.ts
505
+ /**
506
+ * Creates the per-client manager that lazily builds, tears down, and rebuilds
507
+ * the shared `@metamask/connect-multichain` client instance, and forwards its
508
+ * `display_uri` event to callers that don't hold a reference to the raw SDK
509
+ * client.
510
+ *
511
+ * @example
512
+ * ```ts
513
+ * const manager = createMetaMaskClientInstanceManager(client);
514
+ * const metaMaskClient = await manager.getMetaMaskClient();
515
+ * ```
516
+ *
517
+ * @returns A `MetaMaskClientInstanceManager` scoped to `client`.
518
+ * @throws {MetaMaskClientNotInitializedError} From `getMetaMaskClient` if a
519
+ * build was never started -- a defensive guard, since `refreshMetaMaskClient`
520
+ * always starts one. A build that starts but fails instead rejects
521
+ * `getMetaMaskClient()` with its original error.
522
+ * @see createMetaMaskStorageClient
523
+ * @see clearMetaMaskStorage
524
+ * @notInstrumented
525
+ */
526
+ const createMetaMaskClientInstanceManager = (client) => {
527
+ let metaMaskClientPromise;
528
+ let clearEventListeners;
529
+ const { logger } = getCore(client);
530
+ const events = createEventEmitter();
531
+ const refreshMetaMaskClient = () => {
532
+ if (metaMaskClientPromise) return;
533
+ const { appName, appLogoUrl, appUniversalLink, appNativeLink } = getAppMetadata(client);
534
+ const supportedNetworks = getMetaMaskSupportedNetworks(client);
535
+ const nativeScheme = getNativeLinkScheme({ nativeLink: appNativeLink });
536
+ const debug = logger.isEnabled("debug");
537
+ logger.debug("[MetaMask:Client] Creating MetaMask client", {
538
+ appLogoUrl,
539
+ appName,
540
+ appUniversalLink,
541
+ debug,
542
+ nativeScheme,
543
+ supportedNetworks
544
+ });
545
+ if (clearEventListeners) clearEventListeners();
546
+ clearMetaMaskSdkSingleton();
547
+ const builtClientPromise = createMultichainClient({
548
+ api: { supportedNetworks },
549
+ dapp: {
550
+ iconUrl: appLogoUrl,
551
+ name: appName,
552
+ nativeScheme,
553
+ url: appUniversalLink ?? ""
554
+ },
555
+ debug,
556
+ mobile: { preferredOpenLink: createMetaMaskPreferredOpenLink(client) },
557
+ storage: createMetaMaskStorageClient(client),
558
+ ui: { headless: true }
559
+ }).then((metaMaskClient) => {
560
+ const createEventMapper = (event) => {
561
+ return (...args) => {
562
+ logger.debug(`[MetaMask:Events] ${event} event received`, { args });
563
+ events.emit(event, ...args);
564
+ };
565
+ };
566
+ const onDisplayUri = createEventMapper("display_uri");
567
+ metaMaskClient.on("display_uri", onDisplayUri);
568
+ const detachListeners = () => {
569
+ metaMaskClient.off("display_uri", onDisplayUri);
570
+ };
571
+ if (metaMaskClientPromise !== builtClientPromise) {
572
+ detachListeners();
573
+ return metaMaskClient;
574
+ }
575
+ clearEventListeners = detachListeners;
576
+ return metaMaskClient;
577
+ });
578
+ metaMaskClientPromise = builtClientPromise;
579
+ builtClientPromise.catch(() => {
580
+ if (metaMaskClientPromise === builtClientPromise) metaMaskClientPromise = null;
581
+ });
582
+ };
583
+ return {
584
+ clearMetaMaskStorageAndInstance: async () => {
585
+ await clearMetaMaskStorage(client);
586
+ metaMaskClientPromise = null;
587
+ refreshMetaMaskClient();
588
+ },
589
+ events,
590
+ getMetaMaskClient: async () => {
591
+ if (!metaMaskClientPromise) refreshMetaMaskClient();
592
+ if (!metaMaskClientPromise) throw new MetaMaskClientNotInitializedError();
593
+ return metaMaskClientPromise;
594
+ }
595
+ };
596
+ };
597
+
598
+ //#endregion
599
+ //#region src/metaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager/getOrCreateMetaMaskClientInstanceManager.ts
600
+ const META_MASK_CLIENT_INSTANCE_MANAGER_KEY = "META_MASK_CLIENT_INSTANCE_MANAGER";
601
+ /**
602
+ * Returns this client's `MetaMaskClientInstanceManager`, creating it on first
603
+ * access. Every chain's MetaMask wallet provider (EVM, Solana, ...) calls
604
+ * this to reach the SAME manager -- and therefore the same shared multichain
605
+ * session -- rather than each building its own.
606
+ *
607
+ * @example
608
+ * ```ts
609
+ * const manager = getOrCreateMetaMaskClientInstanceManager(client);
610
+ * const metaMaskClient = await manager.getMetaMaskClient();
611
+ * ```
612
+ * @param client - The Dynamic client instance.
613
+ * @returns The client's `MetaMaskClientInstanceManager`.
614
+ * @see createMetaMaskClientInstanceManager
615
+ * @see disconnectMetaMaskWalletProvider
616
+ */
617
+ const getOrCreateMetaMaskClientInstanceManager = createRuntimeServiceAccessKey(META_MASK_CLIENT_INSTANCE_MANAGER_KEY, (client) => createMetaMaskClientInstanceManager(client));
618
+
619
+ //#endregion
620
+ export { UnsupportedMetaMaskChainError as a, name as c, getMetaMaskScopeForNetworkId as i, version as l, clearMetaMaskStorage as n, SUPPORTED_METAMASK_CHAINS as o, getMetaMaskScopes as r, MetaMaskUnscopedNetworkError as s, getOrCreateMetaMaskClientInstanceManager as t };
621
+ //# sourceMappingURL=getOrCreateMetaMaskClientInstanceManager-DbH0IJjn.native.esm.js.map