@drift-labs/sdk 2.85.0-beta.0 → 2.85.0-beta.10

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 (41) hide show
  1. package/VERSION +1 -1
  2. package/bun.lockb +0 -0
  3. package/lib/accounts/bulkAccountLoader.d.ts +3 -3
  4. package/lib/accounts/pollingDriftClientAccountSubscriber.js +10 -2
  5. package/lib/accounts/pollingUserAccountSubscriber.d.ts +4 -3
  6. package/lib/accounts/pollingUserAccountSubscriber.js +7 -6
  7. package/lib/accounts/testBulkAccountLoader.d.ts +4 -0
  8. package/lib/accounts/testBulkAccountLoader.js +45 -0
  9. package/lib/bankrun/bankrunConnection.d.ts +71 -0
  10. package/lib/bankrun/bankrunConnection.js +285 -0
  11. package/lib/blockhashSubscriber/BlockhashSubscriber.js +22 -15
  12. package/lib/constants/perpMarkets.js +10 -0
  13. package/lib/constants/spotMarkets.js +10 -0
  14. package/lib/driftClient.d.ts +2 -2
  15. package/lib/driftClient.js +14 -13
  16. package/lib/events/eventSubscriber.js +12 -4
  17. package/lib/idl/drift.json +28 -8
  18. package/lib/testClient.js +1 -2
  19. package/lib/tokenFaucet.d.ts +3 -1
  20. package/lib/tokenFaucet.js +41 -8
  21. package/lib/tx/txParamProcessor.d.ts +2 -1
  22. package/lib/tx/txParamProcessor.js +7 -3
  23. package/lib/types.d.ts +1 -0
  24. package/lib/user.js +1 -1
  25. package/package.json +3 -1
  26. package/src/accounts/bulkAccountLoader.ts +3 -2
  27. package/src/accounts/pollingDriftClientAccountSubscriber.ts +16 -3
  28. package/src/accounts/pollingUserAccountSubscriber.ts +13 -12
  29. package/src/accounts/testBulkAccountLoader.ts +53 -0
  30. package/src/bankrun/bankrunConnection.ts +466 -0
  31. package/src/blockhashSubscriber/BlockhashSubscriber.ts +24 -19
  32. package/src/constants/perpMarkets.ts +10 -0
  33. package/src/constants/spotMarkets.ts +10 -0
  34. package/src/driftClient.ts +21 -15
  35. package/src/events/eventSubscriber.ts +5 -0
  36. package/src/idl/drift.json +28 -8
  37. package/src/testClient.ts +1 -2
  38. package/src/tokenFaucet.ts +49 -12
  39. package/src/tx/txParamProcessor.ts +11 -2
  40. package/src/types.ts +1 -0
  41. package/src/user.ts +5 -2
@@ -0,0 +1,466 @@
1
+ import {
2
+ TransactionConfirmationStatus,
3
+ AccountInfo,
4
+ Keypair,
5
+ PublicKey,
6
+ Transaction,
7
+ RpcResponseAndContext,
8
+ Commitment,
9
+ TransactionSignature,
10
+ SignatureStatusConfig,
11
+ SignatureStatus,
12
+ GetVersionedTransactionConfig,
13
+ GetTransactionConfig,
14
+ VersionedTransaction,
15
+ SimulateTransactionConfig,
16
+ SimulatedTransactionResponse,
17
+ TransactionReturnData,
18
+ TransactionError,
19
+ SignatureResultCallback,
20
+ ClientSubscriptionId,
21
+ Connection as SolanaConnection,
22
+ SystemProgram,
23
+ Blockhash,
24
+ LogsFilter,
25
+ LogsCallback,
26
+ AccountChangeCallback,
27
+ LAMPORTS_PER_SOL,
28
+ } from '@solana/web3.js';
29
+ import {
30
+ ProgramTestContext,
31
+ BanksClient,
32
+ BanksTransactionResultWithMeta,
33
+ Clock,
34
+ } from 'solana-bankrun';
35
+ import { BankrunProvider } from 'anchor-bankrun';
36
+ import bs58 from 'bs58';
37
+ import { BN, Wallet } from '@coral-xyz/anchor';
38
+ import { Account, TOKEN_PROGRAM_ID, unpackAccount } from '@solana/spl-token';
39
+
40
+ export type Connection = SolanaConnection | BankrunConnection;
41
+
42
+ type BankrunTransactionMetaNormalized = {
43
+ logMessages: string[];
44
+ err: TransactionError;
45
+ };
46
+
47
+ type BankrunTransactionRespose = {
48
+ slot: number;
49
+ meta: BankrunTransactionMetaNormalized;
50
+ };
51
+
52
+ export class BankrunContextWrapper {
53
+ public readonly connection: BankrunConnection;
54
+ public readonly context: ProgramTestContext;
55
+ public readonly provider: BankrunProvider;
56
+ public readonly commitment: Commitment = 'confirmed';
57
+
58
+ constructor(context: ProgramTestContext) {
59
+ this.context = context;
60
+ this.provider = new BankrunProvider(context);
61
+ this.connection = new BankrunConnection(
62
+ this.context.banksClient,
63
+ this.context
64
+ );
65
+ }
66
+
67
+ async sendTransaction(
68
+ tx: Transaction,
69
+ additionalSigners?: Keypair[]
70
+ ): Promise<TransactionSignature> {
71
+ tx.recentBlockhash = (await this.getLatestBlockhash()).toString();
72
+ tx.feePayer = this.context.payer.publicKey;
73
+ if (!additionalSigners) {
74
+ additionalSigners = [];
75
+ }
76
+ tx.sign(this.context.payer, ...additionalSigners);
77
+ return await this.connection.sendTransaction(tx);
78
+ }
79
+
80
+ async getMinimumBalanceForRentExemption(_: number): Promise<number> {
81
+ return 10 * LAMPORTS_PER_SOL;
82
+ }
83
+
84
+ async fundKeypair(
85
+ keypair: Keypair | Wallet,
86
+ lamports: number | bigint
87
+ ): Promise<TransactionSignature> {
88
+ const ixs = [
89
+ SystemProgram.transfer({
90
+ fromPubkey: this.context.payer.publicKey,
91
+ toPubkey: keypair.publicKey,
92
+ lamports,
93
+ }),
94
+ ];
95
+ const tx = new Transaction().add(...ixs);
96
+ return await this.sendTransaction(tx);
97
+ }
98
+
99
+ async getLatestBlockhash(): Promise<Blockhash> {
100
+ const blockhash = await this.connection.getLatestBlockhash('finalized');
101
+
102
+ return blockhash.blockhash;
103
+ }
104
+
105
+ printTxLogs(signature: string): void {
106
+ this.connection.printTxLogs(signature);
107
+ }
108
+
109
+ async moveTimeForward(increment: number): Promise<void> {
110
+ const currentClock = await this.context.banksClient.getClock();
111
+ const newUnixTimestamp = currentClock.unixTimestamp + BigInt(increment);
112
+ const newClock = new Clock(
113
+ currentClock.slot,
114
+ currentClock.epochStartTimestamp,
115
+ currentClock.epoch,
116
+ currentClock.leaderScheduleEpoch,
117
+ newUnixTimestamp
118
+ );
119
+ await this.context.setClock(newClock);
120
+ }
121
+ }
122
+
123
+ export class BankrunConnection {
124
+ private readonly _banksClient: BanksClient;
125
+ private readonly context: ProgramTestContext;
126
+ private transactionToMeta: Map<
127
+ TransactionSignature,
128
+ BanksTransactionResultWithMeta
129
+ > = new Map();
130
+ private clock: Clock;
131
+
132
+ private nextClientSubscriptionId = 0;
133
+ private onLogCallbacks = new Map<number, LogsCallback>();
134
+ private onAccountChangeCallbacks = new Map<
135
+ number,
136
+ [PublicKey, AccountChangeCallback]
137
+ >();
138
+
139
+ constructor(banksClient: BanksClient, context: ProgramTestContext) {
140
+ this._banksClient = banksClient;
141
+ this.context = context;
142
+ }
143
+
144
+ getSlot(): Promise<bigint> {
145
+ return this._banksClient.getSlot();
146
+ }
147
+
148
+ toConnection(): SolanaConnection {
149
+ return this as unknown as SolanaConnection;
150
+ }
151
+
152
+ async getTokenAccount(publicKey: PublicKey): Promise<Account> {
153
+ const info = await this.getAccountInfo(publicKey);
154
+ return unpackAccount(publicKey, info, TOKEN_PROGRAM_ID);
155
+ }
156
+
157
+ async getMultipleAccountsInfo(
158
+ publicKeys: PublicKey[],
159
+ _commitmentOrConfig?: Commitment
160
+ ): Promise<AccountInfo<Buffer>[]> {
161
+ const accountInfos = [];
162
+
163
+ for (const publicKey of publicKeys) {
164
+ const accountInfo = await this.getAccountInfo(publicKey);
165
+ accountInfos.push(accountInfo);
166
+ }
167
+
168
+ return accountInfos;
169
+ }
170
+
171
+ async getAccountInfo(
172
+ publicKey: PublicKey
173
+ ): Promise<null | AccountInfo<Buffer>> {
174
+ const parsedAccountInfo = await this.getParsedAccountInfo(publicKey);
175
+ return parsedAccountInfo ? parsedAccountInfo.value : null;
176
+ }
177
+
178
+ async getAccountInfoAndContext(
179
+ publicKey: PublicKey,
180
+ _commitment?: Commitment
181
+ ): Promise<RpcResponseAndContext<null | AccountInfo<Buffer>>> {
182
+ return await this.getParsedAccountInfo(publicKey);
183
+ }
184
+ async sendRawTransaction(
185
+ rawTransaction: Buffer | Uint8Array | Array<number>,
186
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
187
+ _options?: any
188
+ ): Promise<TransactionSignature> {
189
+ const tx = Transaction.from(rawTransaction);
190
+ const signature = await this.sendTransaction(tx);
191
+ return signature;
192
+ }
193
+
194
+ async sendTransaction(tx: Transaction): Promise<TransactionSignature> {
195
+ const banksTransactionMeta = await this._banksClient.tryProcessTransaction(
196
+ tx
197
+ );
198
+ if (banksTransactionMeta.result) {
199
+ throw new Error(banksTransactionMeta.result);
200
+ }
201
+ const signature = bs58.encode(tx.signatures[0].signature);
202
+ this.transactionToMeta.set(signature, banksTransactionMeta);
203
+ let finalizedCount = 0;
204
+ while (finalizedCount < 10) {
205
+ const signatureStatus = (await this.getSignatureStatus(signature)).value
206
+ .confirmationStatus;
207
+ if (signatureStatus.toString() == '"finalized"') {
208
+ finalizedCount += 1;
209
+ }
210
+ }
211
+
212
+ // update the clock slot/timestamp
213
+ // sometimes race condition causes failures so we retry
214
+ try {
215
+ await this.updateSlotAndClock();
216
+ } catch (e) {
217
+ await this.updateSlotAndClock();
218
+ }
219
+
220
+ if (this.onLogCallbacks.size > 0) {
221
+ const transaction = await this.getTransaction(signature);
222
+
223
+ const context = { slot: transaction.slot };
224
+ const logs = {
225
+ logs: transaction.meta.logMessages,
226
+ err: transaction.meta.err,
227
+ signature,
228
+ };
229
+ for (const logCallback of this.onLogCallbacks.values()) {
230
+ logCallback(logs, context);
231
+ }
232
+ }
233
+
234
+ for (const [
235
+ publicKey,
236
+ callback,
237
+ ] of this.onAccountChangeCallbacks.values()) {
238
+ const accountInfo = await this.getParsedAccountInfo(publicKey);
239
+ callback(accountInfo.value, accountInfo.context);
240
+ }
241
+
242
+ return signature;
243
+ }
244
+
245
+ private async updateSlotAndClock() {
246
+ const currentSlot = await this.getSlot();
247
+ const nextSlot = currentSlot + BigInt(1);
248
+ this.context.warpToSlot(nextSlot);
249
+ const currentClock = await this._banksClient.getClock();
250
+ const newClock = new Clock(
251
+ nextSlot,
252
+ currentClock.epochStartTimestamp,
253
+ currentClock.epoch,
254
+ currentClock.leaderScheduleEpoch,
255
+ currentClock.unixTimestamp + BigInt(1)
256
+ );
257
+ this.context.setClock(newClock);
258
+ this.clock = newClock;
259
+ }
260
+
261
+ getTime(): number {
262
+ return Number(this.clock.unixTimestamp);
263
+ }
264
+
265
+ async getParsedAccountInfo(
266
+ publicKey: PublicKey
267
+ ): Promise<RpcResponseAndContext<AccountInfo<Buffer>>> {
268
+ const accountInfoBytes = await this._banksClient.getAccount(publicKey);
269
+ if (accountInfoBytes === null) {
270
+ return {
271
+ context: { slot: Number(await this._banksClient.getSlot()) },
272
+ value: null,
273
+ };
274
+ }
275
+ accountInfoBytes.data = Buffer.from(accountInfoBytes.data);
276
+ const accountInfoBuffer = accountInfoBytes as AccountInfo<Buffer>;
277
+ return {
278
+ context: { slot: Number(await this._banksClient.getSlot()) },
279
+ value: accountInfoBuffer,
280
+ };
281
+ }
282
+
283
+ async getLatestBlockhash(commitment?: Commitment): Promise<
284
+ Readonly<{
285
+ blockhash: string;
286
+ lastValidBlockHeight: number;
287
+ }>
288
+ > {
289
+ const blockhashAndBlockheight = await this._banksClient.getLatestBlockhash(
290
+ commitment
291
+ );
292
+ return {
293
+ blockhash: blockhashAndBlockheight[0],
294
+ lastValidBlockHeight: Number(blockhashAndBlockheight[1]),
295
+ };
296
+ }
297
+
298
+ async getSignatureStatus(
299
+ signature: string,
300
+ _config?: SignatureStatusConfig
301
+ ): Promise<RpcResponseAndContext<null | SignatureStatus>> {
302
+ const transactionStatus = await this._banksClient.getTransactionStatus(
303
+ signature
304
+ );
305
+ if (transactionStatus === null) {
306
+ return {
307
+ context: { slot: Number(await this._banksClient.getSlot()) },
308
+ value: null,
309
+ };
310
+ }
311
+ return {
312
+ context: { slot: Number(await this._banksClient.getSlot()) },
313
+ value: {
314
+ slot: Number(transactionStatus.slot),
315
+ confirmations: Number(transactionStatus.confirmations),
316
+ err: transactionStatus.err,
317
+ confirmationStatus:
318
+ transactionStatus.confirmationStatus as TransactionConfirmationStatus,
319
+ },
320
+ };
321
+ }
322
+
323
+ /**
324
+ * There's really no direct equivalent to getTransaction exposed by SolanaProgramTest, so we do the best that we can here - it's a little hacky.
325
+ */
326
+ async getTransaction(
327
+ signature: string,
328
+ _rawConfig?: GetTransactionConfig | GetVersionedTransactionConfig
329
+ ): Promise<BankrunTransactionRespose | null> {
330
+ const txMeta = this.transactionToMeta.get(
331
+ signature as TransactionSignature
332
+ );
333
+ if (txMeta === undefined) {
334
+ return null;
335
+ }
336
+ const transactionStatus = await this._banksClient.getTransactionStatus(
337
+ signature
338
+ );
339
+ const meta: BankrunTransactionMetaNormalized = {
340
+ logMessages: txMeta.meta.logMessages,
341
+ err: txMeta.result,
342
+ };
343
+ return {
344
+ slot: Number(transactionStatus.slot),
345
+ meta,
346
+ };
347
+ }
348
+
349
+ findComputeUnitConsumption(signature: string): bigint {
350
+ const txMeta = this.transactionToMeta.get(
351
+ signature as TransactionSignature
352
+ );
353
+ if (txMeta === undefined) {
354
+ throw new Error('Transaction not found');
355
+ }
356
+ return txMeta.meta.computeUnitsConsumed;
357
+ }
358
+
359
+ printTxLogs(signature: string): void {
360
+ const txMeta = this.transactionToMeta.get(
361
+ signature as TransactionSignature
362
+ );
363
+ if (txMeta === undefined) {
364
+ throw new Error('Transaction not found');
365
+ }
366
+ console.log(txMeta.meta.logMessages);
367
+ }
368
+
369
+ async simulateTransaction(
370
+ transaction: Transaction | VersionedTransaction,
371
+ _config?: SimulateTransactionConfig
372
+ ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
373
+ const simulationResult = await this._banksClient.simulateTransaction(
374
+ transaction
375
+ );
376
+ const returnDataProgramId =
377
+ simulationResult.meta?.returnData?.programId.toBase58();
378
+ const returnDataNormalized = Buffer.from(
379
+ simulationResult.meta?.returnData?.data
380
+ ).toString('base64');
381
+ const returnData: TransactionReturnData = {
382
+ programId: returnDataProgramId,
383
+ data: [returnDataNormalized, 'base64'],
384
+ };
385
+ return {
386
+ context: { slot: Number(await this._banksClient.getSlot()) },
387
+ value: {
388
+ err: simulationResult.result,
389
+ logs: simulationResult.meta.logMessages,
390
+ accounts: undefined,
391
+ unitsConsumed: Number(simulationResult.meta.computeUnitsConsumed),
392
+ returnData,
393
+ },
394
+ };
395
+ }
396
+
397
+ onSignature(
398
+ signature: string,
399
+ callback: SignatureResultCallback,
400
+ commitment?: Commitment
401
+ ): ClientSubscriptionId {
402
+ const txMeta = this.transactionToMeta.get(
403
+ signature as TransactionSignature
404
+ );
405
+ this._banksClient.getSlot(commitment).then((slot) => {
406
+ if (txMeta) {
407
+ callback({ err: txMeta.result }, { slot: Number(slot) });
408
+ }
409
+ });
410
+ return 0;
411
+ }
412
+
413
+ async removeSignatureListener(_clientSubscriptionId: number): Promise<void> {
414
+ // Nothing actually has to happen here! Pretty cool, huh?
415
+ // This function signature only exists to match the web3js interface
416
+ }
417
+
418
+ onLogs(
419
+ filter: LogsFilter,
420
+ callback: LogsCallback,
421
+ _commitment?: Commitment
422
+ ): ClientSubscriptionId {
423
+ const subscriptId = this.nextClientSubscriptionId;
424
+
425
+ this.onLogCallbacks.set(subscriptId, callback);
426
+
427
+ this.nextClientSubscriptionId += 1;
428
+
429
+ return subscriptId;
430
+ }
431
+
432
+ async removeOnLogsListener(
433
+ clientSubscriptionId: ClientSubscriptionId
434
+ ): Promise<void> {
435
+ this.onLogCallbacks.delete(clientSubscriptionId);
436
+ }
437
+
438
+ onAccountChange(
439
+ publicKey: PublicKey,
440
+ callback: AccountChangeCallback,
441
+ // @ts-ignore
442
+ _commitment?: Commitment
443
+ ): ClientSubscriptionId {
444
+ const subscriptId = this.nextClientSubscriptionId;
445
+
446
+ this.onAccountChangeCallbacks.set(subscriptId, [publicKey, callback]);
447
+
448
+ this.nextClientSubscriptionId += 1;
449
+
450
+ return subscriptId;
451
+ }
452
+
453
+ async removeAccountChangeListener(
454
+ clientSubscriptionId: ClientSubscriptionId
455
+ ): Promise<void> {
456
+ this.onAccountChangeCallbacks.delete(clientSubscriptionId);
457
+ }
458
+
459
+ async getMinimumBalanceForRentExemption(_: number): Promise<number> {
460
+ return 10 * LAMPORTS_PER_SOL;
461
+ }
462
+ }
463
+
464
+ export function asBN(value: number | bigint): BN {
465
+ return new BN(Number(value));
466
+ }
@@ -75,27 +75,32 @@ export class BlockhashSubscriber {
75
75
  }
76
76
 
77
77
  async updateBlockhash() {
78
- const [resp, lastConfirmedBlockHeight] = await Promise.all([
79
- this.connection.getLatestBlockhashAndContext({
80
- commitment: this.commitment,
81
- }),
82
- this.connection.getBlockHeight({ commitment: this.commitment }),
83
- ]);
84
- this.latestBlockHeight = lastConfirmedBlockHeight;
85
- this.latestBlockHeightContext = resp.context;
86
-
87
- // avoid caching duplicate blockhashes
88
- if (this.blockhashes.length > 0) {
89
- if (
90
- resp.value.blockhash ===
91
- this.blockhashes[this.blockhashes.length - 1].blockhash
92
- ) {
93
- return;
78
+ try {
79
+ const [resp, lastConfirmedBlockHeight] = await Promise.all([
80
+ this.connection.getLatestBlockhashAndContext({
81
+ commitment: this.commitment,
82
+ }),
83
+ this.connection.getBlockHeight({ commitment: this.commitment }),
84
+ ]);
85
+ this.latestBlockHeight = lastConfirmedBlockHeight;
86
+ this.latestBlockHeightContext = resp.context;
87
+
88
+ // avoid caching duplicate blockhashes
89
+ if (this.blockhashes.length > 0) {
90
+ if (
91
+ resp.value.blockhash ===
92
+ this.blockhashes[this.blockhashes.length - 1].blockhash
93
+ ) {
94
+ return;
95
+ }
94
96
  }
95
- }
96
97
 
97
- this.blockhashes.push(resp.value);
98
- this.pruneBlockhashes();
98
+ this.blockhashes.push(resp.value);
99
+ } catch (e) {
100
+ console.error('Error updating blockhash:\n', e);
101
+ } finally {
102
+ this.pruneBlockhashes();
103
+ }
99
104
  }
100
105
 
101
106
  async subscribe() {
@@ -597,6 +597,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
597
597
  launchTs: 1718021389000,
598
598
  oracleSource: OracleSource.SWITCHBOARD,
599
599
  },
600
+ {
601
+ fullName: 'ZEX',
602
+ category: ['DEX', 'Solana'],
603
+ symbol: 'ZEX-PERP',
604
+ baseAssetSymbol: 'ZEX',
605
+ marketIndex: 33,
606
+ oracle: new PublicKey('4gdbqxkMrF1bYVeEJKRmTqCCvJjRCZrRhxvriGY6SwLj'),
607
+ launchTs: 1719415157000,
608
+ oracleSource: OracleSource.SWITCHBOARD,
609
+ },
600
610
  ];
601
611
 
602
612
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
@@ -283,6 +283,16 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
283
283
  precisionExp: SIX,
284
284
  launchTs: 1718811089000,
285
285
  },
286
+ {
287
+ symbol: 'JLP',
288
+ marketIndex: 19,
289
+ oracle: new PublicKey('pmHEXBam7kbmCCg5ED5V7RNMN8e34sKu338KeuFAGof'),
290
+ oracleSource: OracleSource.SWITCHBOARD,
291
+ mint: new PublicKey('27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4'),
292
+ precision: new BN(10).pow(SIX),
293
+ precisionExp: SIX,
294
+ launchTs: 1719415157000,
295
+ },
286
296
  ];
287
297
 
288
298
  export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = {
@@ -1527,6 +1527,7 @@ export class DriftClient {
1527
1527
 
1528
1528
  if (params.useMarketLastSlotCache) {
1529
1529
  const lastUserSlot = this.getUserAccountAndSlot()?.slot;
1530
+
1530
1531
  for (const [
1531
1532
  marketIndex,
1532
1533
  slot,
@@ -2769,7 +2770,7 @@ export class DriftClient {
2769
2770
  }
2770
2771
 
2771
2772
  public async sendSignedTx(
2772
- tx: Transaction,
2773
+ tx: Transaction | VersionedTransaction,
2773
2774
  opts?: ConfirmOptions
2774
2775
  ): Promise<TransactionSignature> {
2775
2776
  const { txSig } = await this.sendTransaction(
@@ -4117,6 +4118,10 @@ export class DriftClient {
4117
4118
  const outMarket = this.getSpotMarketAccount(outMarketIndex);
4118
4119
  const inMarket = this.getSpotMarketAccount(inMarketIndex);
4119
4120
 
4121
+ const isExactOut = swapMode === 'ExactOut' || quote.swapMode === 'ExactOut';
4122
+ const amountIn = new BN(quote.inAmount);
4123
+ const exactOutBufferedAmountIn = amountIn.muln(1001).divn(1000); // Add 10bp buffer
4124
+
4120
4125
  if (!quote) {
4121
4126
  const fetchedQuote = await jupiterClient.getQuote({
4122
4127
  inputMint: inMarket.mint,
@@ -4197,7 +4202,7 @@ export class DriftClient {
4197
4202
  const { beginSwapIx, endSwapIx } = await this.getSwapIx({
4198
4203
  outMarketIndex,
4199
4204
  inMarketIndex,
4200
- amountIn: new BN(quote.inAmount),
4205
+ amountIn: isExactOut ? exactOutBufferedAmountIn : amountIn,
4201
4206
  inTokenAccount: inAssociatedTokenAccount,
4202
4207
  outTokenAccount: outAssociatedTokenAccount,
4203
4208
  reduceOnly,
@@ -4663,7 +4668,8 @@ export class DriftClient {
4663
4668
  await TransactionParamProcessor.getTxSimComputeUnits(
4664
4669
  placeAndTakeTxToSim,
4665
4670
  this.connection,
4666
- txParams.computeUnitsBufferMultiplier ?? 1.2
4671
+ txParams.computeUnitsBufferMultiplier ?? 1.2,
4672
+ txParams.lowerBoundCu
4667
4673
  );
4668
4674
 
4669
4675
  if (shouldExitIfSimulationFails && !simulationResult.success) {
@@ -4783,6 +4789,10 @@ export class DriftClient {
4783
4789
  exitEarlyIfSimFails
4784
4790
  );
4785
4791
 
4792
+ if (!txsToSign) {
4793
+ return null;
4794
+ }
4795
+
4786
4796
  const signedTxs = (
4787
4797
  await this.txHandler.getSignedTransactionMap(
4788
4798
  txsToSign,
@@ -6306,7 +6316,8 @@ export class DriftClient {
6306
6316
  public async getAddInsuranceFundStakeIx(
6307
6317
  marketIndex: number,
6308
6318
  amount: BN,
6309
- collateralAccountPublicKey: PublicKey
6319
+ collateralAccountPublicKey: PublicKey,
6320
+ fromSubAccount?: boolean
6310
6321
  ): Promise<TransactionInstruction> {
6311
6322
  const spotMarket = this.getSpotMarketAccount(marketIndex);
6312
6323
  const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
@@ -6316,8 +6327,8 @@ export class DriftClient {
6316
6327
  );
6317
6328
 
6318
6329
  const remainingAccounts = this.getRemainingAccounts({
6319
- userAccounts: [this.getUserAccount()],
6320
- useMarketLastSlotCache: true,
6330
+ userAccounts: fromSubAccount ? [this.getUserAccount()] : [],
6331
+ useMarketLastSlotCache: fromSubAccount ? true : false,
6321
6332
  writableSpotMarketIndexes: [marketIndex],
6322
6333
  });
6323
6334
 
@@ -6416,7 +6427,8 @@ export class DriftClient {
6416
6427
  const addFundsIx = await this.getAddInsuranceFundStakeIx(
6417
6428
  marketIndex,
6418
6429
  amount,
6419
- tokenAccount
6430
+ tokenAccount,
6431
+ fromSubaccount
6420
6432
  );
6421
6433
 
6422
6434
  addIfStakeIxs.push(addFundsIx);
@@ -6456,8 +6468,7 @@ export class DriftClient {
6456
6468
  );
6457
6469
 
6458
6470
  const remainingAccounts = this.getRemainingAccounts({
6459
- userAccounts: [this.getUserAccount()],
6460
- useMarketLastSlotCache: true,
6471
+ userAccounts: [],
6461
6472
  writableSpotMarketIndexes: [marketIndex],
6462
6473
  });
6463
6474
 
@@ -6566,13 +6577,8 @@ export class DriftClient {
6566
6577
  }
6567
6578
  }
6568
6579
 
6569
- const userAccountExists =
6570
- !!this.getUser()?.accountSubscriber?.isSubscribed &&
6571
- (await this.checkIfAccountExists(this.getUser().userAccountPublicKey));
6572
-
6573
6580
  const remainingAccounts = this.getRemainingAccounts({
6574
- userAccounts: userAccountExists ? [this.getUserAccount()] : [],
6575
- useMarketLastSlotCache: false,
6581
+ userAccounts: [],
6576
6582
  writableSpotMarketIndexes: [marketIndex],
6577
6583
  });
6578
6584
 
@@ -45,6 +45,7 @@ export class EventSubscriber {
45
45
 
46
46
  if (this.options.logProviderConfig.type === 'websocket') {
47
47
  this.logProvider = new WebSocketLogProvider(
48
+ // @ts-ignore
48
49
  this.connection,
49
50
  this.address,
50
51
  this.options.commitment,
@@ -52,6 +53,7 @@ export class EventSubscriber {
52
53
  );
53
54
  } else {
54
55
  this.logProvider = new PollingLogProvider(
56
+ // @ts-ignore
55
57
  this.connection,
56
58
  this.address,
57
59
  options.commitment,
@@ -101,6 +103,7 @@ export class EventSubscriber {
101
103
  this.logProvider.eventEmitter.removeAllListeners('reconnect');
102
104
  this.unsubscribe().then(() => {
103
105
  this.logProvider = new PollingLogProvider(
106
+ // @ts-ignore
104
107
  this.connection,
105
108
  this.address,
106
109
  this.options.commitment,
@@ -148,6 +151,7 @@ export class EventSubscriber {
148
151
  }
149
152
 
150
153
  const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
154
+
151
155
  for (const wrappedEvent of wrappedEvents) {
152
156
  this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
153
157
  }
@@ -188,6 +192,7 @@ export class EventSubscriber {
188
192
  const untilTx: TransactionSignature = this.options.untilTx;
189
193
  while (txFetched < this.options.maxTx) {
190
194
  const response = await fetchLogs(
195
+ // @ts-ignore
191
196
  this.connection,
192
197
  this.address,
193
198
  this.options.commitment === 'finalized' ? 'finalized' : 'confirmed',