@drift-labs/sdk 2.60.0-beta.8 → 2.61.0-beta.0

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.60.0-beta.8
1
+ 2.61.0-beta.0
@@ -12,6 +12,9 @@ class WebSocketProgramAccountSubscriber {
12
12
  this.program = program;
13
13
  this.decodeBuffer = decodeBufferFn;
14
14
  this.resubTimeoutMs = resubTimeoutMs;
15
+ if (this.resubTimeoutMs < 1000) {
16
+ console.log('resubTimeoutMs should be at least 1000ms to avoid spamming resub');
17
+ }
15
18
  this.options = options;
16
19
  this.receivingData = false;
17
20
  }
@@ -33,6 +36,7 @@ class WebSocketProgramAccountSubscriber {
33
36
  }
34
37
  }, (_a = this.options.commitment) !== null && _a !== void 0 ? _a : this.program.provider.opts.commitment, this.options.filters);
35
38
  if (this.resubTimeoutMs) {
39
+ this.receivingData = true;
36
40
  this.setTimeout();
37
41
  }
38
42
  }
@@ -15,7 +15,7 @@ class AuctionSubscriber {
15
15
  if (!this.subscriber) {
16
16
  this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('AuctionSubscriber', 'User', this.driftClient.program, this.driftClient.program.account.user.coder.accounts.decode.bind(this.driftClient.program.account.user.coder.accounts), {
17
17
  filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getUserWithAuctionFilter)()],
18
- commitment: this.driftClient.opts.commitment,
18
+ commitment: this.opts.commitment,
19
19
  }, this.resubTimeoutMs);
20
20
  }
21
21
  await this.subscriber.subscribe((accountId, data, context) => {
@@ -139,6 +139,7 @@ exports.MainnetSpotMarkets = [
139
139
  precision: new __1.BN(10).pow(numericConstants_1.NINE),
140
140
  precisionExp: numericConstants_1.NINE,
141
141
  serumMarket: new web3_js_1.PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
142
+ phoenixMarket: new web3_js_1.PublicKey('BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'),
142
143
  },
143
144
  {
144
145
  symbol: 'WIF',
@@ -149,6 +150,7 @@ exports.MainnetSpotMarkets = [
149
150
  precision: new __1.BN(10).pow(numericConstants_1.SIX),
150
151
  precisionExp: numericConstants_1.SIX,
151
152
  serumMarket: new web3_js_1.PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
153
+ phoenixMarket: new web3_js_1.PublicKey('6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'),
152
154
  },
153
155
  {
154
156
  symbol: 'JUP',
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.59.0",
2
+ "version": "2.60.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -8364,6 +8364,9 @@
8364
8364
  },
8365
8365
  {
8366
8366
  "name": "Borrow"
8367
+ },
8368
+ {
8369
+ "name": "RepayBorrow"
8367
8370
  }
8368
8371
  ]
8369
8372
  }
@@ -8463,6 +8466,9 @@
8463
8466
  },
8464
8467
  {
8465
8468
  "name": "OrderFilledWithLPJit"
8469
+ },
8470
+ {
8471
+ "name": "DeriskLp"
8466
8472
  }
8467
8473
  ]
8468
8474
  }
@@ -8480,6 +8486,9 @@
8480
8486
  },
8481
8487
  {
8482
8488
  "name": "SettleLiquidity"
8489
+ },
8490
+ {
8491
+ "name": "RemoveLiquidityDerisk"
8483
8492
  }
8484
8493
  ]
8485
8494
  }
@@ -8621,10 +8630,6 @@
8621
8630
  {
8622
8631
  "name": "Liquidation",
8623
8632
  "fields": [
8624
- {
8625
- "name": "margin_buffer",
8626
- "type": "u128"
8627
- },
8628
8633
  {
8629
8634
  "name": "market_to_track_margin_requirement",
8630
8635
  "type": {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="bn.js" />
2
- import { PerpMarketAccount, PositionDirection, SpotMarketAccount } from '../types';
2
+ import { PerpMarketAccount, PositionDirection, SpotMarketAccount, UserStatsAccount } from '../types';
3
3
  import { BN } from '@coral-xyz/anchor';
4
4
  import { AssetType } from './amm';
5
5
  import { OraclePriceData } from '../oracles/types';
@@ -114,3 +114,4 @@ export declare function calculateEstimatedEntryPriceWithL2(assetType: AssetType,
114
114
  baseFilled: BN;
115
115
  quoteFilled: BN;
116
116
  };
117
+ export declare function getUser30dRollingVolumeEstimate(userStatsAccount: UserStatsAccount, now?: BN): BN;
package/lib/math/trade.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateEstimatedEntryPriceWithL2 = exports.calculateEstimatedSpotEntryPrice = exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
3
+ exports.getUser30dRollingVolumeEstimate = exports.calculateEstimatedEntryPriceWithL2 = exports.calculateEstimatedSpotEntryPrice = exports.calculateEstimatedPerpEntryPrice = exports.calculateTargetPriceTrade = exports.calculateTradeAcquiredAmounts = exports.calculateTradeSlippage = void 0;
4
4
  const types_1 = require("../types");
5
5
  const anchor_1 = require("@coral-xyz/anchor");
6
6
  const assert_1 = require("../assert/assert");
@@ -621,3 +621,17 @@ function calculateEstimatedEntryPriceWithL2(assetType, amount, direction, basePr
621
621
  };
622
622
  }
623
623
  exports.calculateEstimatedEntryPriceWithL2 = calculateEstimatedEntryPriceWithL2;
624
+ function getUser30dRollingVolumeEstimate(userStatsAccount, now) {
625
+ now = now || new anchor_1.BN(new Date().getTime() / 1000);
626
+ const sinceLastTaker = anchor_1.BN.max(now.sub(userStatsAccount.lastTakerVolume30DTs), numericConstants_1.ZERO);
627
+ const sinceLastMaker = anchor_1.BN.max(now.sub(userStatsAccount.lastMakerVolume30DTs), numericConstants_1.ZERO);
628
+ const thirtyDaysInSeconds = new anchor_1.BN(60 * 60 * 24 * 30);
629
+ const last30dVolume = userStatsAccount.takerVolume30D
630
+ .mul(anchor_1.BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), numericConstants_1.ZERO))
631
+ .div(thirtyDaysInSeconds)
632
+ .add(userStatsAccount.makerVolume30D
633
+ .mul(anchor_1.BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), numericConstants_1.ZERO))
634
+ .div(thirtyDaysInSeconds));
635
+ return last30dVolume;
636
+ }
637
+ exports.getUser30dRollingVolumeEstimate = getUser30dRollingVolumeEstimate;
package/lib/types.d.ts CHANGED
@@ -252,6 +252,9 @@ export declare class OrderActionExplanation {
252
252
  static readonly REDUCE_ONLY_ORDER_INCREASED_POSITION: {
253
253
  reduceOnlyOrderIncreasedPosition: {};
254
254
  };
255
+ static readonly DERISK_LP: {
256
+ deriskLp: {};
257
+ };
255
258
  }
256
259
  export declare class OrderTriggerCondition {
257
260
  static readonly ABOVE: {
@@ -293,6 +296,9 @@ export declare class DepositExplanation {
293
296
  static readonly BORROW: {
294
297
  borrow: {};
295
298
  };
299
+ static readonly REPAY_BORROW: {
300
+ repayBorrow: {};
301
+ };
296
302
  }
297
303
  export declare class SettlePnlExplanation {
298
304
  static readonly NONE: {
@@ -447,6 +453,9 @@ export declare class LPAction {
447
453
  static readonly SETTLE_LIQUIDITY: {
448
454
  settleLiquidity: {};
449
455
  };
456
+ static readonly REMOVE_LIQUIDITY_DERISK: {
457
+ removeLiquidityDerisk: {};
458
+ };
450
459
  }
451
460
  export type FundingRateRecord = {
452
461
  ts: BN;
package/lib/types.js CHANGED
@@ -175,6 +175,9 @@ OrderActionExplanation.ORDER_FILLED_WITH_PHOENIX = {
175
175
  OrderActionExplanation.REDUCE_ONLY_ORDER_INCREASED_POSITION = {
176
176
  reduceOnlyOrderIncreasedPosition: {},
177
177
  };
178
+ OrderActionExplanation.DERISK_LP = {
179
+ deriskLp: {},
180
+ };
178
181
  class OrderTriggerCondition {
179
182
  }
180
183
  exports.OrderTriggerCondition = OrderTriggerCondition;
@@ -198,6 +201,7 @@ exports.DepositExplanation = DepositExplanation;
198
201
  DepositExplanation.NONE = { none: {} };
199
202
  DepositExplanation.TRANSFER = { transfer: {} };
200
203
  DepositExplanation.BORROW = { borrow: {} };
204
+ DepositExplanation.REPAY_BORROW = { repayBorrow: {} };
201
205
  class SettlePnlExplanation {
202
206
  }
203
207
  exports.SettlePnlExplanation = SettlePnlExplanation;
@@ -243,6 +247,7 @@ exports.LPAction = LPAction;
243
247
  LPAction.ADD_LIQUIDITY = { addLiquidity: {} };
244
248
  LPAction.REMOVE_LIQUIDITY = { removeLiquidity: {} };
245
249
  LPAction.SETTLE_LIQUIDITY = { settleLiquidity: {} };
250
+ LPAction.REMOVE_LIQUIDITY_DERISK = { removeLiquidityDerisk: {} };
246
251
  class LiquidationType {
247
252
  }
248
253
  exports.LiquidationType = LiquidationType;
package/lib/user.d.ts CHANGED
@@ -111,7 +111,7 @@ export declare class User {
111
111
  /**
112
112
  * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
113
113
  */
114
- getMarginRequirement(marginCategory: MarginCategory, liquidationBuffer?: BN, strict?: boolean): BN;
114
+ getMarginRequirement(marginCategory: MarginCategory, liquidationBuffer?: BN, strict?: boolean, includeOpenOrders?: boolean): BN;
115
115
  /**
116
116
  * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
117
117
  */
@@ -246,18 +246,20 @@ export declare class User {
246
246
  * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
247
247
  * @param marketIndex
248
248
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
249
+ * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
249
250
  * @returns Precision : PRICE_PRECISION
250
251
  */
251
- liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN): BN;
252
- calculateFreeCollateralDeltaForPerp(market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN): BN | undefined;
253
- calculateFreeCollateralDeltaForSpot(market: SpotMarketAccount, signedTokenAmount: BN): BN;
252
+ liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN, marginCategory?: MarginCategory): BN;
253
+ calculateEntriesEffectOnFreeCollateral(market: PerpMarketAccount, oraclePrice: BN, perpPosition: PerpPosition, positionBaseSizeChange: BN, estimatedEntryPrice: BN): BN;
254
+ calculateFreeCollateralDeltaForPerp(market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN, marginCategory?: MarginCategory): BN | undefined;
255
+ calculateFreeCollateralDeltaForSpot(market: SpotMarketAccount, signedTokenAmount: BN, marginCategory?: MarginCategory): BN;
254
256
  /**
255
257
  * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
256
258
  * @param positionMarketIndex
257
259
  * @param closeQuoteAmount
258
260
  * @returns : Precision PRICE_PRECISION
259
261
  */
260
- liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN): BN;
262
+ liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN, estimatedEntryPrice?: BN): BN;
261
263
  /**
262
264
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
263
265
  *
@@ -338,7 +340,7 @@ export declare class User {
338
340
  * @returns leverageRatio : Precision TEN_THOUSAND
339
341
  */
340
342
  accountLeverageRatioAfterTrade(targetMarketIndex: number, targetMarketType: MarketType, tradeQuoteAmount: BN, tradeSide: PositionDirection, includeOpenOrders?: boolean): BN;
341
- getUserFeeTier(marketType: MarketType): import("./types").FeeTier;
343
+ getUserFeeTier(marketType: MarketType, now?: BN): import("./types").FeeTier;
342
344
  /**
343
345
  * Calculates taker / maker fee (as a percentage, e.g. .001 = 10 basis points) for particular marketType
344
346
  * @param marketType
package/lib/user.js CHANGED
@@ -432,8 +432,8 @@ class User {
432
432
  /**
433
433
  * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
434
434
  */
435
- getMarginRequirement(marginCategory, liquidationBuffer, strict = false) {
436
- return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true, strict).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true, strict));
435
+ getMarginRequirement(marginCategory, liquidationBuffer, strict = false, includeOpenOrders = true) {
436
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders, strict).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, includeOpenOrders, strict));
437
437
  }
438
438
  /**
439
439
  * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
@@ -1080,33 +1080,22 @@ class User {
1080
1080
  * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
1081
1081
  * @param marketIndex
1082
1082
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
1083
+ * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
1083
1084
  * @returns Precision : PRICE_PRECISION
1084
1085
  */
1085
- liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO) {
1086
- const totalCollateral = this.getTotalCollateral('Maintenance');
1087
- const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
1088
- let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(maintenanceMarginRequirement));
1086
+ liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance') {
1087
+ const totalCollateral = this.getTotalCollateral(marginCategory);
1088
+ const marginRequirement = this.getMarginRequirement(marginCategory, undefined, false);
1089
+ let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement));
1089
1090
  const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
1090
1091
  const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1091
- // update free collateral to accoutn from pnl based on entry price
1092
- if (!estimatedEntryPrice.eq(numericConstants_1.ZERO) && !positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
1093
- const costBasis = oraclePrice
1094
- .mul(positionBaseSizeChange.abs())
1095
- .div(numericConstants_1.BASE_PRECISION);
1096
- const newPositionValue = estimatedEntryPrice
1097
- .mul(positionBaseSizeChange.abs())
1098
- .div(numericConstants_1.BASE_PRECISION);
1099
- if (positionBaseSizeChange.gt(numericConstants_1.ZERO)) {
1100
- freeCollateral = freeCollateral.add(costBasis.sub(newPositionValue));
1101
- }
1102
- else {
1103
- freeCollateral = freeCollateral.add(newPositionValue.sub(costBasis));
1104
- }
1105
- }
1106
1092
  const market = this.driftClient.getPerpMarketAccount(marketIndex);
1107
1093
  const currentPerpPosition = this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
1108
1094
  this.getEmptyPosition(marketIndex);
1109
- let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(market, currentPerpPosition, positionBaseSizeChange);
1095
+ positionBaseSizeChange = (0, _1.standardizeBaseAssetAmount)(positionBaseSizeChange, market.amm.orderStepSize);
1096
+ const freeCollateralChangeFromNewPosition = this.calculateEntriesEffectOnFreeCollateral(market, oraclePrice, currentPerpPosition, positionBaseSizeChange, estimatedEntryPrice);
1097
+ freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
1098
+ let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(market, currentPerpPosition, positionBaseSizeChange, marginCategory);
1110
1099
  if (!freeCollateralDelta) {
1111
1100
  return new _1.BN(-1);
1112
1101
  }
@@ -1117,7 +1106,7 @@ class User {
1117
1106
  const spotPosition = this.getSpotPosition(spotMarketWithSameOracle.marketIndex);
1118
1107
  if (spotPosition) {
1119
1108
  const signedTokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketWithSameOracle, spotPosition.balanceType), spotPosition.balanceType);
1120
- const spotFreeCollateralDelta = this.calculateFreeCollateralDeltaForSpot(spotMarketWithSameOracle, signedTokenAmount);
1109
+ const spotFreeCollateralDelta = this.calculateFreeCollateralDeltaForSpot(spotMarketWithSameOracle, signedTokenAmount, marginCategory);
1121
1110
  freeCollateralDelta = freeCollateralDelta.add(spotFreeCollateralDelta || numericConstants_1.ZERO);
1122
1111
  }
1123
1112
  }
@@ -1133,13 +1122,51 @@ class User {
1133
1122
  }
1134
1123
  return liqPrice;
1135
1124
  }
1136
- calculateFreeCollateralDeltaForPerp(market, perpPosition, positionBaseSizeChange) {
1125
+ calculateEntriesEffectOnFreeCollateral(market, oraclePrice, perpPosition, positionBaseSizeChange, estimatedEntryPrice) {
1126
+ let freeCollateralChange = numericConstants_1.ZERO;
1127
+ // update free collateral to account for change in pnl from new position
1128
+ if (!estimatedEntryPrice.eq(numericConstants_1.ZERO) && !positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
1129
+ const costBasis = oraclePrice
1130
+ .mul(positionBaseSizeChange.abs())
1131
+ .div(numericConstants_1.BASE_PRECISION);
1132
+ const newPositionValue = estimatedEntryPrice
1133
+ .mul(positionBaseSizeChange.abs())
1134
+ .div(numericConstants_1.BASE_PRECISION);
1135
+ if (positionBaseSizeChange.gt(numericConstants_1.ZERO)) {
1136
+ freeCollateralChange = costBasis.sub(newPositionValue);
1137
+ }
1138
+ else {
1139
+ console.log('newPositionValue', newPositionValue.toString());
1140
+ console.log('costBasis', costBasis.toString());
1141
+ freeCollateralChange = newPositionValue.sub(costBasis);
1142
+ }
1143
+ // assume worst fee tier
1144
+ const takerFeeTier = this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
1145
+ const takerFee = newPositionValue
1146
+ .muln(takerFeeTier.feeNumerator)
1147
+ .divn(takerFeeTier.feeDenominator);
1148
+ freeCollateralChange = freeCollateralChange.sub(takerFee);
1149
+ }
1150
+ const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
1151
+ const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(positionBaseSizeChange);
1152
+ const newMarginRatio = (0, _1.calculateMarketMarginRatio)(market, newWorstCaseBaseAssetAmount.abs(), 'Maintenance');
1153
+ // update free collateral to account for new margin requirement from position change
1154
+ freeCollateralChange = freeCollateralChange.sub(newWorstCaseBaseAssetAmount
1155
+ .abs()
1156
+ .sub(worstCaseBaseAssetAmount.abs())
1157
+ .mul(oraclePrice)
1158
+ .div(numericConstants_1.BASE_PRECISION)
1159
+ .mul(new _1.BN(newMarginRatio))
1160
+ .div(numericConstants_1.MARGIN_PRECISION));
1161
+ return freeCollateralChange;
1162
+ }
1163
+ calculateFreeCollateralDeltaForPerp(market, perpPosition, positionBaseSizeChange, marginCategory = 'Maintenance') {
1137
1164
  const currentBaseAssetAmount = perpPosition.baseAssetAmount;
1138
1165
  const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
1139
1166
  const orderBaseAssetAmount = worstCaseBaseAssetAmount.sub(currentBaseAssetAmount);
1140
1167
  const proposedBaseAssetAmount = currentBaseAssetAmount.add(positionBaseSizeChange);
1141
1168
  const proposedWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(positionBaseSizeChange);
1142
- const marginRatio = (0, _1.calculateMarketMarginRatio)(market, proposedWorstCaseBaseAssetAmount.abs(), 'Maintenance');
1169
+ const marginRatio = (0, _1.calculateMarketMarginRatio)(market, proposedWorstCaseBaseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio);
1143
1170
  const marginRatioQuotePrecision = new _1.BN(marginRatio)
1144
1171
  .mul(numericConstants_1.QUOTE_PRECISION)
1145
1172
  .div(numericConstants_1.MARGIN_PRECISION);
@@ -1165,17 +1192,17 @@ class User {
1165
1192
  }
1166
1193
  return freeCollateralDelta;
1167
1194
  }
1168
- calculateFreeCollateralDeltaForSpot(market, signedTokenAmount) {
1195
+ calculateFreeCollateralDeltaForSpot(market, signedTokenAmount, marginCategory = 'Maintenance') {
1169
1196
  const tokenPrecision = new _1.BN(Math.pow(10, market.decimals));
1170
1197
  if (signedTokenAmount.gt(numericConstants_1.ZERO)) {
1171
- const assetWeight = (0, spotBalance_1.calculateAssetWeight)(signedTokenAmount, this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price, market, 'Maintenance');
1198
+ const assetWeight = (0, spotBalance_1.calculateAssetWeight)(signedTokenAmount, this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price, market, marginCategory);
1172
1199
  return numericConstants_1.QUOTE_PRECISION.mul(assetWeight)
1173
1200
  .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION)
1174
1201
  .mul(signedTokenAmount)
1175
1202
  .div(tokenPrecision);
1176
1203
  }
1177
1204
  else {
1178
- const liabilityWeight = (0, spotBalance_1.calculateLiabilityWeight)(signedTokenAmount.abs(), market, 'Maintenance');
1205
+ const liabilityWeight = (0, spotBalance_1.calculateLiabilityWeight)(signedTokenAmount.abs(), market, marginCategory);
1179
1206
  return numericConstants_1.QUOTE_PRECISION.neg()
1180
1207
  .mul(liabilityWeight)
1181
1208
  .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION)
@@ -1189,7 +1216,7 @@ class User {
1189
1216
  * @param closeQuoteAmount
1190
1217
  * @returns : Precision PRICE_PRECISION
1191
1218
  */
1192
- liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount) {
1219
+ liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount, estimatedEntryPrice = numericConstants_1.ZERO) {
1193
1220
  const currentPosition = this.getPerpPositionWithLPSettle(positionMarketIndex, undefined, true)[0] || this.getEmptyPosition(positionMarketIndex);
1194
1221
  const closeBaseAmount = currentPosition.baseAssetAmount
1195
1222
  .mul(closeQuoteAmount)
@@ -1198,7 +1225,7 @@ class User {
1198
1225
  .mul(closeQuoteAmount)
1199
1226
  .mod(currentPosition.quoteAssetAmount.abs()))
1200
1227
  .neg();
1201
- return this.liquidationPrice(positionMarketIndex, closeBaseAmount);
1228
+ return this.liquidationPrice(positionMarketIndex, closeBaseAmount, estimatedEntryPrice);
1202
1229
  }
1203
1230
  /**
1204
1231
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
@@ -1613,14 +1640,14 @@ class User {
1613
1640
  .div(netAssetValue);
1614
1641
  return newLeverage;
1615
1642
  }
1616
- getUserFeeTier(marketType) {
1643
+ getUserFeeTier(marketType, now) {
1617
1644
  const state = this.driftClient.getStateAccount();
1618
1645
  let feeTierIndex = 0;
1619
1646
  if ((0, types_1.isVariant)(marketType, 'perp')) {
1620
1647
  const userStatsAccount = this.driftClient
1621
1648
  .getUserStats()
1622
1649
  .getAccount();
1623
- const total30dVolume = userStatsAccount.takerVolume30D.add(userStatsAccount.makerVolume30D); // todo: update using now and lastTs?
1650
+ const total30dVolume = (0, _1.getUser30dRollingVolumeEstimate)(userStatsAccount, now);
1624
1651
  const stakedQuoteAssetAmount = userStatsAccount.ifStakedQuoteAssetAmount;
1625
1652
  const volumeTiers = [
1626
1653
  new _1.BN(100000000).mul(numericConstants_1.QUOTE_PRECISION),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.60.0-beta.8",
3
+ "version": "2.61.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -42,6 +42,11 @@ export class WebSocketProgramAccountSubscriber<T>
42
42
  this.program = program;
43
43
  this.decodeBuffer = decodeBufferFn;
44
44
  this.resubTimeoutMs = resubTimeoutMs;
45
+ if (this.resubTimeoutMs < 1000) {
46
+ console.log(
47
+ 'resubTimeoutMs should be at least 1000ms to avoid spamming resub'
48
+ );
49
+ }
45
50
  this.options = options;
46
51
  this.receivingData = false;
47
52
  }
@@ -73,6 +78,7 @@ export class WebSocketProgramAccountSubscriber<T>
73
78
  );
74
79
 
75
80
  if (this.resubTimeoutMs) {
81
+ this.receivingData = true;
76
82
  this.setTimeout();
77
83
  }
78
84
  }
@@ -33,7 +33,7 @@ export class AuctionSubscriber {
33
33
  ),
34
34
  {
35
35
  filters: [getUserFilter(), getUserWithAuctionFilter()],
36
- commitment: this.driftClient.opts.commitment,
36
+ commitment: this.opts.commitment,
37
37
  },
38
38
  this.resubTimeoutMs
39
39
  );
@@ -170,6 +170,9 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
170
170
  precision: new BN(10).pow(NINE),
171
171
  precisionExp: NINE,
172
172
  serumMarket: new PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
173
+ phoenixMarket: new PublicKey(
174
+ 'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'
175
+ ),
173
176
  },
174
177
  {
175
178
  symbol: 'WIF',
@@ -180,6 +183,9 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
180
183
  precision: new BN(10).pow(SIX),
181
184
  precisionExp: SIX,
182
185
  serumMarket: new PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
186
+ phoenixMarket: new PublicKey(
187
+ '6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'
188
+ ),
183
189
  },
184
190
  {
185
191
  symbol: 'JUP',
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.59.0",
2
+ "version": "2.60.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -8364,6 +8364,9 @@
8364
8364
  },
8365
8365
  {
8366
8366
  "name": "Borrow"
8367
+ },
8368
+ {
8369
+ "name": "RepayBorrow"
8367
8370
  }
8368
8371
  ]
8369
8372
  }
@@ -8463,6 +8466,9 @@
8463
8466
  },
8464
8467
  {
8465
8468
  "name": "OrderFilledWithLPJit"
8469
+ },
8470
+ {
8471
+ "name": "DeriskLp"
8466
8472
  }
8467
8473
  ]
8468
8474
  }
@@ -8480,6 +8486,9 @@
8480
8486
  },
8481
8487
  {
8482
8488
  "name": "SettleLiquidity"
8489
+ },
8490
+ {
8491
+ "name": "RemoveLiquidityDerisk"
8483
8492
  }
8484
8493
  ]
8485
8494
  }
@@ -8621,10 +8630,6 @@
8621
8630
  {
8622
8631
  "name": "Liquidation",
8623
8632
  "fields": [
8624
- {
8625
- "name": "margin_buffer",
8626
- "type": "u128"
8627
- },
8628
8633
  {
8629
8634
  "name": "market_to_track_margin_requirement",
8630
8635
  "type": {
package/src/math/trade.ts CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  PerpMarketAccount,
4
4
  PositionDirection,
5
5
  SpotMarketAccount,
6
+ UserStatsAccount,
6
7
  } from '../types';
7
8
  import { BN } from '@coral-xyz/anchor';
8
9
  import { assert } from '../assert/assert';
@@ -964,3 +965,29 @@ export function calculateEstimatedEntryPriceWithL2(
964
965
  quoteFilled: cumulativeQuoteFilled,
965
966
  };
966
967
  }
968
+
969
+ export function getUser30dRollingVolumeEstimate(
970
+ userStatsAccount: UserStatsAccount,
971
+ now?: BN
972
+ ) {
973
+ now = now || new BN(new Date().getTime() / 1000);
974
+ const sinceLastTaker = BN.max(
975
+ now.sub(userStatsAccount.lastTakerVolume30DTs),
976
+ ZERO
977
+ );
978
+ const sinceLastMaker = BN.max(
979
+ now.sub(userStatsAccount.lastMakerVolume30DTs),
980
+ ZERO
981
+ );
982
+ const thirtyDaysInSeconds = new BN(60 * 60 * 24 * 30);
983
+ const last30dVolume = userStatsAccount.takerVolume30D
984
+ .mul(BN.max(thirtyDaysInSeconds.sub(sinceLastTaker), ZERO))
985
+ .div(thirtyDaysInSeconds)
986
+ .add(
987
+ userStatsAccount.makerVolume30D
988
+ .mul(BN.max(thirtyDaysInSeconds.sub(sinceLastMaker), ZERO))
989
+ .div(thirtyDaysInSeconds)
990
+ );
991
+
992
+ return last30dVolume;
993
+ }
package/src/types.ts CHANGED
@@ -174,6 +174,9 @@ export class OrderActionExplanation {
174
174
  static readonly REDUCE_ONLY_ORDER_INCREASED_POSITION = {
175
175
  reduceOnlyOrderIncreasedPosition: {},
176
176
  };
177
+ static readonly DERISK_LP = {
178
+ deriskLp: {},
179
+ };
177
180
  }
178
181
 
179
182
  export class OrderTriggerCondition {
@@ -197,6 +200,7 @@ export class DepositExplanation {
197
200
  static readonly NONE = { none: {} };
198
201
  static readonly TRANSFER = { transfer: {} };
199
202
  static readonly BORROW = { borrow: {} };
203
+ static readonly REPAY_BORROW = { repayBorrow: {} };
200
204
  }
201
205
 
202
206
  export class SettlePnlExplanation {
@@ -357,6 +361,7 @@ export class LPAction {
357
361
  static readonly ADD_LIQUIDITY = { addLiquidity: {} };
358
362
  static readonly REMOVE_LIQUIDITY = { removeLiquidity: {} };
359
363
  static readonly SETTLE_LIQUIDITY = { settleLiquidity: {} };
364
+ static readonly REMOVE_LIQUIDITY_DERISK = { removeLiquidityDerisk: {} };
360
365
  }
361
366
 
362
367
  export type FundingRateRecord = {
package/src/user.ts CHANGED
@@ -55,11 +55,13 @@ import {
55
55
  getSignedTokenAmount,
56
56
  getStrictTokenValue,
57
57
  getTokenValue,
58
+ getUser30dRollingVolumeEstimate,
58
59
  MarketType,
59
60
  PositionDirection,
60
61
  sigNum,
61
62
  SpotBalanceType,
62
63
  SpotMarketAccount,
64
+ standardizeBaseAssetAmount,
63
65
  } from '.';
64
66
  import {
65
67
  calculateAssetWeight,
@@ -673,19 +675,20 @@ export class User {
673
675
  public getMarginRequirement(
674
676
  marginCategory: MarginCategory,
675
677
  liquidationBuffer?: BN,
676
- strict = false
678
+ strict = false,
679
+ includeOpenOrders = true
677
680
  ): BN {
678
681
  return this.getTotalPerpPositionValue(
679
682
  marginCategory,
680
683
  liquidationBuffer,
681
- true,
684
+ includeOpenOrders,
682
685
  strict
683
686
  ).add(
684
687
  this.getSpotMarketLiabilityValue(
685
688
  undefined,
686
689
  marginCategory,
687
690
  liquidationBuffer,
688
- true,
691
+ includeOpenOrders,
689
692
  strict
690
693
  )
691
694
  );
@@ -1969,19 +1972,22 @@ export class User {
1969
1972
  * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
1970
1973
  * @param marketIndex
1971
1974
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
1975
+ * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
1972
1976
  * @returns Precision : PRICE_PRECISION
1973
1977
  */
1974
1978
  public liquidationPrice(
1975
1979
  marketIndex: number,
1976
1980
  positionBaseSizeChange: BN = ZERO,
1977
- estimatedEntryPrice: BN = ZERO
1981
+ estimatedEntryPrice: BN = ZERO,
1982
+ marginCategory: MarginCategory = 'Maintenance'
1978
1983
  ): BN {
1979
- const totalCollateral = this.getTotalCollateral('Maintenance');
1980
- const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
1981
- let freeCollateral = BN.max(
1982
- ZERO,
1983
- totalCollateral.sub(maintenanceMarginRequirement)
1984
+ const totalCollateral = this.getTotalCollateral(marginCategory);
1985
+ const marginRequirement = this.getMarginRequirement(
1986
+ marginCategory,
1987
+ undefined,
1988
+ false
1984
1989
  );
1990
+ let freeCollateral = BN.max(ZERO, totalCollateral.sub(marginRequirement));
1985
1991
 
1986
1992
  const oracle =
1987
1993
  this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
@@ -1989,30 +1995,32 @@ export class User {
1989
1995
  const oraclePrice =
1990
1996
  this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1991
1997
 
1992
- // update free collateral to accoutn from pnl based on entry price
1993
- if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
1994
- const costBasis = oraclePrice
1995
- .mul(positionBaseSizeChange.abs())
1996
- .div(BASE_PRECISION);
1997
- const newPositionValue = estimatedEntryPrice
1998
- .mul(positionBaseSizeChange.abs())
1999
- .div(BASE_PRECISION);
2000
- if (positionBaseSizeChange.gt(ZERO)) {
2001
- freeCollateral = freeCollateral.add(costBasis.sub(newPositionValue));
2002
- } else {
2003
- freeCollateral = freeCollateral.add(newPositionValue.sub(costBasis));
2004
- }
2005
- }
2006
-
2007
1998
  const market = this.driftClient.getPerpMarketAccount(marketIndex);
2008
1999
  const currentPerpPosition =
2009
2000
  this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
2010
2001
  this.getEmptyPosition(marketIndex);
2011
2002
 
2003
+ positionBaseSizeChange = standardizeBaseAssetAmount(
2004
+ positionBaseSizeChange,
2005
+ market.amm.orderStepSize
2006
+ );
2007
+
2008
+ const freeCollateralChangeFromNewPosition =
2009
+ this.calculateEntriesEffectOnFreeCollateral(
2010
+ market,
2011
+ oraclePrice,
2012
+ currentPerpPosition,
2013
+ positionBaseSizeChange,
2014
+ estimatedEntryPrice
2015
+ );
2016
+
2017
+ freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
2018
+
2012
2019
  let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(
2013
2020
  market,
2014
2021
  currentPerpPosition,
2015
- positionBaseSizeChange
2022
+ positionBaseSizeChange,
2023
+ marginCategory
2016
2024
  );
2017
2025
 
2018
2026
  if (!freeCollateralDelta) {
@@ -2039,7 +2047,8 @@ export class User {
2039
2047
  const spotFreeCollateralDelta =
2040
2048
  this.calculateFreeCollateralDeltaForSpot(
2041
2049
  spotMarketWithSameOracle,
2042
- signedTokenAmount
2050
+ signedTokenAmount,
2051
+ marginCategory
2043
2052
  );
2044
2053
  freeCollateralDelta = freeCollateralDelta.add(
2045
2054
  spotFreeCollateralDelta || ZERO
@@ -2064,10 +2073,72 @@ export class User {
2064
2073
  return liqPrice;
2065
2074
  }
2066
2075
 
2076
+ calculateEntriesEffectOnFreeCollateral(
2077
+ market: PerpMarketAccount,
2078
+ oraclePrice: BN,
2079
+ perpPosition: PerpPosition,
2080
+ positionBaseSizeChange: BN,
2081
+ estimatedEntryPrice: BN
2082
+ ): BN {
2083
+ let freeCollateralChange = ZERO;
2084
+
2085
+ // update free collateral to account for change in pnl from new position
2086
+ if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
2087
+ const costBasis = oraclePrice
2088
+ .mul(positionBaseSizeChange.abs())
2089
+ .div(BASE_PRECISION);
2090
+ const newPositionValue = estimatedEntryPrice
2091
+ .mul(positionBaseSizeChange.abs())
2092
+ .div(BASE_PRECISION);
2093
+ if (positionBaseSizeChange.gt(ZERO)) {
2094
+ freeCollateralChange = costBasis.sub(newPositionValue);
2095
+ } else {
2096
+ console.log('newPositionValue', newPositionValue.toString());
2097
+ console.log('costBasis', costBasis.toString());
2098
+ freeCollateralChange = newPositionValue.sub(costBasis);
2099
+ }
2100
+
2101
+ // assume worst fee tier
2102
+ const takerFeeTier =
2103
+ this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
2104
+ const takerFee = newPositionValue
2105
+ .muln(takerFeeTier.feeNumerator)
2106
+ .divn(takerFeeTier.feeDenominator);
2107
+ freeCollateralChange = freeCollateralChange.sub(takerFee);
2108
+ }
2109
+
2110
+ const worstCaseBaseAssetAmount =
2111
+ calculateWorstCaseBaseAssetAmount(perpPosition);
2112
+
2113
+ const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(
2114
+ positionBaseSizeChange
2115
+ );
2116
+
2117
+ const newMarginRatio = calculateMarketMarginRatio(
2118
+ market,
2119
+ newWorstCaseBaseAssetAmount.abs(),
2120
+ 'Maintenance'
2121
+ );
2122
+
2123
+ // update free collateral to account for new margin requirement from position change
2124
+ freeCollateralChange = freeCollateralChange.sub(
2125
+ newWorstCaseBaseAssetAmount
2126
+ .abs()
2127
+ .sub(worstCaseBaseAssetAmount.abs())
2128
+ .mul(oraclePrice)
2129
+ .div(BASE_PRECISION)
2130
+ .mul(new BN(newMarginRatio))
2131
+ .div(MARGIN_PRECISION)
2132
+ );
2133
+
2134
+ return freeCollateralChange;
2135
+ }
2136
+
2067
2137
  calculateFreeCollateralDeltaForPerp(
2068
2138
  market: PerpMarketAccount,
2069
2139
  perpPosition: PerpPosition,
2070
- positionBaseSizeChange: BN
2140
+ positionBaseSizeChange: BN,
2141
+ marginCategory: MarginCategory = 'Maintenance'
2071
2142
  ): BN | undefined {
2072
2143
  const currentBaseAssetAmount = perpPosition.baseAssetAmount;
2073
2144
 
@@ -2086,7 +2157,8 @@ export class User {
2086
2157
  const marginRatio = calculateMarketMarginRatio(
2087
2158
  market,
2088
2159
  proposedWorstCaseBaseAssetAmount.abs(),
2089
- 'Maintenance'
2160
+ marginCategory,
2161
+ this.getUserAccount().maxMarginRatio
2090
2162
  );
2091
2163
  const marginRatioQuotePrecision = new BN(marginRatio)
2092
2164
  .mul(QUOTE_PRECISION)
@@ -2121,7 +2193,8 @@ export class User {
2121
2193
 
2122
2194
  calculateFreeCollateralDeltaForSpot(
2123
2195
  market: SpotMarketAccount,
2124
- signedTokenAmount: BN
2196
+ signedTokenAmount: BN,
2197
+ marginCategory: MarginCategory = 'Maintenance'
2125
2198
  ): BN {
2126
2199
  const tokenPrecision = new BN(Math.pow(10, market.decimals));
2127
2200
 
@@ -2130,7 +2203,7 @@ export class User {
2130
2203
  signedTokenAmount,
2131
2204
  this.driftClient.getOraclePriceDataAndSlot(market.oracle).data.price,
2132
2205
  market,
2133
- 'Maintenance'
2206
+ marginCategory
2134
2207
  );
2135
2208
 
2136
2209
  return QUOTE_PRECISION.mul(assetWeight)
@@ -2141,7 +2214,7 @@ export class User {
2141
2214
  const liabilityWeight = calculateLiabilityWeight(
2142
2215
  signedTokenAmount.abs(),
2143
2216
  market,
2144
- 'Maintenance'
2217
+ marginCategory
2145
2218
  );
2146
2219
 
2147
2220
  return QUOTE_PRECISION.neg()
@@ -2160,7 +2233,8 @@ export class User {
2160
2233
  */
2161
2234
  public liquidationPriceAfterClose(
2162
2235
  positionMarketIndex: number,
2163
- closeQuoteAmount: BN
2236
+ closeQuoteAmount: BN,
2237
+ estimatedEntryPrice: BN = ZERO
2164
2238
  ): BN {
2165
2239
  const currentPosition =
2166
2240
  this.getPerpPositionWithLPSettle(
@@ -2179,7 +2253,11 @@ export class User {
2179
2253
  )
2180
2254
  .neg();
2181
2255
 
2182
- return this.liquidationPrice(positionMarketIndex, closeBaseAmount);
2256
+ return this.liquidationPrice(
2257
+ positionMarketIndex,
2258
+ closeBaseAmount,
2259
+ estimatedEntryPrice
2260
+ );
2183
2261
  }
2184
2262
 
2185
2263
  /**
@@ -2982,7 +3060,7 @@ export class User {
2982
3060
  return newLeverage;
2983
3061
  }
2984
3062
 
2985
- public getUserFeeTier(marketType: MarketType) {
3063
+ public getUserFeeTier(marketType: MarketType, now?: BN) {
2986
3064
  const state = this.driftClient.getStateAccount();
2987
3065
 
2988
3066
  let feeTierIndex = 0;
@@ -2991,9 +3069,10 @@ export class User {
2991
3069
  .getUserStats()
2992
3070
  .getAccount();
2993
3071
 
2994
- const total30dVolume = userStatsAccount.takerVolume30D.add(
2995
- userStatsAccount.makerVolume30D
2996
- ); // todo: update using now and lastTs?
3072
+ const total30dVolume = getUser30dRollingVolumeEstimate(
3073
+ userStatsAccount,
3074
+ now
3075
+ );
2997
3076
 
2998
3077
  const stakedQuoteAssetAmount = userStatsAccount.ifStakedQuoteAssetAmount;
2999
3078
  const volumeTiers = [