@dynamic-labs/embedded-wallet-solana 3.0.0-alpha.8
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 +3494 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/_virtual/_tslib.cjs +49 -0
- package/_virtual/_tslib.js +44 -0
- package/package.json +47 -0
- package/src/TurnkeySolanaWalletConnectors.cjs +19 -0
- package/src/TurnkeySolanaWalletConnectors.d.ts +2 -0
- package/src/TurnkeySolanaWalletConnectors.js +15 -0
- package/src/index.cjs +10 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.cjs +60 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.d.ts +45 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.js +56 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.cjs +323 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.d.ts +55 -0
- package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.js +319 -0
- package/src/lib/TurnkeySolanaWalletConnector/index.d.ts +1 -0
- package/src/lib/constants.cjs +8 -0
- package/src/lib/constants.d.ts +1 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/utils/createSolanaConnection/createSolanaConnection.cjs +14 -0
- package/src/lib/utils/createSolanaConnection/createSolanaConnection.d.ts +2 -0
- package/src/lib/utils/createSolanaConnection/createSolanaConnection.js +10 -0
- package/src/lib/utils/createSolanaConnection/index.d.ts +1 -0
- package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.cjs +10 -0
- package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.d.ts +1 -0
- package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.js +6 -0
- package/src/lib/utils/getGenesisHashLSKey/index.d.ts +1 -0
- package/src/lib/utils/index.d.ts +3 -0
- package/src/lib/utils/transactionDecoder/index.d.ts +1 -0
- package/src/lib/utils/transactionDecoder/transactionDecoder.cjs +106 -0
- package/src/lib/utils/transactionDecoder/transactionDecoder.d.ts +10 -0
- package/src/lib/utils/transactionDecoder/transactionDecoder.js +101 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { __awaiter, __rest } from '../../../_virtual/_tslib.js';
|
|
3
|
+
import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { WebauthnStamper } from '@turnkey/webauthn-stamper';
|
|
5
|
+
import { TurnkeyClient } from '@turnkey/http';
|
|
6
|
+
import { TurnkeySigner } from '@turnkey/solana';
|
|
7
|
+
import { IframeStamper } from '@turnkey/iframe-stamper';
|
|
8
|
+
import { ProviderChain } from '@dynamic-labs/rpc-provider-solana';
|
|
9
|
+
import { DynamicError, getTLD, PlatformService, bufferToBase64 } from '@dynamic-labs/utils';
|
|
10
|
+
import { SolanaUiTransaction } from '@dynamic-labs/solana-utils';
|
|
11
|
+
import { TurnkeyWalletConnectorBase, findTurnkeyVerifiedCredential, PasskeyService, TURNKEY_API_BASE_URL } from '@dynamic-labs/embedded-wallet';
|
|
12
|
+
import { createSolanaConnection } from '../utils/createSolanaConnection/createSolanaConnection.js';
|
|
13
|
+
import { getGenesisHashLSKey } from '../utils/getGenesisHashLSKey/getGenesisHashLSKey.js';
|
|
14
|
+
import { decodeTransaction, summarizeTransactionDecodedData } from '../utils/transactionDecoder/transactionDecoder.js';
|
|
15
|
+
import { TurnkeySolanaSigner } from './TurnkeySolanaSigner.js';
|
|
16
|
+
|
|
17
|
+
class TurnkeySolanaWalletConnector extends TurnkeyWalletConnectorBase {
|
|
18
|
+
constructor(nameAndKey, props) {
|
|
19
|
+
var _a;
|
|
20
|
+
super(nameAndKey, props);
|
|
21
|
+
// Public fields
|
|
22
|
+
this.connectedChain = 'SOL';
|
|
23
|
+
this.supportedChains = ['SOL'];
|
|
24
|
+
this.verifiedCredentialChain = 'solana';
|
|
25
|
+
this.solNetworks = props.solNetworks;
|
|
26
|
+
this.walletUiUtils = props.walletUiUtils;
|
|
27
|
+
this._turnkeyAccount = undefined;
|
|
28
|
+
this._connectionClient = undefined;
|
|
29
|
+
this.chainRpcProviders = props.chainRpcProviders;
|
|
30
|
+
(_a = this.chainRpcProviders) === null || _a === void 0 ? void 0 : _a.registerSolanaProviders();
|
|
31
|
+
this.__turnkeyClient = this.getTurnkeyClient();
|
|
32
|
+
}
|
|
33
|
+
getRpcUrl() {
|
|
34
|
+
var _a;
|
|
35
|
+
const [network] = this.solNetworks;
|
|
36
|
+
if (!network) {
|
|
37
|
+
throw new DynamicError('No enabled networks');
|
|
38
|
+
}
|
|
39
|
+
return ((_a = network.privateCustomerRpcUrls) === null || _a === void 0 ? void 0 : _a[0]) || network.rpcUrls[0];
|
|
40
|
+
}
|
|
41
|
+
getConnection(commitmentOrConfig) {
|
|
42
|
+
if (!this._connectionClient) {
|
|
43
|
+
const rpcUrl = this.getRpcUrl();
|
|
44
|
+
if (!rpcUrl)
|
|
45
|
+
throw new DynamicError('No rpcUrl');
|
|
46
|
+
this._connectionClient = createSolanaConnection(rpcUrl, commitmentOrConfig);
|
|
47
|
+
}
|
|
48
|
+
return this._connectionClient;
|
|
49
|
+
}
|
|
50
|
+
getWalletClient() {
|
|
51
|
+
return this.getConnection();
|
|
52
|
+
}
|
|
53
|
+
getNetwork() {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
const connection = this.getConnection();
|
|
56
|
+
let genesisHash = localStorage.getItem(getGenesisHashLSKey(connection.rpcEndpoint));
|
|
57
|
+
if (!genesisHash) {
|
|
58
|
+
genesisHash = yield connection.getGenesisHash();
|
|
59
|
+
localStorage.setItem(getGenesisHashLSKey(connection.rpcEndpoint), genesisHash);
|
|
60
|
+
}
|
|
61
|
+
genesisHash = genesisHash.substring(0, 32);
|
|
62
|
+
// see: https://github.com/ChainAgnostic/namespaces/blob/main/solana/caip2.md
|
|
63
|
+
if (genesisHash === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') {
|
|
64
|
+
return 'mainnet';
|
|
65
|
+
}
|
|
66
|
+
else if (genesisHash === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {
|
|
67
|
+
return 'devnet';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return 'testnet';
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
getPublicClient() {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
var _a;
|
|
77
|
+
if (this.solNetworks.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
const configurations = {
|
|
80
|
+
cosmos: [],
|
|
81
|
+
evm: undefined,
|
|
82
|
+
solana: this.solNetworks,
|
|
83
|
+
starknet: undefined,
|
|
84
|
+
};
|
|
85
|
+
if (!this.chainRpcProviders)
|
|
86
|
+
return undefined;
|
|
87
|
+
const providers = this.chainRpcProviders.getProviders(configurations);
|
|
88
|
+
return (_a = this.chainRpcProviders.getSolanaProviderByChainId(providers, '101')) === null || _a === void 0 ? void 0 : _a.provider;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
supportsNetworkSwitching() {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
setVerifiedCredentials(verifiedCredentials) {
|
|
95
|
+
const turnkeyVerifiedCredential = findTurnkeyVerifiedCredential(verifiedCredentials, ProviderChain.SOLANA);
|
|
96
|
+
const didTurnkeyVerifiedCredentialsChanged = JSON.stringify(this.verifiedCredential) !==
|
|
97
|
+
JSON.stringify(turnkeyVerifiedCredential);
|
|
98
|
+
if (!didTurnkeyVerifiedCredentialsChanged) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.verifiedCredential = turnkeyVerifiedCredential;
|
|
102
|
+
this.refreshTurnkeyAccount();
|
|
103
|
+
}
|
|
104
|
+
getAccount() {
|
|
105
|
+
return this.turnkeyAddress;
|
|
106
|
+
}
|
|
107
|
+
endSession() {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
localStorage.removeItem(getGenesisHashLSKey(this.getRpcUrl()));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
refreshTurnkeyAccount() {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
this._turnkeyAccount = undefined;
|
|
115
|
+
return this.getTurnkeyAccount();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
getTurnkeyClient() {
|
|
119
|
+
var _a;
|
|
120
|
+
let rpId = getTLD();
|
|
121
|
+
if (!rpId) {
|
|
122
|
+
rpId = PlatformService.getHostname();
|
|
123
|
+
}
|
|
124
|
+
const passkeyStamper = PasskeyService.createWebauthnStamper({
|
|
125
|
+
rpId,
|
|
126
|
+
});
|
|
127
|
+
const apiKeyStamper = TurnkeyWalletConnectorBase === null || TurnkeyWalletConnectorBase === void 0 ? void 0 : TurnkeyWalletConnectorBase.apiKeyStamper;
|
|
128
|
+
const stamper = apiKeyStamper !== null && apiKeyStamper !== void 0 ? apiKeyStamper : passkeyStamper;
|
|
129
|
+
this.__turnkeyClient =
|
|
130
|
+
(_a = this.getAuthenticatorHandler().client) !== null && _a !== void 0 ? _a : new TurnkeyClient({
|
|
131
|
+
baseUrl: TURNKEY_API_BASE_URL,
|
|
132
|
+
}, stamper);
|
|
133
|
+
return this.__turnkeyClient;
|
|
134
|
+
}
|
|
135
|
+
createTurnkeyAccount(_a) {
|
|
136
|
+
return __awaiter(this, arguments, void 0, function* ({ organizationId, }) {
|
|
137
|
+
const turnkeyClient = this.getTurnkeyClient();
|
|
138
|
+
const signer = new TurnkeySigner({ client: turnkeyClient, organizationId });
|
|
139
|
+
return signer;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
getTurnkeyAccount() {
|
|
143
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
+
var _a, _b, _c, _d;
|
|
145
|
+
if (this._turnkeyAccount &&
|
|
146
|
+
((this.getAuthenticatorHandler().recoveryType === 'passkey' &&
|
|
147
|
+
((_a = this.__turnkeyClient) === null || _a === void 0 ? void 0 : _a.stamper) instanceof WebauthnStamper) ||
|
|
148
|
+
(this.getAuthenticatorHandler().recoveryType === 'email' &&
|
|
149
|
+
((_b = this.__turnkeyClient) === null || _b === void 0 ? void 0 : _b.stamper) instanceof IframeStamper)) &&
|
|
150
|
+
this.__turnkeyClient === this.getAuthenticatorHandler().client) {
|
|
151
|
+
return this._turnkeyAccount;
|
|
152
|
+
}
|
|
153
|
+
const { turnkeySubOrganizationId } = (_c = this.walletProperties) !== null && _c !== void 0 ? _c : {};
|
|
154
|
+
const { address } = (_d = this.verifiedCredential) !== null && _d !== void 0 ? _d : {};
|
|
155
|
+
if (!turnkeySubOrganizationId || !address) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this._turnkeyAccount = yield this.createTurnkeyAccount({
|
|
159
|
+
organizationId: turnkeySubOrganizationId,
|
|
160
|
+
});
|
|
161
|
+
return this._turnkeyAccount;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
getSigner() {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
if (this.isSessionKeyCompatible()) {
|
|
167
|
+
yield this.createOrRestoreSession();
|
|
168
|
+
}
|
|
169
|
+
return new TurnkeySolanaSigner({ walletConnector: this });
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
getBalance() {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
const address = this.getAccount();
|
|
175
|
+
if (!address) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const connectionClient = this.getConnection();
|
|
179
|
+
const publicKey = new PublicKey(address);
|
|
180
|
+
const balance = yield connectionClient.getBalance(publicKey);
|
|
181
|
+
const solBalance = this.lamportsToSol(balance);
|
|
182
|
+
return solBalance.toString();
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
signMessage(messageToSign) {
|
|
186
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
187
|
+
if (!this.turnkeyAddress) {
|
|
188
|
+
throw new DynamicError('No turnkey account');
|
|
189
|
+
}
|
|
190
|
+
const address = this.turnkeyAddress;
|
|
191
|
+
const signedMessage = yield this.walletUiUtils.signMessage({
|
|
192
|
+
handler: () => __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
var _a;
|
|
194
|
+
const enc = new TextEncoder();
|
|
195
|
+
const encodedMessage = enc.encode(messageToSign);
|
|
196
|
+
const signedMessageRaw = yield ((_a = (yield this.getTurnkeyAccount())) === null || _a === void 0 ? void 0 : _a.signMessage(encodedMessage, address));
|
|
197
|
+
return bufferToBase64(signedMessageRaw || Buffer.from([]));
|
|
198
|
+
}),
|
|
199
|
+
message: messageToSign,
|
|
200
|
+
});
|
|
201
|
+
return signedMessage;
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
signTransaction(_a) {
|
|
205
|
+
return __awaiter(this, arguments, void 0, function* ({ transaction, }) {
|
|
206
|
+
const account = yield this.getTurnkeyAccount();
|
|
207
|
+
const address = this.turnkeyAddress;
|
|
208
|
+
if (!account || !address) {
|
|
209
|
+
throw new Error('No turnkey account');
|
|
210
|
+
}
|
|
211
|
+
yield account.addSignature(transaction, address);
|
|
212
|
+
return Buffer.from(transaction.serialize());
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
internalSignAndSendTransaction(transaction, options) {
|
|
216
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
217
|
+
var _a;
|
|
218
|
+
if (!this.turnkeyAddress)
|
|
219
|
+
throw new DynamicError('Solana wallet not found');
|
|
220
|
+
const currentConnection = this.getConnection('confirmed');
|
|
221
|
+
const blockhash = yield currentConnection.getLatestBlockhash();
|
|
222
|
+
if ('version' in transaction) {
|
|
223
|
+
transaction.message.recentBlockhash =
|
|
224
|
+
blockhash.blockhash;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
transaction.recentBlockhash = blockhash.blockhash;
|
|
228
|
+
transaction.feePayer =
|
|
229
|
+
(_a = transaction.feePayer) !== null && _a !== void 0 ? _a : new PublicKey(this.turnkeyAddress);
|
|
230
|
+
}
|
|
231
|
+
const signedTransaction = yield this.signTransaction({ transaction });
|
|
232
|
+
const signature = yield currentConnection.sendRawTransaction(signedTransaction, options);
|
|
233
|
+
// listen for tx confirmation until 60 seconds, which is ~150 blocks expiration
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
const timeout = setTimeout(() => {
|
|
236
|
+
reject(new DynamicError('Transaction timed out'));
|
|
237
|
+
}, 60000);
|
|
238
|
+
currentConnection.onSignature(signature, (result) => {
|
|
239
|
+
clearTimeout(timeout);
|
|
240
|
+
if (result.err) {
|
|
241
|
+
reject(new DynamicError('Transaction failed'));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
resolve(signature);
|
|
245
|
+
}
|
|
246
|
+
}, 'confirmed');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
signAndSendTransaction(transaction, options) {
|
|
251
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
252
|
+
if (!this.turnkeyAddress)
|
|
253
|
+
throw new DynamicError('Solana wallet not found');
|
|
254
|
+
const transactionsData = yield decodeTransaction(transaction, this.getConnection(), this.turnkeyAddress);
|
|
255
|
+
if (!(transactionsData === null || transactionsData === void 0 ? void 0 : transactionsData.length)) {
|
|
256
|
+
throw new DynamicError('Incorrectly formatted transaction instructions');
|
|
257
|
+
}
|
|
258
|
+
const { to, value } = summarizeTransactionDecodedData(transactionsData);
|
|
259
|
+
const uiTransaction = new SolanaUiTransaction({
|
|
260
|
+
connection: this.getConnection(),
|
|
261
|
+
from: this.turnkeyAddress,
|
|
262
|
+
onSubmit: () => __awaiter(this, void 0, void 0, function* () { return this.internalSignAndSendTransaction(transaction, options); }),
|
|
263
|
+
originalTransaction: transaction,
|
|
264
|
+
});
|
|
265
|
+
uiTransaction.to = to;
|
|
266
|
+
uiTransaction.value = value;
|
|
267
|
+
return this.walletUiUtils.sendTransaction(this, uiTransaction);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
sendTransaction(transaction_1, connection_1) {
|
|
271
|
+
return __awaiter(this, arguments, void 0, function* (transaction, connection, options = {}) {
|
|
272
|
+
var _a;
|
|
273
|
+
if (!this.turnkeyAddress)
|
|
274
|
+
throw new DynamicError('Solana wallet not found');
|
|
275
|
+
if (!transaction || !connection) {
|
|
276
|
+
throw new DynamicError('Transaction and connection are required');
|
|
277
|
+
}
|
|
278
|
+
const { signers } = options, sendOptions = __rest(options, ["signers"]);
|
|
279
|
+
const blockhash = yield connection.getLatestBlockhash({
|
|
280
|
+
commitment: options.preflightCommitment,
|
|
281
|
+
minContextSlot: options.minContextSlot,
|
|
282
|
+
});
|
|
283
|
+
if ('version' in transaction) {
|
|
284
|
+
(signers === null || signers === void 0 ? void 0 : signers.length) && transaction.sign(signers);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
transaction.feePayer =
|
|
288
|
+
transaction.feePayer || new PublicKey(this.turnkeyAddress);
|
|
289
|
+
transaction.recentBlockhash =
|
|
290
|
+
transaction.recentBlockhash || blockhash.blockhash;
|
|
291
|
+
(signers === null || signers === void 0 ? void 0 : signers.length) && transaction.partialSign(...signers);
|
|
292
|
+
}
|
|
293
|
+
sendOptions.preflightCommitment =
|
|
294
|
+
sendOptions.preflightCommitment || connection.commitment;
|
|
295
|
+
const signature = yield connection.sendRawTransaction(transaction.serialize(), options);
|
|
296
|
+
const transactionConfirmationStrategy = {
|
|
297
|
+
blockhash: blockhash.blockhash,
|
|
298
|
+
lastValidBlockHeight: blockhash.lastValidBlockHeight,
|
|
299
|
+
signature,
|
|
300
|
+
};
|
|
301
|
+
const result = yield (connection === null || connection === void 0 ? void 0 : connection.confirmTransaction(transactionConfirmationStrategy));
|
|
302
|
+
return ((_a = result === null || result === void 0 ? void 0 : result.value) === null || _a === void 0 ? void 0 : _a.err) ? JSON.stringify(result.value.err) : signature;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
lamportsToSol(lamports) {
|
|
306
|
+
return lamports / LAMPORTS_PER_SOL;
|
|
307
|
+
}
|
|
308
|
+
createUiTransaction(from) {
|
|
309
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
310
|
+
return new SolanaUiTransaction({
|
|
311
|
+
connection: this.getConnection(),
|
|
312
|
+
from,
|
|
313
|
+
onSubmit: (transaction) => __awaiter(this, void 0, void 0, function* () { return this.internalSignAndSendTransaction(transaction); }),
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export { TurnkeySolanaWalletConnector };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TurnkeySolanaWalletConnector } from './TurnkeySolanaWalletConnector';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SOLANA_GENESIS_HASH = "genesis-hash";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var web3_js = require('@solana/web3.js');
|
|
7
|
+
|
|
8
|
+
const createSolanaConnection = (rpcUrl, commitmentOrConfig) => {
|
|
9
|
+
if (!rpcUrl)
|
|
10
|
+
throw new Error('rpcUrl is required');
|
|
11
|
+
return new web3_js.Connection(rpcUrl, commitmentOrConfig);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.createSolanaConnection = createSolanaConnection;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { Connection } from '@solana/web3.js';
|
|
3
|
+
|
|
4
|
+
const createSolanaConnection = (rpcUrl, commitmentOrConfig) => {
|
|
5
|
+
if (!rpcUrl)
|
|
6
|
+
throw new Error('rpcUrl is required');
|
|
7
|
+
return new Connection(rpcUrl, commitmentOrConfig);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { createSolanaConnection };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createSolanaConnection } from './createSolanaConnection';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var constants = require('../../constants.cjs');
|
|
7
|
+
|
|
8
|
+
const getGenesisHashLSKey = (rpcEndpoint) => `${rpcEndpoint}_${constants.SOLANA_GENESIS_HASH}`;
|
|
9
|
+
|
|
10
|
+
exports.getGenesisHashLSKey = getGenesisHashLSKey;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getGenesisHashLSKey: (rpcEndpoint: string) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getGenesisHashLSKey } from './getGenesisHashLSKey';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { decodeTransaction, summarizeTransactionDecodedData, } from './transactionDecoder';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var _tslib = require('../../../../_virtual/_tslib.cjs');
|
|
7
|
+
var web3_js = require('@solana/web3.js');
|
|
8
|
+
var splToken = require('@solana/spl-token');
|
|
9
|
+
var utils = require('@dynamic-labs/utils');
|
|
10
|
+
|
|
11
|
+
const decodeTransaction = (transaction, connection, thisAddress) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
12
|
+
var _a;
|
|
13
|
+
if (!transaction) {
|
|
14
|
+
throw new utils.DynamicError('Transaction is required');
|
|
15
|
+
}
|
|
16
|
+
let decodedInstructions = [];
|
|
17
|
+
if ('version' in transaction) {
|
|
18
|
+
const lookupTableAddresses = transaction.message.addressTableLookups.map((lookup) => new web3_js.PublicKey(lookup.accountKey));
|
|
19
|
+
// for NON simple sol transfers we need to fetch the lookup table accounts
|
|
20
|
+
if (lookupTableAddresses.length > 0) {
|
|
21
|
+
const lookupTables = yield Promise.all(lookupTableAddresses.map((address) => connection.getAddressLookupTable(address)));
|
|
22
|
+
const lookupTableAccounts = lookupTables
|
|
23
|
+
.filter((result) => result !== null)
|
|
24
|
+
.map((result) => result.value);
|
|
25
|
+
if (lookupTableAccounts.length > 0) {
|
|
26
|
+
decodedInstructions = web3_js.TransactionMessage.decompile(transaction.message, {
|
|
27
|
+
addressLookupTableAccounts: lookupTableAccounts,
|
|
28
|
+
}).instructions;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
decodedInstructions = web3_js.TransactionMessage.decompile(transaction.message).instructions;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (!transaction.instructions) {
|
|
36
|
+
decodedInstructions = (_a = web3_js.Transaction.from(Buffer.from(transaction.serialize()))) === null || _a === void 0 ? void 0 : _a.instructions;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
decodedInstructions = transaction.instructions;
|
|
40
|
+
}
|
|
41
|
+
if (!(decodedInstructions === null || decodedInstructions === void 0 ? void 0 : decodedInstructions.length)) {
|
|
42
|
+
throw new utils.DynamicError('Bad formatted instruction');
|
|
43
|
+
}
|
|
44
|
+
const solTransfers = decodedInstructions.filter((instruction) => instruction.programId.equals(web3_js.SystemProgram.programId));
|
|
45
|
+
// non SPL transfers, just SOL
|
|
46
|
+
if (solTransfers.length > 0) {
|
|
47
|
+
return solTransfers.map((decodedInstruction) => {
|
|
48
|
+
const decodedTransferInstruction = web3_js.SystemInstruction.decodeTransfer(decodedInstruction);
|
|
49
|
+
return {
|
|
50
|
+
from: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.fromPubkey.toBase58(),
|
|
51
|
+
to: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.toPubkey.toBase58(),
|
|
52
|
+
value: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.lamports,
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// SPL transfers
|
|
57
|
+
return Promise.all(decodedInstructions.map((instruction) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
59
|
+
if (instruction.programId.equals(splToken.TOKEN_PROGRAM_ID)) {
|
|
60
|
+
const decodedTokenInstruction = splToken.decodeTransferInstructionUnchecked(instruction);
|
|
61
|
+
const { source, destination } = decodedTokenInstruction.keys;
|
|
62
|
+
if (destination) {
|
|
63
|
+
// for contract interactions, ex. swaps, the destination is the turnkey address, flip sender and receiver
|
|
64
|
+
if ((destination === null || destination === void 0 ? void 0 : destination.pubkey.toBase58()) === thisAddress) {
|
|
65
|
+
return {
|
|
66
|
+
from: thisAddress,
|
|
67
|
+
to: source === null || source === void 0 ? void 0 : source.pubkey.toBase58(),
|
|
68
|
+
value: decodedTokenInstruction.data.amount,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Pure SPL transfers, get the address from the destination token account to display to user
|
|
72
|
+
const destinationAccountInfo = yield connection.getParsedAccountInfo(destination.pubkey);
|
|
73
|
+
const isTokenAccount = ((_d = (_c = (_b = destinationAccountInfo.value) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.parsed) === null || _d === void 0 ? void 0 : _d.type) === 'account';
|
|
74
|
+
const destinationOwner = isTokenAccount
|
|
75
|
+
? (_h = (_g = (_f = (_e = destinationAccountInfo.value) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.parsed) === null || _g === void 0 ? void 0 : _g.info) === null || _h === void 0 ? void 0 : _h.owner
|
|
76
|
+
: null;
|
|
77
|
+
const toAddress = destinationOwner
|
|
78
|
+
? destinationOwner
|
|
79
|
+
: destination === null || destination === void 0 ? void 0 : destination.pubkey.toBase58();
|
|
80
|
+
return {
|
|
81
|
+
from: thisAddress,
|
|
82
|
+
to: toAddress,
|
|
83
|
+
value: BigInt(0),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
})));
|
|
89
|
+
});
|
|
90
|
+
const summarizeTransactionDecodedData = (transactionsData) => {
|
|
91
|
+
const recipients = new Set();
|
|
92
|
+
let transferTotalValue = BigInt(0);
|
|
93
|
+
transactionsData.forEach((transactionData) => {
|
|
94
|
+
if (transactionData && transactionData.to) {
|
|
95
|
+
recipients.add(transactionData.to);
|
|
96
|
+
transferTotalValue += transactionData.value;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const to = recipients.size === 1
|
|
100
|
+
? recipients.values().next().value
|
|
101
|
+
: 'dyn_send_transaction.multiple_recipients';
|
|
102
|
+
return { to, value: transferTotalValue };
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
exports.decodeTransaction = decodeTransaction;
|
|
106
|
+
exports.summarizeTransactionDecodedData = summarizeTransactionDecodedData;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Transaction, VersionedTransaction, Connection } from '@solana/web3.js';
|
|
2
|
+
export declare const decodeTransaction: (transaction: Transaction | VersionedTransaction, connection: Connection, thisAddress: string) => Promise<any>;
|
|
3
|
+
export declare const summarizeTransactionDecodedData: (transactionsData: {
|
|
4
|
+
to: string;
|
|
5
|
+
from: string;
|
|
6
|
+
value: bigint;
|
|
7
|
+
}[]) => {
|
|
8
|
+
to: any;
|
|
9
|
+
value: bigint;
|
|
10
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { __awaiter } from '../../../../_virtual/_tslib.js';
|
|
3
|
+
import { PublicKey, TransactionMessage, Transaction, SystemProgram, SystemInstruction } from '@solana/web3.js';
|
|
4
|
+
import { TOKEN_PROGRAM_ID, decodeTransferInstructionUnchecked } from '@solana/spl-token';
|
|
5
|
+
import { DynamicError } from '@dynamic-labs/utils';
|
|
6
|
+
|
|
7
|
+
const decodeTransaction = (transaction, connection, thisAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
8
|
+
var _a;
|
|
9
|
+
if (!transaction) {
|
|
10
|
+
throw new DynamicError('Transaction is required');
|
|
11
|
+
}
|
|
12
|
+
let decodedInstructions = [];
|
|
13
|
+
if ('version' in transaction) {
|
|
14
|
+
const lookupTableAddresses = transaction.message.addressTableLookups.map((lookup) => new PublicKey(lookup.accountKey));
|
|
15
|
+
// for NON simple sol transfers we need to fetch the lookup table accounts
|
|
16
|
+
if (lookupTableAddresses.length > 0) {
|
|
17
|
+
const lookupTables = yield Promise.all(lookupTableAddresses.map((address) => connection.getAddressLookupTable(address)));
|
|
18
|
+
const lookupTableAccounts = lookupTables
|
|
19
|
+
.filter((result) => result !== null)
|
|
20
|
+
.map((result) => result.value);
|
|
21
|
+
if (lookupTableAccounts.length > 0) {
|
|
22
|
+
decodedInstructions = TransactionMessage.decompile(transaction.message, {
|
|
23
|
+
addressLookupTableAccounts: lookupTableAccounts,
|
|
24
|
+
}).instructions;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
decodedInstructions = TransactionMessage.decompile(transaction.message).instructions;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (!transaction.instructions) {
|
|
32
|
+
decodedInstructions = (_a = Transaction.from(Buffer.from(transaction.serialize()))) === null || _a === void 0 ? void 0 : _a.instructions;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
decodedInstructions = transaction.instructions;
|
|
36
|
+
}
|
|
37
|
+
if (!(decodedInstructions === null || decodedInstructions === void 0 ? void 0 : decodedInstructions.length)) {
|
|
38
|
+
throw new DynamicError('Bad formatted instruction');
|
|
39
|
+
}
|
|
40
|
+
const solTransfers = decodedInstructions.filter((instruction) => instruction.programId.equals(SystemProgram.programId));
|
|
41
|
+
// non SPL transfers, just SOL
|
|
42
|
+
if (solTransfers.length > 0) {
|
|
43
|
+
return solTransfers.map((decodedInstruction) => {
|
|
44
|
+
const decodedTransferInstruction = SystemInstruction.decodeTransfer(decodedInstruction);
|
|
45
|
+
return {
|
|
46
|
+
from: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.fromPubkey.toBase58(),
|
|
47
|
+
to: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.toPubkey.toBase58(),
|
|
48
|
+
value: decodedTransferInstruction === null || decodedTransferInstruction === void 0 ? void 0 : decodedTransferInstruction.lamports,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// SPL transfers
|
|
53
|
+
return Promise.all(decodedInstructions.map((instruction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
55
|
+
if (instruction.programId.equals(TOKEN_PROGRAM_ID)) {
|
|
56
|
+
const decodedTokenInstruction = decodeTransferInstructionUnchecked(instruction);
|
|
57
|
+
const { source, destination } = decodedTokenInstruction.keys;
|
|
58
|
+
if (destination) {
|
|
59
|
+
// for contract interactions, ex. swaps, the destination is the turnkey address, flip sender and receiver
|
|
60
|
+
if ((destination === null || destination === void 0 ? void 0 : destination.pubkey.toBase58()) === thisAddress) {
|
|
61
|
+
return {
|
|
62
|
+
from: thisAddress,
|
|
63
|
+
to: source === null || source === void 0 ? void 0 : source.pubkey.toBase58(),
|
|
64
|
+
value: decodedTokenInstruction.data.amount,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Pure SPL transfers, get the address from the destination token account to display to user
|
|
68
|
+
const destinationAccountInfo = yield connection.getParsedAccountInfo(destination.pubkey);
|
|
69
|
+
const isTokenAccount = ((_d = (_c = (_b = destinationAccountInfo.value) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.parsed) === null || _d === void 0 ? void 0 : _d.type) === 'account';
|
|
70
|
+
const destinationOwner = isTokenAccount
|
|
71
|
+
? (_h = (_g = (_f = (_e = destinationAccountInfo.value) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.parsed) === null || _g === void 0 ? void 0 : _g.info) === null || _h === void 0 ? void 0 : _h.owner
|
|
72
|
+
: null;
|
|
73
|
+
const toAddress = destinationOwner
|
|
74
|
+
? destinationOwner
|
|
75
|
+
: destination === null || destination === void 0 ? void 0 : destination.pubkey.toBase58();
|
|
76
|
+
return {
|
|
77
|
+
from: thisAddress,
|
|
78
|
+
to: toAddress,
|
|
79
|
+
value: BigInt(0),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
})));
|
|
85
|
+
});
|
|
86
|
+
const summarizeTransactionDecodedData = (transactionsData) => {
|
|
87
|
+
const recipients = new Set();
|
|
88
|
+
let transferTotalValue = BigInt(0);
|
|
89
|
+
transactionsData.forEach((transactionData) => {
|
|
90
|
+
if (transactionData && transactionData.to) {
|
|
91
|
+
recipients.add(transactionData.to);
|
|
92
|
+
transferTotalValue += transactionData.value;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const to = recipients.size === 1
|
|
96
|
+
? recipients.values().next().value
|
|
97
|
+
: 'dyn_send_transaction.multiple_recipients';
|
|
98
|
+
return { to, value: transferTotalValue };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export { decodeTransaction, summarizeTransactionDecodedData };
|