@drift-labs/sdk 2.54.0-beta.1 → 2.54.0-beta.11

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 (44) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +0 -1
  3. package/lib/constants/perpMarkets.js +20 -0
  4. package/lib/driftClient.d.ts +8 -2
  5. package/lib/driftClient.js +95 -20
  6. package/lib/events/webSocketLogProvider.js +3 -0
  7. package/lib/idl/drift.json +7 -1
  8. package/lib/jupiter/jupiterClient.d.ts +6 -0
  9. package/lib/jupiter/jupiterClient.js +2 -2
  10. package/lib/math/funding.js +24 -1
  11. package/lib/math/oracles.js +2 -2
  12. package/lib/math/superStake.d.ts +51 -0
  13. package/lib/math/superStake.js +10 -2
  14. package/lib/priorityFee/averageOverSlotsStrategy.d.ts +0 -5
  15. package/lib/priorityFee/averageOverSlotsStrategy.js +1 -13
  16. package/lib/priorityFee/maxOverSlotsStrategy.d.ts +0 -5
  17. package/lib/priorityFee/maxOverSlotsStrategy.js +1 -13
  18. package/lib/priorityFee/priorityFeeSubscriber.d.ts +5 -4
  19. package/lib/priorityFee/priorityFeeSubscriber.js +15 -21
  20. package/lib/tx/baseTxSender.js +2 -2
  21. package/lib/tx/retryTxSender.js +4 -21
  22. package/lib/tx/utils.d.ts +5 -1
  23. package/lib/tx/utils.js +20 -1
  24. package/lib/userMap/userMap.js +4 -0
  25. package/package.json +1 -1
  26. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -1
  27. package/src/constants/perpMarkets.ts +20 -0
  28. package/src/driftClient.ts +197 -39
  29. package/src/events/webSocketLogProvider.ts +11 -2
  30. package/src/idl/drift.json +7 -1
  31. package/src/jupiter/jupiterClient.ts +8 -2
  32. package/src/math/funding.ts +28 -1
  33. package/src/math/oracles.ts +2 -2
  34. package/src/math/superStake.ts +60 -1
  35. package/src/priorityFee/averageOverSlotsStrategy.ts +1 -16
  36. package/src/priorityFee/maxOverSlotsStrategy.ts +1 -16
  37. package/src/priorityFee/priorityFeeSubscriber.ts +22 -26
  38. package/src/tx/baseTxSender.ts +3 -2
  39. package/src/tx/retryTxSender.ts +5 -23
  40. package/src/tx/utils.ts +32 -0
  41. package/src/userMap/userMap.ts +3 -0
  42. package/tests/amm/test.ts +275 -2
  43. package/tests/dlob/test.ts +2 -2
  44. package/tests/tx/priorityFeeStrategy.ts +2 -2
@@ -158,14 +158,14 @@ class BaseTxSender {
158
158
  async confirmTransactionPolling(signature, commitment = 'finalized') {
159
159
  var _a;
160
160
  let totalTime = 0;
161
- let backoffTime = 250;
161
+ let backoffTime = 400; // approx block time
162
162
  while (totalTime < this.timeout) {
163
+ await new Promise((resolve) => setTimeout(resolve, backoffTime));
163
164
  const response = await this.connection.getSignatureStatus(signature);
164
165
  const result = response && ((_a = response.value) === null || _a === void 0 ? void 0 : _a[0]);
165
166
  if (result && result.confirmationStatus === commitment) {
166
167
  return { context: result.context, value: { err: null } };
167
168
  }
168
- await new Promise((resolve) => setTimeout(resolve, backoffTime));
169
169
  totalTime += backoffTime;
170
170
  backoffTime = Math.min(backoffTime * 2, 5000);
171
171
  }
@@ -32,15 +32,8 @@ class RetryTxSender extends baseTxSender_1.BaseTxSender {
32
32
  }
33
33
  async sendRawTransaction(rawTransaction, opts) {
34
34
  const startTime = this.getTimestamp();
35
- let txid;
36
- try {
37
- txid = await this.connection.sendRawTransaction(rawTransaction, opts);
38
- this.sendToAdditionalConnections(rawTransaction, opts);
39
- }
40
- catch (e) {
41
- console.error(e);
42
- throw e;
43
- }
35
+ const txid = await this.connection.sendRawTransaction(rawTransaction, opts);
36
+ this.sendToAdditionalConnections(rawTransaction, opts);
44
37
  let done = false;
45
38
  const resolveReference = {
46
39
  resolve: undefined,
@@ -65,18 +58,8 @@ class RetryTxSender extends baseTxSender_1.BaseTxSender {
65
58
  }
66
59
  }
67
60
  })();
68
- let slot;
69
- try {
70
- const result = await this.confirmTransaction(txid, opts.commitment);
71
- slot = result.context.slot;
72
- }
73
- catch (e) {
74
- console.error(e);
75
- throw e;
76
- }
77
- finally {
78
- stopWaiting();
79
- }
61
+ const result = await this.confirmTransaction(txid, opts.commitment);
62
+ const slot = result.context.slot;
80
63
  return { txSig: txid, slot };
81
64
  }
82
65
  }
package/lib/tx/utils.d.ts CHANGED
@@ -1,2 +1,6 @@
1
- import { Transaction, TransactionInstruction } from '@solana/web3.js';
1
+ import { Wallet } from '@coral-xyz/anchor';
2
+ import { Transaction, TransactionInstruction, VersionedTransaction } from '@solana/web3.js';
2
3
  export declare function wrapInTx(instruction: TransactionInstruction, computeUnits?: number, computeUnitsPrice?: number): Transaction;
4
+ export declare function getSignedTransactionMap(wallet: Wallet, txsToSign: (Transaction | VersionedTransaction | undefined)[], keys: string[]): Promise<{
5
+ [key: string]: Transaction | VersionedTransaction | undefined;
6
+ }>;
package/lib/tx/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.wrapInTx = void 0;
3
+ exports.getSignedTransactionMap = exports.wrapInTx = void 0;
4
4
  const web3_js_1 = require("@solana/web3.js");
5
5
  const COMPUTE_UNITS_DEFAULT = 200000;
6
6
  function wrapInTx(instruction, computeUnits = 600000, computeUnitsPrice = 0) {
@@ -18,3 +18,22 @@ function wrapInTx(instruction, computeUnits = 600000, computeUnitsPrice = 0) {
18
18
  return tx.add(instruction);
19
19
  }
20
20
  exports.wrapInTx = wrapInTx;
21
+ /* Helper function for signing multiple transactions where some may be undefined and mapping the output */
22
+ async function getSignedTransactionMap(wallet, txsToSign, keys) {
23
+ const signedTxMap = {};
24
+ const keysWithTx = [];
25
+ txsToSign.forEach((tx, index) => {
26
+ if (tx == undefined) {
27
+ signedTxMap[keys[index]] = undefined;
28
+ }
29
+ else {
30
+ keysWithTx.push(keys[index]);
31
+ }
32
+ });
33
+ const signedTxs = await wallet.signAllTransactions(txsToSign.filter((tx) => tx !== undefined));
34
+ signedTxs.forEach((signedTx, index) => {
35
+ signedTxMap[keysWithTx[index]] = signedTx;
36
+ });
37
+ return signedTxMap;
38
+ }
39
+ exports.getSignedTransactionMap = getSignedTransactionMap;
@@ -236,6 +236,10 @@ class UserMap {
236
236
  await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
237
237
  this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
238
238
  }
239
+ else {
240
+ const userAccount = this.decode('User', buffer);
241
+ this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
242
+ }
239
243
  // give event loop a chance to breathe
240
244
  await new Promise((resolve) => setTimeout(resolve, 0));
241
245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.54.0-beta.1",
3
+ "version": "2.54.0-beta.11",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -54,7 +54,6 @@ export class PollingInsuranceFundStakeAccountSubscriber
54
54
 
55
55
  await this.addToAccountLoader();
56
56
 
57
- await this.fetchIfUnloaded();
58
57
  if (this.doesAccountExist()) {
59
58
  this.eventEmitter.emit('update');
60
59
  }
@@ -234,6 +234,16 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
234
234
  launchTs: 1703173331000,
235
235
  oracleSource: OracleSource.PYTH,
236
236
  },
237
+ {
238
+ fullName: 'AVAX',
239
+ category: ['Rollup', 'Infra'],
240
+ symbol: 'AVAX-PERP',
241
+ baseAssetSymbol: 'AVAX',
242
+ marketIndex: 22,
243
+ oracle: new PublicKey('FVb5h1VmHPfVb1RfqZckchq18GxRv4iKt8T4eVTQAqdz'),
244
+ launchTs: 1704209558000,
245
+ oracleSource: OracleSource.PYTH,
246
+ },
237
247
  ];
238
248
 
239
249
  export const MainnetPerpMarkets: PerpMarketConfig[] = [
@@ -457,6 +467,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
457
467
  launchTs: 1703173331000,
458
468
  oracleSource: OracleSource.PYTH,
459
469
  },
470
+ {
471
+ fullName: 'AVAX',
472
+ category: ['Rollup', 'Infra'],
473
+ symbol: 'AVAX-PERP',
474
+ baseAssetSymbol: 'AVAX',
475
+ marketIndex: 22,
476
+ oracle: new PublicKey('Ax9ujW5B9oqcv59N8m6f1BpTBq2rGeGaBcpKjC5UYsXU'),
477
+ launchTs: 1704209558000,
478
+ oracleSource: OracleSource.PYTH,
479
+ },
460
480
  ];
461
481
 
462
482
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
@@ -88,7 +88,7 @@ import {
88
88
  DataAndSlot,
89
89
  } from './accounts/types';
90
90
  import { TxSender, TxSigAndSlot } from './tx/types';
91
- import { wrapInTx } from './tx/utils';
91
+ import { getSignedTransactionMap, wrapInTx } from './tx/utils';
92
92
  import {
93
93
  BASE_PRECISION,
94
94
  PRICE_PRECISION,
@@ -2565,33 +2565,26 @@ export class DriftClient {
2565
2565
  txParams?: TxParams,
2566
2566
  bracketOrdersParams = new Array<OptionalOrderParams>(),
2567
2567
  referrerInfo?: ReferrerInfo,
2568
- cancelExistingOrders?: boolean
2568
+ cancelExistingOrders?: boolean,
2569
+ settlePnl?: boolean
2569
2570
  ): Promise<{
2570
2571
  txSig: TransactionSignature;
2571
2572
  signedFillTx?: Transaction;
2572
2573
  signedCancelExistingOrdersTx?: Transaction;
2574
+ signedSettlePnlTx?: Transaction;
2573
2575
  }> {
2574
2576
  const marketIndex = orderParams.marketIndex;
2575
2577
  const orderId = userAccount.nextOrderId;
2576
- const bracketOrderIxs = [];
2577
2578
 
2578
- const placePerpOrderIx = await this.getPlacePerpOrderIx(
2579
- orderParams,
2579
+ const ordersIx = await this.getPlaceOrdersIx(
2580
+ [orderParams, ...bracketOrdersParams],
2580
2581
  userAccount.subAccountId
2581
2582
  );
2582
2583
 
2583
- for (const bracketOrderParams of bracketOrdersParams) {
2584
- const placeBracketOrderIx = await this.getPlacePerpOrderIx(
2585
- bracketOrderParams,
2586
- userAccount.subAccountId
2587
- );
2588
- bracketOrderIxs.push(placeBracketOrderIx);
2589
- }
2590
-
2591
- let cancelOrdersIx: TransactionInstruction;
2592
- let cancelExistingOrdersTx: Transaction;
2584
+ /* Cancel open orders in market if requested */
2585
+ let cancelExistingOrdersTx;
2593
2586
  if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
2594
- cancelOrdersIx = await this.getCancelOrdersIx(
2587
+ const cancelOrdersIx = await this.getCancelOrdersIx(
2595
2588
  orderParams.marketType,
2596
2589
  orderParams.marketIndex,
2597
2590
  null,
@@ -2606,10 +2599,27 @@ export class DriftClient {
2606
2599
  );
2607
2600
  }
2608
2601
 
2602
+ /* Settle PnL after fill if requested */
2603
+ let settlePnlTx;
2604
+ if (settlePnl && isVariant(orderParams.marketType, 'perp')) {
2605
+ const settlePnlIx = await this.settlePNLIx(
2606
+ userAccountPublicKey,
2607
+ userAccount,
2608
+ marketIndex
2609
+ );
2610
+
2611
+ //@ts-ignore
2612
+ settlePnlTx = await this.buildTransaction(
2613
+ [settlePnlIx],
2614
+ txParams,
2615
+ this.txVersion
2616
+ );
2617
+ }
2618
+
2609
2619
  // use versioned transactions if there is a lookup table account and wallet is compatible
2610
2620
  if (this.txVersion === 0) {
2611
2621
  const versionedMarketOrderTx = await this.buildTransaction(
2612
- [placePerpOrderIx].concat(bracketOrderIxs),
2622
+ ordersIx,
2613
2623
  txParams,
2614
2624
  0
2615
2625
  );
@@ -2632,17 +2642,31 @@ export class DriftClient {
2632
2642
  0
2633
2643
  );
2634
2644
 
2635
- const [
2645
+ const allPossibleTxs = [
2646
+ versionedMarketOrderTx,
2647
+ versionedFillTx,
2648
+ cancelExistingOrdersTx,
2649
+ settlePnlTx,
2650
+ ];
2651
+ const txKeys = [
2652
+ 'signedVersionedMarketOrderTx',
2653
+ 'signedVersionedFillTx',
2654
+ 'signedCancelExistingOrdersTx',
2655
+ 'signedSettlePnlTx',
2656
+ ];
2657
+
2658
+ const {
2636
2659
  signedVersionedMarketOrderTx,
2637
2660
  signedVersionedFillTx,
2638
2661
  signedCancelExistingOrdersTx,
2639
- ] = await this.provider.wallet.signAllTransactions(
2640
- [
2641
- versionedMarketOrderTx,
2642
- versionedFillTx,
2643
- cancelExistingOrdersTx,
2644
- ].filter((tx) => tx !== undefined)
2662
+ signedSettlePnlTx,
2663
+ } = await getSignedTransactionMap(
2664
+ //@ts-ignore
2665
+ this.provider.wallet,
2666
+ allPossibleTxs,
2667
+ txKeys
2645
2668
  );
2669
+
2646
2670
  const { txSig, slot } = await this.txSender.sendRawTransaction(
2647
2671
  signedVersionedMarketOrderTx.serialize(),
2648
2672
  this.opts
@@ -2655,18 +2679,16 @@ export class DriftClient {
2655
2679
  signedFillTx: signedVersionedFillTx,
2656
2680
  // @ts-ignore
2657
2681
  signedCancelExistingOrdersTx,
2682
+ // @ts-ignore
2683
+ signedSettlePnlTx,
2658
2684
  };
2659
2685
  } else {
2660
2686
  const marketOrderTx = wrapInTx(
2661
- placePerpOrderIx,
2687
+ ordersIx,
2662
2688
  txParams?.computeUnits,
2663
2689
  txParams?.computeUnitsPrice
2664
2690
  );
2665
2691
 
2666
- if (bracketOrderIxs.length > 0) {
2667
- marketOrderTx.add(...bracketOrderIxs);
2668
- }
2669
-
2670
2692
  // Apply the latest blockhash to the txs so that we can sign before sending them
2671
2693
  const currentBlockHash = (
2672
2694
  await this.connection.getLatestBlockhash('finalized')
@@ -2680,12 +2702,33 @@ export class DriftClient {
2680
2702
  cancelExistingOrdersTx.feePayer = userAccount.authority;
2681
2703
  }
2682
2704
 
2683
- const [signedMarketOrderTx, signedCancelExistingOrdersTx] =
2684
- await this.provider.wallet.signAllTransactions(
2685
- [marketOrderTx, cancelExistingOrdersTx].filter(
2686
- (tx) => tx !== undefined
2687
- )
2688
- );
2705
+ if (settlePnlTx) {
2706
+ settlePnlTx.recentBlockhash = currentBlockHash;
2707
+ settlePnlTx.feePayer = userAccount.authority;
2708
+ }
2709
+
2710
+ const allPossibleTxs = [
2711
+ marketOrderTx,
2712
+ cancelExistingOrdersTx,
2713
+ settlePnlTx,
2714
+ ];
2715
+ const txKeys = [
2716
+ 'signedMarketOrderTx',
2717
+ 'signedCancelExistingOrdersTx',
2718
+ 'signedSettlePnlTx',
2719
+ ];
2720
+
2721
+ const {
2722
+ signedMarketOrderTx,
2723
+ signedCancelExistingOrdersTx,
2724
+ signedSettlePnlTx,
2725
+ } = await getSignedTransactionMap(
2726
+ //@ts-ignore
2727
+ this.provider.wallet,
2728
+ allPossibleTxs,
2729
+ txKeys
2730
+ );
2731
+
2689
2732
  const { txSig, slot } = await this.sendTransaction(
2690
2733
  signedMarketOrderTx,
2691
2734
  [],
@@ -2694,7 +2737,14 @@ export class DriftClient {
2694
2737
  );
2695
2738
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2696
2739
 
2697
- return { txSig, signedFillTx: undefined, signedCancelExistingOrdersTx };
2740
+ return {
2741
+ txSig,
2742
+ signedFillTx: undefined,
2743
+ //@ts-ignore
2744
+ signedCancelExistingOrdersTx,
2745
+ //@ts-ignore
2746
+ signedSettlePnlTx,
2747
+ };
2698
2748
  }
2699
2749
  }
2700
2750
 
@@ -3093,7 +3143,7 @@ export class DriftClient {
3093
3143
  }
3094
3144
 
3095
3145
  public async getPlaceOrdersIx(
3096
- params: OrderParams[],
3146
+ params: OptionalOrderParams[],
3097
3147
  subAccountId?: number
3098
3148
  ): Promise<TransactionInstruction> {
3099
3149
  const user = await this.getUserAccountPublicKey(subAccountId);
@@ -3118,7 +3168,9 @@ export class DriftClient {
3118
3168
  useMarketLastSlotCache: true,
3119
3169
  });
3120
3170
 
3121
- return await this.program.instruction.placeOrders(params, {
3171
+ const formattedParams = params.map((item) => getOrderParams(item));
3172
+
3173
+ return await this.program.instruction.placeOrders(formattedParams, {
3122
3174
  accounts: {
3123
3175
  state: await this.getStatePublicKey(),
3124
3176
  user,
@@ -3868,7 +3920,6 @@ export class DriftClient {
3868
3920
  slippageBps,
3869
3921
  swapMode,
3870
3922
  onlyDirectRoutes,
3871
- excludeDexes: ['Raydium CLMM'], // temp exclude to workaround bug with raydium clmm
3872
3923
  });
3873
3924
 
3874
3925
  quote = fetchedQuote;
@@ -4335,6 +4386,113 @@ export class DriftClient {
4335
4386
  return txSig;
4336
4387
  }
4337
4388
 
4389
+ public async placeAndTakePerpWithAdditionalOrders(
4390
+ orderParams: OptionalOrderParams,
4391
+ makerInfo?: MakerInfo | MakerInfo[],
4392
+ referrerInfo?: ReferrerInfo,
4393
+ bracketOrdersParams = new Array<OptionalOrderParams>(),
4394
+ txParams?: TxParams,
4395
+ subAccountId?: number,
4396
+ cancelExistingOrders?: boolean,
4397
+ settlePnl?: boolean
4398
+ ): Promise<{
4399
+ txSig: TransactionSignature;
4400
+ signedCancelExistingOrdersTx?: Transaction;
4401
+ signedSettlePnlTx?: Transaction;
4402
+ }> {
4403
+ let cancelExistingOrdersTx: Transaction;
4404
+ if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
4405
+ const cancelOrdersIx = await this.getCancelOrdersIx(
4406
+ orderParams.marketType,
4407
+ orderParams.marketIndex,
4408
+ null,
4409
+ subAccountId
4410
+ );
4411
+
4412
+ //@ts-ignore
4413
+ cancelExistingOrdersTx = await this.buildTransaction(
4414
+ [cancelOrdersIx],
4415
+ txParams,
4416
+ this.txVersion
4417
+ );
4418
+ }
4419
+
4420
+ /* Settle PnL after fill if requested */
4421
+ let settlePnlTx: Transaction;
4422
+ if (settlePnl && isVariant(orderParams.marketType, 'perp')) {
4423
+ const userAccountPublicKey = await this.getUserAccountPublicKey(
4424
+ subAccountId
4425
+ );
4426
+
4427
+ const settlePnlIx = await this.settlePNLIx(
4428
+ userAccountPublicKey,
4429
+ this.getUserAccount(subAccountId),
4430
+ orderParams.marketIndex
4431
+ );
4432
+
4433
+ //@ts-ignore
4434
+ settlePnlTx = await this.buildTransaction(
4435
+ [settlePnlIx],
4436
+ txParams,
4437
+ this.txVersion
4438
+ );
4439
+ }
4440
+
4441
+ const ixs = [];
4442
+
4443
+ const placeAndTakeIx = await this.getPlaceAndTakePerpOrderIx(
4444
+ orderParams,
4445
+ makerInfo,
4446
+ referrerInfo,
4447
+ subAccountId
4448
+ );
4449
+
4450
+ ixs.push(placeAndTakeIx);
4451
+
4452
+ if (bracketOrdersParams.length > 0) {
4453
+ const bracketOrdersIx = await this.getPlaceOrdersIx(
4454
+ bracketOrdersParams,
4455
+ subAccountId
4456
+ );
4457
+ ixs.push(bracketOrdersIx);
4458
+ }
4459
+
4460
+ const placeAndTakeTx = await this.buildTransaction(ixs, txParams);
4461
+
4462
+ const allPossibleTxs = [
4463
+ placeAndTakeTx,
4464
+ cancelExistingOrdersTx,
4465
+ settlePnlTx,
4466
+ ];
4467
+ const txKeys = [
4468
+ 'signedPlaceAndTakeTx',
4469
+ 'signedCancelExistingOrdersTx',
4470
+ 'signedSettlePnlTx',
4471
+ ];
4472
+
4473
+ const {
4474
+ signedPlaceAndTakeTx,
4475
+ signedCancelExistingOrdersTx,
4476
+ signedSettlePnlTx,
4477
+ } = await getSignedTransactionMap(
4478
+ //@ts-ignore
4479
+ this.provider.wallet,
4480
+ allPossibleTxs,
4481
+ txKeys
4482
+ );
4483
+
4484
+ const { txSig, slot } = await this.sendTransaction(
4485
+ signedPlaceAndTakeTx,
4486
+ [],
4487
+ this.opts,
4488
+ true
4489
+ );
4490
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
4491
+
4492
+ //@ts-ignore
4493
+ return { txSig, signedCancelExistingOrdersTx, signedSettlePnlTx };
4494
+ }
4495
+
4338
4496
  public async getPlaceAndTakePerpOrderIx(
4339
4497
  orderParams: OptionalOrderParams,
4340
4498
  makerInfo?: MakerInfo | MakerInfo[],
@@ -1,5 +1,11 @@
1
1
  import { LogProvider, logProviderCallback } from './types';
2
- import { Commitment, Connection, PublicKey } from '@solana/web3.js';
2
+ import {
3
+ Commitment,
4
+ Connection,
5
+ Context,
6
+ Logs,
7
+ PublicKey,
8
+ } from '@solana/web3.js';
3
9
  import { EventEmitter } from 'events';
4
10
 
5
11
  export class WebSocketLogProvider implements LogProvider {
@@ -45,7 +51,7 @@ export class WebSocketLogProvider implements LogProvider {
45
51
  public setSubscription(callback: logProviderCallback): void {
46
52
  this.subscriptionId = this.connection.onLogs(
47
53
  this.address,
48
- (logs, ctx) => {
54
+ (logs: Logs, ctx: Context) => {
49
55
  if (this.resubTimeoutMs && !this.isUnsubscribing) {
50
56
  this.receivingData = true;
51
57
  clearTimeout(this.timeoutId);
@@ -55,6 +61,9 @@ export class WebSocketLogProvider implements LogProvider {
55
61
  }
56
62
  this.reconnectAttempts = 0;
57
63
  }
64
+ if (logs.err !== null) {
65
+ return;
66
+ }
58
67
  callback(logs.signature, ctx.slot, logs.logs, undefined);
59
68
  },
60
69
  this.commitment
@@ -8524,7 +8524,13 @@
8524
8524
  "kind": "enum",
8525
8525
  "variants": [
8526
8526
  {
8527
- "name": "Standard"
8527
+ "name": "Standard",
8528
+ "fields": [
8529
+ {
8530
+ "name": "track_open_orders_fraction",
8531
+ "type": "bool"
8532
+ }
8533
+ ]
8528
8534
  },
8529
8535
  {
8530
8536
  "name": "Liquidation",
@@ -210,6 +210,12 @@ export interface QuoteResponse {
210
210
  * @memberof QuoteResponse
211
211
  */
212
212
  timeTaken?: number;
213
+ /**
214
+ *
215
+ * @type {string}
216
+ * @memberof QuoteResponse
217
+ */
218
+ error?: string;
213
219
  }
214
220
 
215
221
  export class JupiterClient {
@@ -279,7 +285,7 @@ export class JupiterClient {
279
285
  slippageBps = 50,
280
286
  swapMode = 'ExactIn',
281
287
  onlyDirectRoutes = false,
282
- excludeDexes = [],
288
+ excludeDexes,
283
289
  }: {
284
290
  inputMint: PublicKey;
285
291
  outputMint: PublicKey;
@@ -298,7 +304,7 @@ export class JupiterClient {
298
304
  swapMode,
299
305
  onlyDirectRoutes: onlyDirectRoutes.toString(),
300
306
  maxAccounts: maxAccounts.toString(),
301
- excludeDexes: excludeDexes.join(','),
307
+ ...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
302
308
  }).toString();
303
309
  const quote = await (await fetch(`${this.url}/v6/quote?${params}`)).json();
304
310
  return quote;
@@ -11,6 +11,7 @@ import { PerpMarketAccount, isVariant } from '../types';
11
11
  import { OraclePriceData } from '../oracles/types';
12
12
  import { calculateBidAskPrice } from './amm';
13
13
  import { calculateLiveOracleTwap } from './oracles';
14
+ import { clampBN } from './utils';
14
15
 
15
16
  function calculateLiveMarkTwap(
16
17
  market: PerpMarketAccount,
@@ -160,8 +161,15 @@ export async function calculateAllEstimatedFundingRate(
160
161
  const twapSpreadWithOffset = twapSpread.add(
161
162
  oracleTwap.abs().div(FUNDING_RATE_OFFSET_DENOMINATOR)
162
163
  );
164
+ const maxSpread = getMaxPriceDivergenceForFundingRate(market, oracleTwap);
163
165
 
164
- const twapSpreadPct = twapSpreadWithOffset
166
+ const clampedSpreadWithOffset = clampBN(
167
+ twapSpreadWithOffset,
168
+ maxSpread.mul(new BN(-1)),
169
+ maxSpread
170
+ );
171
+
172
+ const twapSpreadPct = clampedSpreadWithOffset
165
173
  .mul(PRICE_PRECISION)
166
174
  .mul(new BN(100))
167
175
  .div(oracleTwap);
@@ -234,6 +242,25 @@ export async function calculateAllEstimatedFundingRate(
234
242
  return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
235
243
  }
236
244
 
245
+ function getMaxPriceDivergenceForFundingRate(
246
+ market: PerpMarketAccount,
247
+ oracleTwap: BN
248
+ ) {
249
+ if (isVariant(market.contractTier, 'a')) {
250
+ return oracleTwap.divn(33);
251
+ } else if (isVariant(market.contractTier, 'b')) {
252
+ return oracleTwap.divn(33);
253
+ } else if (isVariant(market.contractTier, 'c')) {
254
+ return oracleTwap.divn(20);
255
+ } else if (isVariant(market.contractTier, 'speculative')) {
256
+ return oracleTwap.divn(10);
257
+ } else if (isVariant(market.contractTier, 'isolated')) {
258
+ return oracleTwap.divn(10);
259
+ } else {
260
+ return oracleTwap.divn(10);
261
+ }
262
+ }
263
+
237
264
  /**
238
265
  *
239
266
  * @param market
@@ -47,8 +47,8 @@ export function isOracleValid(
47
47
  .div(oraclePriceData.price)
48
48
  .gt(oracleGuardRails.validity.confidenceIntervalMaxSize);
49
49
 
50
- const oracleIsStale = oraclePriceData.slot
51
- .sub(new BN(slot))
50
+ const oracleIsStale = new BN(slot)
51
+ .sub(oraclePriceData.slot)
52
52
  .gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
53
53
 
54
54
  return !(