@reown/appkit-solana-react-native 0.0.0-chore-solflare-20250730210452 → 0.0.0-chore-spring-effect-20250909214820

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 (52) hide show
  1. package/lib/commonjs/adapter.js +4 -5
  2. package/lib/commonjs/adapter.js.map +1 -1
  3. package/lib/commonjs/connectors/{SolanaDeeplinkConnector.js → DeeplinkConnector.js} +80 -51
  4. package/lib/commonjs/connectors/DeeplinkConnector.js.map +1 -0
  5. package/lib/commonjs/connectors/PhantomConnector.js +5 -4
  6. package/lib/commonjs/connectors/PhantomConnector.js.map +1 -1
  7. package/lib/commonjs/connectors/SolflareConnector.js +5 -4
  8. package/lib/commonjs/connectors/SolflareConnector.js.map +1 -1
  9. package/lib/commonjs/index.js +0 -20
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/providers/{SolanaDeeplinkProvider.js → DeeplinkProvider.js} +177 -138
  12. package/lib/commonjs/providers/DeeplinkProvider.js.map +1 -0
  13. package/lib/module/adapter.js +4 -5
  14. package/lib/module/adapter.js.map +1 -1
  15. package/lib/module/connectors/{SolanaDeeplinkConnector.js → DeeplinkConnector.js} +77 -48
  16. package/lib/module/connectors/DeeplinkConnector.js.map +1 -0
  17. package/lib/module/connectors/PhantomConnector.js +5 -4
  18. package/lib/module/connectors/PhantomConnector.js.map +1 -1
  19. package/lib/module/connectors/SolflareConnector.js +5 -4
  20. package/lib/module/connectors/SolflareConnector.js.map +1 -1
  21. package/lib/module/index.js +2 -6
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/module/providers/{SolanaDeeplinkProvider.js → DeeplinkProvider.js} +175 -136
  24. package/lib/module/providers/DeeplinkProvider.js.map +1 -0
  25. package/lib/typescript/adapter.d.ts +1 -3
  26. package/lib/typescript/adapter.d.ts.map +1 -1
  27. package/lib/typescript/connectors/{SolanaDeeplinkConnector.d.ts → DeeplinkConnector.d.ts} +11 -13
  28. package/lib/typescript/connectors/DeeplinkConnector.d.ts.map +1 -0
  29. package/lib/typescript/connectors/PhantomConnector.d.ts +4 -3
  30. package/lib/typescript/connectors/PhantomConnector.d.ts.map +1 -1
  31. package/lib/typescript/connectors/SolflareConnector.d.ts +4 -3
  32. package/lib/typescript/connectors/SolflareConnector.d.ts.map +1 -1
  33. package/lib/typescript/index.d.ts +1 -3
  34. package/lib/typescript/index.d.ts.map +1 -1
  35. package/lib/typescript/providers/{SolanaDeeplinkProvider.d.ts → DeeplinkProvider.d.ts} +29 -8
  36. package/lib/typescript/providers/DeeplinkProvider.d.ts.map +1 -0
  37. package/lib/typescript/types.d.ts +33 -33
  38. package/lib/typescript/types.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/src/adapter.ts +4 -5
  41. package/src/connectors/{SolanaDeeplinkConnector.ts → DeeplinkConnector.ts} +107 -90
  42. package/src/connectors/PhantomConnector.ts +5 -4
  43. package/src/connectors/SolflareConnector.ts +5 -4
  44. package/src/index.ts +3 -22
  45. package/src/providers/{SolanaDeeplinkProvider.ts → DeeplinkProvider.ts} +271 -225
  46. package/src/types.ts +41 -48
  47. package/lib/commonjs/connectors/SolanaDeeplinkConnector.js.map +0 -1
  48. package/lib/commonjs/providers/SolanaDeeplinkProvider.js.map +0 -1
  49. package/lib/module/connectors/SolanaDeeplinkConnector.js.map +0 -1
  50. package/lib/module/providers/SolanaDeeplinkProvider.js.map +0 -1
  51. package/lib/typescript/connectors/SolanaDeeplinkConnector.d.ts.map +0 -1
  52. package/lib/typescript/providers/SolanaDeeplinkProvider.d.ts.map +0 -1
@@ -9,21 +9,21 @@ import type {
9
9
  Storage
10
10
  } from '@reown/appkit-common-react-native';
11
11
  import type {
12
- SolanaDeeplinkProviderConfig,
13
- SolanaConnectResult,
14
- SolanaWalletSession,
12
+ DeeplinkProviderConfig,
13
+ DeeplinkConnectResult,
14
+ DeeplinkSession,
15
15
  DecryptedConnectData,
16
- SolanaDeeplinkResponse,
16
+ DeeplinkResponse,
17
17
  SignAllTransactionsRequestParams,
18
18
  SignMessageRequestParams,
19
19
  SignTransactionRequestParams,
20
- SolanaRpcMethod,
21
- SolanaConnectParams,
22
- SolanaDisconnectParams,
23
- SolanaSignTransactionParams,
24
- SolanaSignMessageParams,
25
- SolanaSignAllTransactionsParams,
26
- SolanaCluster
20
+ DeeplinkRpcMethod,
21
+ DeeplinkConnectParams,
22
+ DeeplinkDisconnectParams,
23
+ DeeplinkSignTransactionParams,
24
+ DeeplinkSignMessageParams,
25
+ DeeplinkSignAllTransactionsParams,
26
+ Cluster
27
27
  } from '../types';
28
28
  import EventEmitter from 'events';
29
29
 
@@ -40,10 +40,11 @@ function isValidSolanaSigningMethod(method: string): method is SolanaSigningMeth
40
40
  return Object.values(SOLANA_SIGNING_METHODS).includes(method as SolanaSigningMethod);
41
41
  }
42
42
 
43
- export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
44
- private readonly config: SolanaDeeplinkProviderConfig;
43
+ export class DeeplinkProvider extends EventEmitter implements Provider {
44
+ private readonly config: DeeplinkProviderConfig;
45
45
  private dappEncryptionKeyPair: nacl.BoxKeyPair;
46
- private currentCluster: SolanaCluster = 'mainnet-beta';
46
+ private currentCluster: Cluster;
47
+ private sharedKey: Uint8Array | null = null;
47
48
 
48
49
  private storage: Storage;
49
50
 
@@ -51,15 +52,51 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
51
52
  private userPublicKey: string | null = null;
52
53
  private walletEncryptionPublicKeyBs58: string | null = null;
53
54
 
54
- constructor(config: SolanaDeeplinkProviderConfig) {
55
+ // Single subscription management - deep links are sequential by nature
56
+ private activeSubscription: { remove: () => void } | null = null;
57
+ private isOperationPending = false;
58
+
59
+ constructor(config: DeeplinkProviderConfig) {
55
60
  super();
56
61
  this.config = config;
62
+ this.currentCluster = config.cluster ?? 'mainnet-beta';
57
63
  this.dappEncryptionKeyPair = config.dappEncryptionKeyPair;
58
64
  this.storage = config.storage;
59
65
  }
60
66
 
61
- private getStorageKey(): string {
62
- return `@appkit/${this.config.walletType}-provider-session`;
67
+ private getSessionStorageKey(): string {
68
+ return `@appkit/${this.config.type}-provider-session`;
69
+ }
70
+
71
+ /**
72
+ * Cleanup method to be called when the provider is destroyed
73
+ */
74
+ public destroy(): void {
75
+ this.cleanupActiveSubscription();
76
+ this.removeAllListeners();
77
+ }
78
+
79
+ /**
80
+ * Safely cleanup the active subscription
81
+ */
82
+ private cleanupActiveSubscription(): void {
83
+ if (this.activeSubscription) {
84
+ this.activeSubscription.remove();
85
+ this.activeSubscription = null;
86
+ }
87
+ this.isOperationPending = false;
88
+ }
89
+
90
+ /**
91
+ * Safely set a new subscription, ensuring no operation is pending
92
+ */
93
+ private setActiveSubscription(subscription: { remove: () => void }): void {
94
+ // If there's already a pending operation, reject it
95
+ if (this.isOperationPending) {
96
+ this.cleanupActiveSubscription();
97
+ }
98
+ this.activeSubscription = subscription;
99
+ this.isOperationPending = true;
63
100
  }
64
101
 
65
102
  getUserPublicKey(): string | null {
@@ -70,13 +107,70 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
70
107
  return !!this.sessionToken && !!this.userPublicKey && !!this.dappEncryptionKeyPair;
71
108
  }
72
109
 
73
- private buildUrl(rpcMethod: SolanaRpcMethod, params: Record<string, string>): string {
110
+ public getCurrentCluster(): Cluster {
111
+ return this.currentCluster;
112
+ }
113
+
114
+ private buildUrl(rpcMethod: DeeplinkRpcMethod, params: Record<string, string>): string {
74
115
  const query = new URLSearchParams(params).toString();
75
116
 
76
117
  return `${this.config.baseUrl}/${rpcMethod}?${query}`;
77
118
  }
78
119
 
79
- private getRpcMethodName(method: SolanaSigningMethod): SolanaRpcMethod {
120
+ /**
121
+ * Open a deeplink URL and wait for a redirect back to the app. Handles subscription
122
+ * lifecycle and common error extraction from `errorCode`/`errorMessage` query params.
123
+ */
124
+ private async openDeeplinkAndWait<T>(
125
+ deeplinkUrl: string,
126
+ processParams: (params: URLSearchParams) => Promise<T> | T,
127
+ contextLabel: string
128
+ ): Promise<T> {
129
+ return new Promise<T>((resolve, reject) => {
130
+ const handleDeepLink = async (event: { url: string }) => {
131
+ try {
132
+ this.cleanupActiveSubscription();
133
+ const fullUrl = event.url;
134
+ if (!fullUrl.startsWith(this.config.appScheme)) {
135
+ return reject(
136
+ new Error(`${this.config.type} provider: ${contextLabel}: Unexpected redirect URI.`)
137
+ );
138
+ }
139
+ const params = new URLSearchParams(fullUrl.substring(fullUrl.indexOf('?') + 1));
140
+ const errorCode = params.get('errorCode');
141
+ const errorMessage = params.get('errorMessage');
142
+ if (errorCode) {
143
+ return reject(
144
+ new Error(
145
+ `${this.config.type} provider: ${contextLabel} Failed: ${
146
+ errorMessage || 'Unknown error'
147
+ } (Code: ${errorCode})`
148
+ )
149
+ );
150
+ }
151
+ const result = await Promise.resolve(processParams(params));
152
+ resolve(result);
153
+ } catch (error) {
154
+ this.cleanupActiveSubscription();
155
+ reject(error);
156
+ }
157
+ };
158
+
159
+ const subscription = Linking.addEventListener('url', handleDeepLink);
160
+ this.setActiveSubscription(subscription);
161
+
162
+ Linking.openURL(deeplinkUrl).catch(err => {
163
+ this.cleanupActiveSubscription();
164
+ reject(
165
+ new Error(
166
+ `${this.config.type} provider: Failed to open wallet for ${contextLabel}: ${err.message}.`
167
+ )
168
+ );
169
+ });
170
+ });
171
+ }
172
+
173
+ private getRpcMethodName(method: SolanaSigningMethod): DeeplinkRpcMethod {
80
174
  switch (method) {
81
175
  case SOLANA_SIGNING_METHODS.SOLANA_SIGN_TRANSACTION:
82
176
  return 'signTransaction';
@@ -94,27 +188,34 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
94
188
 
95
189
  private encryptPayload(
96
190
  payload: Record<string, unknown>,
97
- walletPublicKeyBs58ToEncryptFor: string
191
+ walletPublicKeyBs58: string
98
192
  ): { nonce: string; encryptedPayload: string } | null {
99
- if (!walletPublicKeyBs58ToEncryptFor) {
193
+ if (!walletPublicKeyBs58) {
100
194
  return null;
101
195
  }
102
196
  try {
103
- const walletPublicKeyBytes = bs58.decode(walletPublicKeyBs58ToEncryptFor);
104
197
  const nonce = nacl.randomBytes(nacl.box.nonceLength);
105
198
  const payloadBytes = Buffer.from(JSON.stringify(payload), 'utf8');
106
- const encryptedPayload = nacl.box(
107
- payloadBytes,
108
- nonce,
109
- walletPublicKeyBytes,
110
- this.dappEncryptionKeyPair.secretKey
111
- );
199
+ let encryptedPayload: Uint8Array | null;
200
+ if (this.sharedKey) {
201
+ encryptedPayload = nacl.box.after(payloadBytes, nonce, this.sharedKey);
202
+ } else {
203
+ const walletPublicKeyBytes = bs58.decode(walletPublicKeyBs58);
204
+ encryptedPayload = nacl.box(
205
+ payloadBytes,
206
+ nonce,
207
+ walletPublicKeyBytes,
208
+ this.dappEncryptionKeyPair.secretKey
209
+ );
210
+ }
112
211
 
113
212
  return {
114
213
  nonce: bs58.encode(nonce),
115
214
  encryptedPayload: bs58.encode(encryptedPayload)
116
215
  };
117
216
  } catch (error) {
217
+ console.warn(`${this.config.type} provider: Failed to encrypt payload.`, error);
218
+
118
219
  return null;
119
220
  }
120
221
  }
@@ -125,37 +226,56 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
125
226
  walletSenderPublicKeyBs58: string
126
227
  ): T | null {
127
228
  try {
128
- const encryptedDataBytes = bs58.decode(encryptedDataBs58);
229
+ const formattedEncryptedDataBs58 = encryptedDataBs58.replace('#', '');
230
+ const encryptedDataBytes = bs58.decode(formattedEncryptedDataBs58);
129
231
  const nonceBytes = bs58.decode(nonceBs58);
130
- const walletSenderPublicKeyBytes = bs58.decode(walletSenderPublicKeyBs58);
131
- const decryptedPayloadBytes = nacl.box.open(
132
- encryptedDataBytes,
133
- nonceBytes,
134
- walletSenderPublicKeyBytes,
135
- this.dappEncryptionKeyPair.secretKey
136
- );
232
+ let decryptedPayloadBytes: Uint8Array | null;
233
+ if (this.sharedKey) {
234
+ decryptedPayloadBytes = nacl.box.open.after(encryptedDataBytes, nonceBytes, this.sharedKey);
235
+ } else {
236
+ const walletSenderPublicKeyBytes = bs58.decode(walletSenderPublicKeyBs58);
237
+ decryptedPayloadBytes = nacl.box.open(
238
+ encryptedDataBytes,
239
+ nonceBytes,
240
+ walletSenderPublicKeyBytes,
241
+ this.dappEncryptionKeyPair.secretKey
242
+ );
243
+ }
137
244
  if (!decryptedPayloadBytes) {
138
245
  return null;
139
246
  }
140
247
 
141
248
  return JSON.parse(Buffer.from(decryptedPayloadBytes).toString('utf8')) as T;
142
249
  } catch (error) {
250
+ console.warn(`${this.config.type} provider: Failed to decrypt payload.`, error);
251
+
143
252
  return null;
144
253
  }
145
254
  }
146
255
 
147
256
  public async restoreSession(): Promise<boolean> {
148
257
  try {
149
- const session = await this.storage.getItem<SolanaWalletSession>(this.getStorageKey());
258
+ const session = await this.storage.getItem<DeeplinkSession>(this.getSessionStorageKey());
150
259
  if (session) {
151
260
  this.setSession(session);
152
261
 
262
+ // Recompute shared key on session restore
263
+ try {
264
+ const walletPublicKeyBytes = bs58.decode(session.walletEncryptionPublicKeyBs58);
265
+ this.sharedKey = nacl.box.before(
266
+ walletPublicKeyBytes,
267
+ this.dappEncryptionKeyPair.secretKey
268
+ );
269
+ } catch (e) {
270
+ this.sharedKey = null;
271
+ }
272
+
153
273
  return true;
154
274
  }
155
275
 
156
276
  return false;
157
277
  } catch (error) {
158
- // console.error(`${this.config.walletType}Provider: Failed to restore session.`, error);
278
+ // console.error(`${this.config.type} provider: Failed to restore session.`, error);
159
279
  await this.clearSessionStorage(); // Clear potentially corrupt data
160
280
 
161
281
  return false;
@@ -166,31 +286,31 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
166
286
  if (!this.sessionToken || !this.userPublicKey || !this.walletEncryptionPublicKeyBs58) {
167
287
  return; // Cannot save incomplete session
168
288
  }
169
- const session: SolanaWalletSession = {
289
+ const session: DeeplinkSession = {
170
290
  sessionToken: this.sessionToken,
171
291
  userPublicKey: this.userPublicKey,
172
292
  walletEncryptionPublicKeyBs58: this.walletEncryptionPublicKeyBs58,
173
293
  cluster: this.currentCluster
174
294
  };
175
295
  try {
176
- await this.storage.setItem(this.getStorageKey(), session);
296
+ await this.storage.setItem(this.getSessionStorageKey(), session);
177
297
  } catch (error) {
178
- // console.error(`${this.config.walletType}Provider: Failed to save session.`, error);
298
+ // console.error(`${this.config.type} provider: Failed to save session.`, error);
179
299
  }
180
300
  }
181
301
 
182
302
  private async clearSessionStorage(): Promise<void> {
183
303
  try {
184
- await this.storage.removeItem(this.getStorageKey());
304
+ await this.storage.removeItem(this.getSessionStorageKey());
185
305
  } catch (error) {
186
- // console.error(`${this.config.walletType}Provider: Failed to clear session storage.`, error);
306
+ // console.error(`${this.config.type} provider: Failed to clear session storage.`, error);
187
307
  }
188
308
  }
189
309
 
190
- public async connect<T = SolanaConnectResult>(params?: { cluster?: SolanaCluster }): Promise<T> {
310
+ public async connect<T = DeeplinkConnectResult>(params?: { cluster?: Cluster }): Promise<T> {
191
311
  const cluster = params?.cluster ?? 'mainnet-beta';
192
312
  this.currentCluster = cluster;
193
- const connectDeeplinkParams: SolanaConnectParams = {
313
+ const connectDeeplinkParams: DeeplinkConnectParams = {
194
314
  app_url: this.config.dappUrl,
195
315
  dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
196
316
  redirect_link: this.config.appScheme,
@@ -198,87 +318,59 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
198
318
  };
199
319
  const url = this.buildUrl('connect', connectDeeplinkParams as any);
200
320
 
201
- return new Promise<SolanaConnectResult>((resolve, reject) => {
202
- let subscription: { remove: () => void } | null = null;
203
- const handleDeepLink = async (event: { url: string }) => {
204
- if (subscription) {
205
- subscription.remove();
321
+ return this.openDeeplinkAndWait<DeeplinkConnectResult>(
322
+ url,
323
+ (responseUrlParams: URLSearchParams) => {
324
+ const responsePayload: DeeplinkResponse = {
325
+ wallet_encryption_public_key: responseUrlParams.get(this.config.encryptionKeyFieldName)!,
326
+ nonce: responseUrlParams.get('nonce')!,
327
+ data: responseUrlParams.get('data')!
328
+ };
329
+ if (
330
+ !responsePayload.wallet_encryption_public_key ||
331
+ !responsePayload.nonce ||
332
+ !responsePayload.data
333
+ ) {
334
+ throw new Error(`${this.config.type} provider: Invalid response - missing parameters.`);
206
335
  }
207
- const fullUrl = event.url;
208
- if (fullUrl.startsWith(this.config.appScheme)) {
209
- const responseUrlParams = new URLSearchParams(
210
- fullUrl.substring(fullUrl.indexOf('?') + 1)
211
- );
212
- const errorCode = responseUrlParams.get('errorCode');
213
- const errorMessage = responseUrlParams.get('errorMessage');
214
- if (errorCode) {
215
- return reject(
216
- new Error(
217
- `${this.config.walletType} Connection Failed: ${
218
- errorMessage || 'Unknown error'
219
- } (Code: ${errorCode})`
220
- )
221
- );
222
- }
223
- const walletEncryptionPublicKey = responseUrlParams.get(
224
- this.config.encryptionKeyFieldName
225
- );
226
336
 
227
- const responsePayload: SolanaDeeplinkResponse = {
228
- wallet_encryption_public_key: walletEncryptionPublicKey!,
229
- nonce: responseUrlParams.get('nonce')!,
230
- data: responseUrlParams.get('data')!
231
- };
232
- if (
233
- !responsePayload.wallet_encryption_public_key ||
234
- !responsePayload.nonce ||
235
- !responsePayload.data
236
- ) {
237
- return reject(
238
- new Error(`${this.config.walletType} Connect: Invalid response - missing parameters.`)
239
- );
240
- }
241
- const decryptedData = this.decryptPayload<DecryptedConnectData>(
242
- responsePayload.data,
243
- responsePayload.nonce,
244
- responsePayload.wallet_encryption_public_key
337
+ const decryptedData = this.decryptPayload<DecryptedConnectData>(
338
+ responsePayload.data,
339
+ responsePayload.nonce,
340
+ responsePayload.wallet_encryption_public_key
341
+ );
342
+ if (!decryptedData || !decryptedData.public_key || !decryptedData.session) {
343
+ throw new Error(
344
+ `${this.config.type} provider: Failed to decrypt or invalid decrypted data.`
245
345
  );
246
- if (!decryptedData || !decryptedData.public_key || !decryptedData.session) {
247
- return reject(
248
- new Error(
249
- `${this.config.walletType} Connect: Failed to decrypt or invalid decrypted data.`
250
- )
251
- );
252
- }
253
- this.userPublicKey = decryptedData.public_key;
254
- this.sessionToken = decryptedData.session;
255
- this.walletEncryptionPublicKeyBs58 = responsePayload.wallet_encryption_public_key;
256
-
257
- // Save session on successful connect
258
- this.saveSession();
259
-
260
- resolve({
261
- userPublicKey: this.userPublicKey,
262
- sessionToken: this.sessionToken,
263
- walletEncryptionPublicKeyBs58: this.walletEncryptionPublicKeyBs58,
264
- cluster
265
- });
266
- } else {
267
- reject(new Error(`${this.config.walletType} Connect: Unexpected redirect URI.`));
268
346
  }
269
- };
270
- subscription = Linking.addEventListener('url', handleDeepLink);
271
- Linking.openURL(url).catch(err => {
272
- if (subscription) {
273
- subscription.remove();
347
+ this.userPublicKey = decryptedData.public_key;
348
+ this.sessionToken = decryptedData.session;
349
+ this.walletEncryptionPublicKeyBs58 = responsePayload.wallet_encryption_public_key;
350
+
351
+ // Precompute shared key for subsequent communications
352
+ try {
353
+ const walletPublicKeyBytes = bs58.decode(this.walletEncryptionPublicKeyBs58);
354
+ this.sharedKey = nacl.box.before(
355
+ walletPublicKeyBytes,
356
+ this.dappEncryptionKeyPair.secretKey
357
+ );
358
+ } catch (e) {
359
+ this.sharedKey = null;
274
360
  }
275
- reject(
276
- new Error(
277
- `Failed to open ${this.config.walletType} wallet: ${err.message}. Is it installed?`
278
- )
279
- );
280
- });
281
- }) as Promise<T>;
361
+
362
+ // Save session on successful connect
363
+ this.saveSession();
364
+
365
+ return {
366
+ userPublicKey: this.userPublicKey!,
367
+ sessionToken: this.sessionToken!,
368
+ walletEncryptionPublicKeyBs58: this.walletEncryptionPublicKeyBs58!,
369
+ cluster
370
+ } as DeeplinkConnectResult;
371
+ },
372
+ 'Connection'
373
+ ) as Promise<T>;
282
374
  }
283
375
 
284
376
  public async disconnect(): Promise<void> {
@@ -296,14 +388,14 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
296
388
  );
297
389
 
298
390
  if (!encryptedDisconnectPayload) {
299
- // console.warn(`${this.config.walletType}Provider: Failed to encrypt disconnect payload. Clearing session locally.`);
391
+ // console.warn(`${this.config.type} provider: Failed to encrypt disconnect payload. Clearing session locally.`);
300
392
  await this.clearSession();
301
393
  this.emit('disconnect');
302
394
 
303
395
  return Promise.resolve(); // Or reject, depending on desired strictness
304
396
  }
305
397
 
306
- const disconnectDeeplinkParams: SolanaDisconnectParams = {
398
+ const disconnectDeeplinkParams: DeeplinkDisconnectParams = {
307
399
  dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
308
400
  redirect_link: this.config.appScheme,
309
401
  payload: encryptedDisconnectPayload.encryptedPayload,
@@ -311,41 +403,28 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
311
403
  };
312
404
  const url = this.buildUrl('disconnect', disconnectDeeplinkParams as any);
313
405
 
314
- return new Promise<void>((resolve, reject) => {
315
- let subscription: { remove: () => void } | null = null;
316
- const handleDeepLink = (event: { url: string }) => {
317
- if (subscription) {
318
- subscription.remove();
319
- }
320
- if (event.url.startsWith(this.config.appScheme)) {
321
- this.clearSession();
322
- resolve();
323
- } else {
324
- this.clearSession();
325
- reject(new Error(`${this.config.walletType} Disconnect: Unexpected redirect URI.`));
326
- }
327
- };
328
- subscription = Linking.addEventListener('url', handleDeepLink);
329
- Linking.openURL(url).catch(err => {
330
- if (subscription) {
331
- subscription.remove();
332
- }
406
+ return this.openDeeplinkAndWait<void>(
407
+ url,
408
+ () => {
333
409
  this.clearSession();
334
- reject(
335
- new Error(`Failed to open ${this.config.walletType} for disconnection: ${err.message}.`)
336
- );
337
- });
338
- });
410
+ },
411
+ 'Disconnection'
412
+ );
339
413
  }
340
414
 
341
415
  public async clearSession(): Promise<void> {
342
416
  this.sessionToken = null;
343
417
  this.userPublicKey = null;
344
418
  this.walletEncryptionPublicKeyBs58 = null;
419
+ if (this.sharedKey) {
420
+ this.sharedKey.fill(0);
421
+ }
422
+ this.sharedKey = null;
423
+ this.cleanupActiveSubscription();
345
424
  await this.clearSessionStorage();
346
425
  }
347
426
 
348
- public setSession(session: SolanaWalletSession): void {
427
+ public setSession(session: DeeplinkSession): void {
349
428
  this.sessionToken = session.sessionToken;
350
429
  this.userPublicKey = session.userPublicKey;
351
430
  this.walletEncryptionPublicKeyBs58 = session.walletEncryptionPublicKeyBs58;
@@ -355,7 +434,7 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
355
434
  public async request<T>(args: RequestArguments, _chainId?: CaipNetworkId): Promise<T> {
356
435
  if (!isValidSolanaSigningMethod(args.method)) {
357
436
  throw new Error(
358
- `${this.config.walletType}Provider: Unsupported method: ${args.method}. Only Solana signing methods are supported.`
437
+ `${this.config.type} provider: Unsupported method: ${args.method}. Only Solana signing methods are supported.`
359
438
  );
360
439
  }
361
440
  const signingMethod = args.method as SolanaSigningMethod;
@@ -363,7 +442,7 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
363
442
 
364
443
  if (!this.isConnected() || !this.sessionToken || !this.walletEncryptionPublicKeyBs58) {
365
444
  throw new Error(
366
- `${this.config.walletType}Provider: Not connected or session details missing. Cannot process request.`
445
+ `${this.config.type} provider: Not connected or session details missing. Cannot process request.`
367
446
  );
368
447
  }
369
448
 
@@ -381,20 +460,20 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
381
460
  }
382
461
 
383
462
  const dataToEncrypt = {
384
- session: this.sessionToken!,
463
+ session: this.sessionToken,
385
464
  transaction: typedParams.transaction
386
465
  };
387
466
  const encryptedData = this.encryptPayload(
388
467
  dataToEncrypt,
389
- this.walletEncryptionPublicKeyBs58!
468
+ this.walletEncryptionPublicKeyBs58
390
469
  );
391
470
  if (!encryptedData) {
392
471
  throw new Error(
393
- `${this.config.walletType}Provider: Failed to encrypt payload for ${signingMethod}.`
472
+ `${this.config.type} provider: Failed to encrypt payload for ${signingMethod}.`
394
473
  );
395
474
  }
396
475
 
397
- const signTxDeeplinkParams: SolanaSignTransactionParams = {
476
+ const signTxDeeplinkParams: DeeplinkSignTransactionParams = {
398
477
  dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
399
478
  redirect_link: this.config.appScheme,
400
479
  cluster: this.currentCluster,
@@ -407,7 +486,9 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
407
486
  case SOLANA_SIGNING_METHODS.SOLANA_SIGN_MESSAGE: {
408
487
  const typedParams = requestParams as SignMessageRequestParams;
409
488
  if (!typedParams || typeof typedParams.message === 'undefined') {
410
- throw new Error(`Missing 'message' in params for ${signingMethod}`);
489
+ throw new Error(
490
+ `${this.config.type} provider: Missing 'message' in params for ${signingMethod}`
491
+ );
411
492
  }
412
493
 
413
494
  let messageBs58: string;
@@ -421,27 +502,29 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
421
502
  messageBs58 = bs58.encode(Buffer.from(typedParams.message));
422
503
  }
423
504
  } else {
424
- throw new Error('Invalid message format for signMessage. Expected Uint8Array or string.');
505
+ throw new Error(
506
+ `${this.config.type} provider: Invalid message format for signMessage. Expected Uint8Array or string.`
507
+ );
425
508
  }
426
509
 
427
510
  const dataToEncrypt = {
428
511
  message: messageBs58,
429
- session: this.sessionToken!,
512
+ session: this.sessionToken,
430
513
  display: typedParams.display || 'utf8'
431
514
  };
432
515
 
433
516
  const encryptedPayloadData = this.encryptPayload(
434
517
  dataToEncrypt,
435
- this.walletEncryptionPublicKeyBs58!
518
+ this.walletEncryptionPublicKeyBs58
436
519
  );
437
520
 
438
521
  if (!encryptedPayloadData) {
439
522
  throw new Error(
440
- `${this.config.walletType}Provider: Failed to encrypt payload for signMessage.`
523
+ `${this.config.type} provider: Failed to encrypt payload for signMessage.`
441
524
  );
442
525
  }
443
526
 
444
- const signMsgDeeplinkQueryPayload: SolanaSignMessageParams = {
527
+ const signMsgDeeplinkQueryPayload: DeeplinkSignMessageParams = {
445
528
  dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
446
529
  redirect_link: this.config.appScheme,
447
530
  payload: encryptedPayloadData.encryptedPayload,
@@ -458,25 +541,25 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
458
541
  !typedParams.transactions.every((t: any) => typeof t === 'string')
459
542
  ) {
460
543
  throw new Error(
461
- `Missing or invalid 'transactions' (array of base58 strings) in params for ${signingMethod}`
544
+ `${this.config.type} provider: Missing or invalid 'transactions' (array of base58 strings) in params for ${signingMethod}`
462
545
  );
463
546
  }
464
547
 
465
548
  const dataToEncrypt = {
466
- session: this.sessionToken!,
549
+ session: this.sessionToken,
467
550
  transactions: typedParams.transactions
468
551
  };
469
552
  const encryptedData = this.encryptPayload(
470
553
  dataToEncrypt,
471
- this.walletEncryptionPublicKeyBs58!
554
+ this.walletEncryptionPublicKeyBs58
472
555
  );
473
556
  if (!encryptedData) {
474
557
  throw new Error(
475
- `${this.config.walletType}Provider: Failed to encrypt payload for ${signingMethod}.`
558
+ `${this.config.type} provider: Failed to encrypt payload for ${signingMethod}.`
476
559
  );
477
560
  }
478
561
 
479
- const signAllTxDeeplinkParams: SolanaSignAllTransactionsParams = {
562
+ const signAllTxDeeplinkParams: DeeplinkSignAllTransactionsParams = {
480
563
  dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
481
564
  redirect_link: this.config.appScheme,
482
565
  cluster: this.currentCluster,
@@ -487,72 +570,35 @@ export class SolanaDeeplinkProvider extends EventEmitter implements Provider {
487
570
  break;
488
571
  }
489
572
  default: {
490
- throw new Error(
491
- `${this.config.walletType}Provider: Unhandled signing method: ${signingMethod}`
492
- );
573
+ throw new Error(`${this.config.type} provider: Unhandled signing method: ${signingMethod}`);
493
574
  }
494
575
  }
495
576
 
496
- return new Promise<T>((resolve, reject) => {
497
- let subscription: { remove: () => void } | null = null;
498
- const handleDeepLink = async (event: { url: string }) => {
499
- if (subscription) {
500
- subscription.remove();
501
- }
502
- const fullUrl = event.url;
503
- if (fullUrl.startsWith(this.config.appScheme)) {
504
- const responseUrlParams = new URLSearchParams(
505
- fullUrl.substring(fullUrl.indexOf('?') + 1)
506
- );
507
- const errorCode = responseUrlParams.get('errorCode');
508
- const errorMessage = responseUrlParams.get('errorMessage');
509
- if (errorCode) {
510
- return reject(
511
- new Error(
512
- `${this.config.walletType} ${signingMethod} Failed: ${
513
- errorMessage || 'Unknown error'
514
- } (Code: ${errorCode})`
515
- )
516
- );
517
- }
518
- const responseNonce = responseUrlParams.get('nonce');
519
- const responseData = responseUrlParams.get('data');
520
- if (!responseNonce || !responseData) {
521
- return reject(
522
- new Error(
523
- `${this.config.walletType} ${signingMethod}: Invalid response - missing nonce or data.`
524
- )
525
- );
526
- }
527
- const decryptedResult = this.decryptPayload<any>(
528
- responseData,
529
- responseNonce,
530
- this.walletEncryptionPublicKeyBs58!
577
+ return this.openDeeplinkAndWait<T>(
578
+ deeplinkUrl,
579
+ (responseUrlParams: URLSearchParams) => {
580
+ const responseNonce = responseUrlParams.get('nonce');
581
+ const responseData = responseUrlParams.get('data');
582
+ if (!responseNonce || !responseData) {
583
+ throw new Error(
584
+ `${this.config.type} provider: ${signingMethod}: Invalid response - missing nonce or data.`
531
585
  );
532
- if (!decryptedResult) {
533
- return reject(
534
- new Error(
535
- `${this.config.walletType} ${signingMethod}: Failed to decrypt response or invalid decrypted data.`
536
- )
537
- );
538
- }
539
- resolve(decryptedResult as T);
540
- } else {
541
- reject(new Error(`${this.config.walletType} ${signingMethod}: Unexpected redirect URI.`));
542
586
  }
543
- };
544
- subscription = Linking.addEventListener('url', handleDeepLink);
545
- Linking.openURL(deeplinkUrl).catch(err => {
546
- if (subscription) {
547
- subscription.remove();
548
- }
549
- reject(
550
- new Error(
551
- `Failed to open ${this.config.walletType} for ${signingMethod}: ${err.message}. Is it installed?`
552
- )
587
+ const decryptedResult = this.decryptPayload<any>(
588
+ responseData,
589
+ responseNonce,
590
+ this.walletEncryptionPublicKeyBs58!
553
591
  );
554
- });
555
- });
592
+ if (!decryptedResult) {
593
+ throw new Error(
594
+ `${this.config.type} provider: ${signingMethod}: Failed to decrypt response or invalid decrypted data.`
595
+ );
596
+ }
597
+
598
+ return decryptedResult as T;
599
+ },
600
+ signingMethod
601
+ );
556
602
  }
557
603
  }
558
604