@dynamic-labs/global-wallet 4.70.0 → 4.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.70.0";
6
+ var version = "4.72.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.70.0";
2
+ var version = "4.72.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/global-wallet",
3
- "version": "4.70.0",
3
+ "version": "4.72.0",
4
4
  "description": "A React SDK for implementing wallet web3 authentication and authorization to your website.",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -14,6 +14,11 @@
14
14
  "import": "./src/index.js",
15
15
  "require": "./src/index.cjs"
16
16
  },
17
+ "./solana": {
18
+ "types": "./src/solana/index.d.ts",
19
+ "import": "./src/solana/index.js",
20
+ "require": "./src/solana/index.cjs"
21
+ },
17
22
  "./package.json": "./package.json"
18
23
  },
19
24
  "homepage": "https://www.dynamic.xyz/",
@@ -22,10 +27,10 @@
22
27
  "@walletconnect/utils": "2.21.5",
23
28
  "@reown/walletkit": "1.1.2",
24
29
  "jsqr": "1.4.0",
25
- "@dynamic-labs/assert-package-version": "4.70.0",
26
- "@dynamic-labs/ethereum-core": "4.70.0",
27
- "@dynamic-labs/utils": "4.70.0",
28
- "@dynamic-labs/wallet-connector-core": "4.70.0"
30
+ "@dynamic-labs/assert-package-version": "4.72.0",
31
+ "@dynamic-labs/ethereum-core": "4.72.0",
32
+ "@dynamic-labs/utils": "4.72.0",
33
+ "@dynamic-labs/wallet-connector-core": "4.72.0"
29
34
  },
30
35
  "overrides": {
31
36
  "elliptic": "6.6.1",
@@ -33,7 +38,17 @@
33
38
  "node-forge": "^1.3.2"
34
39
  },
35
40
  "peerDependencies": {
41
+ "@solana/web3.js": "1.98.1",
42
+ "bs58": "5.0.0",
36
43
  "react": ">=18.0.0 <20.0.0",
37
- "viem": "^2.28.4"
44
+ "viem": "^2.45.3"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@solana/web3.js": {
48
+ "optional": true
49
+ },
50
+ "bs58": {
51
+ "optional": true
52
+ }
38
53
  }
39
54
  }
@@ -38,6 +38,25 @@ const buildCombinedNamespaces = (params) => {
38
38
  methods: Array.from(methods),
39
39
  };
40
40
  };
41
+ const buildCombinedNamespacesForKey = (params, key) => {
42
+ var _a, _b;
43
+ const required = ((_a = params.requiredNamespaces) === null || _a === void 0 ? void 0 : _a[key]) || {};
44
+ const optional = ((_b = params.optionalNamespaces) === null || _b === void 0 ? void 0 : _b[key]) || {};
45
+ return {
46
+ chains: [
47
+ ...new Set([...(required.chains || []), ...(optional.chains || [])]),
48
+ ],
49
+ events: [
50
+ ...new Set([...(required.events || []), ...(optional.events || [])]),
51
+ ],
52
+ methods: [
53
+ ...new Set([...(required.methods || []), ...(optional.methods || [])]),
54
+ ],
55
+ };
56
+ };
57
+ const JSON_RPC_VERSION = '2.0';
58
+ const JSON_RPC_INTERNAL_ERROR_CODE = 5000;
59
+ const SESSION_MESSAGE_LISTENER_SCOPE = 'sessionMessageListener';
41
60
  const globalConnectorState = {
42
61
  isGlobalTransaction: false,
43
62
  };
@@ -47,6 +66,7 @@ const GlobalWalletExtension = {
47
66
  let WCListenersOn = false;
48
67
  let pendingSessionProposal;
49
68
  let signer = (yield connector.getSigner());
69
+ const { storage, onSessionRequest, solanaRequestHandler, solanaNamespaceBuilder, } = settings;
50
70
  /**
51
71
  * Defines a shared property 'isGlobalTransaction' on the connector instance.
52
72
  *
@@ -82,18 +102,28 @@ const GlobalWalletExtension = {
82
102
  });
83
103
  return;
84
104
  }
85
- const { chains, events, methods } = buildCombinedNamespaces(params);
86
- const address = yield connector.getAddress();
105
+ const supportedNamespaces = {};
106
+ // Build EIP-155 namespace if requested
107
+ const eip155Namespace = buildCombinedNamespacesForKey(params, 'eip155');
108
+ if (eip155Namespace.chains.length > 0) {
109
+ const evmAddress = yield connector.getAddress();
110
+ supportedNamespaces.eip155 = {
111
+ accounts: eip155Namespace.chains.map((chain) => `${chain}:${evmAddress}`),
112
+ chains: eip155Namespace.chains,
113
+ events: eip155Namespace.events,
114
+ methods: eip155Namespace.methods,
115
+ };
116
+ }
117
+ // Build Solana namespace via extension if available
118
+ if (solanaNamespaceBuilder) {
119
+ const solanaNamespace = yield solanaNamespaceBuilder(params);
120
+ if (solanaNamespace) {
121
+ Object.assign(supportedNamespaces, solanaNamespace);
122
+ }
123
+ }
87
124
  const approvedNamespaces = utils$1.buildApprovedNamespaces({
88
125
  proposal: params,
89
- supportedNamespaces: {
90
- eip155: {
91
- accounts: chains.map((chain) => `${chain}:${address}`),
92
- chains,
93
- events,
94
- methods,
95
- },
96
- },
126
+ supportedNamespaces,
97
127
  });
98
128
  yield web3wallet.approveSession({
99
129
  id,
@@ -114,7 +144,7 @@ const GlobalWalletExtension = {
114
144
  getPendingPairing: () => pendingSessionProposal,
115
145
  getWeb3Wallet: () => web3walletOrUndefined,
116
146
  initWeb3Wallet: () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
117
- web3walletOrUndefined = yield getWalletKitSingleton.getWalletKitSingleton(settings.walletConnectDappProjectId);
147
+ web3walletOrUndefined = yield getWalletKitSingleton.getWalletKitSingleton(settings.walletConnectDappProjectId, storage ? { storage } : undefined);
118
148
  }),
119
149
  initializeListeners: () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
120
150
  if (WCListenersOn)
@@ -122,66 +152,69 @@ const GlobalWalletExtension = {
122
152
  const onSessionProposal = (_a) => _tslib.__awaiter(void 0, [_a], void 0, function* ({ params, id }) {
123
153
  pendingSessionProposal = { id, params };
124
154
  });
125
- const sessionMessageListener = (_b) => _tslib.__awaiter(void 0, [_b], void 0, function* ({ params, id, topic }) {
155
+ const handleRequest = (id, handler) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
156
+ let response;
157
+ try {
158
+ connector.isGlobalTransaction = true;
159
+ const result = yield handler();
160
+ response = { id, jsonrpc: JSON_RPC_VERSION, result };
161
+ }
162
+ catch (e) {
163
+ const errorMessage = e instanceof Error ? e.message : String(e);
164
+ walletConnectorCore.logger.error('[GlobalWalletExtension] Error handling session request', {
165
+ error: errorMessage,
166
+ id,
167
+ stack: e instanceof Error ? e.stack : undefined,
168
+ });
169
+ response = {
170
+ error: {
171
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
172
+ message: errorMessage,
173
+ },
174
+ id,
175
+ jsonrpc: JSON_RPC_VERSION,
176
+ };
177
+ }
178
+ finally {
179
+ connector.isGlobalTransaction = false;
180
+ }
181
+ return response;
182
+ });
183
+ const handleEvmRequest = (_b) => _tslib.__awaiter(void 0, [_b], void 0, function* ({ params, id, topic, }) {
126
184
  const { request, chainId } = params;
127
- // since we cant listen to chain events from the dapp, just change when a request comes in
128
185
  const [requestParamsMessage, typedDataToSign] = request.params;
129
- const handleRequest = (handler) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
130
- let response;
131
- try {
132
- // tell wallet ui utils that the transaction is coming from global connectivity
133
- // so we can force the confirmation ui to be on
134
- connector.isGlobalTransaction = true;
135
- const result = yield handler();
136
- response = { id, jsonrpc: '2.0', result };
137
- }
138
- catch (e) {
139
- walletConnectorCore.logger.error('Error listening to session request', e);
140
- response = {
141
- error: {
142
- code: 5000,
143
- message: 'User rejected.',
144
- },
145
- id,
146
- jsonrpc: '2.0',
147
- };
148
- }
149
- finally {
150
- connector.isGlobalTransaction = false;
151
- }
152
- return response;
153
- });
154
186
  let response;
155
187
  const handleEthSignTypedDataV4 = () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
156
- return handleRequest(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
188
+ return handleRequest(id, () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
157
189
  return signer.signTypedData(Object.assign(Object.assign({}, JSON.parse(typedDataToSign)), { account: requestParamsMessage }));
158
190
  }));
159
191
  });
160
192
  const handlePersonalSign = () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
161
193
  const message = Buffer.from(requestParamsMessage.slice(2), 'hex').toString('utf8');
162
- return handleRequest(() => {
194
+ return handleRequest(id, () => {
163
195
  const chainIdMatch = message.match(/Chain ID: (\d+)/);
164
- const chainId = chainIdMatch ? chainIdMatch[1] : undefined;
165
- if ('signMessageForChain' in connector && chainId) {
166
- return connector.signMessageForChain(message, chainId);
196
+ const matchedChainId = chainIdMatch ? chainIdMatch[1] : undefined;
197
+ if ('signMessageForChain' in connector && matchedChainId) {
198
+ return connector.signMessageForChain(message, matchedChainId);
167
199
  }
168
200
  return connector.signMessage(message);
169
201
  });
170
202
  });
171
203
  const handleEthSendTransaction = () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
172
- if (chainId) {
173
- yield connector.switchNetwork({
174
- networkChainId: chainId.split(':')[1],
175
- });
176
- }
177
- return handleRequest(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
204
+ return handleRequest(id, () => _tslib.__awaiter(void 0, void 0, void 0, function* () {
205
+ if (chainId) {
206
+ yield connector.switchNetwork({
207
+ networkChainId: chainId.split(':')[1],
208
+ });
209
+ // Re-obtain the signer after switching networks
210
+ // as the wallet client is chain-specific
211
+ signer = (yield connector.getSigner());
212
+ }
178
213
  const estimatedFeesPerGas = yield (yield connector.getPublicClient()).estimateFeesPerGas();
179
214
  const type = (estimatedFeesPerGas === null || estimatedFeesPerGas === void 0 ? void 0 : estimatedFeesPerGas.maxFeePerGas)
180
215
  ? 'eip1559'
181
216
  : 'legacy';
182
- return signer.sendTransaction(ethereumCore.unFormatTransaction(Object.assign(Object.assign({}, requestParamsMessage), {
183
- // strip the gasPrice if it should be a eip1559 tx or tx will fail
184
- gasPrice: estimatedFeesPerGas && estimatedFeesPerGas.maxFeePerGas
217
+ return signer.sendTransaction(ethereumCore.unFormatTransaction(Object.assign(Object.assign({}, requestParamsMessage), { gasPrice: estimatedFeesPerGas && estimatedFeesPerGas.maxFeePerGas
185
218
  ? undefined
186
219
  : requestParamsMessage.gasPrice, type, value: requestParamsMessage.value })));
187
220
  }));
@@ -199,19 +232,116 @@ const GlobalWalletExtension = {
199
232
  else if (request.method === 'eth_sendTransaction') {
200
233
  response = yield handleEthSendTransaction();
201
234
  }
202
- if (response) {
203
- const web3wallet = globalWallet.assertWeb3walletDefined('sessionMessageListener');
204
- yield web3wallet.respondSessionRequest({
205
- response,
235
+ else {
236
+ response = {
237
+ error: {
238
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
239
+ message: `Unsupported EVM method: ${request.method}`,
240
+ },
241
+ id,
242
+ jsonrpc: JSON_RPC_VERSION,
243
+ };
244
+ }
245
+ return { response, topic };
246
+ });
247
+ const sessionMessageListener = (_c) => _tslib.__awaiter(void 0, [_c], void 0, function* ({ params, id, topic }) {
248
+ var _d;
249
+ walletConnectorCore.logger.debug('[GlobalWalletExtension] session_request received', {
250
+ id,
251
+ method: (_d = params === null || params === void 0 ? void 0 : params.request) === null || _d === void 0 ? void 0 : _d.method,
252
+ topic,
253
+ });
254
+ try {
255
+ // If an approval callback is provided (mobile flow), wait for native approval
256
+ if (onSessionRequest) {
257
+ const approved = yield onSessionRequest({ id, params, topic });
258
+ if (!approved) {
259
+ walletConnectorCore.logger.debug('[GlobalWalletExtension] session_request rejected by user', { id, topic });
260
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
261
+ yield web3wallet.respondSessionRequest({
262
+ response: {
263
+ error: {
264
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
265
+ message: 'User rejected.',
266
+ },
267
+ id,
268
+ jsonrpc: JSON_RPC_VERSION,
269
+ },
270
+ topic,
271
+ });
272
+ return;
273
+ }
274
+ walletConnectorCore.logger.debug('[GlobalWalletExtension] session_request approved by user', { id, topic });
275
+ }
276
+ const { chainId } = params;
277
+ let result;
278
+ if (chainId === null || chainId === void 0 ? void 0 : chainId.startsWith('solana:')) {
279
+ if (solanaRequestHandler) {
280
+ result = yield solanaRequestHandler({
281
+ handleRequest,
282
+ id,
283
+ params,
284
+ topic,
285
+ });
286
+ }
287
+ else {
288
+ result = {
289
+ response: {
290
+ error: {
291
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
292
+ message: 'Unsupported chain: solana',
293
+ },
294
+ id,
295
+ jsonrpc: JSON_RPC_VERSION,
296
+ },
297
+ topic,
298
+ };
299
+ }
300
+ }
301
+ else {
302
+ result = yield handleEvmRequest({ id, params, topic });
303
+ }
304
+ walletConnectorCore.logger.debug('[GlobalWalletExtension] session_request handled', {
305
+ hasResponse: Boolean(result === null || result === void 0 ? void 0 : result.response),
306
+ id,
206
307
  topic,
207
308
  });
309
+ if (result === null || result === void 0 ? void 0 : result.response) {
310
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
311
+ yield web3wallet.respondSessionRequest({
312
+ response: result.response,
313
+ topic: result.topic,
314
+ });
315
+ walletConnectorCore.logger.debug('[GlobalWalletExtension] respondSessionRequest sent', { id, topic });
316
+ }
317
+ }
318
+ catch (e) {
319
+ const errorMessage = e instanceof Error ? e.message : String(e);
320
+ walletConnectorCore.logger.error('[GlobalWalletExtension] Unhandled error in session_request', { error: errorMessage, id, topic });
321
+ try {
322
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
323
+ yield web3wallet.respondSessionRequest({
324
+ response: {
325
+ error: {
326
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
327
+ message: errorMessage,
328
+ },
329
+ id,
330
+ jsonrpc: JSON_RPC_VERSION,
331
+ },
332
+ topic,
333
+ });
334
+ }
335
+ catch (_e) {
336
+ // Best-effort error response
337
+ }
208
338
  }
209
339
  });
210
340
  yield globalWallet.initWeb3Wallet();
211
341
  const web3wallet = globalWallet.assertWeb3walletDefined('initializeListeners');
212
342
  web3wallet.on('session_proposal', onSessionProposal);
213
343
  web3wallet.on('session_request', sessionMessageListener);
214
- connector.on('chainChange', (_c) => _tslib.__awaiter(void 0, [_c], void 0, function* ({ chain }) {
344
+ connector.on('chainChange', (_f) => _tslib.__awaiter(void 0, [_f], void 0, function* ({ chain }) {
215
345
  const connections = yield web3wallet.getActiveSessions();
216
346
  const connectionTopics = Object.keys(connections !== null && connections !== void 0 ? connections : {});
217
347
  // tell all connected dapps that the chain has changed
@@ -242,3 +372,4 @@ const GlobalWalletExtension = {
242
372
 
243
373
  exports.GlobalWalletExtension = GlobalWalletExtension;
244
374
  exports.buildCombinedNamespaces = buildCombinedNamespaces;
375
+ exports.buildCombinedNamespacesForKey = buildCombinedNamespacesForKey;
@@ -20,9 +20,42 @@ declare module '@dynamic-labs/wallet-connector-core' {
20
20
  }
21
21
  }
22
22
  }
23
+ type CombinedNamespace = {
24
+ chains: string[];
25
+ events: string[];
26
+ methods: string[];
27
+ };
23
28
  export declare const buildCombinedNamespaces: (params: any) => {
24
29
  chains: any[];
25
30
  events: any[];
26
31
  methods: any[];
27
32
  };
33
+ export declare const buildCombinedNamespacesForKey: (params: any, key: string) => CombinedNamespace;
34
+ export type WalletKitStorage = {
35
+ getKeys(): Promise<string[]>;
36
+ getEntries<T = unknown>(): Promise<[string, T][]>;
37
+ getItem<T = unknown>(key: string): Promise<T | undefined>;
38
+ setItem<T = unknown>(key: string, value: T): Promise<void>;
39
+ removeItem(key: string): Promise<void>;
40
+ };
41
+ export type GlobalWalletExtensionSettings = {
42
+ walletConnectDappProjectId: string;
43
+ storage?: WalletKitStorage;
44
+ onSessionRequest?: (event: {
45
+ id: number;
46
+ topic: string;
47
+ params: any;
48
+ }) => Promise<boolean>;
49
+ solanaRequestHandler?: (args: {
50
+ params: any;
51
+ id: number;
52
+ topic: string;
53
+ handleRequest: (id: number, handler: () => Promise<any>) => Promise<any>;
54
+ }) => Promise<{
55
+ response: any;
56
+ topic: string;
57
+ }>;
58
+ solanaNamespaceBuilder?: (params: any) => Promise<Record<string, any> | undefined>;
59
+ };
28
60
  export declare const GlobalWalletExtension: WalletConnectorExtension;
61
+ export {};
@@ -30,6 +30,25 @@ const buildCombinedNamespaces = (params) => {
30
30
  methods: Array.from(methods),
31
31
  };
32
32
  };
33
+ const buildCombinedNamespacesForKey = (params, key) => {
34
+ var _a, _b;
35
+ const required = ((_a = params.requiredNamespaces) === null || _a === void 0 ? void 0 : _a[key]) || {};
36
+ const optional = ((_b = params.optionalNamespaces) === null || _b === void 0 ? void 0 : _b[key]) || {};
37
+ return {
38
+ chains: [
39
+ ...new Set([...(required.chains || []), ...(optional.chains || [])]),
40
+ ],
41
+ events: [
42
+ ...new Set([...(required.events || []), ...(optional.events || [])]),
43
+ ],
44
+ methods: [
45
+ ...new Set([...(required.methods || []), ...(optional.methods || [])]),
46
+ ],
47
+ };
48
+ };
49
+ const JSON_RPC_VERSION = '2.0';
50
+ const JSON_RPC_INTERNAL_ERROR_CODE = 5000;
51
+ const SESSION_MESSAGE_LISTENER_SCOPE = 'sessionMessageListener';
33
52
  const globalConnectorState = {
34
53
  isGlobalTransaction: false,
35
54
  };
@@ -39,6 +58,7 @@ const GlobalWalletExtension = {
39
58
  let WCListenersOn = false;
40
59
  let pendingSessionProposal;
41
60
  let signer = (yield connector.getSigner());
61
+ const { storage, onSessionRequest, solanaRequestHandler, solanaNamespaceBuilder, } = settings;
42
62
  /**
43
63
  * Defines a shared property 'isGlobalTransaction' on the connector instance.
44
64
  *
@@ -74,18 +94,28 @@ const GlobalWalletExtension = {
74
94
  });
75
95
  return;
76
96
  }
77
- const { chains, events, methods } = buildCombinedNamespaces(params);
78
- const address = yield connector.getAddress();
97
+ const supportedNamespaces = {};
98
+ // Build EIP-155 namespace if requested
99
+ const eip155Namespace = buildCombinedNamespacesForKey(params, 'eip155');
100
+ if (eip155Namespace.chains.length > 0) {
101
+ const evmAddress = yield connector.getAddress();
102
+ supportedNamespaces.eip155 = {
103
+ accounts: eip155Namespace.chains.map((chain) => `${chain}:${evmAddress}`),
104
+ chains: eip155Namespace.chains,
105
+ events: eip155Namespace.events,
106
+ methods: eip155Namespace.methods,
107
+ };
108
+ }
109
+ // Build Solana namespace via extension if available
110
+ if (solanaNamespaceBuilder) {
111
+ const solanaNamespace = yield solanaNamespaceBuilder(params);
112
+ if (solanaNamespace) {
113
+ Object.assign(supportedNamespaces, solanaNamespace);
114
+ }
115
+ }
79
116
  const approvedNamespaces = buildApprovedNamespaces({
80
117
  proposal: params,
81
- supportedNamespaces: {
82
- eip155: {
83
- accounts: chains.map((chain) => `${chain}:${address}`),
84
- chains,
85
- events,
86
- methods,
87
- },
88
- },
118
+ supportedNamespaces,
89
119
  });
90
120
  yield web3wallet.approveSession({
91
121
  id,
@@ -106,7 +136,7 @@ const GlobalWalletExtension = {
106
136
  getPendingPairing: () => pendingSessionProposal,
107
137
  getWeb3Wallet: () => web3walletOrUndefined,
108
138
  initWeb3Wallet: () => __awaiter(void 0, void 0, void 0, function* () {
109
- web3walletOrUndefined = yield getWalletKitSingleton(settings.walletConnectDappProjectId);
139
+ web3walletOrUndefined = yield getWalletKitSingleton(settings.walletConnectDappProjectId, storage ? { storage } : undefined);
110
140
  }),
111
141
  initializeListeners: () => __awaiter(void 0, void 0, void 0, function* () {
112
142
  if (WCListenersOn)
@@ -114,66 +144,69 @@ const GlobalWalletExtension = {
114
144
  const onSessionProposal = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, id }) {
115
145
  pendingSessionProposal = { id, params };
116
146
  });
117
- const sessionMessageListener = (_b) => __awaiter(void 0, [_b], void 0, function* ({ params, id, topic }) {
147
+ const handleRequest = (id, handler) => __awaiter(void 0, void 0, void 0, function* () {
148
+ let response;
149
+ try {
150
+ connector.isGlobalTransaction = true;
151
+ const result = yield handler();
152
+ response = { id, jsonrpc: JSON_RPC_VERSION, result };
153
+ }
154
+ catch (e) {
155
+ const errorMessage = e instanceof Error ? e.message : String(e);
156
+ logger.error('[GlobalWalletExtension] Error handling session request', {
157
+ error: errorMessage,
158
+ id,
159
+ stack: e instanceof Error ? e.stack : undefined,
160
+ });
161
+ response = {
162
+ error: {
163
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
164
+ message: errorMessage,
165
+ },
166
+ id,
167
+ jsonrpc: JSON_RPC_VERSION,
168
+ };
169
+ }
170
+ finally {
171
+ connector.isGlobalTransaction = false;
172
+ }
173
+ return response;
174
+ });
175
+ const handleEvmRequest = (_b) => __awaiter(void 0, [_b], void 0, function* ({ params, id, topic, }) {
118
176
  const { request, chainId } = params;
119
- // since we cant listen to chain events from the dapp, just change when a request comes in
120
177
  const [requestParamsMessage, typedDataToSign] = request.params;
121
- const handleRequest = (handler) => __awaiter(void 0, void 0, void 0, function* () {
122
- let response;
123
- try {
124
- // tell wallet ui utils that the transaction is coming from global connectivity
125
- // so we can force the confirmation ui to be on
126
- connector.isGlobalTransaction = true;
127
- const result = yield handler();
128
- response = { id, jsonrpc: '2.0', result };
129
- }
130
- catch (e) {
131
- logger.error('Error listening to session request', e);
132
- response = {
133
- error: {
134
- code: 5000,
135
- message: 'User rejected.',
136
- },
137
- id,
138
- jsonrpc: '2.0',
139
- };
140
- }
141
- finally {
142
- connector.isGlobalTransaction = false;
143
- }
144
- return response;
145
- });
146
178
  let response;
147
179
  const handleEthSignTypedDataV4 = () => __awaiter(void 0, void 0, void 0, function* () {
148
- return handleRequest(() => __awaiter(void 0, void 0, void 0, function* () {
180
+ return handleRequest(id, () => __awaiter(void 0, void 0, void 0, function* () {
149
181
  return signer.signTypedData(Object.assign(Object.assign({}, JSON.parse(typedDataToSign)), { account: requestParamsMessage }));
150
182
  }));
151
183
  });
152
184
  const handlePersonalSign = () => __awaiter(void 0, void 0, void 0, function* () {
153
185
  const message = Buffer.from(requestParamsMessage.slice(2), 'hex').toString('utf8');
154
- return handleRequest(() => {
186
+ return handleRequest(id, () => {
155
187
  const chainIdMatch = message.match(/Chain ID: (\d+)/);
156
- const chainId = chainIdMatch ? chainIdMatch[1] : undefined;
157
- if ('signMessageForChain' in connector && chainId) {
158
- return connector.signMessageForChain(message, chainId);
188
+ const matchedChainId = chainIdMatch ? chainIdMatch[1] : undefined;
189
+ if ('signMessageForChain' in connector && matchedChainId) {
190
+ return connector.signMessageForChain(message, matchedChainId);
159
191
  }
160
192
  return connector.signMessage(message);
161
193
  });
162
194
  });
163
195
  const handleEthSendTransaction = () => __awaiter(void 0, void 0, void 0, function* () {
164
- if (chainId) {
165
- yield connector.switchNetwork({
166
- networkChainId: chainId.split(':')[1],
167
- });
168
- }
169
- return handleRequest(() => __awaiter(void 0, void 0, void 0, function* () {
196
+ return handleRequest(id, () => __awaiter(void 0, void 0, void 0, function* () {
197
+ if (chainId) {
198
+ yield connector.switchNetwork({
199
+ networkChainId: chainId.split(':')[1],
200
+ });
201
+ // Re-obtain the signer after switching networks
202
+ // as the wallet client is chain-specific
203
+ signer = (yield connector.getSigner());
204
+ }
170
205
  const estimatedFeesPerGas = yield (yield connector.getPublicClient()).estimateFeesPerGas();
171
206
  const type = (estimatedFeesPerGas === null || estimatedFeesPerGas === void 0 ? void 0 : estimatedFeesPerGas.maxFeePerGas)
172
207
  ? 'eip1559'
173
208
  : 'legacy';
174
- return signer.sendTransaction(unFormatTransaction(Object.assign(Object.assign({}, requestParamsMessage), {
175
- // strip the gasPrice if it should be a eip1559 tx or tx will fail
176
- gasPrice: estimatedFeesPerGas && estimatedFeesPerGas.maxFeePerGas
209
+ return signer.sendTransaction(unFormatTransaction(Object.assign(Object.assign({}, requestParamsMessage), { gasPrice: estimatedFeesPerGas && estimatedFeesPerGas.maxFeePerGas
177
210
  ? undefined
178
211
  : requestParamsMessage.gasPrice, type, value: requestParamsMessage.value })));
179
212
  }));
@@ -191,19 +224,116 @@ const GlobalWalletExtension = {
191
224
  else if (request.method === 'eth_sendTransaction') {
192
225
  response = yield handleEthSendTransaction();
193
226
  }
194
- if (response) {
195
- const web3wallet = globalWallet.assertWeb3walletDefined('sessionMessageListener');
196
- yield web3wallet.respondSessionRequest({
197
- response,
227
+ else {
228
+ response = {
229
+ error: {
230
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
231
+ message: `Unsupported EVM method: ${request.method}`,
232
+ },
233
+ id,
234
+ jsonrpc: JSON_RPC_VERSION,
235
+ };
236
+ }
237
+ return { response, topic };
238
+ });
239
+ const sessionMessageListener = (_c) => __awaiter(void 0, [_c], void 0, function* ({ params, id, topic }) {
240
+ var _d;
241
+ logger.debug('[GlobalWalletExtension] session_request received', {
242
+ id,
243
+ method: (_d = params === null || params === void 0 ? void 0 : params.request) === null || _d === void 0 ? void 0 : _d.method,
244
+ topic,
245
+ });
246
+ try {
247
+ // If an approval callback is provided (mobile flow), wait for native approval
248
+ if (onSessionRequest) {
249
+ const approved = yield onSessionRequest({ id, params, topic });
250
+ if (!approved) {
251
+ logger.debug('[GlobalWalletExtension] session_request rejected by user', { id, topic });
252
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
253
+ yield web3wallet.respondSessionRequest({
254
+ response: {
255
+ error: {
256
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
257
+ message: 'User rejected.',
258
+ },
259
+ id,
260
+ jsonrpc: JSON_RPC_VERSION,
261
+ },
262
+ topic,
263
+ });
264
+ return;
265
+ }
266
+ logger.debug('[GlobalWalletExtension] session_request approved by user', { id, topic });
267
+ }
268
+ const { chainId } = params;
269
+ let result;
270
+ if (chainId === null || chainId === void 0 ? void 0 : chainId.startsWith('solana:')) {
271
+ if (solanaRequestHandler) {
272
+ result = yield solanaRequestHandler({
273
+ handleRequest,
274
+ id,
275
+ params,
276
+ topic,
277
+ });
278
+ }
279
+ else {
280
+ result = {
281
+ response: {
282
+ error: {
283
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
284
+ message: 'Unsupported chain: solana',
285
+ },
286
+ id,
287
+ jsonrpc: JSON_RPC_VERSION,
288
+ },
289
+ topic,
290
+ };
291
+ }
292
+ }
293
+ else {
294
+ result = yield handleEvmRequest({ id, params, topic });
295
+ }
296
+ logger.debug('[GlobalWalletExtension] session_request handled', {
297
+ hasResponse: Boolean(result === null || result === void 0 ? void 0 : result.response),
298
+ id,
198
299
  topic,
199
300
  });
301
+ if (result === null || result === void 0 ? void 0 : result.response) {
302
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
303
+ yield web3wallet.respondSessionRequest({
304
+ response: result.response,
305
+ topic: result.topic,
306
+ });
307
+ logger.debug('[GlobalWalletExtension] respondSessionRequest sent', { id, topic });
308
+ }
309
+ }
310
+ catch (e) {
311
+ const errorMessage = e instanceof Error ? e.message : String(e);
312
+ logger.error('[GlobalWalletExtension] Unhandled error in session_request', { error: errorMessage, id, topic });
313
+ try {
314
+ const web3wallet = globalWallet.assertWeb3walletDefined(SESSION_MESSAGE_LISTENER_SCOPE);
315
+ yield web3wallet.respondSessionRequest({
316
+ response: {
317
+ error: {
318
+ code: JSON_RPC_INTERNAL_ERROR_CODE,
319
+ message: errorMessage,
320
+ },
321
+ id,
322
+ jsonrpc: JSON_RPC_VERSION,
323
+ },
324
+ topic,
325
+ });
326
+ }
327
+ catch (_e) {
328
+ // Best-effort error response
329
+ }
200
330
  }
201
331
  });
202
332
  yield globalWallet.initWeb3Wallet();
203
333
  const web3wallet = globalWallet.assertWeb3walletDefined('initializeListeners');
204
334
  web3wallet.on('session_proposal', onSessionProposal);
205
335
  web3wallet.on('session_request', sessionMessageListener);
206
- connector.on('chainChange', (_c) => __awaiter(void 0, [_c], void 0, function* ({ chain }) {
336
+ connector.on('chainChange', (_f) => __awaiter(void 0, [_f], void 0, function* ({ chain }) {
207
337
  const connections = yield web3wallet.getActiveSessions();
208
338
  const connectionTopics = Object.keys(connections !== null && connections !== void 0 ? connections : {});
209
339
  // tell all connected dapps that the chain has changed
@@ -232,4 +362,4 @@ const GlobalWalletExtension = {
232
362
  name: 'global-wallet-extension',
233
363
  };
234
364
 
235
- export { GlobalWalletExtension, buildCombinedNamespaces };
365
+ export { GlobalWalletExtension, buildCombinedNamespaces, buildCombinedNamespacesForKey };
@@ -6,10 +6,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
6
6
  var _tslib = require('../../_virtual/_tslib.cjs');
7
7
  var walletkit = require('@reown/walletkit');
8
8
  var core = require('@walletconnect/core');
9
+ var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
9
10
  var MultipleWalletConnectProjectIdsError = require('../errors/MultipleWalletConnectProjectIdsError.cjs');
10
11
 
11
12
  let walletKitWithProjectIdPromise = undefined;
12
- const getWalletKitSingleton = (projectId) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
13
+ let initialStorageProvided = false;
14
+ const getWalletKitSingleton = (projectId, options) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
13
15
  if (walletKitWithProjectIdPromise) {
14
16
  const valueFromPreviousCall = yield walletKitWithProjectIdPromise;
15
17
  if (valueFromPreviousCall.projectId !== projectId) {
@@ -18,9 +20,20 @@ const getWalletKitSingleton = (projectId) => _tslib.__awaiter(void 0, void 0, vo
18
20
  originalProjectId: valueFromPreviousCall.projectId,
19
21
  });
20
22
  }
23
+ if (Boolean(options === null || options === void 0 ? void 0 : options.storage) !== initialStorageProvided) {
24
+ walletConnectorCore.logger.warn('[getWalletKitSingleton] storage option differs from the initial call and will be ignored. The singleton was already created ' +
25
+ (initialStorageProvided ? 'with' : 'without') +
26
+ ' a custom storage provider.');
27
+ }
21
28
  return valueFromPreviousCall.walletKit;
22
29
  }
23
- const core$1 = new core.Core({ projectId });
30
+ initialStorageProvided = Boolean(options === null || options === void 0 ? void 0 : options.storage);
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ const coreOptions = { projectId };
33
+ if (options === null || options === void 0 ? void 0 : options.storage) {
34
+ coreOptions.storage = options.storage;
35
+ }
36
+ const core$1 = new core.Core(coreOptions);
24
37
  walletKitWithProjectIdPromise = walletkit.WalletKit.init({
25
38
  core: core$1,
26
39
  metadata: {
@@ -1,3 +1,13 @@
1
1
  import { IWalletKit } from '@reown/walletkit';
2
- export declare const getWalletKitSingleton: (projectId: string) => Promise<IWalletKit>;
2
+ type WalletKitSingletonOptions = {
3
+ storage?: {
4
+ getKeys(): Promise<string[]>;
5
+ getEntries<T = unknown>(): Promise<[string, T][]>;
6
+ getItem<T = unknown>(key: string): Promise<T | undefined>;
7
+ setItem<T = unknown>(key: string, value: T): Promise<void>;
8
+ removeItem(key: string): Promise<void>;
9
+ };
10
+ };
11
+ export declare const getWalletKitSingleton: (projectId: string, options?: WalletKitSingletonOptions) => Promise<IWalletKit>;
3
12
  export declare const testOnly__resetWalletKitSingleton: () => void;
13
+ export {};
@@ -2,10 +2,12 @@
2
2
  import { __awaiter } from '../../_virtual/_tslib.js';
3
3
  import { WalletKit } from '@reown/walletkit';
4
4
  import { Core } from '@walletconnect/core';
5
+ import { logger } from '@dynamic-labs/wallet-connector-core';
5
6
  import { MultipleWalletConnectProjectIdsError } from '../errors/MultipleWalletConnectProjectIdsError.js';
6
7
 
7
8
  let walletKitWithProjectIdPromise = undefined;
8
- const getWalletKitSingleton = (projectId) => __awaiter(void 0, void 0, void 0, function* () {
9
+ let initialStorageProvided = false;
10
+ const getWalletKitSingleton = (projectId, options) => __awaiter(void 0, void 0, void 0, function* () {
9
11
  if (walletKitWithProjectIdPromise) {
10
12
  const valueFromPreviousCall = yield walletKitWithProjectIdPromise;
11
13
  if (valueFromPreviousCall.projectId !== projectId) {
@@ -14,9 +16,20 @@ const getWalletKitSingleton = (projectId) => __awaiter(void 0, void 0, void 0, f
14
16
  originalProjectId: valueFromPreviousCall.projectId,
15
17
  });
16
18
  }
19
+ if (Boolean(options === null || options === void 0 ? void 0 : options.storage) !== initialStorageProvided) {
20
+ logger.warn('[getWalletKitSingleton] storage option differs from the initial call and will be ignored. The singleton was already created ' +
21
+ (initialStorageProvided ? 'with' : 'without') +
22
+ ' a custom storage provider.');
23
+ }
17
24
  return valueFromPreviousCall.walletKit;
18
25
  }
19
- const core = new Core({ projectId });
26
+ initialStorageProvided = Boolean(options === null || options === void 0 ? void 0 : options.storage);
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const coreOptions = { projectId };
29
+ if (options === null || options === void 0 ? void 0 : options.storage) {
30
+ coreOptions.storage = options.storage;
31
+ }
32
+ const core = new Core(coreOptions);
20
33
  walletKitWithProjectIdPromise = WalletKit.init({
21
34
  core,
22
35
  metadata: {
package/src/index.cjs CHANGED
@@ -11,3 +11,4 @@ assertPackageVersion.assertPackageVersion('@dynamic-labs/global-wallet', _packag
11
11
 
12
12
  exports.GlobalWalletExtension = GlobalWalletConnector.GlobalWalletExtension;
13
13
  exports.buildCombinedNamespaces = GlobalWalletConnector.buildCombinedNamespaces;
14
+ exports.buildCombinedNamespacesForKey = GlobalWalletConnector.buildCombinedNamespacesForKey;
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
  import { assertPackageVersion } from '@dynamic-labs/assert-package-version';
3
3
  import { version } from '../package.js';
4
- export { GlobalWalletExtension, buildCombinedNamespaces } from './GlobalWalletConnector.js';
4
+ export { GlobalWalletExtension, buildCombinedNamespaces, buildCombinedNamespacesForKey } from './GlobalWalletConnector.js';
5
5
 
6
6
  assertPackageVersion('@dynamic-labs/global-wallet', version);
@@ -0,0 +1,7 @@
1
+ import { InternalWalletConnector } from '@dynamic-labs/wallet-connector-core';
2
+ import { GlobalWalletExtensionSettings } from '../GlobalWalletConnector';
3
+ export type SolanaGlobalWalletSettings = {
4
+ solanaConnector: InternalWalletConnector;
5
+ solanaAddress?: string;
6
+ };
7
+ export declare const withSolanaSupport: (baseSettings: GlobalWalletExtensionSettings, solanaSettings: SolanaGlobalWalletSettings) => GlobalWalletExtensionSettings;