@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 +20 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +12 -12
- package/src/walletConnect/SolanaWalletConnectConnector/SolanaWalletConnectConnector.cjs +173 -62
- package/src/walletConnect/SolanaWalletConnectConnector/SolanaWalletConnectConnector.d.ts +22 -3
- package/src/walletConnect/SolanaWalletConnectConnector/SolanaWalletConnectConnector.js +174 -63
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
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/solana",
|
|
3
|
-
"version": "4.
|
|
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.
|
|
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.
|
|
33
|
-
"@dynamic-labs/embedded-wallet-solana": "4.
|
|
34
|
-
"@dynamic-labs/logger": "4.
|
|
35
|
-
"@dynamic-labs/rpc-providers": "4.
|
|
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.
|
|
38
|
-
"@dynamic-labs/types": "4.
|
|
39
|
-
"@dynamic-labs/utils": "4.
|
|
40
|
-
"@dynamic-labs/waas-svm": "4.
|
|
41
|
-
"@dynamic-labs/wallet-book": "4.
|
|
42
|
-
"@dynamic-labs/wallet-connector-core": "4.
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
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
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
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
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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 };
|