@drift-labs/sdk 2.82.0-beta.2 → 2.82.0-beta.20
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/README.md +65 -47
- package/VERSION +1 -1
- package/lib/accounts/types.d.ts +4 -3
- package/lib/accounts/webSocketAccountSubscriber.d.ts +3 -3
- package/lib/accounts/webSocketAccountSubscriber.js +16 -9
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +5 -5
- package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +2 -2
- package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +5 -3
- package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +3 -3
- package/lib/accounts/webSocketProgramAccountSubscriber.js +15 -9
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts +3 -3
- package/lib/accounts/webSocketUserAccountSubscriber.js +3 -3
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +3 -3
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -3
- package/lib/auctionSubscriber/auctionSubscriber.d.ts +2 -2
- package/lib/auctionSubscriber/auctionSubscriber.js +3 -3
- package/lib/auctionSubscriber/types.d.ts +1 -0
- package/lib/clock/clockSubscriber.d.ts +29 -0
- package/lib/clock/clockSubscriber.js +74 -0
- package/lib/constants/perpMarkets.js +2 -2
- package/lib/constants/spotMarkets.js +11 -0
- package/lib/dlob/DLOB.js +2 -2
- package/lib/dlob/orderBookLevels.js +1 -0
- package/lib/driftClient.d.ts +21 -14
- package/lib/driftClient.js +192 -255
- package/lib/driftClientConfig.d.ts +3 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/jupiter/jupiterClient.d.ts +2 -1
- package/lib/jupiter/jupiterClient.js +10 -6
- package/lib/math/exchangeStatus.d.ts +2 -2
- package/lib/math/orders.d.ts +1 -1
- package/lib/math/orders.js +2 -2
- package/lib/orderSubscriber/OrderSubscriber.js +6 -3
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -3
- package/lib/orderSubscriber/WebsocketSubscription.js +3 -3
- package/lib/orderSubscriber/types.d.ts +1 -0
- package/lib/priorityFee/driftPriorityFeeMethod.d.ts +13 -3
- package/lib/priorityFee/driftPriorityFeeMethod.js +2 -2
- package/lib/priorityFee/index.d.ts +2 -0
- package/lib/priorityFee/index.js +2 -0
- package/lib/priorityFee/priorityFeeSubscriber.d.ts +1 -4
- package/lib/priorityFee/priorityFeeSubscriber.js +5 -4
- package/lib/priorityFee/priorityFeeSubscriberMap.d.ts +48 -0
- package/lib/priorityFee/priorityFeeSubscriberMap.js +88 -0
- package/lib/priorityFee/types.d.ts +8 -3
- package/lib/priorityFee/types.js +2 -1
- package/lib/tx/baseTxSender.d.ts +8 -6
- package/lib/tx/baseTxSender.js +9 -51
- package/lib/tx/fastSingleTxSender.d.ts +6 -6
- package/lib/tx/fastSingleTxSender.js +3 -31
- package/lib/tx/forwardOnlyTxSender.d.ts +4 -2
- package/lib/tx/forwardOnlyTxSender.js +2 -1
- package/lib/tx/retryTxSender.d.ts +4 -2
- package/lib/tx/retryTxSender.js +2 -1
- package/lib/tx/txHandler.d.ts +138 -0
- package/lib/tx/txHandler.js +396 -0
- package/lib/tx/txParamProcessor.d.ts +6 -10
- package/lib/tx/txParamProcessor.js +13 -17
- package/lib/tx/types.d.ts +8 -7
- package/lib/tx/types.js +12 -1
- package/lib/tx/whileValidTxSender.d.ts +7 -6
- package/lib/tx/whileValidTxSender.js +7 -28
- package/lib/types.d.ts +24 -4
- package/lib/types.js +10 -1
- package/lib/user.d.ts +0 -10
- package/lib/user.js +6 -29
- package/lib/userConfig.d.ts +1 -0
- package/lib/userMap/WebsocketSubscription.d.ts +4 -3
- package/lib/userMap/WebsocketSubscription.js +3 -3
- package/lib/userMap/userMap.js +4 -1
- package/lib/userMap/userMapConfig.d.ts +1 -0
- package/lib/userStats.js +6 -3
- package/lib/userStatsConfig.d.ts +1 -0
- package/lib/util/chainClock.d.ts +17 -0
- package/lib/util/chainClock.js +29 -0
- package/package.json +3 -3
- package/src/accounts/types.ts +5 -4
- package/src/accounts/webSocketAccountSubscriber.ts +35 -23
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +7 -6
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +6 -4
- package/src/accounts/webSocketProgramAccountSubscriber.ts +32 -22
- package/src/accounts/webSocketUserAccountSubscriber.ts +5 -4
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +5 -4
- package/src/auctionSubscriber/auctionSubscriber.ts +10 -4
- package/src/auctionSubscriber/types.ts +1 -0
- package/src/clock/clockSubscriber.ts +113 -0
- package/src/constants/perpMarkets.ts +2 -2
- package/src/constants/spotMarkets.ts +13 -0
- package/src/dlob/DLOB.ts +2 -2
- package/src/dlob/orderBookLevels.ts +2 -0
- package/src/driftClient.ts +295 -386
- package/src/driftClientConfig.ts +3 -0
- package/src/index.ts +2 -0
- package/src/jupiter/jupiterClient.ts +15 -6
- package/src/math/exchangeStatus.ts +2 -1
- package/src/math/orders.ts +3 -2
- package/src/orderSubscriber/OrderSubscriber.ts +4 -1
- package/src/orderSubscriber/WebsocketSubscription.ts +6 -5
- package/src/orderSubscriber/types.ts +1 -0
- package/src/priorityFee/driftPriorityFeeMethod.ts +16 -4
- package/src/priorityFee/index.ts +2 -0
- package/src/priorityFee/priorityFeeSubscriber.ts +7 -7
- package/src/priorityFee/priorityFeeSubscriberMap.ts +112 -0
- package/src/priorityFee/types.ts +16 -3
- package/src/tx/baseTxSender.ts +29 -79
- package/src/tx/fastSingleTxSender.ts +10 -55
- package/src/tx/forwardOnlyTxSender.ts +5 -1
- package/src/tx/retryTxSender.ts +5 -1
- package/src/tx/txHandler.ts +625 -0
- package/src/tx/txParamProcessor.ts +16 -28
- package/src/tx/types.ts +14 -18
- package/src/tx/whileValidTxSender.ts +24 -48
- package/src/types.ts +26 -2
- package/src/user.ts +7 -32
- package/src/userConfig.ts +1 -0
- package/src/userMap/WebsocketSubscription.ts +6 -5
- package/src/userMap/userMap.ts +4 -1
- package/src/userMap/userMapConfig.ts +1 -0
- package/src/userStats.ts +4 -1
- package/src/userStatsConfig.ts +1 -0
- package/src/util/chainClock.ts +41 -0
- package/tests/dlob/helpers.ts +3 -0
- package/lib/tx/utils.d.ts +0 -6
- package/lib/tx/utils.js +0 -39
- package/src/tx/utils.ts +0 -64
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressLookupTableAccount,
|
|
3
|
+
BlockhashWithExpiryBlockHeight,
|
|
4
|
+
Commitment,
|
|
5
|
+
ComputeBudgetProgram,
|
|
6
|
+
ConfirmOptions,
|
|
7
|
+
Connection,
|
|
8
|
+
Message,
|
|
9
|
+
MessageV0,
|
|
10
|
+
Signer,
|
|
11
|
+
Transaction,
|
|
12
|
+
TransactionInstruction,
|
|
13
|
+
TransactionMessage,
|
|
14
|
+
TransactionVersion,
|
|
15
|
+
VersionedTransaction,
|
|
16
|
+
} from '@solana/web3.js';
|
|
17
|
+
import { TransactionParamProcessor } from './txParamProcessor';
|
|
18
|
+
import bs58 from 'bs58';
|
|
19
|
+
import {
|
|
20
|
+
BaseTxParams,
|
|
21
|
+
DriftClientMetricsEvents,
|
|
22
|
+
IWallet,
|
|
23
|
+
SignedTxData,
|
|
24
|
+
TxParams,
|
|
25
|
+
} from '../types';
|
|
26
|
+
|
|
27
|
+
export const COMPUTE_UNITS_DEFAULT = 200_000;
|
|
28
|
+
|
|
29
|
+
export type TxBuildingProps = {
|
|
30
|
+
instructions: TransactionInstruction | TransactionInstruction[];
|
|
31
|
+
txVersion: TransactionVersion;
|
|
32
|
+
connection: Connection;
|
|
33
|
+
preFlightCommitment: Commitment;
|
|
34
|
+
fetchMarketLookupTableAccount: () => Promise<AddressLookupTableAccount>;
|
|
35
|
+
lookupTables?: AddressLookupTableAccount[];
|
|
36
|
+
forceVersionedTransaction?: boolean;
|
|
37
|
+
txParams?: TxParams;
|
|
38
|
+
recentBlockHash?: BlockhashWithExpiryBlockHeight;
|
|
39
|
+
wallet?: IWallet;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* This class is responsible for creating and signing transactions.
|
|
44
|
+
*/
|
|
45
|
+
export class TxHandler {
|
|
46
|
+
private blockHashToLastValidBlockHeightLookup: Record<string, number> = {};
|
|
47
|
+
private returnBlockHeightsWithSignedTxCallbackData = false;
|
|
48
|
+
|
|
49
|
+
private connection: Connection;
|
|
50
|
+
private wallet: IWallet;
|
|
51
|
+
private confirmationOptions: ConfirmOptions;
|
|
52
|
+
|
|
53
|
+
private onSignedCb?: (txSigs: DriftClientMetricsEvents['txSigned']) => void;
|
|
54
|
+
|
|
55
|
+
constructor(props: {
|
|
56
|
+
connection: Connection;
|
|
57
|
+
wallet: IWallet;
|
|
58
|
+
confirmationOptions: ConfirmOptions;
|
|
59
|
+
opts?: {
|
|
60
|
+
returnBlockHeightsWithSignedTxCallbackData?: boolean;
|
|
61
|
+
onSignedCb?: (txSigs: DriftClientMetricsEvents['txSigned']) => void;
|
|
62
|
+
};
|
|
63
|
+
}) {
|
|
64
|
+
this.connection = props.connection;
|
|
65
|
+
this.wallet = props.wallet;
|
|
66
|
+
this.confirmationOptions = props.confirmationOptions;
|
|
67
|
+
|
|
68
|
+
// #Optionals
|
|
69
|
+
this.returnBlockHeightsWithSignedTxCallbackData =
|
|
70
|
+
props.opts?.returnBlockHeightsWithSignedTxCallbackData ?? false;
|
|
71
|
+
this.onSignedCb = props.opts?.onSignedCb;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private addHashAndExpiryToLookup(
|
|
75
|
+
hashAndExpiry: BlockhashWithExpiryBlockHeight
|
|
76
|
+
) {
|
|
77
|
+
if (!this.returnBlockHeightsWithSignedTxCallbackData) return;
|
|
78
|
+
|
|
79
|
+
this.blockHashToLastValidBlockHeightLookup[hashAndExpiry.blockhash] =
|
|
80
|
+
hashAndExpiry.lastValidBlockHeight;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private getProps = (wallet?: IWallet, confirmationOpts?: ConfirmOptions) =>
|
|
84
|
+
[wallet ?? this.wallet, confirmationOpts ?? this.confirmationOptions] as [
|
|
85
|
+
IWallet,
|
|
86
|
+
ConfirmOptions,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
public updateWallet(wallet: IWallet) {
|
|
90
|
+
this.wallet = wallet;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Created this to prevent non-finalized blockhashes being used when building transactions. We want to always use finalized because otherwise it's easy to get the BlockHashNotFound error (RPC uses finalized to validate a transaction). Using an older blockhash when building transactions should never really be a problem right now.
|
|
95
|
+
*
|
|
96
|
+
* https://www.helius.dev/blog/how-to-deal-with-blockhash-errors-on-solana#why-do-blockhash-errors-occur
|
|
97
|
+
*
|
|
98
|
+
* @returns
|
|
99
|
+
*/
|
|
100
|
+
public getLatestBlockhashForTransaction() {
|
|
101
|
+
return this.connection.getLatestBlockhash('finalized');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Applies recent blockhash and signs a given transaction
|
|
106
|
+
* @param tx
|
|
107
|
+
* @param additionalSigners
|
|
108
|
+
* @param wallet
|
|
109
|
+
* @param confirmationOpts
|
|
110
|
+
* @param preSigned
|
|
111
|
+
* @param recentBlockhash
|
|
112
|
+
* @returns
|
|
113
|
+
*/
|
|
114
|
+
public async prepareTx(
|
|
115
|
+
tx: Transaction,
|
|
116
|
+
additionalSigners: Array<Signer>,
|
|
117
|
+
wallet?: IWallet,
|
|
118
|
+
confirmationOpts?: ConfirmOptions,
|
|
119
|
+
preSigned?: boolean,
|
|
120
|
+
recentBlockhash?: BlockhashWithExpiryBlockHeight
|
|
121
|
+
): Promise<Transaction> {
|
|
122
|
+
if (preSigned) {
|
|
123
|
+
return tx;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[wallet, confirmationOpts] = this.getProps(wallet, confirmationOpts);
|
|
127
|
+
|
|
128
|
+
tx.feePayer = wallet.publicKey;
|
|
129
|
+
recentBlockhash = recentBlockhash
|
|
130
|
+
? recentBlockhash
|
|
131
|
+
: await this.getLatestBlockhashForTransaction();
|
|
132
|
+
tx.recentBlockhash = recentBlockhash.blockhash;
|
|
133
|
+
|
|
134
|
+
this.addHashAndExpiryToLookup(recentBlockhash);
|
|
135
|
+
|
|
136
|
+
const signedTx = await this.signTx(tx, additionalSigners);
|
|
137
|
+
|
|
138
|
+
return signedTx;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private isVersionedTransaction(tx: Transaction | VersionedTransaction) {
|
|
142
|
+
return (tx as VersionedTransaction)?.message && true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private getTxSigFromSignedTx(signedTx: Transaction | VersionedTransaction) {
|
|
146
|
+
if (this.isVersionedTransaction(signedTx)) {
|
|
147
|
+
return bs58.encode(
|
|
148
|
+
Buffer.from((signedTx as VersionedTransaction).signatures[0])
|
|
149
|
+
) as string;
|
|
150
|
+
} else {
|
|
151
|
+
return bs58.encode(
|
|
152
|
+
Buffer.from((signedTx as Transaction).signature)
|
|
153
|
+
) as string;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private getBlockhashFromSignedTx(
|
|
158
|
+
signedTx: Transaction | VersionedTransaction
|
|
159
|
+
) {
|
|
160
|
+
if (this.isVersionedTransaction(signedTx)) {
|
|
161
|
+
return (signedTx as VersionedTransaction).message.recentBlockhash;
|
|
162
|
+
} else {
|
|
163
|
+
return (signedTx as Transaction).recentBlockhash;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private async signTx(
|
|
168
|
+
tx: Transaction,
|
|
169
|
+
additionalSigners: Array<Signer>,
|
|
170
|
+
wallet?: IWallet
|
|
171
|
+
): Promise<Transaction> {
|
|
172
|
+
[wallet] = this.getProps(wallet);
|
|
173
|
+
|
|
174
|
+
additionalSigners
|
|
175
|
+
.filter((s): s is Signer => s !== undefined)
|
|
176
|
+
.forEach((kp) => {
|
|
177
|
+
tx.partialSign(kp);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const signedTx = await wallet.signTransaction(tx);
|
|
181
|
+
|
|
182
|
+
// Turn txSig Buffer into base58 string
|
|
183
|
+
const txSig = this.getTxSigFromSignedTx(signedTx);
|
|
184
|
+
|
|
185
|
+
this.handleSignedTxData([
|
|
186
|
+
{
|
|
187
|
+
txSig,
|
|
188
|
+
signedTx,
|
|
189
|
+
blockHash: this.getBlockhashFromSignedTx(signedTx),
|
|
190
|
+
},
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
return signedTx;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async signVersionedTx(
|
|
197
|
+
tx: VersionedTransaction,
|
|
198
|
+
additionalSigners: Array<Signer>,
|
|
199
|
+
recentBlockHash?: BlockhashWithExpiryBlockHeight,
|
|
200
|
+
wallet?: IWallet
|
|
201
|
+
): Promise<VersionedTransaction> {
|
|
202
|
+
[wallet] = this.getProps(wallet);
|
|
203
|
+
|
|
204
|
+
if (recentBlockHash) {
|
|
205
|
+
tx.message.recentBlockhash = recentBlockHash.blockhash;
|
|
206
|
+
|
|
207
|
+
this.addHashAndExpiryToLookup(recentBlockHash);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
additionalSigners
|
|
211
|
+
?.filter((s): s is Signer => s !== undefined)
|
|
212
|
+
.forEach((kp) => {
|
|
213
|
+
tx.sign([kp]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
//@ts-ignore
|
|
217
|
+
const signedTx = (await wallet.signTransaction(tx)) as VersionedTransaction;
|
|
218
|
+
|
|
219
|
+
// Turn txSig Buffer into base58 string
|
|
220
|
+
const txSig = this.getTxSigFromSignedTx(signedTx);
|
|
221
|
+
|
|
222
|
+
this.handleSignedTxData([
|
|
223
|
+
{
|
|
224
|
+
txSig,
|
|
225
|
+
signedTx,
|
|
226
|
+
blockHash: this.getBlockhashFromSignedTx(signedTx),
|
|
227
|
+
},
|
|
228
|
+
]);
|
|
229
|
+
|
|
230
|
+
return signedTx;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private handleSignedTxData(
|
|
234
|
+
txData: Omit<SignedTxData, 'lastValidBlockHeight'>[]
|
|
235
|
+
) {
|
|
236
|
+
if (!this.returnBlockHeightsWithSignedTxCallbackData) {
|
|
237
|
+
if (this.onSignedCb) {
|
|
238
|
+
this.onSignedCb(txData);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const fullTxData = txData.map((tx) => {
|
|
245
|
+
const lastValidBlockHeight =
|
|
246
|
+
this.blockHashToLastValidBlockHeightLookup[tx.blockHash];
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
...tx,
|
|
250
|
+
lastValidBlockHeight,
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (this.onSignedCb) {
|
|
255
|
+
this.onSignedCb(fullTxData);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Gets transaction params with extra processing applied, like using the simulated compute units or using a dynamically calculated compute unit price.
|
|
261
|
+
* @param txBuildingProps
|
|
262
|
+
* @returns
|
|
263
|
+
*/
|
|
264
|
+
private async getProcessedTransactionParams(
|
|
265
|
+
txBuildingProps: TxBuildingProps
|
|
266
|
+
): Promise<BaseTxParams> {
|
|
267
|
+
const baseTxParams: BaseTxParams = {
|
|
268
|
+
computeUnits: txBuildingProps?.txParams?.computeUnits,
|
|
269
|
+
computeUnitsPrice: txBuildingProps?.txParams?.computeUnitsPrice,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const processedTxParams = await TransactionParamProcessor.process({
|
|
273
|
+
baseTxParams,
|
|
274
|
+
txBuilder: (updatedTxParams) =>
|
|
275
|
+
this.buildTransaction({
|
|
276
|
+
...txBuildingProps,
|
|
277
|
+
txParams: updatedTxParams.txParams ?? baseTxParams,
|
|
278
|
+
forceVersionedTransaction: true,
|
|
279
|
+
}) as Promise<VersionedTransaction>,
|
|
280
|
+
processConfig: {
|
|
281
|
+
useSimulatedComputeUnits:
|
|
282
|
+
txBuildingProps.txParams.useSimulatedComputeUnits,
|
|
283
|
+
computeUnitsBufferMultiplier:
|
|
284
|
+
txBuildingProps.txParams.computeUnitsBufferMultiplier,
|
|
285
|
+
useSimulatedComputeUnitsForCUPriceCalculation:
|
|
286
|
+
txBuildingProps.txParams
|
|
287
|
+
.useSimulatedComputeUnitsForCUPriceCalculation,
|
|
288
|
+
getCUPriceFromComputeUnits:
|
|
289
|
+
txBuildingProps.txParams.getCUPriceFromComputeUnits,
|
|
290
|
+
},
|
|
291
|
+
processParams: {
|
|
292
|
+
connection: this.connection,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return processedTxParams;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private _generateVersionedTransaction(
|
|
300
|
+
recentBlockHash: BlockhashWithExpiryBlockHeight,
|
|
301
|
+
message: Message | MessageV0
|
|
302
|
+
) {
|
|
303
|
+
this.addHashAndExpiryToLookup(recentBlockHash);
|
|
304
|
+
|
|
305
|
+
return new VersionedTransaction(message);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
public generateLegacyVersionedTransaction(
|
|
309
|
+
recentBlockhash: BlockhashWithExpiryBlockHeight,
|
|
310
|
+
ixs: TransactionInstruction[],
|
|
311
|
+
wallet?: IWallet
|
|
312
|
+
) {
|
|
313
|
+
[wallet] = this.getProps(wallet);
|
|
314
|
+
|
|
315
|
+
const message = new TransactionMessage({
|
|
316
|
+
payerKey: wallet.publicKey,
|
|
317
|
+
recentBlockhash: recentBlockhash.blockhash,
|
|
318
|
+
instructions: ixs,
|
|
319
|
+
}).compileToLegacyMessage();
|
|
320
|
+
|
|
321
|
+
return this._generateVersionedTransaction(recentBlockhash, message);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public generateVersionedTransaction(
|
|
325
|
+
recentBlockhash: BlockhashWithExpiryBlockHeight,
|
|
326
|
+
ixs: TransactionInstruction[],
|
|
327
|
+
lookupTableAccounts: AddressLookupTableAccount[],
|
|
328
|
+
wallet?: IWallet
|
|
329
|
+
) {
|
|
330
|
+
[wallet] = this.getProps(wallet);
|
|
331
|
+
|
|
332
|
+
const message = new TransactionMessage({
|
|
333
|
+
payerKey: wallet.publicKey,
|
|
334
|
+
recentBlockhash: recentBlockhash.blockhash,
|
|
335
|
+
instructions: ixs,
|
|
336
|
+
}).compileToV0Message(lookupTableAccounts);
|
|
337
|
+
|
|
338
|
+
return this._generateVersionedTransaction(recentBlockhash, message);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
public generateLegacyTransaction(ixs: TransactionInstruction[]) {
|
|
342
|
+
return new Transaction().add(...ixs);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Accepts multiple instructions and builds a transaction for each. Prevents needing to spam RPC with requests for the same blockhash.
|
|
347
|
+
* @param props
|
|
348
|
+
* @returns
|
|
349
|
+
*/
|
|
350
|
+
public async buildBulkTransactions(
|
|
351
|
+
props: Omit<TxBuildingProps, 'instructions'> & {
|
|
352
|
+
instructions: (TransactionInstruction | TransactionInstruction[])[];
|
|
353
|
+
}
|
|
354
|
+
) {
|
|
355
|
+
const recentBlockHash =
|
|
356
|
+
props?.recentBlockHash ?? (await this.getLatestBlockhashForTransaction());
|
|
357
|
+
|
|
358
|
+
return await Promise.all(
|
|
359
|
+
props.instructions.map((ix) => {
|
|
360
|
+
if (!ix) return undefined;
|
|
361
|
+
return this.buildTransaction({
|
|
362
|
+
...props,
|
|
363
|
+
instructions: ix,
|
|
364
|
+
recentBlockHash,
|
|
365
|
+
});
|
|
366
|
+
})
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
*
|
|
372
|
+
* @param instructions
|
|
373
|
+
* @param txParams
|
|
374
|
+
* @param txVersion
|
|
375
|
+
* @param lookupTables
|
|
376
|
+
* @param forceVersionedTransaction Return a VersionedTransaction instance even if the version of the transaction is Legacy
|
|
377
|
+
* @returns
|
|
378
|
+
*/
|
|
379
|
+
public async buildTransaction(
|
|
380
|
+
props: TxBuildingProps
|
|
381
|
+
): Promise<Transaction | VersionedTransaction> {
|
|
382
|
+
const {
|
|
383
|
+
instructions,
|
|
384
|
+
txVersion,
|
|
385
|
+
txParams,
|
|
386
|
+
connection: _connection,
|
|
387
|
+
preFlightCommitment: _preFlightCommitment,
|
|
388
|
+
fetchMarketLookupTableAccount,
|
|
389
|
+
forceVersionedTransaction,
|
|
390
|
+
} = props;
|
|
391
|
+
|
|
392
|
+
let { lookupTables } = props;
|
|
393
|
+
|
|
394
|
+
// # Collect and process Tx Params
|
|
395
|
+
let baseTxParams: BaseTxParams = {
|
|
396
|
+
computeUnits: txParams?.computeUnits,
|
|
397
|
+
computeUnitsPrice: txParams?.computeUnitsPrice,
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
if (txParams?.useSimulatedComputeUnits) {
|
|
401
|
+
const processedTxParams = await this.getProcessedTransactionParams(props);
|
|
402
|
+
|
|
403
|
+
baseTxParams = {
|
|
404
|
+
...baseTxParams,
|
|
405
|
+
...processedTxParams,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// # Create Tx Instructions
|
|
410
|
+
const allIx = [];
|
|
411
|
+
const computeUnits = baseTxParams?.computeUnits;
|
|
412
|
+
if (computeUnits !== 200_000) {
|
|
413
|
+
allIx.push(
|
|
414
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
415
|
+
units: computeUnits,
|
|
416
|
+
})
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const computeUnitsPrice = baseTxParams?.computeUnitsPrice;
|
|
421
|
+
|
|
422
|
+
if (computeUnitsPrice !== 0) {
|
|
423
|
+
allIx.push(
|
|
424
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
425
|
+
microLamports: computeUnitsPrice,
|
|
426
|
+
})
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (Array.isArray(instructions)) {
|
|
431
|
+
allIx.push(...instructions);
|
|
432
|
+
} else {
|
|
433
|
+
allIx.push(instructions);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const recentBlockHash =
|
|
437
|
+
props?.recentBlockHash ?? (await this.getLatestBlockhashForTransaction());
|
|
438
|
+
|
|
439
|
+
// # Create and return Transaction
|
|
440
|
+
if (txVersion === 'legacy') {
|
|
441
|
+
if (forceVersionedTransaction) {
|
|
442
|
+
return this.generateLegacyVersionedTransaction(recentBlockHash, allIx);
|
|
443
|
+
} else {
|
|
444
|
+
return this.generateLegacyTransaction(allIx);
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
const marketLookupTable = await fetchMarketLookupTableAccount();
|
|
448
|
+
|
|
449
|
+
lookupTables = lookupTables
|
|
450
|
+
? [...lookupTables, marketLookupTable]
|
|
451
|
+
: [marketLookupTable];
|
|
452
|
+
|
|
453
|
+
return this.generateVersionedTransaction(
|
|
454
|
+
recentBlockHash,
|
|
455
|
+
allIx,
|
|
456
|
+
lookupTables
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
public wrapInTx(
|
|
462
|
+
instruction: TransactionInstruction,
|
|
463
|
+
computeUnits = 600_000,
|
|
464
|
+
computeUnitsPrice = 0
|
|
465
|
+
): Transaction {
|
|
466
|
+
const tx = new Transaction();
|
|
467
|
+
if (computeUnits != COMPUTE_UNITS_DEFAULT) {
|
|
468
|
+
tx.add(
|
|
469
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
470
|
+
units: computeUnits,
|
|
471
|
+
})
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (computeUnitsPrice != 0) {
|
|
476
|
+
tx.add(
|
|
477
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
478
|
+
microLamports: computeUnitsPrice,
|
|
479
|
+
})
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return tx.add(instruction);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Build a map of transactions from an array of instructions for multiple transactions.
|
|
488
|
+
* @param txsToSign
|
|
489
|
+
* @param keys
|
|
490
|
+
* @param wallet
|
|
491
|
+
* @param commitment
|
|
492
|
+
* @returns
|
|
493
|
+
*/
|
|
494
|
+
public async buildTransactionMap(
|
|
495
|
+
txsToSign: (Transaction | undefined)[],
|
|
496
|
+
keys: string[],
|
|
497
|
+
wallet?: IWallet,
|
|
498
|
+
commitment?: Commitment,
|
|
499
|
+
recentBlockHash?: BlockhashWithExpiryBlockHeight
|
|
500
|
+
) {
|
|
501
|
+
recentBlockHash = recentBlockHash
|
|
502
|
+
? recentBlockHash
|
|
503
|
+
: await this.getLatestBlockhashForTransaction();
|
|
504
|
+
|
|
505
|
+
this.addHashAndExpiryToLookup(recentBlockHash);
|
|
506
|
+
|
|
507
|
+
for (const tx of txsToSign) {
|
|
508
|
+
if (!tx) continue;
|
|
509
|
+
tx.recentBlockhash = recentBlockHash.blockhash;
|
|
510
|
+
tx.feePayer = wallet?.publicKey ?? this.wallet?.publicKey;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return this.getSignedTransactionMap(txsToSign, keys, wallet);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Get a map of signed and prepared transactions from an array of legacy transactions
|
|
518
|
+
* @param txsToSign
|
|
519
|
+
* @param keys
|
|
520
|
+
* @param wallet
|
|
521
|
+
* @param commitment
|
|
522
|
+
* @returns
|
|
523
|
+
*/
|
|
524
|
+
public async getPreparedAndSignedLegacyTransactionMap(
|
|
525
|
+
txsToSign: (Transaction | undefined)[],
|
|
526
|
+
keys: string[],
|
|
527
|
+
wallet?: IWallet,
|
|
528
|
+
commitment?: Commitment,
|
|
529
|
+
recentBlockHash?: BlockhashWithExpiryBlockHeight
|
|
530
|
+
) {
|
|
531
|
+
recentBlockHash = recentBlockHash
|
|
532
|
+
? recentBlockHash
|
|
533
|
+
: await this.getLatestBlockhashForTransaction();
|
|
534
|
+
|
|
535
|
+
this.addHashAndExpiryToLookup(recentBlockHash);
|
|
536
|
+
|
|
537
|
+
for (const tx of txsToSign) {
|
|
538
|
+
if (!tx) continue;
|
|
539
|
+
tx.recentBlockhash = recentBlockHash.blockhash;
|
|
540
|
+
tx.feePayer = wallet?.publicKey ?? this.wallet?.publicKey;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return this.getSignedTransactionMap(txsToSign, keys, wallet);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Get a map of signed transactions from an array of transactions to sign.
|
|
548
|
+
* @param txsToSign
|
|
549
|
+
* @param keys
|
|
550
|
+
* @param wallet
|
|
551
|
+
* @returns
|
|
552
|
+
*/
|
|
553
|
+
public async getSignedTransactionMap(
|
|
554
|
+
txsToSign: (Transaction | VersionedTransaction | undefined)[],
|
|
555
|
+
keys: string[],
|
|
556
|
+
wallet?: IWallet
|
|
557
|
+
): Promise<{
|
|
558
|
+
[key: string]: Transaction | VersionedTransaction | undefined;
|
|
559
|
+
}> {
|
|
560
|
+
[wallet] = this.getProps(wallet);
|
|
561
|
+
|
|
562
|
+
const signedTxMap: {
|
|
563
|
+
[key: string]: Transaction | VersionedTransaction | undefined;
|
|
564
|
+
} = {};
|
|
565
|
+
|
|
566
|
+
const keysWithTx = [];
|
|
567
|
+
txsToSign.forEach((tx, index) => {
|
|
568
|
+
if (tx == undefined) {
|
|
569
|
+
signedTxMap[keys[index]] = undefined;
|
|
570
|
+
} else {
|
|
571
|
+
keysWithTx.push(keys[index]);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const signedTxs = await wallet.signAllTransactions(
|
|
576
|
+
txsToSign
|
|
577
|
+
.map((tx) => {
|
|
578
|
+
return tx as Transaction;
|
|
579
|
+
})
|
|
580
|
+
.filter((tx) => tx !== undefined)
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
this.handleSignedTxData(
|
|
584
|
+
signedTxs.map((signedTx) => {
|
|
585
|
+
return {
|
|
586
|
+
txSig: this.getTxSigFromSignedTx(signedTx),
|
|
587
|
+
signedTx,
|
|
588
|
+
blockHash: this.getBlockhashFromSignedTx(signedTx),
|
|
589
|
+
};
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
signedTxs.forEach((signedTx, index) => {
|
|
594
|
+
signedTxMap[keysWithTx[index]] = signedTx;
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
return signedTxMap;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Builds and signs transactions from a given array of instructions for multiple transactions.
|
|
602
|
+
* @param props
|
|
603
|
+
* @returns
|
|
604
|
+
*/
|
|
605
|
+
public async buildAndSignTransactionMap(
|
|
606
|
+
props: Omit<TxBuildingProps, 'instructions'> & {
|
|
607
|
+
keys: string[];
|
|
608
|
+
instructions: (TransactionInstruction | TransactionInstruction[])[];
|
|
609
|
+
}
|
|
610
|
+
) {
|
|
611
|
+
const transactions = await this.buildBulkTransactions(props);
|
|
612
|
+
|
|
613
|
+
const preppedTransactions =
|
|
614
|
+
props.txVersion === 'legacy'
|
|
615
|
+
? this.getPreparedAndSignedLegacyTransactionMap(
|
|
616
|
+
transactions as Transaction[],
|
|
617
|
+
props.keys,
|
|
618
|
+
props.wallet,
|
|
619
|
+
props.preFlightCommitment
|
|
620
|
+
)
|
|
621
|
+
: this.getSignedTransactionMap(transactions, props.keys, props.wallet);
|
|
622
|
+
|
|
623
|
+
return preppedTransactions;
|
|
624
|
+
}
|
|
625
|
+
}
|