@reown/appkit-solana-react-native 2.0.0 → 2.0.1

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 (75) hide show
  1. package/lib/commonjs/adapter.js +183 -4
  2. package/lib/commonjs/adapter.js.map +1 -1
  3. package/lib/commonjs/connectors/DeeplinkConnector.js +271 -0
  4. package/lib/commonjs/connectors/DeeplinkConnector.js.map +1 -0
  5. package/lib/commonjs/connectors/PhantomConnector.js +18 -226
  6. package/lib/commonjs/connectors/PhantomConnector.js.map +1 -1
  7. package/lib/commonjs/connectors/SolflareConnector.js +39 -0
  8. package/lib/commonjs/connectors/SolflareConnector.js.map +1 -0
  9. package/lib/commonjs/helpers.js +0 -1
  10. package/lib/commonjs/helpers.js.map +1 -1
  11. package/lib/commonjs/index.js +8 -1
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/package.json +1 -0
  14. package/lib/commonjs/providers/DeeplinkProvider.js +432 -0
  15. package/lib/commonjs/providers/DeeplinkProvider.js.map +1 -0
  16. package/lib/commonjs/types.js.map +1 -1
  17. package/lib/commonjs/utils/createSPLTokenTransaction.js +96 -0
  18. package/lib/commonjs/utils/createSPLTokenTransaction.js.map +1 -0
  19. package/lib/commonjs/utils/createSendTransaction.js +30 -0
  20. package/lib/commonjs/utils/createSendTransaction.js.map +1 -0
  21. package/lib/module/adapter.js +184 -4
  22. package/lib/module/adapter.js.map +1 -1
  23. package/lib/module/connectors/DeeplinkConnector.js +265 -0
  24. package/lib/module/connectors/DeeplinkConnector.js.map +1 -0
  25. package/lib/module/connectors/PhantomConnector.js +21 -226
  26. package/lib/module/connectors/PhantomConnector.js.map +1 -1
  27. package/lib/module/connectors/SolflareConnector.js +34 -0
  28. package/lib/module/connectors/SolflareConnector.js.map +1 -0
  29. package/lib/module/helpers.js +2 -1
  30. package/lib/module/helpers.js.map +1 -1
  31. package/lib/module/index.js +7 -4
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/providers/DeeplinkProvider.js +426 -0
  34. package/lib/module/providers/DeeplinkProvider.js.map +1 -0
  35. package/lib/module/types.js +2 -0
  36. package/lib/module/types.js.map +1 -1
  37. package/lib/module/utils/createSPLTokenTransaction.js +92 -0
  38. package/lib/module/utils/createSPLTokenTransaction.js.map +1 -0
  39. package/lib/module/utils/createSendTransaction.js +26 -0
  40. package/lib/module/utils/createSendTransaction.js.map +1 -0
  41. package/lib/typescript/adapter.d.ts +13 -3
  42. package/lib/typescript/adapter.d.ts.map +1 -1
  43. package/lib/typescript/connectors/DeeplinkConnector.d.ts +30 -0
  44. package/lib/typescript/connectors/DeeplinkConnector.d.ts.map +1 -0
  45. package/lib/typescript/connectors/PhantomConnector.d.ts +8 -22
  46. package/lib/typescript/connectors/PhantomConnector.d.ts.map +1 -1
  47. package/lib/typescript/connectors/SolflareConnector.d.ts +12 -0
  48. package/lib/typescript/connectors/SolflareConnector.d.ts.map +1 -0
  49. package/lib/typescript/index.d.ts +3 -2
  50. package/lib/typescript/index.d.ts.map +1 -1
  51. package/lib/typescript/providers/DeeplinkProvider.d.ts +59 -0
  52. package/lib/typescript/providers/DeeplinkProvider.d.ts.map +1 -0
  53. package/lib/typescript/types.d.ts +35 -32
  54. package/lib/typescript/types.d.ts.map +1 -1
  55. package/lib/typescript/utils/createSPLTokenTransaction.d.ts +4 -0
  56. package/lib/typescript/utils/createSPLTokenTransaction.d.ts.map +1 -0
  57. package/lib/typescript/utils/createSendTransaction.d.ts +10 -0
  58. package/lib/typescript/utils/createSendTransaction.d.ts.map +1 -0
  59. package/package.json +13 -5
  60. package/src/adapter.ts +221 -4
  61. package/src/connectors/DeeplinkConnector.ts +353 -0
  62. package/src/connectors/PhantomConnector.ts +19 -311
  63. package/src/connectors/SolflareConnector.ts +36 -0
  64. package/src/index.ts +6 -5
  65. package/src/providers/DeeplinkProvider.ts +605 -0
  66. package/src/types.ts +38 -37
  67. package/src/utils/createSPLTokenTransaction.ts +152 -0
  68. package/src/utils/createSendTransaction.ts +41 -0
  69. package/lib/commonjs/providers/PhantomProvider.js +0 -391
  70. package/lib/commonjs/providers/PhantomProvider.js.map +0 -1
  71. package/lib/module/providers/PhantomProvider.js +0 -383
  72. package/lib/module/providers/PhantomProvider.js.map +0 -1
  73. package/lib/typescript/providers/PhantomProvider.d.ts +0 -37
  74. package/lib/typescript/providers/PhantomProvider.d.ts.map +0 -1
  75. package/src/providers/PhantomProvider.ts +0 -530
@@ -0,0 +1,36 @@
1
+ import { ConstantsUtil, type WalletInfo } from '@reown/appkit-common-react-native';
2
+ import { DeeplinkConnector } from './DeeplinkConnector';
3
+ import type { SolflareConnectorConfig } from '../types';
4
+
5
+ const SOLFLARE_BASE_URL = 'https://solflare.com/ul/v1';
6
+ const SOLFLARE_CONNECTOR_STORAGE_KEY = '@appkit/solflare-connector-data';
7
+ const SOLFLARE_DAPP_KEYPAIR_STORAGE_KEY = '@appkit/solflare-dapp-secret-key';
8
+
9
+ export class SolflareConnector extends DeeplinkConnector {
10
+ constructor(config?: SolflareConnectorConfig) {
11
+ super({ type: 'solflare', cluster: config?.cluster });
12
+ }
13
+
14
+ override getWalletInfo(): WalletInfo {
15
+ return {
16
+ name: ConstantsUtil.SOLFLARE_CUSTOM_WALLET.name,
17
+ type: 'external'
18
+ };
19
+ }
20
+
21
+ protected getBaseUrl(): string {
22
+ return SOLFLARE_BASE_URL;
23
+ }
24
+
25
+ protected getStorageKey(): string {
26
+ return SOLFLARE_CONNECTOR_STORAGE_KEY;
27
+ }
28
+
29
+ protected getDappKeypairStorageKey(): string {
30
+ return SOLFLARE_DAPP_KEYPAIR_STORAGE_KEY;
31
+ }
32
+
33
+ protected getEncryptionKeyFieldName(): string {
34
+ return 'solflare_encryption_public_key';
35
+ }
36
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,9 @@
1
- // Connectors
2
- export { PhantomConnector } from './connectors/PhantomConnector';
1
+ // Adapter
2
+ export { SolanaAdapter } from './adapter';
3
3
 
4
4
  // Types
5
- export type { PhantomConnectorConfig } from './types';
5
+ export type { PhantomConnectorConfig, SolflareConnectorConfig } from './types';
6
6
 
7
- // Adapter
8
- export { SolanaAdapter } from './adapter';
7
+ // Connectors
8
+ export { PhantomConnector } from './connectors/PhantomConnector';
9
+ export { SolflareConnector } from './connectors/SolflareConnector';
@@ -0,0 +1,605 @@
1
+ import { Linking } from 'react-native';
2
+ import nacl from 'tweetnacl';
3
+ import bs58 from 'bs58';
4
+ import { Buffer } from 'buffer';
5
+ import type {
6
+ Provider,
7
+ RequestArguments,
8
+ CaipNetworkId,
9
+ Storage
10
+ } from '@reown/appkit-common-react-native';
11
+ import type {
12
+ DeeplinkProviderConfig,
13
+ DeeplinkConnectResult,
14
+ DeeplinkSession,
15
+ DecryptedConnectData,
16
+ DeeplinkResponse,
17
+ SignAllTransactionsRequestParams,
18
+ SignMessageRequestParams,
19
+ SignTransactionRequestParams,
20
+ DeeplinkRpcMethod,
21
+ DeeplinkConnectParams,
22
+ DeeplinkDisconnectParams,
23
+ DeeplinkSignTransactionParams,
24
+ DeeplinkSignMessageParams,
25
+ DeeplinkSignAllTransactionsParams,
26
+ Cluster
27
+ } from '../types';
28
+ import EventEmitter from 'events';
29
+
30
+ export const SOLANA_SIGNING_METHODS = {
31
+ SOLANA_SIGN_TRANSACTION: 'solana_signTransaction',
32
+ SOLANA_SIGN_MESSAGE: 'solana_signMessage',
33
+ SOLANA_SIGN_AND_SEND_TRANSACTION: 'solana_signAndSendTransaction',
34
+ SOLANA_SIGN_ALL_TRANSACTIONS: 'solana_signAllTransactions'
35
+ } as const;
36
+
37
+ type SolanaSigningMethod = Values<typeof SOLANA_SIGNING_METHODS>;
38
+
39
+ function isValidSolanaSigningMethod(method: string): method is SolanaSigningMethod {
40
+ return Object.values(SOLANA_SIGNING_METHODS).includes(method as SolanaSigningMethod);
41
+ }
42
+
43
+ export class DeeplinkProvider extends EventEmitter implements Provider {
44
+ private readonly config: DeeplinkProviderConfig;
45
+ private dappEncryptionKeyPair: nacl.BoxKeyPair;
46
+ private currentCluster: Cluster;
47
+ private sharedKey: Uint8Array | null = null;
48
+
49
+ private storage: Storage;
50
+
51
+ private sessionToken: string | null = null;
52
+ private userPublicKey: string | null = null;
53
+ private walletEncryptionPublicKeyBs58: string | null = null;
54
+
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) {
60
+ super();
61
+ this.config = config;
62
+ this.currentCluster = config.cluster ?? 'mainnet-beta';
63
+ this.dappEncryptionKeyPair = config.dappEncryptionKeyPair;
64
+ this.storage = config.storage;
65
+ }
66
+
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;
100
+ }
101
+
102
+ getUserPublicKey(): string | null {
103
+ return this.userPublicKey;
104
+ }
105
+
106
+ isConnected(): boolean {
107
+ return !!this.sessionToken && !!this.userPublicKey && !!this.dappEncryptionKeyPair;
108
+ }
109
+
110
+ public getCurrentCluster(): Cluster {
111
+ return this.currentCluster;
112
+ }
113
+
114
+ private buildUrl(rpcMethod: DeeplinkRpcMethod, params: Record<string, string>): string {
115
+ const query = new URLSearchParams(params).toString();
116
+
117
+ return `${this.config.baseUrl}/${rpcMethod}?${query}`;
118
+ }
119
+
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 {
174
+ switch (method) {
175
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_TRANSACTION:
176
+ return 'signTransaction';
177
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_AND_SEND_TRANSACTION:
178
+ return 'signAndSendTransaction';
179
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_ALL_TRANSACTIONS:
180
+ return 'signAllTransactions';
181
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_MESSAGE:
182
+ return 'signMessage';
183
+ default:
184
+ // Should not happen due to type constraints on `method`
185
+ throw new Error(`Unsupported Solana signing method: ${method}`);
186
+ }
187
+ }
188
+
189
+ private encryptPayload(
190
+ payload: Record<string, unknown>,
191
+ walletPublicKeyBs58: string
192
+ ): { nonce: string; encryptedPayload: string } | null {
193
+ if (!walletPublicKeyBs58) {
194
+ return null;
195
+ }
196
+ try {
197
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
198
+ const payloadBytes = Buffer.from(JSON.stringify(payload), 'utf8');
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
+ }
211
+
212
+ return {
213
+ nonce: bs58.encode(nonce),
214
+ encryptedPayload: bs58.encode(encryptedPayload)
215
+ };
216
+ } catch (error) {
217
+ console.warn(`${this.config.type} provider: Failed to encrypt payload.`, error);
218
+
219
+ return null;
220
+ }
221
+ }
222
+
223
+ private decryptPayload<T>(
224
+ encryptedDataBs58: string,
225
+ nonceBs58: string,
226
+ walletSenderPublicKeyBs58: string
227
+ ): T | null {
228
+ try {
229
+ const formattedEncryptedDataBs58 = encryptedDataBs58.replace('#', '');
230
+ const encryptedDataBytes = bs58.decode(formattedEncryptedDataBs58);
231
+ const nonceBytes = bs58.decode(nonceBs58);
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
+ }
244
+ if (!decryptedPayloadBytes) {
245
+ return null;
246
+ }
247
+
248
+ return JSON.parse(Buffer.from(decryptedPayloadBytes).toString('utf8')) as T;
249
+ } catch (error) {
250
+ console.warn(`${this.config.type} provider: Failed to decrypt payload.`, error);
251
+
252
+ return null;
253
+ }
254
+ }
255
+
256
+ public async restoreSession(): Promise<boolean> {
257
+ try {
258
+ const session = await this.storage.getItem<DeeplinkSession>(this.getSessionStorageKey());
259
+ if (session) {
260
+ this.setSession(session);
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
+
273
+ return true;
274
+ }
275
+
276
+ return false;
277
+ } catch (error) {
278
+ // console.error(`${this.config.type} provider: Failed to restore session.`, error);
279
+ await this.clearSessionStorage(); // Clear potentially corrupt data
280
+
281
+ return false;
282
+ }
283
+ }
284
+
285
+ private async saveSession(): Promise<void> {
286
+ if (!this.sessionToken || !this.userPublicKey || !this.walletEncryptionPublicKeyBs58) {
287
+ return; // Cannot save incomplete session
288
+ }
289
+ const session: DeeplinkSession = {
290
+ sessionToken: this.sessionToken,
291
+ userPublicKey: this.userPublicKey,
292
+ walletEncryptionPublicKeyBs58: this.walletEncryptionPublicKeyBs58,
293
+ cluster: this.currentCluster
294
+ };
295
+ try {
296
+ await this.storage.setItem(this.getSessionStorageKey(), session);
297
+ } catch (error) {
298
+ // console.error(`${this.config.type} provider: Failed to save session.`, error);
299
+ }
300
+ }
301
+
302
+ private async clearSessionStorage(): Promise<void> {
303
+ try {
304
+ await this.storage.removeItem(this.getSessionStorageKey());
305
+ } catch (error) {
306
+ // console.error(`${this.config.type} provider: Failed to clear session storage.`, error);
307
+ }
308
+ }
309
+
310
+ public async connect<T = DeeplinkConnectResult>(params?: { cluster?: Cluster }): Promise<T> {
311
+ const cluster = params?.cluster ?? 'mainnet-beta';
312
+ this.currentCluster = cluster;
313
+ const connectDeeplinkParams: DeeplinkConnectParams = {
314
+ app_url: this.config.dappUrl,
315
+ dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
316
+ redirect_link: this.config.appScheme,
317
+ cluster
318
+ };
319
+ const url = this.buildUrl('connect', connectDeeplinkParams as any);
320
+
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.`);
335
+ }
336
+
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.`
345
+ );
346
+ }
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;
360
+ }
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>;
374
+ }
375
+
376
+ public async disconnect(): Promise<void> {
377
+ if (!this.sessionToken || !this.walletEncryptionPublicKeyBs58) {
378
+ await this.clearSession();
379
+ this.emit('disconnect');
380
+
381
+ return Promise.resolve();
382
+ }
383
+
384
+ const payloadToEncrypt = { session: this.sessionToken };
385
+ const encryptedDisconnectPayload = this.encryptPayload(
386
+ payloadToEncrypt,
387
+ this.walletEncryptionPublicKeyBs58
388
+ );
389
+
390
+ if (!encryptedDisconnectPayload) {
391
+ // console.warn(`${this.config.type} provider: Failed to encrypt disconnect payload. Clearing session locally.`);
392
+ await this.clearSession();
393
+ this.emit('disconnect');
394
+
395
+ return Promise.resolve(); // Or reject, depending on desired strictness
396
+ }
397
+
398
+ const disconnectDeeplinkParams: DeeplinkDisconnectParams = {
399
+ dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
400
+ redirect_link: this.config.appScheme,
401
+ payload: encryptedDisconnectPayload.encryptedPayload,
402
+ nonce: encryptedDisconnectPayload.nonce
403
+ };
404
+ const url = this.buildUrl('disconnect', disconnectDeeplinkParams as any);
405
+
406
+ return this.openDeeplinkAndWait<void>(
407
+ url,
408
+ () => {
409
+ this.clearSession();
410
+ },
411
+ 'Disconnection'
412
+ );
413
+ }
414
+
415
+ public async clearSession(): Promise<void> {
416
+ this.sessionToken = null;
417
+ this.userPublicKey = null;
418
+ this.walletEncryptionPublicKeyBs58 = null;
419
+ if (this.sharedKey) {
420
+ this.sharedKey.fill(0);
421
+ }
422
+ this.sharedKey = null;
423
+ this.cleanupActiveSubscription();
424
+ await this.clearSessionStorage();
425
+ }
426
+
427
+ public setSession(session: DeeplinkSession): void {
428
+ this.sessionToken = session.sessionToken;
429
+ this.userPublicKey = session.userPublicKey;
430
+ this.walletEncryptionPublicKeyBs58 = session.walletEncryptionPublicKeyBs58;
431
+ this.currentCluster = session.cluster;
432
+ }
433
+
434
+ public async request<T>(args: RequestArguments, _chainId?: CaipNetworkId): Promise<T> {
435
+ if (!isValidSolanaSigningMethod(args.method)) {
436
+ throw new Error(
437
+ `${this.config.type} provider: Unsupported method: ${args.method}. Only Solana signing methods are supported.`
438
+ );
439
+ }
440
+ const signingMethod = args.method as SolanaSigningMethod;
441
+ const requestParams = args.params as any;
442
+
443
+ if (!this.isConnected() || !this.sessionToken || !this.walletEncryptionPublicKeyBs58) {
444
+ throw new Error(
445
+ `${this.config.type} provider: Not connected or session details missing. Cannot process request.`
446
+ );
447
+ }
448
+
449
+ const rpcMethodName = this.getRpcMethodName(signingMethod);
450
+ let deeplinkUrl = '';
451
+
452
+ switch (signingMethod) {
453
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_TRANSACTION:
454
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_AND_SEND_TRANSACTION: {
455
+ const typedParams = requestParams as SignTransactionRequestParams;
456
+ if (!typedParams || typeof typedParams.transaction !== 'string') {
457
+ throw new Error(
458
+ `Missing or invalid 'transaction' (base58 string) in params for ${signingMethod}`
459
+ );
460
+ }
461
+
462
+ const dataToEncrypt = {
463
+ session: this.sessionToken,
464
+ transaction: typedParams.transaction
465
+ };
466
+ const encryptedData = this.encryptPayload(
467
+ dataToEncrypt,
468
+ this.walletEncryptionPublicKeyBs58
469
+ );
470
+ if (!encryptedData) {
471
+ throw new Error(
472
+ `${this.config.type} provider: Failed to encrypt payload for ${signingMethod}.`
473
+ );
474
+ }
475
+
476
+ const signTxDeeplinkParams: DeeplinkSignTransactionParams = {
477
+ dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
478
+ redirect_link: this.config.appScheme,
479
+ cluster: this.currentCluster,
480
+ payload: encryptedData.encryptedPayload,
481
+ nonce: encryptedData.nonce
482
+ };
483
+ deeplinkUrl = this.buildUrl(rpcMethodName, signTxDeeplinkParams as any);
484
+ break;
485
+ }
486
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_MESSAGE: {
487
+ const typedParams = requestParams as SignMessageRequestParams;
488
+ if (!typedParams || typeof typedParams.message === 'undefined') {
489
+ throw new Error(
490
+ `${this.config.type} provider: Missing 'message' in params for ${signingMethod}`
491
+ );
492
+ }
493
+
494
+ let messageBs58: string;
495
+ if (typedParams.message instanceof Uint8Array) {
496
+ messageBs58 = bs58.encode(typedParams.message);
497
+ } else if (typeof typedParams.message === 'string') {
498
+ try {
499
+ bs58.decode(typedParams.message);
500
+ messageBs58 = typedParams.message;
501
+ } catch (e) {
502
+ messageBs58 = bs58.encode(Buffer.from(typedParams.message));
503
+ }
504
+ } else {
505
+ throw new Error(
506
+ `${this.config.type} provider: Invalid message format for signMessage. Expected Uint8Array or string.`
507
+ );
508
+ }
509
+
510
+ const dataToEncrypt = {
511
+ message: messageBs58,
512
+ session: this.sessionToken,
513
+ display: typedParams.display || 'utf8'
514
+ };
515
+
516
+ const encryptedPayloadData = this.encryptPayload(
517
+ dataToEncrypt,
518
+ this.walletEncryptionPublicKeyBs58
519
+ );
520
+
521
+ if (!encryptedPayloadData) {
522
+ throw new Error(
523
+ `${this.config.type} provider: Failed to encrypt payload for signMessage.`
524
+ );
525
+ }
526
+
527
+ const signMsgDeeplinkQueryPayload: DeeplinkSignMessageParams = {
528
+ dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
529
+ redirect_link: this.config.appScheme,
530
+ payload: encryptedPayloadData.encryptedPayload,
531
+ nonce: encryptedPayloadData.nonce
532
+ };
533
+ deeplinkUrl = this.buildUrl(rpcMethodName, signMsgDeeplinkQueryPayload as any);
534
+ break;
535
+ }
536
+ case SOLANA_SIGNING_METHODS.SOLANA_SIGN_ALL_TRANSACTIONS: {
537
+ const typedParams = requestParams as SignAllTransactionsRequestParams;
538
+ if (
539
+ !typedParams ||
540
+ !Array.isArray(typedParams.transactions) ||
541
+ !typedParams.transactions.every((t: any) => typeof t === 'string')
542
+ ) {
543
+ throw new Error(
544
+ `${this.config.type} provider: Missing or invalid 'transactions' (array of base58 strings) in params for ${signingMethod}`
545
+ );
546
+ }
547
+
548
+ const dataToEncrypt = {
549
+ session: this.sessionToken,
550
+ transactions: typedParams.transactions
551
+ };
552
+ const encryptedData = this.encryptPayload(
553
+ dataToEncrypt,
554
+ this.walletEncryptionPublicKeyBs58
555
+ );
556
+ if (!encryptedData) {
557
+ throw new Error(
558
+ `${this.config.type} provider: Failed to encrypt payload for ${signingMethod}.`
559
+ );
560
+ }
561
+
562
+ const signAllTxDeeplinkParams: DeeplinkSignAllTransactionsParams = {
563
+ dapp_encryption_public_key: bs58.encode(this.dappEncryptionKeyPair.publicKey),
564
+ redirect_link: this.config.appScheme,
565
+ cluster: this.currentCluster,
566
+ payload: encryptedData.encryptedPayload,
567
+ nonce: encryptedData.nonce
568
+ };
569
+ deeplinkUrl = this.buildUrl(rpcMethodName, signAllTxDeeplinkParams as any);
570
+ break;
571
+ }
572
+ default: {
573
+ throw new Error(`${this.config.type} provider: Unhandled signing method: ${signingMethod}`);
574
+ }
575
+ }
576
+
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.`
585
+ );
586
+ }
587
+ const decryptedResult = this.decryptPayload<any>(
588
+ responseData,
589
+ responseNonce,
590
+ this.walletEncryptionPublicKeyBs58!
591
+ );
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
+ );
602
+ }
603
+ }
604
+
605
+ type Values<T> = T[keyof T];