@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.
Files changed (35) hide show
  1. package/CHANGELOG.md +3494 -0
  2. package/LICENSE +21 -0
  3. package/README.md +11 -0
  4. package/_virtual/_tslib.cjs +49 -0
  5. package/_virtual/_tslib.js +44 -0
  6. package/package.json +47 -0
  7. package/src/TurnkeySolanaWalletConnectors.cjs +19 -0
  8. package/src/TurnkeySolanaWalletConnectors.d.ts +2 -0
  9. package/src/TurnkeySolanaWalletConnectors.js +15 -0
  10. package/src/index.cjs +10 -0
  11. package/src/index.d.ts +2 -0
  12. package/src/index.js +2 -0
  13. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.cjs +60 -0
  14. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.d.ts +45 -0
  15. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaSigner.js +56 -0
  16. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.cjs +323 -0
  17. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.d.ts +55 -0
  18. package/src/lib/TurnkeySolanaWalletConnector/TurnkeySolanaWalletConnector.js +319 -0
  19. package/src/lib/TurnkeySolanaWalletConnector/index.d.ts +1 -0
  20. package/src/lib/constants.cjs +8 -0
  21. package/src/lib/constants.d.ts +1 -0
  22. package/src/lib/constants.js +4 -0
  23. package/src/lib/utils/createSolanaConnection/createSolanaConnection.cjs +14 -0
  24. package/src/lib/utils/createSolanaConnection/createSolanaConnection.d.ts +2 -0
  25. package/src/lib/utils/createSolanaConnection/createSolanaConnection.js +10 -0
  26. package/src/lib/utils/createSolanaConnection/index.d.ts +1 -0
  27. package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.cjs +10 -0
  28. package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.d.ts +1 -0
  29. package/src/lib/utils/getGenesisHashLSKey/getGenesisHashLSKey.js +6 -0
  30. package/src/lib/utils/getGenesisHashLSKey/index.d.ts +1 -0
  31. package/src/lib/utils/index.d.ts +3 -0
  32. package/src/lib/utils/transactionDecoder/index.d.ts +1 -0
  33. package/src/lib/utils/transactionDecoder/transactionDecoder.cjs +106 -0
  34. package/src/lib/utils/transactionDecoder/transactionDecoder.d.ts +10 -0
  35. 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,8 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ const SOLANA_GENESIS_HASH = 'genesis-hash';
7
+
8
+ exports.SOLANA_GENESIS_HASH = SOLANA_GENESIS_HASH;
@@ -0,0 +1 @@
1
+ export declare const SOLANA_GENESIS_HASH = "genesis-hash";
@@ -0,0 +1,4 @@
1
+ 'use client'
2
+ const SOLANA_GENESIS_HASH = 'genesis-hash';
3
+
4
+ export { SOLANA_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,2 @@
1
+ import { Commitment, Connection, ConnectionConfig } from '@solana/web3.js';
2
+ export declare const createSolanaConnection: (rpcUrl: string, commitmentOrConfig?: Commitment | ConnectionConfig) => Connection;
@@ -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,6 @@
1
+ 'use client'
2
+ import { SOLANA_GENESIS_HASH } from '../../constants.js';
3
+
4
+ const getGenesisHashLSKey = (rpcEndpoint) => `${rpcEndpoint}_${SOLANA_GENESIS_HASH}`;
5
+
6
+ export { getGenesisHashLSKey };
@@ -0,0 +1 @@
1
+ export { getGenesisHashLSKey } from './getGenesisHashLSKey';
@@ -0,0 +1,3 @@
1
+ export { createSolanaConnection } from './createSolanaConnection';
2
+ export { getGenesisHashLSKey } from './getGenesisHashLSKey';
3
+ export { decodeTransaction, summarizeTransactionDecodedData, } from './transactionDecoder';
@@ -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 };