@dynamic-labs/solana 4.49.0 → 4.50.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 +15 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +12 -12
- package/src/index.cjs +2 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/injected/walletStandard/createSolanaSignerFromWalletStandard/createSolanaSignerFromWalletStandard.cjs +17 -7
- package/src/injected/walletStandard/createSolanaSignerFromWalletStandard/createSolanaSignerFromWalletStandard.js +17 -7
- package/src/phantomRedirect/PhantomRedirect/PhantomRedirect.cjs +200 -63
- package/src/phantomRedirect/PhantomRedirect/PhantomRedirect.d.ts +18 -2
- package/src/phantomRedirect/PhantomRedirect/PhantomRedirect.js +202 -65
- package/src/phantomRedirect/storage/storage.cjs +77 -4
- package/src/phantomRedirect/storage/storage.d.ts +10 -0
- package/src/phantomRedirect/storage/storage.js +77 -4
- package/src/phantomRedirect/types.d.ts +1 -1
- package/src/walletConnect/SolanaWalletConnectConnector/SolanaWalletConnectConnector.cjs +12 -11
- package/src/walletConnect/SolanaWalletConnectConnector/SolanaWalletConnectConnector.js +13 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
|
|
2
|
+
## [4.50.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.49.0...v4.50.0) (2025-12-09)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* add usePhantomRedirectEvents hook ([#10030](https://github.com/dynamic-labs/dynamic-auth/issues/10030)) ([fb8585e](https://github.com/dynamic-labs/dynamic-auth/commit/fb8585eec9847cd350be71d28291e9831a3f31b1))
|
|
8
|
+
* introduce embedded wallet reveal complete events ([#10068](https://github.com/dynamic-labs/dynamic-auth/issues/10068)) ([8ea79ce](https://github.com/dynamic-labs/dynamic-auth/commit/8ea79ceaba36745d46759c8e8c6681895b092096))
|
|
9
|
+
* **react-native:** add download file ([#9954](https://github.com/dynamic-labs/dynamic-auth/issues/9954)) ([408b00f](https://github.com/dynamic-labs/dynamic-auth/commit/408b00fabd2afbd87d12efb51e78ecbd89a323d3))
|
|
10
|
+
* **react-native:** add solana Phantom support with client module for listening to events ([#10033](https://github.com/dynamic-labs/dynamic-auth/issues/10033)) ([a5a2d11](https://github.com/dynamic-labs/dynamic-auth/commit/a5a2d11ccd3c102e22dece808a0ba4116155a582))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* remove overflow from token balance dropdown ([#10044](https://github.com/dynamic-labs/dynamic-auth/issues/10044)) ([b37c5bb](https://github.com/dynamic-labs/dynamic-auth/commit/b37c5bb8e8c916b8d685b495555a3a3481cd5f0d))
|
|
16
|
+
|
|
2
17
|
## [4.49.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.48.2...v4.49.0) (2025-12-05)
|
|
3
18
|
|
|
4
19
|
|
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.50.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.50.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.50.1",
|
|
33
|
+
"@dynamic-labs/embedded-wallet-solana": "4.50.1",
|
|
34
|
+
"@dynamic-labs/logger": "4.50.1",
|
|
35
|
+
"@dynamic-labs/rpc-providers": "4.50.1",
|
|
36
36
|
"@dynamic-labs/sdk-api-core": "0.0.831",
|
|
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.50.1",
|
|
38
|
+
"@dynamic-labs/types": "4.50.1",
|
|
39
|
+
"@dynamic-labs/utils": "4.50.1",
|
|
40
|
+
"@dynamic-labs/waas-svm": "4.50.1",
|
|
41
|
+
"@dynamic-labs/wallet-book": "4.50.1",
|
|
42
|
+
"@dynamic-labs/wallet-connector-core": "4.50.1",
|
|
43
43
|
"eventemitter3": "5.0.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {}
|
package/src/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var createSolanaSignerFromWalletStandard = require('./injected/walletStandard/cr
|
|
|
22
22
|
var getWalletStandardWallets = require('./injected/walletStandard/getWalletStandardWallets/getWalletStandardWallets.cjs');
|
|
23
23
|
var hasAllWalletStandardRequiredFeatures = require('./injected/walletStandard/hasAllWalletStandardRequiredFeatures/hasAllWalletStandardRequiredFeatures.cjs');
|
|
24
24
|
require('./injected/FallbackSolanaConnector/FallbackSolanaConnector.cjs');
|
|
25
|
+
var PhantomRedirect = require('./phantomRedirect/PhantomRedirect/PhantomRedirect.cjs');
|
|
25
26
|
|
|
26
27
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
27
28
|
assertPackageVersion.assertPackageVersion('@dynamic-labs/solana', _package.version);
|
|
@@ -44,3 +45,4 @@ exports.isSignedMessage = isSignedMessage.isSignedMessage;
|
|
|
44
45
|
exports.createSolanaSignerFromWalletStandard = createSolanaSignerFromWalletStandard.createSolanaSignerFromWalletStandard;
|
|
45
46
|
exports.getWalletStandardWallets = getWalletStandardWallets.getWalletStandardWallets;
|
|
46
47
|
exports.hasAllWalletStandardRequiredFeatures = hasAllWalletStandardRequiredFeatures.hasAllWalletStandardRequiredFeatures;
|
|
48
|
+
exports.PhantomRedirect = PhantomRedirect.PhantomRedirect;
|
package/src/index.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export { SolanaWalletConnectorsWithConfig } from './SolanaWalletConnectorsWithCo
|
|
|
8
8
|
export { isBackpackSolanaSigner } from './utils/isBackpackSolanaSigner';
|
|
9
9
|
export { isSignedMessage } from './utils/isSignedMessage';
|
|
10
10
|
export { getWalletStandardWallets, createSolanaSignerFromWalletStandard, hasAllWalletStandardRequiredFeatures, } from './injected';
|
|
11
|
+
export { PhantomRedirect } from './phantomRedirect/PhantomRedirect';
|
package/src/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { createSolanaSignerFromWalletStandard } from './injected/walletStandard/
|
|
|
18
18
|
export { getWalletStandardWallets } from './injected/walletStandard/getWalletStandardWallets/getWalletStandardWallets.js';
|
|
19
19
|
export { hasAllWalletStandardRequiredFeatures } from './injected/walletStandard/hasAllWalletStandardRequiredFeatures/hasAllWalletStandardRequiredFeatures.js';
|
|
20
20
|
import './injected/FallbackSolanaConnector/FallbackSolanaConnector.js';
|
|
21
|
+
export { PhantomRedirect } from './phantomRedirect/PhantomRedirect/PhantomRedirect.js';
|
|
21
22
|
|
|
22
23
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
23
24
|
assertPackageVersion('@dynamic-labs/solana', version);
|
|
@@ -79,20 +79,30 @@ const createSolanaSignerFromWalletStandard = ({ wallet, walletConnector, }) => {
|
|
|
79
79
|
return `solana:${cluster}`;
|
|
80
80
|
};
|
|
81
81
|
const signTransaction = (transaction) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
82
|
+
const signedTransactions = yield signAllTransactions([transaction]);
|
|
83
|
+
return signedTransactions[0];
|
|
84
|
+
});
|
|
85
|
+
const signAllTransactions = (transactions) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
86
|
+
const signTransactionMethod = features['solana:signTransaction'].signTransaction;
|
|
82
87
|
const account = yield getCurrentAccount();
|
|
83
|
-
const
|
|
88
|
+
const chain = getChain();
|
|
89
|
+
const transactionsInput = transactions.map((transaction) => ({
|
|
84
90
|
account,
|
|
85
|
-
chain
|
|
91
|
+
chain,
|
|
86
92
|
transaction: transaction.serialize({
|
|
87
93
|
requireAllSignatures: false,
|
|
88
94
|
}),
|
|
95
|
+
}));
|
|
96
|
+
const signTransactionResult = yield signTransactionMethod(...transactionsInput);
|
|
97
|
+
const signedTransactions = signTransactionResult.map(({ signedTransaction }, index) => {
|
|
98
|
+
const inputTransaction = transactions[index];
|
|
99
|
+
if (isVersionedTransaction(inputTransaction)) {
|
|
100
|
+
return web3_js.VersionedTransaction.deserialize(signedTransaction);
|
|
101
|
+
}
|
|
102
|
+
return web3_js.Transaction.from(signedTransaction);
|
|
89
103
|
});
|
|
90
|
-
|
|
91
|
-
return web3_js.VersionedTransaction.deserialize(signTransactionResult[0].signedTransaction);
|
|
92
|
-
}
|
|
93
|
-
return web3_js.Transaction.from(signTransactionResult[0].signedTransaction);
|
|
104
|
+
return signedTransactions;
|
|
94
105
|
});
|
|
95
|
-
const signAllTransactions = (transactions) => _tslib.__awaiter(void 0, void 0, void 0, function* () { return Promise.all(transactions.map(signTransaction)); });
|
|
96
106
|
const signAndSendTransaction = (transaction) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
97
107
|
var _c;
|
|
98
108
|
const signAndSendTransactionMethod = (_c = features['solana:signAndSendTransaction']) === null || _c === void 0 ? void 0 : _c.signAndSendTransaction;
|
|
@@ -71,20 +71,30 @@ const createSolanaSignerFromWalletStandard = ({ wallet, walletConnector, }) => {
|
|
|
71
71
|
return `solana:${cluster}`;
|
|
72
72
|
};
|
|
73
73
|
const signTransaction = (transaction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
|
+
const signedTransactions = yield signAllTransactions([transaction]);
|
|
75
|
+
return signedTransactions[0];
|
|
76
|
+
});
|
|
77
|
+
const signAllTransactions = (transactions) => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
|
+
const signTransactionMethod = features['solana:signTransaction'].signTransaction;
|
|
74
79
|
const account = yield getCurrentAccount();
|
|
75
|
-
const
|
|
80
|
+
const chain = getChain();
|
|
81
|
+
const transactionsInput = transactions.map((transaction) => ({
|
|
76
82
|
account,
|
|
77
|
-
chain
|
|
83
|
+
chain,
|
|
78
84
|
transaction: transaction.serialize({
|
|
79
85
|
requireAllSignatures: false,
|
|
80
86
|
}),
|
|
87
|
+
}));
|
|
88
|
+
const signTransactionResult = yield signTransactionMethod(...transactionsInput);
|
|
89
|
+
const signedTransactions = signTransactionResult.map(({ signedTransaction }, index) => {
|
|
90
|
+
const inputTransaction = transactions[index];
|
|
91
|
+
if (isVersionedTransaction(inputTransaction)) {
|
|
92
|
+
return VersionedTransaction.deserialize(signedTransaction);
|
|
93
|
+
}
|
|
94
|
+
return Transaction.from(signedTransaction);
|
|
81
95
|
});
|
|
82
|
-
|
|
83
|
-
return VersionedTransaction.deserialize(signTransactionResult[0].signedTransaction);
|
|
84
|
-
}
|
|
85
|
-
return Transaction.from(signTransactionResult[0].signedTransaction);
|
|
96
|
+
return signedTransactions;
|
|
86
97
|
});
|
|
87
|
-
const signAllTransactions = (transactions) => __awaiter(void 0, void 0, void 0, function* () { return Promise.all(transactions.map(signTransaction)); });
|
|
88
98
|
const signAndSendTransaction = (transaction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
99
|
var _c;
|
|
90
100
|
const signAndSendTransactionMethod = (_c = features['solana:signAndSendTransaction']) === null || _c === void 0 ? void 0 : _c.signAndSendTransaction;
|
|
@@ -14,6 +14,7 @@ var clearRedirectUrlForPhantom = require('../clearRedirectUrlForPhantom/clearRed
|
|
|
14
14
|
var decryptPayload = require('../decryptPayload/decryptPayload.cjs');
|
|
15
15
|
var encryptPayload = require('../encryptPayload/encryptPayload.cjs');
|
|
16
16
|
var storage = require('../storage/storage.cjs');
|
|
17
|
+
var logger = require('../../utils/logger.cjs');
|
|
17
18
|
|
|
18
19
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
19
20
|
|
|
@@ -29,6 +30,59 @@ class PhantomRedirect extends solanaCore.SolanaWalletConnector {
|
|
|
29
30
|
getMethod() {
|
|
30
31
|
throw new Error('Method not implemented.');
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Sets up a Promise/listener pattern for native mobile redirects.
|
|
35
|
+
* Returns undefined if not on native mobile.
|
|
36
|
+
*/
|
|
37
|
+
setupNativeMobileListener({ eventName, methodName, getResult, shouldIgnoreEvent, }) {
|
|
38
|
+
if (!utils.PlatformService.isNativeMobile) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
42
|
+
storage.storage.requestId.set(requestId);
|
|
43
|
+
logger.logger.logVerboseTroubleshootingMessage(`[PhantomRedirect] ${methodName} - setting up listener`, { requestId });
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const listener = (event) => {
|
|
46
|
+
// Check requestId mismatch or custom validation
|
|
47
|
+
if (event.requestId !== requestId ||
|
|
48
|
+
(shouldIgnoreEvent === null || shouldIgnoreEvent === void 0 ? void 0 : shouldIgnoreEvent(event, requestId))) {
|
|
49
|
+
logger.logger.logVerboseTroubleshootingMessage(`[PhantomRedirect] ${methodName} - ignoring event (requestId mismatch)`, {
|
|
50
|
+
expectedRequestId: requestId,
|
|
51
|
+
receivedRequestId: event.requestId,
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
logger.logger.logVerboseTroubleshootingMessage(`[PhantomRedirect] ${methodName} - listener received matching event`, { errorCode: event.errorCode, requestId });
|
|
56
|
+
// Clean up this listener
|
|
57
|
+
this.off(eventName, listener);
|
|
58
|
+
if (event.errorCode) {
|
|
59
|
+
reject(new Error(event.errorMessage || event.errorCode));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
resolve(getResult(event));
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
this.on(eventName, listener);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Encrypts payload, builds Phantom redirect URL, stores method, and opens URL.
|
|
70
|
+
*/
|
|
71
|
+
openPhantomUrl({ payload, sharedSecret, encryptionPublicKey, phantomEndpoint, methodToStore, }) {
|
|
72
|
+
const [nonce, encryptedPayload] = encryptPayload.encryptPayload(payload, sharedSecret);
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
dapp_encryption_public_key: bs58__default["default"].encode(encryptionPublicKey),
|
|
75
|
+
nonce: bs58__default["default"].encode(nonce),
|
|
76
|
+
payload: bs58__default["default"].encode(encryptedPayload),
|
|
77
|
+
redirect_link: clearRedirectUrlForPhantom.clearRedirectUrlForPhantom(utils.PlatformService.getUrl()),
|
|
78
|
+
});
|
|
79
|
+
const url = buildUrl.buildUrl(phantomEndpoint, params);
|
|
80
|
+
storage.storage.method.set(methodToStore);
|
|
81
|
+
logger.logger.debug(`[PhantomRedirect] ${methodToStore} - opening Phantom`, {
|
|
82
|
+
isNativeMobile: utils.PlatformService.isNativeMobile,
|
|
83
|
+
});
|
|
84
|
+
utils.PlatformService.openURL(url);
|
|
85
|
+
}
|
|
32
86
|
getAddress() {
|
|
33
87
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
34
88
|
const address = storage.storage.address.get();
|
|
@@ -92,36 +146,58 @@ class PhantomRedirect extends solanaCore.SolanaWalletConnector {
|
|
|
92
146
|
}
|
|
93
147
|
signMessage(messageToSign) {
|
|
94
148
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
logger.logger.debug('[PhantomRedirect] signMessage called', {
|
|
150
|
+
messageLength: messageToSign.length,
|
|
151
|
+
messagePreview: messageToSign.substring(0, 200),
|
|
152
|
+
});
|
|
95
153
|
const { session, sharedSecret, encryptionPublicKey } = this.getInputsOrThrow('signMessage', [], ['session', 'sharedSecret', 'encryptionPublicKey']);
|
|
96
154
|
storage.storage.message.set(messageToSign);
|
|
97
155
|
const payload = {
|
|
98
156
|
message: bs58__default["default"].encode(Buffer.from(messageToSign)),
|
|
99
157
|
session,
|
|
100
158
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
159
|
+
this.openPhantomUrl({
|
|
160
|
+
encryptionPublicKey,
|
|
161
|
+
methodToStore: 'signMessage',
|
|
162
|
+
payload,
|
|
163
|
+
phantomEndpoint: 'signMessage',
|
|
164
|
+
sharedSecret,
|
|
107
165
|
});
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
166
|
+
const nativePromise = this.setupNativeMobileListener({
|
|
167
|
+
eventName: 'signMessage',
|
|
168
|
+
getResult: (event) => event.signature,
|
|
169
|
+
methodName: 'signMessage',
|
|
170
|
+
shouldIgnoreEvent: (event) => event.message !== messageToSign,
|
|
171
|
+
});
|
|
172
|
+
if (nativePromise) {
|
|
173
|
+
return nativePromise;
|
|
174
|
+
}
|
|
175
|
+
// On mobile web browsers, Phantom opens in a new tab and redirects back
|
|
176
|
+
// to a fresh page load, losing this Promise/listener context.
|
|
177
|
+
// The signature is handled via PhantomRedirectContext which reads URL
|
|
178
|
+
// params on page load and emits the 'signMessage' event, but there's
|
|
179
|
+
// no way to return it to the original caller. Throwing here prevents
|
|
180
|
+
// the SDK from clearing local storage when no signature is returned.
|
|
181
|
+
logger.logger.debug('[PhantomRedirect] signMessage - mobile web, throwing ignore error');
|
|
119
182
|
throw new Error('ignore');
|
|
120
183
|
});
|
|
121
184
|
}
|
|
122
185
|
extractSignature() {
|
|
186
|
+
var _a;
|
|
187
|
+
logger.logger.debug('[PhantomRedirect] extractSignature called');
|
|
123
188
|
const { data, nonce, sharedSecret, message } = this.getInputsOrThrow('extractSignature', ['data', 'nonce'], ['sharedSecret', 'message']);
|
|
189
|
+
logger.logger.debug('[PhantomRedirect] extractSignature - retrieved from storage', {
|
|
190
|
+
dataPresent: Boolean(data),
|
|
191
|
+
message,
|
|
192
|
+
messageLength: message === null || message === void 0 ? void 0 : message.length,
|
|
193
|
+
noncePresent: Boolean(nonce),
|
|
194
|
+
sharedSecretPresent: Boolean(sharedSecret),
|
|
195
|
+
});
|
|
124
196
|
const signMessageData = decryptPayload.decryptPayload(data, nonce, sharedSecret);
|
|
197
|
+
logger.logger.debug('[PhantomRedirect] extractSignature - decrypted payload', {
|
|
198
|
+
signature: signMessageData.signature,
|
|
199
|
+
signatureLength: (_a = signMessageData.signature) === null || _a === void 0 ? void 0 : _a.length,
|
|
200
|
+
});
|
|
125
201
|
return {
|
|
126
202
|
message,
|
|
127
203
|
signature: signMessageData.signature,
|
|
@@ -136,14 +212,55 @@ class PhantomRedirect extends solanaCore.SolanaWalletConnector {
|
|
|
136
212
|
extractTransaction() {
|
|
137
213
|
const { data, nonce, sharedSecret } = this.getInputsOrThrow('extractTransaction', ['data', 'nonce'], ['sharedSecret']);
|
|
138
214
|
const signTransactionData = decryptPayload.decryptPayload(data, nonce, sharedSecret);
|
|
139
|
-
const
|
|
140
|
-
|
|
215
|
+
const transactionBytes = bs58__default["default"].decode(signTransactionData.transaction);
|
|
216
|
+
// Try to deserialize as VersionedTransaction first, fall back to legacy Transaction
|
|
217
|
+
// VersionedTransaction has a version prefix byte, legacy transactions don't
|
|
218
|
+
try {
|
|
219
|
+
return web3_js.VersionedTransaction.deserialize(transactionBytes);
|
|
220
|
+
}
|
|
221
|
+
catch (_a) {
|
|
222
|
+
// If VersionedTransaction fails, try legacy Transaction
|
|
223
|
+
return web3_js.Transaction.from(transactionBytes);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Extracts the signed transaction and sends it to the network.
|
|
228
|
+
* Used for signAndSendTransaction since Phantom redirect doesn't support it natively.
|
|
229
|
+
* @returns The transaction signature
|
|
230
|
+
*/
|
|
231
|
+
extractAndSendTransaction() {
|
|
232
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
233
|
+
logger.logger.debug('[PhantomRedirect] extractAndSendTransaction called');
|
|
234
|
+
const signedTransaction = this.extractTransaction();
|
|
235
|
+
// Get stored send options if any
|
|
236
|
+
const sendOptionsJson = storage.storage.sendOptions.get();
|
|
237
|
+
storage.storage.sendOptions.remove();
|
|
238
|
+
const sendOptions = sendOptionsJson
|
|
239
|
+
? JSON.parse(sendOptionsJson)
|
|
240
|
+
: undefined;
|
|
241
|
+
logger.logger.debug('[PhantomRedirect] Sending transaction to network', {
|
|
242
|
+
hasSendOptions: Boolean(sendOptions),
|
|
243
|
+
isVersioned: signedTransaction instanceof web3_js.VersionedTransaction,
|
|
244
|
+
});
|
|
245
|
+
// Send the signed transaction to the network
|
|
246
|
+
const serialized = signedTransaction.serialize();
|
|
247
|
+
const signature = yield this.getWalletClient().sendRawTransaction(serialized, sendOptions);
|
|
248
|
+
logger.logger.debug('[PhantomRedirect] Transaction sent successfully', {
|
|
249
|
+
signature,
|
|
250
|
+
});
|
|
251
|
+
return signature;
|
|
252
|
+
});
|
|
141
253
|
}
|
|
142
254
|
consumeMethod() {
|
|
143
255
|
const method = storage.storage.method.get();
|
|
144
256
|
storage.storage.method.remove();
|
|
145
257
|
return method;
|
|
146
258
|
}
|
|
259
|
+
consumeRequestId() {
|
|
260
|
+
const requestId = storage.storage.requestId.get();
|
|
261
|
+
storage.storage.requestId.remove();
|
|
262
|
+
return requestId;
|
|
263
|
+
}
|
|
147
264
|
getSigner() {
|
|
148
265
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
149
266
|
const address = storage.storage.address.get();
|
|
@@ -206,55 +323,67 @@ class PhantomRedirect extends solanaCore.SolanaWalletConnector {
|
|
|
206
323
|
session,
|
|
207
324
|
transactions: serializedTransactions,
|
|
208
325
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
326
|
+
this.openPhantomUrl({
|
|
327
|
+
encryptionPublicKey,
|
|
328
|
+
methodToStore: 'signAllTransactions',
|
|
329
|
+
payload,
|
|
330
|
+
phantomEndpoint: 'signAllTransactions',
|
|
331
|
+
sharedSecret,
|
|
332
|
+
});
|
|
333
|
+
const nativePromise = this.setupNativeMobileListener({
|
|
334
|
+
eventName: 'signAllTransactions',
|
|
335
|
+
getResult: (event) => (event.transactions || []),
|
|
336
|
+
methodName: 'signAllTransactions',
|
|
215
337
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
338
|
+
if (nativePromise) {
|
|
339
|
+
return nativePromise;
|
|
340
|
+
}
|
|
341
|
+
// On mobile web browsers, return empty array
|
|
342
|
+
// The signed transactions are handled via PhantomRedirectContext
|
|
219
343
|
return [];
|
|
220
344
|
}),
|
|
221
345
|
signAndSendTransaction: (transaction, options) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
346
|
+
// Phantom redirect doesn't support signAndSendTransaction natively,
|
|
347
|
+
// so we use signTransaction and then send it manually after redirect
|
|
348
|
+
const serializedTransaction = bs58__default["default"].encode(transaction.serialize({ requireAllSignatures: false }));
|
|
222
349
|
const { session, sharedSecret, encryptionPublicKey } = this.getInputsOrThrow('signAndSendTransaction', [], ['session', 'sharedSecret', 'encryptionPublicKey']);
|
|
223
350
|
const payload = {
|
|
224
|
-
options,
|
|
225
351
|
session,
|
|
226
|
-
transaction:
|
|
352
|
+
transaction: serializedTransaction,
|
|
227
353
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
354
|
+
// Store send options to use when sending the transaction
|
|
355
|
+
if (options) {
|
|
356
|
+
storage.storage.sendOptions.set(JSON.stringify(options));
|
|
357
|
+
}
|
|
358
|
+
// Use signTransaction endpoint since Phantom redirect doesn't support signAndSendTransaction
|
|
359
|
+
// but store method as signAndSendTransaction so we know to send after signing
|
|
360
|
+
this.openPhantomUrl({
|
|
361
|
+
encryptionPublicKey,
|
|
362
|
+
methodToStore: 'signAndSendTransaction',
|
|
363
|
+
payload,
|
|
364
|
+
phantomEndpoint: 'signTransaction',
|
|
365
|
+
sharedSecret,
|
|
234
366
|
});
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
367
|
+
const nativePromise = this.setupNativeMobileListener({
|
|
368
|
+
eventName: 'signAndSendTransaction',
|
|
369
|
+
getResult: (event) => ({ signature: event.signature || '' }),
|
|
370
|
+
methodName: 'signAndSendTransaction',
|
|
371
|
+
});
|
|
372
|
+
if (nativePromise) {
|
|
373
|
+
return nativePromise;
|
|
374
|
+
}
|
|
375
|
+
// On mobile web browsers, return empty signature
|
|
376
|
+
// The actual signature is handled via PhantomRedirectContext
|
|
239
377
|
return { signature: '' };
|
|
240
378
|
}),
|
|
241
379
|
signMessage: (message) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
380
|
+
// Delegate to connector's signMessage which properly stores method and sets up listeners
|
|
381
|
+
const messageString = new TextDecoder().decode(message);
|
|
382
|
+
const signature = yield this.signMessage(messageString);
|
|
383
|
+
// Convert signature string to Uint8Array for ISolana interface
|
|
384
|
+
return {
|
|
385
|
+
signature: signature ? bs58__default["default"].decode(signature) : new Uint8Array(0),
|
|
246
386
|
};
|
|
247
|
-
const [nonce, encryptedPayload] = encryptPayload.encryptPayload(payload, sharedSecret);
|
|
248
|
-
const params = new URLSearchParams({
|
|
249
|
-
dapp_encryption_public_key: bs58__default["default"].encode(encryptionPublicKey),
|
|
250
|
-
nonce: bs58__default["default"].encode(nonce),
|
|
251
|
-
payload: bs58__default["default"].encode(encryptedPayload),
|
|
252
|
-
redirect_link: clearRedirectUrlForPhantom.clearRedirectUrlForPhantom(utils.PlatformService.getUrl()),
|
|
253
|
-
});
|
|
254
|
-
const url = buildUrl.buildUrl('signMessage', params);
|
|
255
|
-
utils.PlatformService.openURL(url);
|
|
256
|
-
// actual signature will be retrived upon redirect back to dapp
|
|
257
|
-
return { signature: Buffer.from('') };
|
|
258
387
|
}),
|
|
259
388
|
signTransaction: (transaction) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
260
389
|
const serializedTransaction = bs58__default["default"].encode(transaction.serialize({
|
|
@@ -265,15 +394,23 @@ class PhantomRedirect extends solanaCore.SolanaWalletConnector {
|
|
|
265
394
|
session,
|
|
266
395
|
transaction: serializedTransaction,
|
|
267
396
|
};
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
397
|
+
this.openPhantomUrl({
|
|
398
|
+
encryptionPublicKey,
|
|
399
|
+
methodToStore: 'signTransaction',
|
|
400
|
+
payload,
|
|
401
|
+
phantomEndpoint: 'signTransaction',
|
|
402
|
+
sharedSecret,
|
|
403
|
+
});
|
|
404
|
+
const nativePromise = this.setupNativeMobileListener({
|
|
405
|
+
eventName: 'signTransaction',
|
|
406
|
+
getResult: (event) => event.transaction,
|
|
407
|
+
methodName: 'signTransaction',
|
|
274
408
|
});
|
|
275
|
-
|
|
276
|
-
|
|
409
|
+
if (nativePromise) {
|
|
410
|
+
return nativePromise;
|
|
411
|
+
}
|
|
412
|
+
// On mobile web browsers, return the original transaction
|
|
413
|
+
// The signed transaction is handled via PhantomRedirectContext
|
|
277
414
|
return transaction;
|
|
278
415
|
}),
|
|
279
416
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Transaction } from '@solana/web3.js';
|
|
1
|
+
import { Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
2
2
|
import { SolanaWalletConnector, type ISolana } from '@dynamic-labs/solana-core';
|
|
3
3
|
import { IPhantomRedirectConnector } from '@dynamic-labs/wallet-connector-core';
|
|
4
4
|
import { Method } from '../types';
|
|
@@ -7,6 +7,15 @@ export declare class PhantomRedirect extends SolanaWalletConnector implements IP
|
|
|
7
7
|
overrideKey: string;
|
|
8
8
|
constructor(props: any);
|
|
9
9
|
getMethod(): 'signMessage' | 'signAndSendTransaction' | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Sets up a Promise/listener pattern for native mobile redirects.
|
|
12
|
+
* Returns undefined if not on native mobile.
|
|
13
|
+
*/
|
|
14
|
+
private setupNativeMobileListener;
|
|
15
|
+
/**
|
|
16
|
+
* Encrypts payload, builds Phantom redirect URL, stores method, and opens URL.
|
|
17
|
+
*/
|
|
18
|
+
private openPhantomUrl;
|
|
10
19
|
getAddress(): Promise<string | undefined>;
|
|
11
20
|
connect(): Promise<void>;
|
|
12
21
|
getSession(): Promise<string>;
|
|
@@ -16,8 +25,15 @@ export declare class PhantomRedirect extends SolanaWalletConnector implements IP
|
|
|
16
25
|
message: string;
|
|
17
26
|
};
|
|
18
27
|
extractTransactions(): Transaction[];
|
|
19
|
-
extractTransaction(): Transaction;
|
|
28
|
+
extractTransaction(): Transaction | VersionedTransaction;
|
|
29
|
+
/**
|
|
30
|
+
* Extracts the signed transaction and sends it to the network.
|
|
31
|
+
* Used for signAndSendTransaction since Phantom redirect doesn't support it natively.
|
|
32
|
+
* @returns The transaction signature
|
|
33
|
+
*/
|
|
34
|
+
extractAndSendTransaction(): Promise<string>;
|
|
20
35
|
consumeMethod(): Method | undefined;
|
|
36
|
+
consumeRequestId(): string | undefined;
|
|
21
37
|
getSigner(): Promise<ISolana | undefined>;
|
|
22
38
|
getConnectedAccounts(): Promise<string[]>;
|
|
23
39
|
endSession(): Promise<void>;
|