@drift-labs/sdk 2.0.13 → 2.0.15

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.
@@ -300,12 +300,31 @@ export class DriftClient {
300
300
  return this.accountSubscriber.getStateAccountAndSlot().data;
301
301
  }
302
302
 
303
+ /**
304
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
305
+ */
306
+ public async forceGetStateAccount(): Promise<StateAccount> {
307
+ await this.accountSubscriber.fetch();
308
+ return this.accountSubscriber.getStateAccountAndSlot().data;
309
+ }
310
+
303
311
  public getPerpMarketAccount(
304
312
  marketIndex: number
305
313
  ): PerpMarketAccount | undefined {
306
314
  return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
307
315
  }
308
316
 
317
+ /**
318
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
319
+ * @param marketIndex
320
+ */
321
+ public async forceGetPerpMarketAccount(
322
+ marketIndex: number
323
+ ): Promise<PerpMarketAccount | undefined> {
324
+ await this.accountSubscriber.fetch();
325
+ return this.accountSubscriber.getMarketAccountAndSlot(marketIndex)?.data;
326
+ }
327
+
309
328
  public getPerpMarketAccounts(): PerpMarketAccount[] {
310
329
  return this.accountSubscriber
311
330
  .getMarketAccountsAndSlots()
@@ -318,6 +337,17 @@ export class DriftClient {
318
337
  return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
319
338
  }
320
339
 
340
+ /**
341
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
342
+ * @param marketIndex
343
+ */
344
+ public async forceGetSpotMarketAccount(
345
+ marketIndex: number
346
+ ): Promise<SpotMarketAccount | undefined> {
347
+ await this.accountSubscriber.fetch();
348
+ return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data;
349
+ }
350
+
321
351
  public getSpotMarketAccounts(): SpotMarketAccount[] {
322
352
  return this.accountSubscriber
323
353
  .getSpotMarketAccountsAndSlots()
@@ -679,6 +709,17 @@ export class DriftClient {
679
709
  return this.getUser(subAccountId).getUserAccount();
680
710
  }
681
711
 
712
+ /**
713
+ * Forces a fetch to rpc before returning accounts. Useful for anchor tests.
714
+ * @param subAccountId
715
+ */
716
+ public async forceGetUserAccount(
717
+ subAccountId?: number
718
+ ): Promise<UserAccount | undefined> {
719
+ await this.getUser(subAccountId).fetchAccounts();
720
+ return this.getUser(subAccountId).getUserAccount();
721
+ }
722
+
682
723
  public getUserAccountAndSlot(
683
724
  subAccountId?: number
684
725
  ): DataAndSlot<UserAccount> | undefined {
@@ -1432,7 +1432,7 @@
1432
1432
  },
1433
1433
  {
1434
1434
  "name": "spotMarket",
1435
- "isMut": false,
1435
+ "isMut": true,
1436
1436
  "isSigner": false
1437
1437
  },
1438
1438
  {
@@ -3233,6 +3233,48 @@
3233
3233
  }
3234
3234
  ]
3235
3235
  },
3236
+ {
3237
+ "name": "updateInitialPctToLiquidate",
3238
+ "accounts": [
3239
+ {
3240
+ "name": "admin",
3241
+ "isMut": false,
3242
+ "isSigner": true
3243
+ },
3244
+ {
3245
+ "name": "state",
3246
+ "isMut": true,
3247
+ "isSigner": false
3248
+ }
3249
+ ],
3250
+ "args": [
3251
+ {
3252
+ "name": "initialPctToLiquidate",
3253
+ "type": "u16"
3254
+ }
3255
+ ]
3256
+ },
3257
+ {
3258
+ "name": "updateLiquidationDuration",
3259
+ "accounts": [
3260
+ {
3261
+ "name": "admin",
3262
+ "isMut": false,
3263
+ "isSigner": true
3264
+ },
3265
+ {
3266
+ "name": "state",
3267
+ "isMut": true,
3268
+ "isSigner": false
3269
+ }
3270
+ ],
3271
+ "args": [
3272
+ {
3273
+ "name": "liquidationDuration",
3274
+ "type": "u8"
3275
+ }
3276
+ ]
3277
+ },
3236
3278
  {
3237
3279
  "name": "updateOracleGuardRails",
3238
3280
  "accounts": [
@@ -4320,12 +4362,20 @@
4320
4362
  "defined": "ExchangeStatus"
4321
4363
  }
4322
4364
  },
4365
+ {
4366
+ "name": "liquidationDuration",
4367
+ "type": "u8"
4368
+ },
4369
+ {
4370
+ "name": "initialPctToLiquidate",
4371
+ "type": "u16"
4372
+ },
4323
4373
  {
4324
4374
  "name": "padding",
4325
4375
  "type": {
4326
4376
  "array": [
4327
4377
  "u8",
4328
- 17
4378
+ 14
4329
4379
  ]
4330
4380
  }
4331
4381
  }
@@ -4420,8 +4470,8 @@
4420
4470
  "type": "u64"
4421
4471
  },
4422
4472
  {
4423
- "name": "liquidationStartTs",
4424
- "type": "i64"
4473
+ "name": "liquidationStartSlot",
4474
+ "type": "u64"
4425
4475
  },
4426
4476
  {
4427
4477
  "name": "nextOrderId",
@@ -5596,11 +5646,11 @@
5596
5646
  "type": "u64"
5597
5647
  },
5598
5648
  {
5599
- "name": "lastNetBaseAssetAmountPerLp",
5649
+ "name": "lastBaseAssetAmountPerLp",
5600
5650
  "type": "i64"
5601
5651
  },
5602
5652
  {
5603
- "name": "lastNetQuoteAssetAmountPerLp",
5653
+ "name": "lastQuoteAssetAmountPerLp",
5604
5654
  "type": "i64"
5605
5655
  },
5606
5656
  {
@@ -6016,6 +6066,9 @@
6016
6066
  },
6017
6067
  {
6018
6068
  "name": "OrderFillWithSerum"
6069
+ },
6070
+ {
6071
+ "name": "NoBorrowLiquidity"
6019
6072
  }
6020
6073
  ]
6021
6074
  }
@@ -8454,11 +8507,16 @@
8454
8507
  },
8455
8508
  {
8456
8509
  "code": 6219,
8510
+ "name": "UnableToGetLimitPrice",
8511
+ "msg": "Unable To Get Limit Price"
8512
+ },
8513
+ {
8514
+ "code": 6220,
8457
8515
  "name": "InvalidLiquidation",
8458
8516
  "msg": "Invalid Liquidation"
8459
8517
  },
8460
8518
  {
8461
- "code": 6220,
8519
+ "code": 6221,
8462
8520
  "name": "SpotFulfillmentConfigDisabled",
8463
8521
  "msg": "Spot Fulfullment Config Disabled"
8464
8522
  }
package/src/math/amm.ts CHANGED
@@ -10,6 +10,8 @@ import {
10
10
  QUOTE_PRECISION,
11
11
  MARGIN_PRECISION,
12
12
  PRICE_DIV_PEG,
13
+ PERCENTAGE_PRECISION,
14
+ BASE_PRECISION,
13
15
  } from '../constants/numericConstants';
14
16
  import {
15
17
  AMM,
@@ -19,7 +21,7 @@ import {
19
21
  isVariant,
20
22
  } from '../types';
21
23
  import { assert } from '../assert/assert';
22
- import { squareRootBN, standardizeBaseAssetAmount } from '..';
24
+ import { squareRootBN, clampBN, standardizeBaseAssetAmount } from '..';
23
25
 
24
26
  import { OraclePriceData } from '../oracles/types';
25
27
  import {
@@ -28,6 +30,8 @@ import {
28
30
  calculateBudgetedPeg,
29
31
  } from './repeg';
30
32
 
33
+ import { calculateLiveOracleStd } from './oracles';
34
+
31
35
  export function calculatePegFromTargetPrice(
32
36
  targetPrice: BN,
33
37
  baseAssetReserve: BN,
@@ -63,7 +67,8 @@ export function calculateOptimalPegAndBudget(
63
67
  const totalFeeLB = amm.totalExchangeFee.div(new BN(2));
64
68
  const budget = BN.max(ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
65
69
  if (budget.lt(prePegCost)) {
66
- const maxPriceSpread = new BN(amm.maxSpread)
70
+ const halfMaxPriceSpread = new BN(amm.maxSpread)
71
+ .div(new BN(2))
67
72
  .mul(targetPrice)
68
73
  .div(BID_ASK_SPREAD_PRECISION);
69
74
 
@@ -72,8 +77,8 @@ export function calculateOptimalPegAndBudget(
72
77
  let newBudget: BN;
73
78
  const targetPriceGap = reservePriceBefore.sub(targetPrice);
74
79
 
75
- if (targetPriceGap.abs().gt(maxPriceSpread)) {
76
- const markAdj = targetPriceGap.abs().sub(maxPriceSpread);
80
+ if (targetPriceGap.abs().gt(halfMaxPriceSpread)) {
81
+ const markAdj = targetPriceGap.abs().sub(halfMaxPriceSpread);
77
82
 
78
83
  if (targetPriceGap.lt(new BN(0))) {
79
84
  newTargetPrice = reservePriceBefore.add(markAdj);
@@ -173,6 +178,8 @@ export function calculateUpdatedAMM(
173
178
 
174
179
  newAmm.totalFeeMinusDistributions =
175
180
  newAmm.totalFeeMinusDistributions.sub(prepegCost);
181
+ newAmm.netRevenueSinceLastFunding =
182
+ newAmm.netRevenueSinceLastFunding.sub(prepegCost);
176
183
 
177
184
  return newAmm;
178
185
  }
@@ -315,14 +322,14 @@ export function calculateMarketOpenBidAsk(
315
322
  ): [BN, BN] {
316
323
  // open orders
317
324
  let openAsks;
318
- if (maxBaseAssetReserve > baseAssetReserve) {
325
+ if (maxBaseAssetReserve.gt(baseAssetReserve)) {
319
326
  openAsks = maxBaseAssetReserve.sub(baseAssetReserve).mul(new BN(-1));
320
327
  } else {
321
328
  openAsks = ZERO;
322
329
  }
323
330
 
324
331
  let openBids;
325
- if (minBaseAssetReserve < baseAssetReserve) {
332
+ if (minBaseAssetReserve.lt(baseAssetReserve)) {
326
333
  openBids = baseAssetReserve.sub(minBaseAssetReserve);
327
334
  } else {
328
335
  openBids = ZERO;
@@ -331,12 +338,18 @@ export function calculateMarketOpenBidAsk(
331
338
  }
332
339
 
333
340
  export function calculateInventoryScale(
334
- netBaseAssetAmount: BN,
341
+ baseAssetAmountWithAmm: BN,
335
342
  baseAssetReserve: BN,
336
343
  minBaseAssetReserve: BN,
337
- maxBaseAssetReserve: BN
344
+ maxBaseAssetReserve: BN,
345
+ directionalSpread: number,
346
+ maxSpread: number
338
347
  ): number {
339
- const maxScale = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
348
+ if (baseAssetAmountWithAmm.eq(ZERO)) {
349
+ return 0;
350
+ }
351
+
352
+ const defaultLargeBidAskFactor = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
340
353
  // inventory skew
341
354
  const [openBids, openAsks] = calculateMarketOpenBidAsk(
342
355
  baseAssetReserve,
@@ -348,13 +361,27 @@ export function calculateInventoryScale(
348
361
  new BN(1),
349
362
  BN.min(openBids.abs(), openAsks.abs())
350
363
  );
351
- const inventoryScale =
352
- BN.min(
353
- maxScale,
354
- netBaseAssetAmount.mul(maxScale).div(minSideLiquidity).abs()
364
+
365
+ const inventoryScaleMax =
366
+ BN.max(
367
+ defaultLargeBidAskFactor,
368
+ new BN(maxSpread / 2)
369
+ .mul(BID_ASK_SPREAD_PRECISION)
370
+ .div(new BN(Math.max(directionalSpread, 1)))
355
371
  ).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
356
372
 
357
- return inventoryScale;
373
+ const inventoryScale =
374
+ baseAssetAmountWithAmm
375
+ .mul(BN.max(baseAssetAmountWithAmm.abs(), BASE_PRECISION))
376
+ .div(BASE_PRECISION)
377
+ .mul(defaultLargeBidAskFactor)
378
+ .div(minSideLiquidity)
379
+ .abs()
380
+ .toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
381
+
382
+ const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
383
+
384
+ return inventorySpreadScale;
358
385
  }
359
386
 
360
387
  export function calculateEffectiveLeverage(
@@ -392,6 +419,47 @@ export function calculateMaxSpread(marginRatioInitial: number): number {
392
419
  return maxTargetSpread;
393
420
  }
394
421
 
422
+ export function calculateVolSpreadBN(
423
+ lastOracleConfPct: BN,
424
+ reservePrice: BN,
425
+ markStd: BN,
426
+ oracleStd: BN,
427
+ longIntensity: BN,
428
+ shortIntensity: BN,
429
+ volume24H: BN
430
+ ): [BN, BN] {
431
+ const marketAvgStdPct = markStd
432
+ .add(oracleStd)
433
+ .mul(PERCENTAGE_PRECISION)
434
+ .div(reservePrice.mul(new BN(2)));
435
+ const volSpread = BN.max(lastOracleConfPct, marketAvgStdPct.div(new BN(2)));
436
+
437
+ const clampMax = PERCENTAGE_PRECISION.mul(new BN(16)).div(new BN(10));
438
+ const clampMin = PERCENTAGE_PRECISION.div(new BN(10));
439
+
440
+ const longVolSpreadFactor = clampBN(
441
+ longIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
442
+ clampMin,
443
+ clampMax
444
+ );
445
+ const shortVolSpreadFactor = clampBN(
446
+ shortIntensity.mul(PERCENTAGE_PRECISION).div(BN.max(ONE, volume24H)),
447
+ clampMin,
448
+ clampMax
449
+ );
450
+
451
+ const longVolSpread = BN.max(
452
+ lastOracleConfPct,
453
+ volSpread.mul(longVolSpreadFactor).div(PERCENTAGE_PRECISION)
454
+ );
455
+ const shortVolSpread = BN.max(
456
+ lastOracleConfPct,
457
+ volSpread.mul(shortVolSpreadFactor).div(PERCENTAGE_PRECISION)
458
+ );
459
+
460
+ return [longVolSpread, shortVolSpread];
461
+ }
462
+
395
463
  export function calculateSpreadBN(
396
464
  baseSpread: number,
397
465
  lastOracleReservePriceSpreadPct: BN,
@@ -400,27 +468,42 @@ export function calculateSpreadBN(
400
468
  quoteAssetReserve: BN,
401
469
  terminalQuoteAssetReserve: BN,
402
470
  pegMultiplier: BN,
403
- netBaseAssetAmount: BN,
471
+ baseAssetAmountWithAmm: BN,
404
472
  reservePrice: BN,
405
473
  totalFeeMinusDistributions: BN,
406
474
  baseAssetReserve: BN,
407
475
  minBaseAssetReserve: BN,
408
- maxBaseAssetReserve: BN
476
+ maxBaseAssetReserve: BN,
477
+ markStd: BN,
478
+ oracleStd: BN,
479
+ longIntensity: BN,
480
+ shortIntensity: BN,
481
+ volume24H: BN
409
482
  ): [number, number] {
410
- let longSpread = baseSpread / 2;
411
- let shortSpread = baseSpread / 2;
483
+ const [longVolSpread, shortVolSpread] = calculateVolSpreadBN(
484
+ lastOracleConfPct,
485
+ reservePrice,
486
+ markStd,
487
+ oracleStd,
488
+ longIntensity,
489
+ shortIntensity,
490
+ volume24H
491
+ );
492
+
493
+ let longSpread = Math.max(baseSpread / 2, longVolSpread.toNumber());
494
+ let shortSpread = Math.max(baseSpread / 2, shortVolSpread.toNumber());
412
495
 
413
- if (lastOracleReservePriceSpreadPct.gte(ZERO)) {
496
+ if (lastOracleReservePriceSpreadPct.gt(ZERO)) {
414
497
  shortSpread = Math.max(
415
498
  shortSpread,
416
499
  lastOracleReservePriceSpreadPct.abs().toNumber() +
417
- lastOracleConfPct.toNumber()
500
+ shortVolSpread.toNumber()
418
501
  );
419
502
  } else if (lastOracleReservePriceSpreadPct.lt(ZERO)) {
420
503
  longSpread = Math.max(
421
504
  longSpread,
422
505
  lastOracleReservePriceSpreadPct.abs().toNumber() +
423
- lastOracleConfPct.toNumber()
506
+ longVolSpread.toNumber()
424
507
  );
425
508
  }
426
509
 
@@ -429,22 +512,18 @@ export function calculateSpreadBN(
429
512
  lastOracleReservePriceSpreadPct.abs().toNumber()
430
513
  );
431
514
 
432
- const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = 10;
433
-
434
- const inventoryScale = calculateInventoryScale(
435
- netBaseAssetAmount,
515
+ const inventorySpreadScale = calculateInventoryScale(
516
+ baseAssetAmountWithAmm,
436
517
  baseAssetReserve,
437
518
  minBaseAssetReserve,
438
- maxBaseAssetReserve
439
- );
440
- const inventorySpreadScale = Math.min(
441
- MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
442
- 1 + inventoryScale
519
+ maxBaseAssetReserve,
520
+ baseAssetAmountWithAmm.gt(ZERO) ? longSpread : shortSpread,
521
+ maxTargetSpread
443
522
  );
444
523
 
445
- if (netBaseAssetAmount.gt(ZERO)) {
524
+ if (baseAssetAmountWithAmm.gt(ZERO)) {
446
525
  longSpread *= inventorySpreadScale;
447
- } else if (netBaseAssetAmount.lt(ZERO)) {
526
+ } else if (baseAssetAmountWithAmm.lt(ZERO)) {
448
527
  shortSpread *= inventorySpreadScale;
449
528
  }
450
529
 
@@ -453,24 +532,22 @@ export function calculateSpreadBN(
453
532
  quoteAssetReserve,
454
533
  terminalQuoteAssetReserve,
455
534
  pegMultiplier,
456
- netBaseAssetAmount,
535
+ baseAssetAmountWithAmm,
457
536
  reservePrice,
458
537
  totalFeeMinusDistributions
459
538
  );
460
539
 
540
+ const MAX_SPREAD_SCALE = 10;
461
541
  if (totalFeeMinusDistributions.gt(ZERO)) {
462
- const spreadScale = Math.min(
463
- MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
464
- 1 + effectiveLeverage
465
- );
466
- if (netBaseAssetAmount.gt(ZERO)) {
542
+ const spreadScale = Math.min(MAX_SPREAD_SCALE, 1 + effectiveLeverage);
543
+ if (baseAssetAmountWithAmm.gt(ZERO)) {
467
544
  longSpread *= spreadScale;
468
545
  } else {
469
546
  shortSpread *= spreadScale;
470
547
  }
471
548
  } else {
472
- longSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
473
- shortSpread *= MAX_BID_ASK_INVENTORY_SKEW_FACTOR;
549
+ longSpread *= MAX_SPREAD_SCALE;
550
+ shortSpread *= MAX_SPREAD_SCALE;
474
551
  }
475
552
 
476
553
  const totalSpread = longSpread + shortSpread;
@@ -504,7 +581,6 @@ export function calculateSpread(
504
581
 
505
582
  const targetPrice = oraclePriceData?.price || reservePrice;
506
583
  const confInterval = oraclePriceData.confidence || ZERO;
507
-
508
584
  const targetMarkSpreadPct = reservePrice
509
585
  .sub(targetPrice)
510
586
  .mul(BID_ASK_SPREAD_PRECISION)
@@ -514,6 +590,9 @@ export function calculateSpread(
514
590
  .mul(BID_ASK_SPREAD_PRECISION)
515
591
  .div(reservePrice);
516
592
 
593
+ const now = new BN(new Date().getTime() / 1000); //todo
594
+ const liveOracleStd = calculateLiveOracleStd(amm, oraclePriceData, now);
595
+
517
596
  const [longSpread, shortSpread] = calculateSpreadBN(
518
597
  amm.baseSpread,
519
598
  targetMarkSpreadPct,
@@ -527,7 +606,12 @@ export function calculateSpread(
527
606
  amm.totalFeeMinusDistributions,
528
607
  amm.baseAssetReserve,
529
608
  amm.minBaseAssetReserve,
530
- amm.maxBaseAssetReserve
609
+ amm.maxBaseAssetReserve,
610
+ amm.markStd,
611
+ liveOracleStd,
612
+ amm.longIntensityVolume,
613
+ amm.shortIntensityVolume,
614
+ amm.volume24H
531
615
  );
532
616
 
533
617
  let spread: number;
@@ -88,3 +88,55 @@ export function isOracleTooDivergent(
88
88
 
89
89
  return tooDivergent;
90
90
  }
91
+
92
+ export function calculateLiveOracleTwap(
93
+ amm: AMM,
94
+ oraclePriceData: OraclePriceData,
95
+ now: BN
96
+ ): BN {
97
+ const sinceLastUpdate = now.sub(
98
+ amm.historicalOracleData.lastOraclePriceTwapTs
99
+ );
100
+ const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
101
+
102
+ const clampRange = amm.historicalOracleData.lastOraclePriceTwap.div(
103
+ new BN(3)
104
+ );
105
+
106
+ const clampedOraclePrice = BN.min(
107
+ amm.historicalOracleData.lastOraclePriceTwap.add(clampRange),
108
+ BN.max(
109
+ oraclePriceData.price,
110
+ amm.historicalOracleData.lastOraclePriceTwap.sub(clampRange)
111
+ )
112
+ );
113
+
114
+ const newOracleTwap = amm.historicalOracleData.lastOraclePriceTwap
115
+ .mul(sinceStart)
116
+ .add(clampedOraclePrice)
117
+ .mul(sinceLastUpdate)
118
+ .div(sinceStart.add(sinceLastUpdate));
119
+
120
+ return newOracleTwap;
121
+ }
122
+
123
+ export function calculateLiveOracleStd(
124
+ amm: AMM,
125
+ oraclePriceData: OraclePriceData,
126
+ now: BN
127
+ ): BN {
128
+ const sinceLastUpdate = now.sub(
129
+ amm.historicalOracleData.lastOraclePriceTwapTs
130
+ );
131
+ const sinceStart = BN.max(ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
132
+
133
+ const liveOracleTwap = calculateLiveOracleTwap(amm, oraclePriceData, now);
134
+
135
+ const priceDeltaVsTwap = oraclePriceData.price.sub(liveOracleTwap).abs();
136
+
137
+ const oracleStd = priceDeltaVsTwap.add(
138
+ amm.oracleStd.mul(sinceStart).div(sinceStart.add(sinceLastUpdate))
139
+ );
140
+
141
+ return oracleStd;
142
+ }
@@ -127,8 +127,9 @@ export function standardizeBaseAssetAmount(
127
127
  export function getLimitPrice(
128
128
  order: Order,
129
129
  oraclePriceData: OraclePriceData,
130
- slot: number
131
- ): BN {
130
+ slot: number,
131
+ fallbackPrice?: BN
132
+ ): BN | undefined {
132
133
  let limitPrice;
133
134
  if (order.oraclePriceOffset !== 0) {
134
135
  limitPrice = oraclePriceData.price.add(new BN(order.oraclePriceOffset));
@@ -138,13 +139,7 @@ export function getLimitPrice(
138
139
  } else if (!order.price.eq(ZERO)) {
139
140
  limitPrice = order.price;
140
141
  } else {
141
- // check oracle validity?
142
- const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
143
- if (isVariant(order.direction, 'long')) {
144
- limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
145
- } else {
146
- limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
147
- }
142
+ limitPrice = fallbackPrice;
148
143
  }
149
144
  } else {
150
145
  limitPrice = order.price;
@@ -153,18 +148,6 @@ export function getLimitPrice(
153
148
  return limitPrice;
154
149
  }
155
150
 
156
- export function getOptionalLimitPrice(
157
- order: Order,
158
- oraclePriceData: OraclePriceData,
159
- slot: number
160
- ): BN | undefined {
161
- if (hasLimitPrice(order, slot)) {
162
- return getLimitPrice(order, oraclePriceData, slot);
163
- } else {
164
- return undefined;
165
- }
166
- }
167
-
168
151
  export function hasLimitPrice(order: Order, slot: number): boolean {
169
152
  return (
170
153
  order.price.gt(ZERO) ||
@@ -202,7 +185,7 @@ export function calculateBaseAssetAmountForAmmToFulfill(
202
185
  return ZERO;
203
186
  }
204
187
 
205
- const limitPrice = getOptionalLimitPrice(order, oraclePriceData, slot);
188
+ const limitPrice = getLimitPrice(order, oraclePriceData, slot);
206
189
  let baseAssetAmount;
207
190
 
208
191
  const updatedAMM = calculateUpdatedAMM(market.amm, oraclePriceData);
package/src/math/utils.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { BN } from '../';
2
2
 
3
+ export function clampBN(x: BN, min: BN, max: BN): BN {
4
+ return BN.max(min, BN.min(x, max));
5
+ }
6
+
3
7
  export const squareRootBN = (n, closeness = new BN(1)) => {
4
8
  // Assuming the sqrt of n as n only
5
9
  let x = n;
package/src/types.ts CHANGED
@@ -519,6 +519,8 @@ export type StateAccount = {
519
519
  perpFeeStructure: FeeStructure;
520
520
  spotFeeStructure: FeeStructure;
521
521
  lpCooldownTime: BN;
522
+ initialPctToLiquidate: number;
523
+ liquidationDuration: number;
522
524
  };
523
525
 
524
526
  export type PerpMarketAccount = {
@@ -745,8 +747,8 @@ export type PerpPosition = {
745
747
  settledPnl: BN;
746
748
  lpShares: BN;
747
749
  remainderBaseAssetAmount: number;
748
- lastNetBaseAssetAmountPerLp: BN;
749
- lastNetQuoteAssetAmountPerLp: BN;
750
+ lastBaseAssetAmountPerLp: BN;
751
+ lastQuoteAssetAmountPerLp: BN;
750
752
  };
751
753
 
752
754
  export type UserStatsAccount = {
@@ -790,6 +792,8 @@ export type UserAccount = {
790
792
  totalWithdraws: BN;
791
793
  totalSocialLoss: BN;
792
794
  cumulativePerpFunding: BN;
795
+ liquidationMarginFreed: BN;
796
+ liquidationStartSlot: BN;
793
797
  };
794
798
 
795
799
  export type SpotPosition = {