@drift-labs/sdk 2.54.0-beta.3 → 2.54.0-beta.5

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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.54.0-beta.3
1
+ 2.54.0-beta.5
@@ -22,7 +22,6 @@ class PollingInsuranceFundStakeAccountSubscriber {
22
22
  };
23
23
  }
24
24
  await this.addToAccountLoader();
25
- await this.fetchIfUnloaded();
26
25
  if (this.doesAccountExist()) {
27
26
  this.eventEmitter.emit('update');
28
27
  }
@@ -310,7 +310,7 @@ export declare class DriftClient {
310
310
  direction?: PositionDirection;
311
311
  }, placeOrderParams: OrderParams[], txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
312
312
  placeOrders(params: OrderParams[], txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
313
- getPlaceOrdersIx(params: OrderParams[], subAccountId?: number): Promise<TransactionInstruction>;
313
+ getPlaceOrdersIx(params: OptionalOrderParams[], subAccountId?: number): Promise<TransactionInstruction>;
314
314
  fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams, fillerPublicKey?: number): Promise<TransactionSignature>;
315
315
  getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, fillerSubAccountId?: number): Promise<TransactionInstruction>;
316
316
  getRevertFillIx(fillerPublicKey?: PublicKey): Promise<TransactionInstruction>;
@@ -427,6 +427,10 @@ export declare class DriftClient {
427
427
  updateUserOpenOrdersCount(userAccountPublicKey: PublicKey, user: UserAccount, txParams?: TxParams, fillerPublicKey?: PublicKey): Promise<TransactionSignature>;
428
428
  getUpdateUserOpenOrdersCountIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, fillerPublicKey?: PublicKey): Promise<TransactionInstruction>;
429
429
  placeAndTakePerpOrder(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
430
+ placeAndTakePerpWithAdditionalOrders(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, bracketOrdersParams?: OptionalOrderParams[], txParams?: TxParams, subAccountId?: number, cancelExistingOrders?: boolean): Promise<{
431
+ txSig: TransactionSignature;
432
+ signedCancelExistingOrdersTx?: Transaction;
433
+ }>;
430
434
  getPlaceAndTakePerpOrderIx(orderParams: OptionalOrderParams, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
431
435
  placeAndMakePerpOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number): Promise<TransactionSignature>;
432
436
  getPlaceAndMakePerpOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo, subAccountId?: number): Promise<TransactionInstruction>;
@@ -1422,12 +1422,7 @@ class DriftClient {
1422
1422
  async sendMarketOrderAndGetSignedFillTx(orderParams, userAccountPublicKey, userAccount, makerInfo, txParams, bracketOrdersParams = new Array(), referrerInfo, cancelExistingOrders) {
1423
1423
  const marketIndex = orderParams.marketIndex;
1424
1424
  const orderId = userAccount.nextOrderId;
1425
- const bracketOrderIxs = [];
1426
- const placePerpOrderIx = await this.getPlacePerpOrderIx(orderParams, userAccount.subAccountId);
1427
- for (const bracketOrderParams of bracketOrdersParams) {
1428
- const placeBracketOrderIx = await this.getPlacePerpOrderIx(bracketOrderParams, userAccount.subAccountId);
1429
- bracketOrderIxs.push(placeBracketOrderIx);
1430
- }
1425
+ const ordersIx = await this.getPlaceOrdersIx([orderParams, ...bracketOrdersParams], userAccount.subAccountId);
1431
1426
  let cancelOrdersIx;
1432
1427
  let cancelExistingOrdersTx;
1433
1428
  if (cancelExistingOrders && (0, types_1.isVariant)(orderParams.marketType, 'perp')) {
@@ -1437,7 +1432,7 @@ class DriftClient {
1437
1432
  }
1438
1433
  // use versioned transactions if there is a lookup table account and wallet is compatible
1439
1434
  if (this.txVersion === 0) {
1440
- const versionedMarketOrderTx = await this.buildTransaction([placePerpOrderIx].concat(bracketOrderIxs), txParams, 0);
1435
+ const versionedMarketOrderTx = await this.buildTransaction(ordersIx, txParams, 0);
1441
1436
  const fillPerpOrderIx = await this.getFillPerpOrderIx(userAccountPublicKey, userAccount, {
1442
1437
  orderId,
1443
1438
  marketIndex,
@@ -1459,10 +1454,7 @@ class DriftClient {
1459
1454
  };
1460
1455
  }
1461
1456
  else {
1462
- const marketOrderTx = (0, utils_1.wrapInTx)(placePerpOrderIx, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnits, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnitsPrice);
1463
- if (bracketOrderIxs.length > 0) {
1464
- marketOrderTx.add(...bracketOrderIxs);
1465
- }
1457
+ const marketOrderTx = (0, utils_1.wrapInTx)(ordersIx, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnits, txParams === null || txParams === void 0 ? void 0 : txParams.computeUnitsPrice);
1466
1458
  // Apply the latest blockhash to the txs so that we can sign before sending them
1467
1459
  const currentBlockHash = (await this.connection.getLatestBlockhash('finalized')).blockhash;
1468
1460
  marketOrderTx.recentBlockhash = currentBlockHash;
@@ -1708,7 +1700,8 @@ class DriftClient {
1708
1700
  readableSpotMarketIndexes,
1709
1701
  useMarketLastSlotCache: true,
1710
1702
  });
1711
- return await this.program.instruction.placeOrders(params, {
1703
+ const formattedParams = params.map((item) => (0, orderParams_1.getOrderParams)(item));
1704
+ return await this.program.instruction.placeOrders(formattedParams, {
1712
1705
  accounts: {
1713
1706
  state: await this.getStatePublicKey(),
1714
1707
  user,
@@ -2168,7 +2161,6 @@ class DriftClient {
2168
2161
  slippageBps,
2169
2162
  swapMode,
2170
2163
  onlyDirectRoutes,
2171
- excludeDexes: ['Raydium CLMM'], // temp exclude to workaround bug with raydium clmm
2172
2164
  });
2173
2165
  quote = fetchedQuote;
2174
2166
  }
@@ -2412,6 +2404,25 @@ class DriftClient {
2412
2404
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2413
2405
  return txSig;
2414
2406
  }
2407
+ async placeAndTakePerpWithAdditionalOrders(orderParams, makerInfo, referrerInfo, bracketOrdersParams = new Array(), txParams, subAccountId, cancelExistingOrders) {
2408
+ let signedCancelExistingOrdersTx;
2409
+ if (cancelExistingOrders && (0, types_1.isVariant)(orderParams.marketType, 'perp')) {
2410
+ const cancelOrdersIx = await this.getCancelOrdersIx(orderParams.marketType, orderParams.marketIndex, null, subAccountId);
2411
+ const cancelExistingOrdersTx = await this.buildTransaction([cancelOrdersIx], txParams, this.txVersion);
2412
+ // @ts-ignore
2413
+ signedCancelExistingOrdersTx = await this.provider.wallet.signTransaction(cancelExistingOrdersTx);
2414
+ }
2415
+ const ixs = [];
2416
+ const placeAndTakeIx = await this.getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo, subAccountId);
2417
+ ixs.push(placeAndTakeIx);
2418
+ if (bracketOrdersParams.length > 0) {
2419
+ const bracketOrdersIx = await this.getPlaceOrdersIx(bracketOrdersParams, subAccountId);
2420
+ ixs.push(bracketOrdersIx);
2421
+ }
2422
+ const { txSig, slot } = await this.sendTransaction(await this.buildTransaction(ixs, txParams), [], this.opts);
2423
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2424
+ return { txSig, signedCancelExistingOrdersTx };
2425
+ }
2415
2426
  async getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo, subAccountId) {
2416
2427
  orderParams = (0, orderParams_1.getOrderParams)(orderParams, { marketType: types_1.MarketType.PERP });
2417
2428
  const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
@@ -195,6 +195,12 @@ export interface QuoteResponse {
195
195
  * @memberof QuoteResponse
196
196
  */
197
197
  timeTaken?: number;
198
+ /**
199
+ *
200
+ * @type {string}
201
+ * @memberof QuoteResponse
202
+ */
203
+ error?: string;
198
204
  }
199
205
  export declare class JupiterClient {
200
206
  url: string;
@@ -44,7 +44,7 @@ class JupiterClient {
44
44
  * @param onlyDirectRoutes whether to only return direct routes
45
45
  */
46
46
  async getQuote({ inputMint, outputMint, amount, maxAccounts = 50, // 50 is an estimated amount with buffer
47
- slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes = [], }) {
47
+ slippageBps = 50, swapMode = 'ExactIn', onlyDirectRoutes = false, excludeDexes, }) {
48
48
  const params = new URLSearchParams({
49
49
  inputMint: inputMint.toString(),
50
50
  outputMint: outputMint.toString(),
@@ -53,7 +53,7 @@ class JupiterClient {
53
53
  swapMode,
54
54
  onlyDirectRoutes: onlyDirectRoutes.toString(),
55
55
  maxAccounts: maxAccounts.toString(),
56
- excludeDexes: excludeDexes.join(','),
56
+ ...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
57
57
  }).toString();
58
58
  const quote = await (await (0, node_fetch_1.default)(`${this.url}/v6/quote?${params}`)).json();
59
59
  return quote;
@@ -6,6 +6,7 @@ const numericConstants_1 = require("../constants/numericConstants");
6
6
  const types_1 = require("../types");
7
7
  const amm_1 = require("./amm");
8
8
  const oracles_1 = require("./oracles");
9
+ const utils_1 = require("./utils");
9
10
  function calculateLiveMarkTwap(market, oraclePriceData, markPrice, now, period = new anchor_1.BN(3600)) {
10
11
  now = now || new anchor_1.BN((Date.now() / 1000).toFixed(0));
11
12
  const lastMarkTwapWithMantissa = market.amm.lastMarkPriceTwap;
@@ -73,7 +74,9 @@ async function calculateAllEstimatedFundingRate(market, oraclePriceData, markPri
73
74
  // }
74
75
  const twapSpread = markTwap.sub(oracleTwap);
75
76
  const twapSpreadWithOffset = twapSpread.add(oracleTwap.abs().div(numericConstants_1.FUNDING_RATE_OFFSET_DENOMINATOR));
76
- const twapSpreadPct = twapSpreadWithOffset
77
+ const maxSpread = getMaxPriceDivergenceForFundingRate(market, oracleTwap);
78
+ const clampedSpreadWithOffset = (0, utils_1.clampBN)(twapSpreadWithOffset, maxSpread.mul(new anchor_1.BN(-1)), maxSpread);
79
+ const twapSpreadPct = clampedSpreadWithOffset
77
80
  .mul(numericConstants_1.PRICE_PRECISION)
78
81
  .mul(new anchor_1.BN(100))
79
82
  .div(oracleTwap);
@@ -135,6 +138,26 @@ async function calculateAllEstimatedFundingRate(market, oraclePriceData, markPri
135
138
  return [markTwap, oracleTwap, lowerboundEst, cappedAltEst, interpEst];
136
139
  }
137
140
  exports.calculateAllEstimatedFundingRate = calculateAllEstimatedFundingRate;
141
+ function getMaxPriceDivergenceForFundingRate(market, oracleTwap) {
142
+ if ((0, types_1.isVariant)(market.contractTier, 'a')) {
143
+ return oracleTwap.divn(33);
144
+ }
145
+ else if ((0, types_1.isVariant)(market.contractTier, 'b')) {
146
+ return oracleTwap.divn(33);
147
+ }
148
+ else if ((0, types_1.isVariant)(market.contractTier, 'c')) {
149
+ return oracleTwap.divn(20);
150
+ }
151
+ else if ((0, types_1.isVariant)(market.contractTier, 'speculative')) {
152
+ return oracleTwap.divn(10);
153
+ }
154
+ else if ((0, types_1.isVariant)(market.contractTier, 'isolated')) {
155
+ return oracleTwap.divn(10);
156
+ }
157
+ else {
158
+ return oracleTwap.divn(10);
159
+ }
160
+ }
138
161
  /**
139
162
  *
140
163
  * @param market
@@ -25,8 +25,8 @@ function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
25
25
  .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
26
26
  .div(oraclePriceData.price)
27
27
  .gt(oracleGuardRails.validity.confidenceIntervalMaxSize);
28
- const oracleIsStale = oraclePriceData.slot
29
- .sub(new index_1.BN(slot))
28
+ const oracleIsStale = new index_1.BN(slot)
29
+ .sub(oraclePriceData.slot)
30
30
  .gt(oracleGuardRails.validity.slotsBeforeStaleForAmm);
31
31
  return !(!oraclePriceData.hasSufficientNumberOfDataPoints ||
32
32
  oracleIsStale ||
@@ -233,7 +233,9 @@ async function calculateSolEarned({ marketIndex, user, depositRecords, }) {
233
233
  const now = Date.now() / 1000;
234
234
  const timestamps = [
235
235
  now,
236
- ...depositRecords.map((r) => r.ts.toNumber()),
236
+ ...depositRecords
237
+ .filter((r) => r.marketIndex === marketIndex)
238
+ .map((r) => r.ts.toNumber()),
237
239
  ];
238
240
  let lstRatios = new Map();
239
241
  const getMsolPrice = async (timestamp) => {
@@ -1,10 +1,5 @@
1
1
  import { PriorityFeeStrategy } from './types';
2
2
  export declare class AverageOverSlotsStrategy implements PriorityFeeStrategy {
3
- private lookbackSlots;
4
- /**
5
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
6
- */
7
- constructor(lookbackSlots?: number);
8
3
  calculate(samples: {
9
4
  slot: number;
10
5
  prioritizationFee: number;
@@ -2,27 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AverageOverSlotsStrategy = void 0;
4
4
  class AverageOverSlotsStrategy {
5
- /**
6
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
7
- */
8
- constructor(lookbackSlots = 10) {
9
- this.lookbackSlots = lookbackSlots;
10
- }
11
5
  calculate(samples) {
12
6
  if (samples.length === 0) {
13
7
  return 0;
14
8
  }
15
- const stopSlot = samples[0].slot - this.lookbackSlots;
16
9
  let runningSumFees = 0;
17
- let countFees = 0;
18
10
  for (let i = 0; i < samples.length; i++) {
19
- if (samples[i].slot <= stopSlot) {
20
- return runningSumFees / countFees;
21
- }
22
11
  runningSumFees += samples[i].prioritizationFee;
23
- countFees++;
24
12
  }
25
- return runningSumFees / countFees;
13
+ return runningSumFees / samples.length;
26
14
  }
27
15
  }
28
16
  exports.AverageOverSlotsStrategy = AverageOverSlotsStrategy;
@@ -1,10 +1,5 @@
1
1
  import { PriorityFeeStrategy } from './types';
2
2
  export declare class MaxOverSlotsStrategy implements PriorityFeeStrategy {
3
- private lookbackSlots;
4
- /**
5
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
6
- */
7
- constructor(lookbackSlots?: number);
8
3
  calculate(samples: {
9
4
  slot: number;
10
5
  prioritizationFee: number;
@@ -2,26 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MaxOverSlotsStrategy = void 0;
4
4
  class MaxOverSlotsStrategy {
5
- /**
6
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
7
- */
8
- constructor(lookbackSlots = 10) {
9
- this.lookbackSlots = lookbackSlots;
10
- }
11
5
  calculate(samples) {
12
6
  if (samples.length === 0) {
13
7
  return 0;
14
8
  }
15
9
  // Assuming samples are sorted in descending order of slot.
16
- const stopSlot = samples[0].slot - this.lookbackSlots;
17
10
  let currMaxFee = samples[0].prioritizationFee;
18
11
  for (let i = 0; i < samples.length; i++) {
19
- if (samples[i].slot <= stopSlot) {
20
- return currMaxFee;
21
- }
22
- if (samples[i].prioritizationFee > currMaxFee) {
23
- currMaxFee = samples[i].prioritizationFee;
24
- }
12
+ currMaxFee = Math.max(samples[i].prioritizationFee, currMaxFee);
25
13
  }
26
14
  return currMaxFee;
27
15
  }
@@ -9,13 +9,17 @@ export declare class PriorityFeeSubscriber {
9
9
  customStrategy?: PriorityFeeStrategy;
10
10
  averageStrategy: AverageOverSlotsStrategy;
11
11
  maxStrategy: MaxOverSlotsStrategy;
12
+ lookbackDistance: number;
12
13
  intervalId?: ReturnType<typeof setTimeout>;
13
14
  latestPriorityFee: number;
14
- lastStrategyResult: number;
15
15
  lastCustomStrategyResult: number;
16
16
  lastAvgStrategyResult: number;
17
17
  lastMaxStrategyResult: number;
18
18
  lastSlotSeen: number;
19
+ /**
20
+ * @param props
21
+ * customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
22
+ */
19
23
  constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck, }: {
20
24
  connection: Connection;
21
25
  frequencyMs: number;
@@ -23,9 +27,6 @@ export declare class PriorityFeeSubscriber {
23
27
  customStrategy?: PriorityFeeStrategy;
24
28
  slotsToCheck?: number;
25
29
  });
26
- get avgPriorityFee(): number;
27
- get maxPriorityFee(): number;
28
- get customPriorityFee(): number;
29
30
  subscribe(): Promise<void>;
30
31
  load(): Promise<void>;
31
32
  unsubscribe(): Promise<void>;
@@ -4,11 +4,14 @@ exports.PriorityFeeSubscriber = void 0;
4
4
  const averageOverSlotsStrategy_1 = require("./averageOverSlotsStrategy");
5
5
  const maxOverSlotsStrategy_1 = require("./maxOverSlotsStrategy");
6
6
  class PriorityFeeSubscriber {
7
+ /**
8
+ * @param props
9
+ * customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
10
+ */
7
11
  constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck = 10, }) {
8
12
  this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
9
13
  this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy();
10
14
  this.latestPriorityFee = 0;
11
- this.lastStrategyResult = 0;
12
15
  this.lastCustomStrategyResult = 0;
13
16
  this.lastAvgStrategyResult = 0;
14
17
  this.lastMaxStrategyResult = 0;
@@ -16,25 +19,13 @@ class PriorityFeeSubscriber {
16
19
  this.connection = connection;
17
20
  this.frequencyMs = frequencyMs;
18
21
  this.addresses = addresses;
19
- if (slotsToCheck) {
20
- this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy(slotsToCheck);
21
- this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy(slotsToCheck);
22
+ if (!customStrategy) {
23
+ this.customStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
22
24
  }
23
- if (customStrategy) {
25
+ else {
24
26
  this.customStrategy = customStrategy;
25
27
  }
26
- }
27
- get avgPriorityFee() {
28
- return Math.floor(this.lastAvgStrategyResult);
29
- }
30
- get maxPriorityFee() {
31
- return Math.floor(this.lastMaxStrategyResult);
32
- }
33
- get customPriorityFee() {
34
- if (!this.customStrategy) {
35
- console.error('Custom strategy not set');
36
- }
37
- return Math.floor(this.lastCustomStrategyResult);
28
+ this.lookbackDistance = slotsToCheck;
38
29
  }
39
30
  async subscribe() {
40
31
  if (this.intervalId) {
@@ -45,19 +36,22 @@ class PriorityFeeSubscriber {
45
36
  async load() {
46
37
  // @ts-ignore
47
38
  const rpcJSONResponse = await this.connection._rpcRequest('getRecentPrioritizationFees', [this.addresses]);
48
- // getRecentPrioritizationFees returns results unsorted
49
39
  const results = rpcJSONResponse === null || rpcJSONResponse === void 0 ? void 0 : rpcJSONResponse.result;
50
40
  if (!results.length)
51
41
  return;
42
+ // # Sort and filter results based on the slot lookback setting
52
43
  const descResults = results.sort((a, b) => b.slot - a.slot);
53
44
  const mostRecentResult = descResults[0];
45
+ const cutoffSlot = mostRecentResult.slot - this.lookbackDistance;
46
+ const resultsToUse = descResults.filter((result) => result.slot >= cutoffSlot);
47
+ // # Handle results
54
48
  this.latestPriorityFee = mostRecentResult.prioritizationFee;
55
49
  this.lastSlotSeen = mostRecentResult.slot;
56
- this.lastAvgStrategyResult = this.averageStrategy.calculate(descResults);
57
- this.lastMaxStrategyResult = this.maxStrategy.calculate(descResults);
50
+ this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse);
51
+ this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse);
58
52
  if (this.customStrategy) {
59
53
  this.lastCustomStrategyResult =
60
- this.customStrategy.calculate(descResults);
54
+ this.customStrategy.calculate(resultsToUse);
61
55
  }
62
56
  }
63
57
  async unsubscribe() {
@@ -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.3",
3
+ "version": "2.54.0-beta.5",
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
  }
@@ -2573,21 +2573,12 @@ export class DriftClient {
2573
2573
  }> {
2574
2574
  const marketIndex = orderParams.marketIndex;
2575
2575
  const orderId = userAccount.nextOrderId;
2576
- const bracketOrderIxs = [];
2577
2576
 
2578
- const placePerpOrderIx = await this.getPlacePerpOrderIx(
2579
- orderParams,
2577
+ const ordersIx = await this.getPlaceOrdersIx(
2578
+ [orderParams, ...bracketOrdersParams],
2580
2579
  userAccount.subAccountId
2581
2580
  );
2582
2581
 
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
2582
  let cancelOrdersIx: TransactionInstruction;
2592
2583
  let cancelExistingOrdersTx: Transaction;
2593
2584
  if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
@@ -2609,7 +2600,7 @@ export class DriftClient {
2609
2600
  // use versioned transactions if there is a lookup table account and wallet is compatible
2610
2601
  if (this.txVersion === 0) {
2611
2602
  const versionedMarketOrderTx = await this.buildTransaction(
2612
- [placePerpOrderIx].concat(bracketOrderIxs),
2603
+ ordersIx,
2613
2604
  txParams,
2614
2605
  0
2615
2606
  );
@@ -2658,15 +2649,11 @@ export class DriftClient {
2658
2649
  };
2659
2650
  } else {
2660
2651
  const marketOrderTx = wrapInTx(
2661
- placePerpOrderIx,
2652
+ ordersIx,
2662
2653
  txParams?.computeUnits,
2663
2654
  txParams?.computeUnitsPrice
2664
2655
  );
2665
2656
 
2666
- if (bracketOrderIxs.length > 0) {
2667
- marketOrderTx.add(...bracketOrderIxs);
2668
- }
2669
-
2670
2657
  // Apply the latest blockhash to the txs so that we can sign before sending them
2671
2658
  const currentBlockHash = (
2672
2659
  await this.connection.getLatestBlockhash('finalized')
@@ -3093,7 +3080,7 @@ export class DriftClient {
3093
3080
  }
3094
3081
 
3095
3082
  public async getPlaceOrdersIx(
3096
- params: OrderParams[],
3083
+ params: OptionalOrderParams[],
3097
3084
  subAccountId?: number
3098
3085
  ): Promise<TransactionInstruction> {
3099
3086
  const user = await this.getUserAccountPublicKey(subAccountId);
@@ -3118,7 +3105,9 @@ export class DriftClient {
3118
3105
  useMarketLastSlotCache: true,
3119
3106
  });
3120
3107
 
3121
- return await this.program.instruction.placeOrders(params, {
3108
+ const formattedParams = params.map((item) => getOrderParams(item));
3109
+
3110
+ return await this.program.instruction.placeOrders(formattedParams, {
3122
3111
  accounts: {
3123
3112
  state: await this.getStatePublicKey(),
3124
3113
  user,
@@ -3868,7 +3857,6 @@ export class DriftClient {
3868
3857
  slippageBps,
3869
3858
  swapMode,
3870
3859
  onlyDirectRoutes,
3871
- excludeDexes: ['Raydium CLMM'], // temp exclude to workaround bug with raydium clmm
3872
3860
  });
3873
3861
 
3874
3862
  quote = fetchedQuote;
@@ -4335,6 +4323,69 @@ export class DriftClient {
4335
4323
  return txSig;
4336
4324
  }
4337
4325
 
4326
+ public async placeAndTakePerpWithAdditionalOrders(
4327
+ orderParams: OptionalOrderParams,
4328
+ makerInfo?: MakerInfo | MakerInfo[],
4329
+ referrerInfo?: ReferrerInfo,
4330
+ bracketOrdersParams = new Array<OptionalOrderParams>(),
4331
+ txParams?: TxParams,
4332
+ subAccountId?: number,
4333
+ cancelExistingOrders?: boolean
4334
+ ): Promise<{
4335
+ txSig: TransactionSignature;
4336
+ signedCancelExistingOrdersTx?: Transaction;
4337
+ }> {
4338
+ let signedCancelExistingOrdersTx: Transaction;
4339
+
4340
+ if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
4341
+ const cancelOrdersIx = await this.getCancelOrdersIx(
4342
+ orderParams.marketType,
4343
+ orderParams.marketIndex,
4344
+ null,
4345
+ subAccountId
4346
+ );
4347
+
4348
+ const cancelExistingOrdersTx = await this.buildTransaction(
4349
+ [cancelOrdersIx],
4350
+ txParams,
4351
+ this.txVersion
4352
+ );
4353
+
4354
+ // @ts-ignore
4355
+ signedCancelExistingOrdersTx = await this.provider.wallet.signTransaction(
4356
+ cancelExistingOrdersTx
4357
+ );
4358
+ }
4359
+
4360
+ const ixs = [];
4361
+
4362
+ const placeAndTakeIx = await this.getPlaceAndTakePerpOrderIx(
4363
+ orderParams,
4364
+ makerInfo,
4365
+ referrerInfo,
4366
+ subAccountId
4367
+ );
4368
+
4369
+ ixs.push(placeAndTakeIx);
4370
+
4371
+ if (bracketOrdersParams.length > 0) {
4372
+ const bracketOrdersIx = await this.getPlaceOrdersIx(
4373
+ bracketOrdersParams,
4374
+ subAccountId
4375
+ );
4376
+ ixs.push(bracketOrdersIx);
4377
+ }
4378
+
4379
+ const { txSig, slot } = await this.sendTransaction(
4380
+ await this.buildTransaction(ixs, txParams),
4381
+ [],
4382
+ this.opts
4383
+ );
4384
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
4385
+
4386
+ return { txSig, signedCancelExistingOrdersTx };
4387
+ }
4388
+
4338
4389
  public async getPlaceAndTakePerpOrderIx(
4339
4390
  orderParams: OptionalOrderParams,
4340
4391
  makerInfo?: MakerInfo | MakerInfo[],
@@ -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 !(
@@ -439,7 +439,9 @@ export async function calculateSolEarned({
439
439
  const now = Date.now() / 1000;
440
440
  const timestamps: number[] = [
441
441
  now,
442
- ...depositRecords.map((r) => r.ts.toNumber()),
442
+ ...depositRecords
443
+ .filter((r) => r.marketIndex === marketIndex)
444
+ .map((r) => r.ts.toNumber()),
443
445
  ];
444
446
 
445
447
  let lstRatios = new Map<number, number>();
@@ -1,30 +1,15 @@
1
1
  import { PriorityFeeStrategy } from './types';
2
2
 
3
3
  export class AverageOverSlotsStrategy implements PriorityFeeStrategy {
4
- private lookbackSlots: number;
5
-
6
- /**
7
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
8
- */
9
- constructor(lookbackSlots = 10) {
10
- this.lookbackSlots = lookbackSlots;
11
- }
12
-
13
4
  calculate(samples: { slot: number; prioritizationFee: number }[]): number {
14
5
  if (samples.length === 0) {
15
6
  return 0;
16
7
  }
17
- const stopSlot = samples[0].slot - this.lookbackSlots;
18
8
  let runningSumFees = 0;
19
- let countFees = 0;
20
9
 
21
10
  for (let i = 0; i < samples.length; i++) {
22
- if (samples[i].slot <= stopSlot) {
23
- return runningSumFees / countFees;
24
- }
25
11
  runningSumFees += samples[i].prioritizationFee;
26
- countFees++;
27
12
  }
28
- return runningSumFees / countFees;
13
+ return runningSumFees / samples.length;
29
14
  }
30
15
  }
@@ -1,30 +1,15 @@
1
1
  import { PriorityFeeStrategy } from './types';
2
2
 
3
3
  export class MaxOverSlotsStrategy implements PriorityFeeStrategy {
4
- private lookbackSlots: number;
5
-
6
- /**
7
- * @param lookbackSlots The number of slots to look back from the max slot in the sample
8
- */
9
- constructor(lookbackSlots = 10) {
10
- this.lookbackSlots = lookbackSlots;
11
- }
12
-
13
4
  calculate(samples: { slot: number; prioritizationFee: number }[]): number {
14
5
  if (samples.length === 0) {
15
6
  return 0;
16
7
  }
17
8
  // Assuming samples are sorted in descending order of slot.
18
- const stopSlot = samples[0].slot - this.lookbackSlots;
19
9
  let currMaxFee = samples[0].prioritizationFee;
20
10
 
21
11
  for (let i = 0; i < samples.length; i++) {
22
- if (samples[i].slot <= stopSlot) {
23
- return currMaxFee;
24
- }
25
- if (samples[i].prioritizationFee > currMaxFee) {
26
- currMaxFee = samples[i].prioritizationFee;
27
- }
12
+ currMaxFee = Math.max(samples[i].prioritizationFee, currMaxFee);
28
13
  }
29
14
  return currMaxFee;
30
15
  }
@@ -10,16 +10,20 @@ export class PriorityFeeSubscriber {
10
10
  customStrategy?: PriorityFeeStrategy;
11
11
  averageStrategy = new AverageOverSlotsStrategy();
12
12
  maxStrategy = new MaxOverSlotsStrategy();
13
+ lookbackDistance: number;
13
14
 
14
15
  intervalId?: ReturnType<typeof setTimeout>;
15
16
 
16
17
  latestPriorityFee = 0;
17
- lastStrategyResult = 0;
18
18
  lastCustomStrategyResult = 0;
19
19
  lastAvgStrategyResult = 0;
20
20
  lastMaxStrategyResult = 0;
21
21
  lastSlotSeen = 0;
22
22
 
23
+ /**
24
+ * @param props
25
+ * customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
26
+ */
23
27
  public constructor({
24
28
  connection,
25
29
  frequencyMs,
@@ -36,28 +40,12 @@ export class PriorityFeeSubscriber {
36
40
  this.connection = connection;
37
41
  this.frequencyMs = frequencyMs;
38
42
  this.addresses = addresses;
39
- if (slotsToCheck) {
40
- this.averageStrategy = new AverageOverSlotsStrategy(slotsToCheck);
41
- this.maxStrategy = new MaxOverSlotsStrategy(slotsToCheck);
42
- }
43
- if (customStrategy) {
43
+ if (!customStrategy) {
44
+ this.customStrategy = new AverageOverSlotsStrategy();
45
+ } else {
44
46
  this.customStrategy = customStrategy;
45
47
  }
46
- }
47
-
48
- public get avgPriorityFee(): number {
49
- return Math.floor(this.lastAvgStrategyResult);
50
- }
51
-
52
- public get maxPriorityFee(): number {
53
- return Math.floor(this.lastMaxStrategyResult);
54
- }
55
-
56
- public get customPriorityFee(): number {
57
- if (!this.customStrategy) {
58
- console.error('Custom strategy not set');
59
- }
60
- return Math.floor(this.lastCustomStrategyResult);
48
+ this.lookbackDistance = slotsToCheck;
61
49
  }
62
50
 
63
51
  public async subscribe(): Promise<void> {
@@ -75,21 +63,29 @@ export class PriorityFeeSubscriber {
75
63
  [this.addresses]
76
64
  );
77
65
 
78
- // getRecentPrioritizationFees returns results unsorted
79
66
  const results: { slot: number; prioritizationFee: number }[] =
80
67
  rpcJSONResponse?.result;
68
+
81
69
  if (!results.length) return;
82
- const descResults = results.sort((a, b) => b.slot - a.slot);
83
70
 
71
+ // # Sort and filter results based on the slot lookback setting
72
+ const descResults = results.sort((a, b) => b.slot - a.slot);
84
73
  const mostRecentResult = descResults[0];
74
+ const cutoffSlot = mostRecentResult.slot - this.lookbackDistance;
75
+
76
+ const resultsToUse = descResults.filter(
77
+ (result) => result.slot >= cutoffSlot
78
+ );
79
+
80
+ // # Handle results
85
81
  this.latestPriorityFee = mostRecentResult.prioritizationFee;
86
82
  this.lastSlotSeen = mostRecentResult.slot;
87
83
 
88
- this.lastAvgStrategyResult = this.averageStrategy.calculate(descResults);
89
- this.lastMaxStrategyResult = this.maxStrategy.calculate(descResults);
84
+ this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse);
85
+ this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse);
90
86
  if (this.customStrategy) {
91
87
  this.lastCustomStrategyResult =
92
- this.customStrategy.calculate(descResults);
88
+ this.customStrategy.calculate(resultsToUse);
93
89
  }
94
90
  }
95
91
 
@@ -337,6 +337,9 @@ export class UserMap implements UserMapInterface {
337
337
  const userAccount = this.decode('User', buffer);
338
338
  await this.addPubkey(new PublicKey(key), userAccount);
339
339
  this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
340
+ } else {
341
+ const userAccount = this.decode('User', buffer);
342
+ this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
340
343
  }
341
344
  // give event loop a chance to breathe
342
345
  await new Promise((resolve) => setTimeout(resolve, 0));
package/tests/amm/test.ts CHANGED
@@ -28,6 +28,9 @@ import {
28
28
  squareRootBN,
29
29
  calculateReferencePriceOffset,
30
30
  calculateInventoryLiquidityRatio,
31
+ ContractTier,
32
+ isOracleValid,
33
+ OracleGuardRails,
31
34
  } from '../../src';
32
35
  import { mockPerpMarkets } from '../dlob/helpers';
33
36
 
@@ -875,11 +878,12 @@ describe('AMM Tests', () => {
875
878
  const mockMarket1 = myMockPerpMarkets[0];
876
879
  const mockAmm = mockMarket1.amm;
877
880
  const now = new BN(new Date().getTime() / 1000); //todo
881
+ const slot = 999999999;
878
882
 
879
883
  const oraclePriceData = {
880
884
  price: new BN(13.553 * PRICE_PRECISION.toNumber()),
881
- slot: new BN(68 + 1),
882
- confidence: new BN(1),
885
+ slot: new BN(slot),
886
+ confidence: new BN(1000),
883
887
  hasSufficientNumberOfDataPoints: true,
884
888
  };
885
889
  mockAmm.oracleStd = new BN(0.18 * PRICE_PRECISION.toNumber());
@@ -901,6 +905,127 @@ describe('AMM Tests', () => {
901
905
  const liveOracleStd = calculateLiveOracleStd(mockAmm, oraclePriceData, now);
902
906
  console.log('liveOracleStd:', liveOracleStd.toNumber());
903
907
  assert(liveOracleStd.eq(new BN(192962)));
908
+
909
+ const oracleGuardRails: OracleGuardRails = {
910
+ priceDivergence: {
911
+ markOraclePercentDivergence: PERCENTAGE_PRECISION.divn(10),
912
+ oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.divn(10),
913
+ },
914
+ validity: {
915
+ slotsBeforeStaleForAmm: new BN(10),
916
+ slotsBeforeStaleForMargin: new BN(60),
917
+ confidenceIntervalMaxSize: new BN(20000),
918
+ tooVolatileRatio: new BN(5),
919
+ },
920
+ };
921
+
922
+ // good oracle
923
+ assert(isOracleValid(mockAmm, oraclePriceData, oracleGuardRails, slot + 5));
924
+
925
+ // conf too high
926
+ assert(
927
+ !isOracleValid(
928
+ mockAmm,
929
+ {
930
+ price: new BN(13.553 * PRICE_PRECISION.toNumber()),
931
+ slot: new BN(slot),
932
+ confidence: new BN(13.553 * PRICE_PRECISION.toNumber() * 0.021),
933
+ hasSufficientNumberOfDataPoints: true,
934
+ },
935
+ oracleGuardRails,
936
+ slot
937
+ )
938
+ );
939
+
940
+ // not hasSufficientNumberOfDataPoints
941
+ assert(
942
+ !isOracleValid(
943
+ mockAmm,
944
+ {
945
+ price: new BN(13.553 * PRICE_PRECISION.toNumber()),
946
+ slot: new BN(slot),
947
+ confidence: new BN(1),
948
+ hasSufficientNumberOfDataPoints: false,
949
+ },
950
+ oracleGuardRails,
951
+ slot
952
+ )
953
+ );
954
+
955
+ // negative oracle price
956
+ assert(
957
+ !isOracleValid(
958
+ mockAmm,
959
+ {
960
+ price: new BN(-1 * PRICE_PRECISION.toNumber()),
961
+ slot: new BN(slot),
962
+ confidence: new BN(1),
963
+ hasSufficientNumberOfDataPoints: true,
964
+ },
965
+ oracleGuardRails,
966
+ slot
967
+ )
968
+ );
969
+
970
+ // too delayed for amm
971
+ assert(
972
+ !isOracleValid(
973
+ mockAmm,
974
+ {
975
+ price: new BN(13.553 * PRICE_PRECISION.toNumber()),
976
+ slot: new BN(slot),
977
+ confidence: new BN(1),
978
+ hasSufficientNumberOfDataPoints: true,
979
+ },
980
+ oracleGuardRails,
981
+ slot + 100
982
+ )
983
+ );
984
+
985
+ // im passing stale slot (should not call oracle invalid)
986
+ assert(
987
+ isOracleValid(
988
+ mockAmm,
989
+ {
990
+ price: new BN(13.553 * PRICE_PRECISION.toNumber()),
991
+ slot: new BN(slot + 100),
992
+ confidence: new BN(1),
993
+ hasSufficientNumberOfDataPoints: true,
994
+ },
995
+ oracleGuardRails,
996
+ slot
997
+ )
998
+ );
999
+
1000
+ // too volatile (more than 5x higher)
1001
+ assert(
1002
+ !isOracleValid(
1003
+ mockAmm,
1004
+ {
1005
+ price: new BN(113.553 * PRICE_PRECISION.toNumber()),
1006
+ slot: new BN(slot + 5),
1007
+ confidence: new BN(1),
1008
+ hasSufficientNumberOfDataPoints: true,
1009
+ },
1010
+ oracleGuardRails,
1011
+ slot
1012
+ )
1013
+ );
1014
+
1015
+ // too volatile (more than 1/5 lower)
1016
+ assert(
1017
+ !isOracleValid(
1018
+ mockAmm,
1019
+ {
1020
+ price: new BN(0.553 * PRICE_PRECISION.toNumber()),
1021
+ slot: new BN(slot + 5),
1022
+ confidence: new BN(1),
1023
+ hasSufficientNumberOfDataPoints: true,
1024
+ },
1025
+ oracleGuardRails,
1026
+ slot
1027
+ )
1028
+ );
904
1029
  });
905
1030
 
906
1031
  it('predicted funding rate mock1', async () => {
@@ -1065,6 +1190,154 @@ describe('AMM Tests', () => {
1065
1190
  assert(est2.eq(new BN('-719')));
1066
1191
  });
1067
1192
 
1193
+ it('predicted funding rate mock clamp', async () => {
1194
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
1195
+ const mockMarket1 = myMockPerpMarkets[0];
1196
+
1197
+ // make it like OP
1198
+ const now = new BN(1688881915);
1199
+
1200
+ mockMarket1.amm.fundingPeriod = new BN(3600);
1201
+ mockMarket1.amm.lastFundingRateTs = new BN(1688864415);
1202
+
1203
+ const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium
1204
+ const oraclePriceData: OraclePriceData = {
1205
+ price: new BN(1.924 * PRICE_PRECISION.toNumber()),
1206
+ slot: new BN(0),
1207
+ confidence: new BN(1),
1208
+ hasSufficientNumberOfDataPoints: true,
1209
+ };
1210
+ mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
1211
+ 1.9535 * PRICE_PRECISION.toNumber()
1212
+ );
1213
+
1214
+ // mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);
1215
+
1216
+ mockMarket1.amm.lastMarkPriceTwap = new BN(
1217
+ 1.218363 * PRICE_PRECISION.toNumber()
1218
+ );
1219
+ mockMarket1.amm.lastBidPriceTwap = new BN(
1220
+ 1.218363 * PRICE_PRECISION.toNumber()
1221
+ );
1222
+ mockMarket1.amm.lastAskPriceTwap = new BN(
1223
+ 1.218364 * PRICE_PRECISION.toNumber()
1224
+ );
1225
+ mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688878815);
1226
+
1227
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(
1228
+ 1.820964 * PRICE_PRECISION.toNumber()
1229
+ );
1230
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(
1231
+ 1688879991
1232
+ );
1233
+ mockMarket1.contractTier = ContractTier.A;
1234
+
1235
+ const [
1236
+ _markTwapLive,
1237
+ _oracleTwapLive,
1238
+ _lowerboundEst,
1239
+ _cappedAltEst,
1240
+ _interpEst,
1241
+ ] = await calculateAllEstimatedFundingRate(
1242
+ mockMarket1,
1243
+ oraclePriceData,
1244
+ currentMarkPrice,
1245
+ now
1246
+ );
1247
+
1248
+ // console.log(_markTwapLive.toString());
1249
+ // console.log(_oracleTwapLive.toString());
1250
+ // console.log(_lowerboundEst.toString());
1251
+ // console.log(_cappedAltEst.toString());
1252
+ // console.log(_interpEst.toString());
1253
+ // console.log('-----');
1254
+
1255
+ let [markTwapLive, oracleTwapLive, est1, est2] =
1256
+ await calculateLongShortFundingRateAndLiveTwaps(
1257
+ mockMarket1,
1258
+ oraclePriceData,
1259
+ currentMarkPrice,
1260
+ now
1261
+ );
1262
+
1263
+ console.log(
1264
+ 'markTwapLive:',
1265
+ mockMarket1.amm.lastMarkPriceTwap.toString(),
1266
+ '->',
1267
+ markTwapLive.toString()
1268
+ );
1269
+ console.log(
1270
+ 'oracTwapLive:',
1271
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
1272
+ '->',
1273
+ oracleTwapLive.toString()
1274
+ );
1275
+ console.log('pred funding:', est1.toString(), est2.toString());
1276
+
1277
+ assert(markTwapLive.eq(new BN('1680634')));
1278
+ assert(oracleTwapLive.eq(new BN('1876031')));
1279
+ assert(est1.eq(est2));
1280
+ assert(est2.eq(new BN('-126261')));
1281
+
1282
+ mockMarket1.contractTier = ContractTier.C;
1283
+
1284
+ [markTwapLive, oracleTwapLive, est1, est2] =
1285
+ await calculateLongShortFundingRateAndLiveTwaps(
1286
+ mockMarket1,
1287
+ oraclePriceData,
1288
+ currentMarkPrice,
1289
+ now
1290
+ );
1291
+
1292
+ console.log(
1293
+ 'markTwapLive:',
1294
+ mockMarket1.amm.lastMarkPriceTwap.toString(),
1295
+ '->',
1296
+ markTwapLive.toString()
1297
+ );
1298
+ console.log(
1299
+ 'oracTwapLive:',
1300
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
1301
+ '->',
1302
+ oracleTwapLive.toString()
1303
+ );
1304
+ console.log('pred funding:', est1.toString(), est2.toString());
1305
+
1306
+ assert(markTwapLive.eq(new BN('1680634')));
1307
+ assert(oracleTwapLive.eq(new BN('1876031')));
1308
+ assert(est1.eq(est2));
1309
+ assert(est2.eq(new BN('-208332')));
1310
+
1311
+ mockMarket1.contractTier = ContractTier.SPECULATIVE;
1312
+
1313
+ [markTwapLive, oracleTwapLive, est1, est2] =
1314
+ await calculateLongShortFundingRateAndLiveTwaps(
1315
+ mockMarket1,
1316
+ oraclePriceData,
1317
+ currentMarkPrice,
1318
+ now
1319
+ );
1320
+
1321
+ console.log(
1322
+ 'markTwapLive:',
1323
+ mockMarket1.amm.lastMarkPriceTwap.toString(),
1324
+ '->',
1325
+ markTwapLive.toString()
1326
+ );
1327
+ console.log(
1328
+ 'oracTwapLive:',
1329
+ mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
1330
+ '->',
1331
+ oracleTwapLive.toString()
1332
+ );
1333
+ console.log('pred funding:', est1.toString(), est2.toString());
1334
+
1335
+ assert(markTwapLive.eq(new BN('1680634')));
1336
+ assert(oracleTwapLive.eq(new BN('1876031')));
1337
+ assert(est1.eq(est2));
1338
+ assert(est2.eq(new BN('-416666')));
1339
+ });
1340
+
1068
1341
  it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
1069
1342
  const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
1070
1343
 
@@ -76,7 +76,7 @@ function insertOrderToDLOB(
76
76
  auctionEndPrice,
77
77
  maxTs,
78
78
  },
79
- userAccount,
79
+ userAccount.toString(),
80
80
  slot.toNumber()
81
81
  );
82
82
  }
@@ -127,7 +127,7 @@ function insertTriggerOrderToDLOB(
127
127
  auctionEndPrice,
128
128
  maxTs,
129
129
  },
130
- userAccount,
130
+ userAccount.toString(),
131
131
  slot.toNumber()
132
132
  );
133
133
  }
@@ -66,7 +66,7 @@ describe('PriorityFeeStrategy', () => {
66
66
  });
67
67
 
68
68
  it('MaxOverSlotsStrategy should calculate the max prioritization fee over slots', () => {
69
- const maxOverSlotsStrategy = new MaxOverSlotsStrategy(5);
69
+ const maxOverSlotsStrategy = new MaxOverSlotsStrategy();
70
70
  const samples = [
71
71
  { slot: 6, prioritizationFee: 432 },
72
72
  { slot: 3, prioritizationFee: 543 },
@@ -80,7 +80,7 @@ describe('PriorityFeeStrategy', () => {
80
80
  });
81
81
 
82
82
  it('AverageOverSlotsStrategy should calculate the average prioritization fee over slots', () => {
83
- const averageOverSlotsStrategy = new AverageOverSlotsStrategy(5);
83
+ const averageOverSlotsStrategy = new AverageOverSlotsStrategy();
84
84
  const samples = [
85
85
  { slot: 6, prioritizationFee: 432 },
86
86
  { slot: 3, prioritizationFee: 543 },