@dynamic-labs/solana 4.40.2 → 4.41.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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
1
 
2
+ ### [4.41.1](https://github.com/dynamic-labs/dynamic-auth/compare/v4.41.0...v4.41.1) (2025-10-30)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * aptos login with petra keyless accounts ([#9795](https://github.com/dynamic-labs/dynamic-auth/issues/9795)) ([99dc34b](https://github.com/dynamic-labs/dynamic-auth/commit/99dc34b95d1ec23d168c9e092e2d735b1d50a71d))
8
+ * backpack aptos login due to invalid public key ([#9806](https://github.com/dynamic-labs/dynamic-auth/issues/9806)) ([ec8b761](https://github.com/dynamic-labs/dynamic-auth/commit/ec8b76181acff7906fd92c77c2fca42ce52010ae))
9
+
10
+ ## [4.41.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.40.2...v4.41.0) (2025-10-29)
11
+
12
+
13
+ ### Features
14
+
15
+ * **react-native:** add MFA recovery methods ([#9788](https://github.com/dynamic-labs/dynamic-auth/issues/9788)) ([664e0d5](https://github.com/dynamic-labs/dynamic-auth/commit/664e0d5878530abc9e645c8801a87f3109cfd8f9))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * crypto.com link opening on mobile ([#9779](https://github.com/dynamic-labs/dynamic-auth/issues/9779)) ([3439ac9](https://github.com/dynamic-labs/dynamic-auth/commit/3439ac9e2610360c6204f6195433ae3e05e26c30))
21
+
2
22
  ### [4.40.2](https://github.com/dynamic-labs/dynamic-auth/compare/v4.40.1...v4.40.2) (2025-10-28)
3
23
 
4
24
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.40.2";
6
+ var version = "4.41.1";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.40.2";
2
+ var version = "4.41.1";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/solana",
3
- "version": "4.40.2",
3
+ "version": "4.41.1",
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",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "homepage": "https://www.dynamic.xyz/",
20
20
  "dependencies": {
21
- "@dynamic-labs/wallet-connect": "4.40.2",
21
+ "@dynamic-labs/wallet-connect": "4.41.1",
22
22
  "@solana/web3.js": "1.98.1",
23
23
  "@wallet-standard/app": "1.0.1",
24
24
  "@wallet-standard/base": "1.0.1",
@@ -29,17 +29,17 @@
29
29
  "@walletconnect/sign-client": "2.21.5",
30
30
  "@walletconnect/utils": "2.21.5",
31
31
  "@walletconnect/types": "2.21.5",
32
- "@dynamic-labs/assert-package-version": "4.40.2",
33
- "@dynamic-labs/embedded-wallet-solana": "4.40.2",
34
- "@dynamic-labs/logger": "4.40.2",
35
- "@dynamic-labs/rpc-providers": "4.40.2",
32
+ "@dynamic-labs/assert-package-version": "4.41.1",
33
+ "@dynamic-labs/embedded-wallet-solana": "4.41.1",
34
+ "@dynamic-labs/logger": "4.41.1",
35
+ "@dynamic-labs/rpc-providers": "4.41.1",
36
36
  "@dynamic-labs/sdk-api-core": "0.0.813",
37
- "@dynamic-labs/solana-core": "4.40.2",
38
- "@dynamic-labs/types": "4.40.2",
39
- "@dynamic-labs/utils": "4.40.2",
40
- "@dynamic-labs/waas-svm": "4.40.2",
41
- "@dynamic-labs/wallet-book": "4.40.2",
42
- "@dynamic-labs/wallet-connector-core": "4.40.2",
37
+ "@dynamic-labs/solana-core": "4.41.1",
38
+ "@dynamic-labs/types": "4.41.1",
39
+ "@dynamic-labs/utils": "4.41.1",
40
+ "@dynamic-labs/waas-svm": "4.41.1",
41
+ "@dynamic-labs/wallet-book": "4.41.1",
42
+ "@dynamic-labs/wallet-connector-core": "4.41.1",
43
43
  "eventemitter3": "5.0.1"
44
44
  },
45
45
  "peerDependencies": {}
@@ -55,11 +55,18 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
55
55
  }
56
56
  return SolanaWalletConnectConnector.signClientReference;
57
57
  }
58
+ getAvailableMethods() {
59
+ var _a, _b;
60
+ if (!this.session) {
61
+ return [];
62
+ }
63
+ return (_b = (_a = this.session.namespaces.solana) === null || _a === void 0 ? void 0 : _a.methods) !== null && _b !== void 0 ? _b : [];
64
+ }
58
65
  getSupportedNetworks() {
59
66
  return _tslib.__awaiter(this, void 0, void 0, function* () {
60
67
  var _a, _b;
61
68
  if (!this.session) {
62
- return [];
69
+ return this.solNetworks.map((network) => network.chainId.toString());
63
70
  }
64
71
  const sessionChains = (_b = (_a = this.session.namespaces.solana) === null || _a === void 0 ? void 0 : _a.chains) !== null && _b !== void 0 ? _b : [];
65
72
  return this.solNetworks
@@ -80,6 +87,46 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
80
87
  this.activeAccountEmitter.off('activeAccountDidChange', listener);
81
88
  };
82
89
  }
90
+ isSendBalanceUnsupported() {
91
+ // If there is no session, we assume the wallet supports send balance
92
+ // If we didn't, the send TX button would turn disabled on a refresh
93
+ if (!this.session) {
94
+ return false;
95
+ }
96
+ const supportedMethods = this.getAvailableMethods();
97
+ return (!supportedMethods.includes('solana_signAndSendTransaction') &&
98
+ !supportedMethods.includes('solana_signTransaction'));
99
+ }
100
+ createUiTransaction(from) {
101
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
102
+ yield this.validateActiveWallet(from);
103
+ if (this.isSendBalanceUnsupported()) {
104
+ throw new utils.DynamicError('Wallet does not support signing transactions. Please connect to a wallet that supports signing transactions.');
105
+ }
106
+ const transaction = new solanaCore.SolanaUiTransaction({
107
+ connection: this.getWalletClient(),
108
+ from,
109
+ onSubmit: (transaction) => _tslib.__awaiter(this, void 0, void 0, function* () {
110
+ var _a;
111
+ if (!transaction)
112
+ return;
113
+ const blockhash = yield this.getWalletClient().getLatestBlockhash();
114
+ if ('version' in transaction) {
115
+ transaction.message.recentBlockhash =
116
+ blockhash.blockhash;
117
+ }
118
+ else {
119
+ // Use the 'from' parameter which is the validated active wallet address
120
+ transaction.recentBlockhash = blockhash.blockhash;
121
+ transaction.feePayer =
122
+ (_a = transaction.feePayer) !== null && _a !== void 0 ? _a : new web3_js.PublicKey(from);
123
+ }
124
+ return yield this.signAndSendTransaction(transaction);
125
+ }),
126
+ });
127
+ return transaction;
128
+ });
129
+ }
83
130
  getSigner() {
84
131
  return _tslib.__awaiter(this, void 0, void 0, function* () {
85
132
  return createSolanaSignerForWalletConnect.createSolanaSignerForWalletConnect({ walletConnector: this });
@@ -90,6 +137,23 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
90
137
  throw new Error('Connect method not implemented.');
91
138
  });
92
139
  }
140
+ static globalInit(args) {
141
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
142
+ var _a, _b;
143
+ SolanaWalletConnectConnector.signClientPromise = walletConnect.getSignClientSingleton({
144
+ appIcon: (_a = args.appLogoUrl) !== null && _a !== void 0 ? _a : '',
145
+ appName: (_b = args.appName) !== null && _b !== void 0 ? _b : '',
146
+ projectId: args.projectId,
147
+ });
148
+ SolanaWalletConnectConnector.signClientReference =
149
+ yield SolanaWalletConnectConnector.signClientPromise;
150
+ SolanaWalletConnectConnector.signClientReference.on('session_event', (event) => {
151
+ SolanaWalletConnectConnector.sessionEventListeners.forEach((listener) => {
152
+ listener(event);
153
+ });
154
+ });
155
+ });
156
+ }
93
157
  init() {
94
158
  return _tslib.__awaiter(this, void 0, void 0, function* () {
95
159
  if (this.isInitialized) {
@@ -102,64 +166,58 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
102
166
  this.setupWCEventListeners();
103
167
  return;
104
168
  }
105
- const { appLogoUrl, appName, projectId } = this.constructorProps;
106
- SolanaWalletConnectConnector.signClientPromise = walletConnect.getSignClientSingleton({
107
- appIcon: appLogoUrl !== null && appLogoUrl !== void 0 ? appLogoUrl : '',
108
- appName: appName !== null && appName !== void 0 ? appName : '',
109
- projectId,
110
- });
111
- SolanaWalletConnectConnector.signClientReference =
112
- yield SolanaWalletConnectConnector.signClientPromise;
169
+ yield SolanaWalletConnectConnector.globalInit(this.constructorProps);
113
170
  this.setupWCEventListeners();
114
171
  });
115
172
  }
116
- setupWCEventListeners() {
117
- logger.logger.debug('[SolanaWalletConnect] setupWCEventListeners');
118
- this.signClient.on('session_event', ({ params: { event }, topic }) => {
119
- var _a, _b;
120
- // Ignore events for wallets other than the one we are connected to
121
- if (!this.session || topic !== this.session.topic) {
122
- return;
123
- }
124
- if (event.name === 'chainChanged') {
125
- const chainParam = event.data;
126
- let chainId = chainParam;
127
- // Handle potentially CAIP-2 format
128
- if (chainParam.startsWith('solana:')) {
129
- const chainHash = chainParam.split(':')[1];
130
- chainId =
131
- (_b = (_a = this.solNetworks
132
- .find((network) => network.genesisHash === chainHash)) === null || _a === void 0 ? void 0 : _a.networkId.toString()) !== null && _b !== void 0 ? _b : chainParam;
133
- }
134
- logger.logger.debug('[SolanaWalletConnect] onChainChange', { chainId });
135
- if (chainId === this.getNetworkId()) {
136
- logger.logger.debug(`[SolanaWalletConnect] onChainChange - ignoring chainChanged event with same chain id as current chain id: ${chainId}`);
137
- return;
138
- }
139
- // This will already emit an event so no need to call this.emit('chainChange', { chain: chainId });
140
- this.switchNetwork({ networkChainId: parseInt(chainId) });
141
- return;
142
- }
143
- if (event.name === 'accountsChanged') {
144
- const accountsParam = event.data;
145
- const accounts = utils.filterDuplicates(accountsParam.map((account) => {
146
- // Handle potentially CAIP-10 format
147
- if (account.startsWith('solana:')) {
148
- return account.split(':')[2];
149
- }
150
- return account;
151
- }));
152
- logger.logger.debug('[SolanaWalletConnect] onAccountChanged', { accounts });
153
- this.emit('accountChange', { accounts });
154
- return;
173
+ handleSessionEvent({ params: { event }, topic }) {
174
+ var _a, _b;
175
+ // Ignore events for wallets other than the one we are connected to
176
+ if (!this.session || topic !== this.session.topic) {
177
+ return;
178
+ }
179
+ if (event.name === 'chainChanged') {
180
+ const chainParam = event.data;
181
+ let chainId = chainParam;
182
+ // Handle potentially CAIP-2 format
183
+ if (typeof chainParam === 'string' && chainParam.startsWith('solana:')) {
184
+ const chainHash = chainParam.split(':')[1];
185
+ chainId =
186
+ (_b = (_a = this.solNetworks
187
+ .find((network) => network.genesisHash === chainHash)) === null || _a === void 0 ? void 0 : _a.networkId.toString()) !== null && _b !== void 0 ? _b : chainParam;
155
188
  }
156
- if (event.name === 'disconnected') {
157
- logger.logger.debug('[SolanaWalletConnect] onDisconnect');
158
- this.emit('disconnect');
159
- this.endSession();
189
+ logger.logger.debug('[SolanaWalletConnect] onChainChange', { chainId });
190
+ if (chainId === this.getNetworkId()) {
191
+ logger.logger.debug(`[SolanaWalletConnect] onChainChange - ignoring chainChanged event with same chain id as current chain id: ${chainId}`);
160
192
  return;
161
193
  }
162
- });
194
+ // This will already emit an event so no need to call this.emit('chainChange', { chain: chainId });
195
+ this.switchNetwork({ networkChainId: parseInt(String(chainId)) });
196
+ return;
197
+ }
198
+ if (event.name === 'accountsChanged') {
199
+ const accountsParam = event.data;
200
+ const accounts = utils.filterDuplicates(accountsParam.map((account) => {
201
+ // Handle potentially CAIP-10 format
202
+ if (account.startsWith('solana:')) {
203
+ return account.split(':')[2];
204
+ }
205
+ return account;
206
+ }));
207
+ logger.logger.debug('[SolanaWalletConnect] onAccountChanged', { accounts });
208
+ this.emit('accountChange', { accounts });
209
+ return;
210
+ }
211
+ if (event.name === 'disconnected') {
212
+ logger.logger.debug('[SolanaWalletConnect] onDisconnect');
213
+ this.emit('disconnect');
214
+ this.endSession();
215
+ return;
216
+ }
217
+ }
218
+ setupWCEventListeners() {
219
+ logger.logger.debug('[SolanaWalletConnect] setupWCEventListeners');
220
+ SolanaWalletConnectConnector.sessionEventListeners.push((event) => this.handleSessionEvent(event));
163
221
  }
164
222
  endSession() {
165
223
  return _tslib.__awaiter(this, void 0, void 0, function* () {
@@ -212,8 +270,14 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
212
270
  }
213
271
  logger.logger.debug('[SolanaWalletConnect] getAddress - connecting to WalletConnect');
214
272
  const chains = this.solNetworks.map((network) => `solana:${network.genesisHash}`);
273
+ // Await for the promise in case this gets called right as the window is loaded
274
+ // and the sign client is still initializing
275
+ const signClient = yield SolanaWalletConnectConnector.signClientPromise;
276
+ if (!signClient) {
277
+ throw new utils.DynamicError('Failed to access sign client for Wallet Connect Solana: Sign client not initialized');
278
+ }
215
279
  try {
216
- const { approval, uri } = yield this.signClient.connect({
280
+ const { approval, uri } = yield signClient.connect({
217
281
  optionalNamespaces: {
218
282
  solana: {
219
283
  chains,
@@ -330,12 +394,27 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
330
394
  if (!activeAddress) {
331
395
  throw new utils.DynamicError('Active account address is required');
332
396
  }
397
+ logger.logger.debug('[SolanaWalletConnect] Signing transaction', {
398
+ activeAddress,
399
+ isVersioned: 'version' in transaction,
400
+ transaction,
401
+ });
333
402
  const serializedTransaction = this.serializeTransaction(transaction);
403
+ logger.logger.debug('[SolanaWalletConnect] Sending to wallet for signing', {
404
+ serializedLength: serializedTransaction.length,
405
+ });
334
406
  const { transaction: signedTransaction } = yield this.signClientRequest({
335
407
  method: 'solana_signTransaction',
336
408
  params: { transaction: serializedTransaction },
337
409
  });
410
+ logger.logger.debug('[SolanaWalletConnect] Received signed transaction from wallet', {
411
+ signedTransactionLength: signedTransaction.length,
412
+ signedTransactionPreview: signedTransaction.substring(0, 50),
413
+ });
338
414
  const decodedTransaction = bs58__default["default"].decode(signedTransaction);
415
+ logger.logger.debug('[SolanaWalletConnect] Decoded signed transaction', {
416
+ decodedLength: decodedTransaction.length,
417
+ });
339
418
  if (solanaCore.isVersionedTransaction(transaction)) {
340
419
  return web3_js.VersionedTransaction.deserialize(decodedTransaction);
341
420
  }
@@ -361,12 +440,45 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
361
440
  }
362
441
  signAndSendTransaction(transaction, options) {
363
442
  return _tslib.__awaiter(this, void 0, void 0, function* () {
364
- const serializedTransaction = this.serializeTransaction(transaction);
365
- const { signature } = yield this.signClientRequest({
366
- method: 'solana_signAndSendTransaction',
367
- params: { options, transaction: serializedTransaction },
368
- });
369
- return signature;
443
+ const supportedMethods = this.getAvailableMethods();
444
+ // Try to use signAndSendTransaction if supported
445
+ if (supportedMethods.includes('solana_signAndSendTransaction')) {
446
+ const serializedTransaction = this.serializeTransaction(transaction);
447
+ const { signature } = yield this.signClientRequest({
448
+ method: 'solana_signAndSendTransaction',
449
+ params: { options, transaction: serializedTransaction },
450
+ });
451
+ return signature;
452
+ }
453
+ // Fallback: use signTransaction + sendRawTransaction
454
+ if (supportedMethods.includes('solana_signTransaction')) {
455
+ logger.logger.debug('[SolanaWalletConnect] Using fallback: signTransaction + sendRawTransaction');
456
+ const signedTransaction = yield this.signTransaction(transaction);
457
+ const isSigned = solanaCore.isTxAlreadySigned(signedTransaction);
458
+ logger.logger.debug('[SolanaWalletConnect] Transaction signed, checking signatures', {
459
+ hasSignatures: 'signatures' in signedTransaction &&
460
+ signedTransaction.signatures.length > 0,
461
+ isSigned,
462
+ signaturesLength: 'signatures' in signedTransaction
463
+ ? signedTransaction.signatures.length
464
+ : 'N/A',
465
+ });
466
+ if (!isSigned) {
467
+ throw new utils.DynamicError('Transaction returned from wallet was not properly signed. The wallet may have rejected the signing request.');
468
+ }
469
+ // When sending a signed transaction, serialize it without flags
470
+ // (requireAllSignatures and verifySignatures are only for unsigned transactions)
471
+ const serialized = signedTransaction.serialize();
472
+ logger.logger.debug('[SolanaWalletConnect] Transaction serialized', {
473
+ serializedLength: serialized.length,
474
+ });
475
+ const signature = yield this.getWalletClient().sendRawTransaction(serialized, options);
476
+ logger.logger.debug('[SolanaWalletConnect] Transaction sent successfully', {
477
+ signature,
478
+ });
479
+ return signature;
480
+ }
481
+ throw new utils.DynamicError('Wallet does not support signing and sending transactions. Please connect to a wallet that supports at least solana_signTransaction.');
370
482
  });
371
483
  }
372
484
  getDeepLink() {
@@ -374,8 +486,6 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
374
486
  deepLinks: this.metadata.deepLinks,
375
487
  mode: 'regular',
376
488
  preference: this.deepLinkPreference,
377
- // TODO: verify whether we need the uri
378
- // uri: provider.signer.uri,
379
489
  });
380
490
  logger.logger.logVerboseTroubleshootingMessage('[SolanaWalletConnect] getDeepLink - deepLink', deepLink);
381
491
  if (!deepLink) {
@@ -421,5 +531,6 @@ class SolanaWalletConnectConnector extends solanaCore.SolanaWalletConnector {
421
531
  });
422
532
  }
423
533
  }
534
+ SolanaWalletConnectConnector.sessionEventListeners = [];
424
535
 
425
536
  exports.SolanaWalletConnectConnector = SolanaWalletConnectConnector;
@@ -1,8 +1,9 @@
1
1
  import { SendOptions, Transaction, VersionedTransaction } from '@solana/web3.js';
2
2
  import SignClient from '@walletconnect/sign-client';
3
- import type { SessionTypes } from '@walletconnect/types';
3
+ import type { SessionTypes, SignClientTypes } from '@walletconnect/types';
4
4
  import { ISolanaSigner, SolanaWalletConnector, SolanaWalletConnectorOpts } from '@dynamic-labs/solana-core';
5
- import { DeepLinkVariant, GetAddressOpts, IWalletConnectConnector } from '@dynamic-labs/wallet-connector-core';
5
+ import { DeepLinkVariant, GetAddressOpts, ISendBalanceWalletConnector, IWalletConnectConnector } from '@dynamic-labs/wallet-connector-core';
6
+ import { IUITransaction } from '@dynamic-labs/types';
6
7
  export type SolanaWalletConnectConnectorOpts = SolanaWalletConnectorOpts & {
7
8
  projectId?: string;
8
9
  walletName: string;
@@ -11,10 +12,18 @@ export type SolanaWalletConnectConnectorOpts = SolanaWalletConnectorOpts & {
11
12
  appName?: string;
12
13
  overrideKey?: string;
13
14
  };
14
- export declare class SolanaWalletConnectConnector extends SolanaWalletConnector implements IWalletConnectConnector {
15
+ type SessionEventArgs = SignClientTypes.BaseEventArgs<{
16
+ event: {
17
+ name: string;
18
+ data: any;
19
+ };
20
+ chainId: string;
21
+ }>;
22
+ export declare class SolanaWalletConnectConnector extends SolanaWalletConnector implements IWalletConnectConnector, ISendBalanceWalletConnector {
15
23
  name: string;
16
24
  static signClientReference: SignClient | undefined;
17
25
  static signClientPromise: Promise<SignClient> | undefined;
26
+ static sessionEventListeners: ((args: SessionEventArgs) => void)[];
18
27
  /**
19
28
  * When a WalletConnect connection is initiated, we store the connection URI
20
29
  * so we can use it to handle the URI for wallet app connection and still have
@@ -35,12 +44,21 @@ export declare class SolanaWalletConnectConnector extends SolanaWalletConnector
35
44
  * immediately available.
36
45
  */
37
46
  get signClient(): SignClient;
47
+ getAvailableMethods(): string[];
38
48
  getSupportedNetworks(): Promise<string[]>;
39
49
  getActiveAddress(): string | undefined;
40
50
  listenToActiveAccountChange(listener: (account: string) => void): VoidFunction;
51
+ isSendBalanceUnsupported(): boolean;
52
+ createUiTransaction(from: string): Promise<IUITransaction>;
41
53
  getSigner(): Promise<ISolanaSigner>;
42
54
  connect(): Promise<void>;
55
+ static globalInit(args: {
56
+ appLogoUrl: string;
57
+ appName: string;
58
+ projectId: string;
59
+ }): Promise<void>;
43
60
  init(): Promise<void>;
61
+ handleSessionEvent({ params: { event }, topic }: SessionEventArgs): void;
44
62
  private setupWCEventListeners;
45
63
  endSession(): Promise<void>;
46
64
  private displayUri;
@@ -62,3 +80,4 @@ export declare class SolanaWalletConnectConnector extends SolanaWalletConnector
62
80
  getConnectionUri(): string | undefined;
63
81
  validateActiveWallet(expectedAddress: string): Promise<void>;
64
82
  }
83
+ export {};
@@ -4,7 +4,7 @@ import { PublicKey, VersionedTransaction, Transaction } from '@solana/web3.js';
4
4
  import { SDK_ERRORS } from '@walletconnect/utils';
5
5
  import bs58 from 'bs58';
6
6
  import EventEmitter from 'eventemitter3';
7
- import { SolanaWalletConnector, isVersionedTransaction } from '@dynamic-labs/solana-core';
7
+ import { SolanaWalletConnector, SolanaUiTransaction, isVersionedTransaction, isTxAlreadySigned } from '@dynamic-labs/solana-core';
8
8
  import { StorageService, DynamicError, filterDuplicates, isMobile, PlatformService, bufferToBase64 } from '@dynamic-labs/utils';
9
9
  import { getSignClientSingleton } from '@dynamic-labs/wallet-connect';
10
10
  import { performPlatformSpecificConnectionMethod, getDeepLink, isSameAddress } from '@dynamic-labs/wallet-connector-core';
@@ -46,11 +46,18 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
46
46
  }
47
47
  return SolanaWalletConnectConnector.signClientReference;
48
48
  }
49
+ getAvailableMethods() {
50
+ var _a, _b;
51
+ if (!this.session) {
52
+ return [];
53
+ }
54
+ return (_b = (_a = this.session.namespaces.solana) === null || _a === void 0 ? void 0 : _a.methods) !== null && _b !== void 0 ? _b : [];
55
+ }
49
56
  getSupportedNetworks() {
50
57
  return __awaiter(this, void 0, void 0, function* () {
51
58
  var _a, _b;
52
59
  if (!this.session) {
53
- return [];
60
+ return this.solNetworks.map((network) => network.chainId.toString());
54
61
  }
55
62
  const sessionChains = (_b = (_a = this.session.namespaces.solana) === null || _a === void 0 ? void 0 : _a.chains) !== null && _b !== void 0 ? _b : [];
56
63
  return this.solNetworks
@@ -71,6 +78,46 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
71
78
  this.activeAccountEmitter.off('activeAccountDidChange', listener);
72
79
  };
73
80
  }
81
+ isSendBalanceUnsupported() {
82
+ // If there is no session, we assume the wallet supports send balance
83
+ // If we didn't, the send TX button would turn disabled on a refresh
84
+ if (!this.session) {
85
+ return false;
86
+ }
87
+ const supportedMethods = this.getAvailableMethods();
88
+ return (!supportedMethods.includes('solana_signAndSendTransaction') &&
89
+ !supportedMethods.includes('solana_signTransaction'));
90
+ }
91
+ createUiTransaction(from) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ yield this.validateActiveWallet(from);
94
+ if (this.isSendBalanceUnsupported()) {
95
+ throw new DynamicError('Wallet does not support signing transactions. Please connect to a wallet that supports signing transactions.');
96
+ }
97
+ const transaction = new SolanaUiTransaction({
98
+ connection: this.getWalletClient(),
99
+ from,
100
+ onSubmit: (transaction) => __awaiter(this, void 0, void 0, function* () {
101
+ var _a;
102
+ if (!transaction)
103
+ return;
104
+ const blockhash = yield this.getWalletClient().getLatestBlockhash();
105
+ if ('version' in transaction) {
106
+ transaction.message.recentBlockhash =
107
+ blockhash.blockhash;
108
+ }
109
+ else {
110
+ // Use the 'from' parameter which is the validated active wallet address
111
+ transaction.recentBlockhash = blockhash.blockhash;
112
+ transaction.feePayer =
113
+ (_a = transaction.feePayer) !== null && _a !== void 0 ? _a : new PublicKey(from);
114
+ }
115
+ return yield this.signAndSendTransaction(transaction);
116
+ }),
117
+ });
118
+ return transaction;
119
+ });
120
+ }
74
121
  getSigner() {
75
122
  return __awaiter(this, void 0, void 0, function* () {
76
123
  return createSolanaSignerForWalletConnect({ walletConnector: this });
@@ -81,6 +128,23 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
81
128
  throw new Error('Connect method not implemented.');
82
129
  });
83
130
  }
131
+ static globalInit(args) {
132
+ return __awaiter(this, void 0, void 0, function* () {
133
+ var _a, _b;
134
+ SolanaWalletConnectConnector.signClientPromise = getSignClientSingleton({
135
+ appIcon: (_a = args.appLogoUrl) !== null && _a !== void 0 ? _a : '',
136
+ appName: (_b = args.appName) !== null && _b !== void 0 ? _b : '',
137
+ projectId: args.projectId,
138
+ });
139
+ SolanaWalletConnectConnector.signClientReference =
140
+ yield SolanaWalletConnectConnector.signClientPromise;
141
+ SolanaWalletConnectConnector.signClientReference.on('session_event', (event) => {
142
+ SolanaWalletConnectConnector.sessionEventListeners.forEach((listener) => {
143
+ listener(event);
144
+ });
145
+ });
146
+ });
147
+ }
84
148
  init() {
85
149
  return __awaiter(this, void 0, void 0, function* () {
86
150
  if (this.isInitialized) {
@@ -93,64 +157,58 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
93
157
  this.setupWCEventListeners();
94
158
  return;
95
159
  }
96
- const { appLogoUrl, appName, projectId } = this.constructorProps;
97
- SolanaWalletConnectConnector.signClientPromise = getSignClientSingleton({
98
- appIcon: appLogoUrl !== null && appLogoUrl !== void 0 ? appLogoUrl : '',
99
- appName: appName !== null && appName !== void 0 ? appName : '',
100
- projectId,
101
- });
102
- SolanaWalletConnectConnector.signClientReference =
103
- yield SolanaWalletConnectConnector.signClientPromise;
160
+ yield SolanaWalletConnectConnector.globalInit(this.constructorProps);
104
161
  this.setupWCEventListeners();
105
162
  });
106
163
  }
107
- setupWCEventListeners() {
108
- logger.debug('[SolanaWalletConnect] setupWCEventListeners');
109
- this.signClient.on('session_event', ({ params: { event }, topic }) => {
110
- var _a, _b;
111
- // Ignore events for wallets other than the one we are connected to
112
- if (!this.session || topic !== this.session.topic) {
113
- return;
114
- }
115
- if (event.name === 'chainChanged') {
116
- const chainParam = event.data;
117
- let chainId = chainParam;
118
- // Handle potentially CAIP-2 format
119
- if (chainParam.startsWith('solana:')) {
120
- const chainHash = chainParam.split(':')[1];
121
- chainId =
122
- (_b = (_a = this.solNetworks
123
- .find((network) => network.genesisHash === chainHash)) === null || _a === void 0 ? void 0 : _a.networkId.toString()) !== null && _b !== void 0 ? _b : chainParam;
124
- }
125
- logger.debug('[SolanaWalletConnect] onChainChange', { chainId });
126
- if (chainId === this.getNetworkId()) {
127
- logger.debug(`[SolanaWalletConnect] onChainChange - ignoring chainChanged event with same chain id as current chain id: ${chainId}`);
128
- return;
129
- }
130
- // This will already emit an event so no need to call this.emit('chainChange', { chain: chainId });
131
- this.switchNetwork({ networkChainId: parseInt(chainId) });
132
- return;
133
- }
134
- if (event.name === 'accountsChanged') {
135
- const accountsParam = event.data;
136
- const accounts = filterDuplicates(accountsParam.map((account) => {
137
- // Handle potentially CAIP-10 format
138
- if (account.startsWith('solana:')) {
139
- return account.split(':')[2];
140
- }
141
- return account;
142
- }));
143
- logger.debug('[SolanaWalletConnect] onAccountChanged', { accounts });
144
- this.emit('accountChange', { accounts });
145
- return;
164
+ handleSessionEvent({ params: { event }, topic }) {
165
+ var _a, _b;
166
+ // Ignore events for wallets other than the one we are connected to
167
+ if (!this.session || topic !== this.session.topic) {
168
+ return;
169
+ }
170
+ if (event.name === 'chainChanged') {
171
+ const chainParam = event.data;
172
+ let chainId = chainParam;
173
+ // Handle potentially CAIP-2 format
174
+ if (typeof chainParam === 'string' && chainParam.startsWith('solana:')) {
175
+ const chainHash = chainParam.split(':')[1];
176
+ chainId =
177
+ (_b = (_a = this.solNetworks
178
+ .find((network) => network.genesisHash === chainHash)) === null || _a === void 0 ? void 0 : _a.networkId.toString()) !== null && _b !== void 0 ? _b : chainParam;
146
179
  }
147
- if (event.name === 'disconnected') {
148
- logger.debug('[SolanaWalletConnect] onDisconnect');
149
- this.emit('disconnect');
150
- this.endSession();
180
+ logger.debug('[SolanaWalletConnect] onChainChange', { chainId });
181
+ if (chainId === this.getNetworkId()) {
182
+ logger.debug(`[SolanaWalletConnect] onChainChange - ignoring chainChanged event with same chain id as current chain id: ${chainId}`);
151
183
  return;
152
184
  }
153
- });
185
+ // This will already emit an event so no need to call this.emit('chainChange', { chain: chainId });
186
+ this.switchNetwork({ networkChainId: parseInt(String(chainId)) });
187
+ return;
188
+ }
189
+ if (event.name === 'accountsChanged') {
190
+ const accountsParam = event.data;
191
+ const accounts = filterDuplicates(accountsParam.map((account) => {
192
+ // Handle potentially CAIP-10 format
193
+ if (account.startsWith('solana:')) {
194
+ return account.split(':')[2];
195
+ }
196
+ return account;
197
+ }));
198
+ logger.debug('[SolanaWalletConnect] onAccountChanged', { accounts });
199
+ this.emit('accountChange', { accounts });
200
+ return;
201
+ }
202
+ if (event.name === 'disconnected') {
203
+ logger.debug('[SolanaWalletConnect] onDisconnect');
204
+ this.emit('disconnect');
205
+ this.endSession();
206
+ return;
207
+ }
208
+ }
209
+ setupWCEventListeners() {
210
+ logger.debug('[SolanaWalletConnect] setupWCEventListeners');
211
+ SolanaWalletConnectConnector.sessionEventListeners.push((event) => this.handleSessionEvent(event));
154
212
  }
155
213
  endSession() {
156
214
  return __awaiter(this, void 0, void 0, function* () {
@@ -203,8 +261,14 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
203
261
  }
204
262
  logger.debug('[SolanaWalletConnect] getAddress - connecting to WalletConnect');
205
263
  const chains = this.solNetworks.map((network) => `solana:${network.genesisHash}`);
264
+ // Await for the promise in case this gets called right as the window is loaded
265
+ // and the sign client is still initializing
266
+ const signClient = yield SolanaWalletConnectConnector.signClientPromise;
267
+ if (!signClient) {
268
+ throw new DynamicError('Failed to access sign client for Wallet Connect Solana: Sign client not initialized');
269
+ }
206
270
  try {
207
- const { approval, uri } = yield this.signClient.connect({
271
+ const { approval, uri } = yield signClient.connect({
208
272
  optionalNamespaces: {
209
273
  solana: {
210
274
  chains,
@@ -321,12 +385,27 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
321
385
  if (!activeAddress) {
322
386
  throw new DynamicError('Active account address is required');
323
387
  }
388
+ logger.debug('[SolanaWalletConnect] Signing transaction', {
389
+ activeAddress,
390
+ isVersioned: 'version' in transaction,
391
+ transaction,
392
+ });
324
393
  const serializedTransaction = this.serializeTransaction(transaction);
394
+ logger.debug('[SolanaWalletConnect] Sending to wallet for signing', {
395
+ serializedLength: serializedTransaction.length,
396
+ });
325
397
  const { transaction: signedTransaction } = yield this.signClientRequest({
326
398
  method: 'solana_signTransaction',
327
399
  params: { transaction: serializedTransaction },
328
400
  });
401
+ logger.debug('[SolanaWalletConnect] Received signed transaction from wallet', {
402
+ signedTransactionLength: signedTransaction.length,
403
+ signedTransactionPreview: signedTransaction.substring(0, 50),
404
+ });
329
405
  const decodedTransaction = bs58.decode(signedTransaction);
406
+ logger.debug('[SolanaWalletConnect] Decoded signed transaction', {
407
+ decodedLength: decodedTransaction.length,
408
+ });
330
409
  if (isVersionedTransaction(transaction)) {
331
410
  return VersionedTransaction.deserialize(decodedTransaction);
332
411
  }
@@ -352,12 +431,45 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
352
431
  }
353
432
  signAndSendTransaction(transaction, options) {
354
433
  return __awaiter(this, void 0, void 0, function* () {
355
- const serializedTransaction = this.serializeTransaction(transaction);
356
- const { signature } = yield this.signClientRequest({
357
- method: 'solana_signAndSendTransaction',
358
- params: { options, transaction: serializedTransaction },
359
- });
360
- return signature;
434
+ const supportedMethods = this.getAvailableMethods();
435
+ // Try to use signAndSendTransaction if supported
436
+ if (supportedMethods.includes('solana_signAndSendTransaction')) {
437
+ const serializedTransaction = this.serializeTransaction(transaction);
438
+ const { signature } = yield this.signClientRequest({
439
+ method: 'solana_signAndSendTransaction',
440
+ params: { options, transaction: serializedTransaction },
441
+ });
442
+ return signature;
443
+ }
444
+ // Fallback: use signTransaction + sendRawTransaction
445
+ if (supportedMethods.includes('solana_signTransaction')) {
446
+ logger.debug('[SolanaWalletConnect] Using fallback: signTransaction + sendRawTransaction');
447
+ const signedTransaction = yield this.signTransaction(transaction);
448
+ const isSigned = isTxAlreadySigned(signedTransaction);
449
+ logger.debug('[SolanaWalletConnect] Transaction signed, checking signatures', {
450
+ hasSignatures: 'signatures' in signedTransaction &&
451
+ signedTransaction.signatures.length > 0,
452
+ isSigned,
453
+ signaturesLength: 'signatures' in signedTransaction
454
+ ? signedTransaction.signatures.length
455
+ : 'N/A',
456
+ });
457
+ if (!isSigned) {
458
+ throw new DynamicError('Transaction returned from wallet was not properly signed. The wallet may have rejected the signing request.');
459
+ }
460
+ // When sending a signed transaction, serialize it without flags
461
+ // (requireAllSignatures and verifySignatures are only for unsigned transactions)
462
+ const serialized = signedTransaction.serialize();
463
+ logger.debug('[SolanaWalletConnect] Transaction serialized', {
464
+ serializedLength: serialized.length,
465
+ });
466
+ const signature = yield this.getWalletClient().sendRawTransaction(serialized, options);
467
+ logger.debug('[SolanaWalletConnect] Transaction sent successfully', {
468
+ signature,
469
+ });
470
+ return signature;
471
+ }
472
+ throw new DynamicError('Wallet does not support signing and sending transactions. Please connect to a wallet that supports at least solana_signTransaction.');
361
473
  });
362
474
  }
363
475
  getDeepLink() {
@@ -365,8 +477,6 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
365
477
  deepLinks: this.metadata.deepLinks,
366
478
  mode: 'regular',
367
479
  preference: this.deepLinkPreference,
368
- // TODO: verify whether we need the uri
369
- // uri: provider.signer.uri,
370
480
  });
371
481
  logger.logVerboseTroubleshootingMessage('[SolanaWalletConnect] getDeepLink - deepLink', deepLink);
372
482
  if (!deepLink) {
@@ -412,5 +522,6 @@ class SolanaWalletConnectConnector extends SolanaWalletConnector {
412
522
  });
413
523
  }
414
524
  }
525
+ SolanaWalletConnectConnector.sessionEventListeners = [];
415
526
 
416
527
  export { SolanaWalletConnectConnector };