@drift-labs/sdk 0.2.0-master.25 → 0.2.0-master.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +14 -13
  2. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +30 -27
  3. package/lib/accounts/types.d.ts +9 -9
  4. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +15 -14
  5. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +38 -34
  6. package/lib/addresses/pda.d.ts +7 -6
  7. package/lib/addresses/pda.js +31 -27
  8. package/lib/admin.d.ts +13 -7
  9. package/lib/admin.js +111 -44
  10. package/lib/clearingHouse.d.ts +71 -42
  11. package/lib/clearingHouse.js +767 -278
  12. package/lib/clearingHouseConfig.d.ts +2 -2
  13. package/lib/clearingHouseUser.d.ts +24 -22
  14. package/lib/clearingHouseUser.js +273 -177
  15. package/lib/config.d.ts +7 -7
  16. package/lib/config.js +21 -21
  17. package/lib/constants/numericConstants.d.ts +12 -12
  18. package/lib/constants/numericConstants.js +13 -13
  19. package/lib/constants/{markets.d.ts → perpMarkets.d.ts} +5 -5
  20. package/lib/constants/{markets.js → perpMarkets.js} +4 -4
  21. package/lib/constants/{banks.d.ts → spotMarkets.d.ts} +6 -6
  22. package/lib/constants/{banks.js → spotMarkets.js} +16 -16
  23. package/lib/dlob/DLOB.d.ts +73 -0
  24. package/lib/dlob/DLOB.js +557 -0
  25. package/lib/dlob/DLOBNode.d.ts +52 -0
  26. package/lib/dlob/DLOBNode.js +82 -0
  27. package/lib/dlob/NodeList.d.ts +26 -0
  28. package/lib/dlob/NodeList.js +138 -0
  29. package/lib/events/types.d.ts +2 -1
  30. package/lib/events/types.js +1 -0
  31. package/lib/examples/makeTradeExample.js +7 -7
  32. package/lib/idl/clearing_house.json +1152 -503
  33. package/lib/index.d.ts +10 -3
  34. package/lib/index.js +10 -3
  35. package/lib/math/amm.d.ts +2 -2
  36. package/lib/math/amm.js +1 -1
  37. package/lib/math/funding.d.ts +6 -6
  38. package/lib/math/funding.js +2 -1
  39. package/lib/math/margin.d.ts +4 -4
  40. package/lib/math/margin.js +18 -11
  41. package/lib/math/market.d.ts +11 -10
  42. package/lib/math/market.js +30 -7
  43. package/lib/math/oracles.d.ts +2 -1
  44. package/lib/math/oracles.js +11 -1
  45. package/lib/math/orders.d.ts +6 -6
  46. package/lib/math/orders.js +31 -16
  47. package/lib/math/position.d.ts +13 -13
  48. package/lib/math/position.js +19 -19
  49. package/lib/math/spotBalance.d.ts +22 -0
  50. package/lib/math/spotBalance.js +193 -0
  51. package/lib/math/spotMarket.d.ts +4 -0
  52. package/lib/math/spotMarket.js +8 -0
  53. package/lib/math/spotPosition.d.ts +6 -0
  54. package/lib/math/spotPosition.js +23 -0
  55. package/lib/math/state.js +2 -2
  56. package/lib/math/trade.d.ts +4 -4
  57. package/lib/orderParams.d.ts +4 -4
  58. package/lib/orderParams.js +12 -4
  59. package/lib/serum/serumSubscriber.d.ts +23 -0
  60. package/lib/serum/serumSubscriber.js +41 -0
  61. package/lib/serum/types.d.ts +11 -0
  62. package/lib/serum/types.js +2 -0
  63. package/lib/tx/retryTxSender.d.ts +1 -1
  64. package/lib/tx/retryTxSender.js +4 -2
  65. package/lib/tx/types.d.ts +1 -1
  66. package/lib/types.d.ts +148 -57
  67. package/lib/types.js +39 -11
  68. package/lib/userMap/userMap.d.ts +25 -0
  69. package/lib/userMap/userMap.js +73 -0
  70. package/lib/userMap/userStatsMap.d.ts +19 -0
  71. package/lib/userMap/userStatsMap.js +68 -0
  72. package/package.json +6 -3
  73. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +42 -38
  74. package/src/accounts/types.ts +12 -9
  75. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +65 -52
  76. package/src/addresses/pda.ts +49 -44
  77. package/src/admin.ts +190 -55
  78. package/src/clearingHouse.ts +1092 -365
  79. package/src/clearingHouseConfig.ts +2 -2
  80. package/src/clearingHouseUser.ts +518 -255
  81. package/src/config.ts +30 -30
  82. package/src/constants/numericConstants.ts +17 -15
  83. package/src/constants/{markets.ts → perpMarkets.ts} +5 -5
  84. package/src/constants/{banks.ts → spotMarkets.ts} +19 -19
  85. package/src/dlob/DLOB.ts +884 -0
  86. package/src/dlob/DLOBNode.ts +163 -0
  87. package/src/dlob/NodeList.ts +185 -0
  88. package/src/events/types.ts +3 -0
  89. package/src/examples/makeTradeExample.js +152 -75
  90. package/src/examples/makeTradeExample.ts +10 -8
  91. package/src/idl/clearing_house.json +1152 -503
  92. package/src/index.ts +10 -3
  93. package/src/math/amm.ts +6 -3
  94. package/src/math/funding.ts +7 -7
  95. package/src/math/margin.ts +34 -23
  96. package/src/math/market.ts +72 -20
  97. package/src/math/oracles.ts +18 -1
  98. package/src/math/orders.ts +33 -25
  99. package/src/math/position.ts +31 -31
  100. package/src/math/spotBalance.ts +316 -0
  101. package/src/math/spotMarket.ts +9 -0
  102. package/src/math/spotPosition.ts +47 -0
  103. package/src/math/state.ts +2 -2
  104. package/src/math/trade.ts +4 -4
  105. package/src/orderParams.ts +16 -8
  106. package/src/serum/serumSubscriber.ts +80 -0
  107. package/src/serum/types.ts +13 -0
  108. package/src/tx/retryTxSender.ts +5 -2
  109. package/src/tx/types.ts +2 -1
  110. package/src/types.ts +135 -56
  111. package/src/userMap/userMap.ts +100 -0
  112. package/src/userMap/userStatsMap.ts +110 -0
  113. package/tests/bn/test.ts +2 -3
  114. package/tests/dlob/helpers.ts +322 -0
  115. package/tests/dlob/test.ts +2865 -0
  116. package/lib/math/bankBalance.d.ts +0 -15
  117. package/lib/math/bankBalance.js +0 -150
  118. package/src/constants/numericConstants.js +0 -41
  119. package/src/math/bankBalance.ts +0 -258
  120. package/src/math/oracles.js +0 -26
  121. package/src/math/state.js +0 -15
  122. package/src/orderParams.js +0 -20
  123. package/src/slot/SlotSubscriber.js +0 -39
  124. package/src/tokenFaucet.js +0 -189
@@ -7,7 +7,7 @@ import {
7
7
  MarginCategory,
8
8
  Order,
9
9
  UserAccount,
10
- UserPosition,
10
+ PerpPosition,
11
11
  } from './types';
12
12
  import { calculateEntryPrice } from './math/position';
13
13
  import {
@@ -20,8 +20,8 @@ import {
20
20
  AMM_RESERVE_PRECISION,
21
21
  PRICE_TO_QUOTE_PRECISION,
22
22
  MARGIN_PRECISION,
23
- BANK_WEIGHT_PRECISION,
24
- BANK_BALANCE_PRECISION_EXP,
23
+ SPOT_MARKET_WEIGHT_PRECISION,
24
+ QUOTE_SPOT_MARKET_INDEX,
25
25
  } from './constants/numericConstants';
26
26
  import {
27
27
  UserAccountSubscriber,
@@ -38,13 +38,14 @@ import {
38
38
  PositionDirection,
39
39
  calculateTradeSlippage,
40
40
  BN,
41
- BankAccount,
41
+ SpotMarketAccount,
42
+ getTokenValue,
42
43
  } from '.';
43
44
  import {
44
45
  getTokenAmount,
45
46
  calculateAssetWeight,
46
47
  calculateLiabilityWeight,
47
- } from './math/bankBalance';
48
+ } from './math/spotBalance';
48
49
  import {
49
50
  calculateBaseAssetValueWithOracle,
50
51
  calculateWorstCaseBaseAssetAmount,
@@ -53,6 +54,10 @@ import { OraclePriceData } from './oracles/types';
53
54
  import { ClearingHouseUserConfig } from './clearingHouseUserConfig';
54
55
  import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
55
56
  import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
57
+ import {
58
+ getWorstCaseTokenAmounts,
59
+ isSpotPositionAvailable,
60
+ } from './math/spotPosition';
56
61
  export class ClearingHouseUser {
57
62
  clearingHouse: ClearingHouse;
58
63
  userAccountPublicKey: PublicKey;
@@ -120,15 +125,16 @@ export class ClearingHouseUser {
120
125
  * @param marketIndex
121
126
  * @returns userPosition
122
127
  */
123
- public getUserPosition(marketIndex: BN): UserPosition | undefined {
124
- return this.getUserAccount().positions.find((position) =>
128
+ public getUserPosition(marketIndex: BN): PerpPosition | undefined {
129
+ return this.getUserAccount().perpPositions.find((position) =>
125
130
  position.marketIndex.eq(marketIndex)
126
131
  );
127
132
  }
128
133
 
129
- public getEmptyPosition(marketIndex: BN): UserPosition {
134
+ public getEmptyPosition(marketIndex: BN): PerpPosition {
130
135
  return {
131
136
  baseAssetAmount: ZERO,
137
+ remainderBaseAssetAmount: ZERO,
132
138
  lastCumulativeFundingRate: ZERO,
133
139
  marketIndex,
134
140
  quoteAssetAmount: ZERO,
@@ -144,7 +150,7 @@ export class ClearingHouseUser {
144
150
  };
145
151
  }
146
152
 
147
- public getClonedPosition(position: UserPosition): UserPosition {
153
+ public getClonedPosition(position: PerpPosition): PerpPosition {
148
154
  const clonedPosition = Object.assign({}, position);
149
155
  return clonedPosition;
150
156
  }
@@ -187,11 +193,13 @@ export class ClearingHouseUser {
187
193
  * @returns : the dust base asset amount (ie, < stepsize)
188
194
  * @returns : pnl from settle
189
195
  */
190
- public getSettledLPPosition(marketIndex: BN): [UserPosition, BN, BN] {
196
+ public getSettledLPPosition(marketIndex: BN): [PerpPosition, BN, BN] {
191
197
  const _position = this.getUserPosition(marketIndex);
192
198
  const position = this.getClonedPosition(_position);
193
199
 
194
- const market = this.clearingHouse.getMarketAccount(position.marketIndex);
200
+ const market = this.clearingHouse.getPerpMarketAccount(
201
+ position.marketIndex
202
+ );
195
203
  const nShares = position.lpShares;
196
204
 
197
205
  const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
@@ -210,24 +218,33 @@ export class ClearingHouseUser {
210
218
  return sign;
211
219
  }
212
220
 
213
- const remainder = deltaBaa
214
- .abs()
215
- .mod(market.amm.baseAssetAmountStepSize)
216
- .mul(sign(deltaBaa));
217
- const _standardizedBaa = deltaBaa.sub(remainder);
218
-
219
- let remainderBaa;
220
- if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
221
- remainderBaa = remainder;
222
- } else {
223
- remainderBaa = deltaBaa;
221
+ function standardize(amount, stepsize) {
222
+ const remainder = amount.abs().mod(stepsize).mul(sign(amount));
223
+ const standardizedAmount = amount.sub(remainder);
224
+ return [standardizedAmount, remainder];
224
225
  }
225
- const standardizedBaa = deltaBaa.sub(remainderBaa);
226
226
 
227
- const reaminderPerLP = remainderBaa.mul(AMM_RESERVE_PRECISION).div(nShares);
227
+ const [standardizedBaa, remainderBaa] = standardize(
228
+ deltaBaa,
229
+ market.amm.baseAssetAmountStepSize
230
+ );
228
231
 
229
- position.lastNetBaseAssetAmountPerLp =
230
- market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
232
+ position.remainderBaseAssetAmount =
233
+ position.remainderBaseAssetAmount.add(remainderBaa);
234
+
235
+ if (
236
+ position.remainderBaseAssetAmount
237
+ .abs()
238
+ .gte(market.amm.baseAssetAmountStepSize)
239
+ ) {
240
+ const [newStandardizedBaa, newRemainderBaa] = standardize(
241
+ position.remainderBaseAssetAmount,
242
+ market.amm.baseAssetAmountStepSize
243
+ );
244
+ position.baseAssetAmount =
245
+ position.baseAssetAmount.add(newStandardizedBaa);
246
+ position.remainderBaseAssetAmount = newRemainderBaa;
247
+ }
231
248
 
232
249
  let updateType;
233
250
  if (position.baseAssetAmount.eq(ZERO)) {
@@ -300,75 +317,22 @@ export class ClearingHouseUser {
300
317
  /**
301
318
  * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
302
319
  */
303
- public getMarginRequirement(type: MarginCategory): BN {
304
- return this.getUserAccount()
305
- .positions.reduce((marginRequirement, marketPosition) => {
306
- const market = this.clearingHouse.getMarketAccount(
307
- marketPosition.marketIndex
308
- );
309
-
310
- if (marketPosition.lpShares.gt(ZERO)) {
311
- // is an lp
312
- // clone so we dont mutate the position
313
- marketPosition = this.getClonedPosition(marketPosition);
314
-
315
- // settle position
316
- const [settledPosition, dustBaa, _] = this.getSettledLPPosition(
317
- market.marketIndex
318
- );
319
- marketPosition.baseAssetAmount =
320
- settledPosition.baseAssetAmount.add(dustBaa);
321
- marketPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
322
-
323
- // open orders
324
- let openAsks;
325
- if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
326
- openAsks = market.amm.maxBaseAssetReserve
327
- .sub(market.amm.baseAssetReserve)
328
- .mul(marketPosition.lpShares)
329
- .div(market.amm.sqrtK)
330
- .mul(new BN(-1));
331
- } else {
332
- openAsks = ZERO;
333
- }
334
-
335
- let openBids;
336
- if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
337
- openBids = market.amm.baseAssetReserve
338
- .sub(market.amm.minBaseAssetReserve)
339
- .mul(marketPosition.lpShares)
340
- .div(market.amm.sqrtK);
341
- } else {
342
- openBids = ZERO;
343
- }
344
-
345
- marketPosition.openAsks = marketPosition.openAsks.add(openAsks);
346
- marketPosition.openBids = marketPosition.openBids.add(openBids);
347
- }
348
-
349
- const worstCaseBaseAssetAmount =
350
- calculateWorstCaseBaseAssetAmount(marketPosition);
351
-
352
- const worstCaseAssetValue = worstCaseBaseAssetAmount
353
- .abs()
354
- .mul(this.getOracleDataForMarket(market.marketIndex).price)
355
- .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
356
-
357
- return marginRequirement.add(
358
- worstCaseAssetValue
359
- .mul(
360
- new BN(
361
- calculateMarketMarginRatio(
362
- market,
363
- worstCaseBaseAssetAmount.abs(),
364
- type
365
- )
366
- )
367
- )
368
- .div(MARGIN_PRECISION)
369
- );
370
- }, ZERO)
371
- .add(this.getBankLiabilityValue(undefined, type));
320
+ public getMarginRequirement(
321
+ marginCategory: MarginCategory,
322
+ liquidationBuffer?: BN
323
+ ): BN {
324
+ return this.getTotalPerpPositionValue(
325
+ marginCategory,
326
+ liquidationBuffer,
327
+ true
328
+ ).add(
329
+ this.getSpotMarketLiabilityValue(
330
+ undefined,
331
+ marginCategory,
332
+ liquidationBuffer,
333
+ true
334
+ )
335
+ );
372
336
  }
373
337
 
374
338
  /**
@@ -381,8 +345,8 @@ export class ClearingHouseUser {
381
345
  /**
382
346
  * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
383
347
  */
384
- public getMaintenanceMarginRequirement(): BN {
385
- return this.getMarginRequirement('Maintenance');
348
+ public getMaintenanceMarginRequirement(liquidationBuffer?: BN): BN {
349
+ return this.getMarginRequirement('Maintenance', liquidationBuffer);
386
350
  }
387
351
 
388
352
  /**
@@ -394,19 +358,22 @@ export class ClearingHouseUser {
394
358
  marketIndex?: BN,
395
359
  withWeightMarginCategory?: MarginCategory
396
360
  ): BN {
361
+ const quoteSpotMarket = this.clearingHouse.getQuoteSpotMarketAccount();
397
362
  return this.getUserAccount()
398
- .positions.filter((pos) =>
363
+ .perpPositions.filter((pos) =>
399
364
  marketIndex ? pos.marketIndex === marketIndex : true
400
365
  )
401
- .reduce((unrealizedPnl, marketPosition) => {
402
- const market = this.clearingHouse.getMarketAccount(
403
- marketPosition.marketIndex
366
+ .reduce((unrealizedPnl, perpPosition) => {
367
+ const market = this.clearingHouse.getPerpMarketAccount(
368
+ perpPosition.marketIndex
404
369
  );
370
+ const oraclePriceData = this.getOracleDataForMarket(market.marketIndex);
371
+
405
372
  let positionUnrealizedPnl = calculatePositionPNL(
406
373
  market,
407
- marketPosition,
374
+ perpPosition,
408
375
  withFunding,
409
- this.getOracleDataForMarket(market.marketIndex)
376
+ oraclePriceData
410
377
  );
411
378
 
412
379
  if (withWeightMarginCategory !== undefined) {
@@ -415,11 +382,13 @@ export class ClearingHouseUser {
415
382
  .mul(
416
383
  calculateUnrealizedAssetWeight(
417
384
  market,
385
+ quoteSpotMarket,
418
386
  positionUnrealizedPnl,
419
- withWeightMarginCategory
387
+ withWeightMarginCategory,
388
+ oraclePriceData
420
389
  )
421
390
  )
422
- .div(new BN(BANK_WEIGHT_PRECISION));
391
+ .div(new BN(SPOT_MARKET_WEIGHT_PRECISION));
423
392
  }
424
393
  }
425
394
 
@@ -433,119 +402,287 @@ export class ClearingHouseUser {
433
402
  */
434
403
  public getUnrealizedFundingPNL(marketIndex?: BN): BN {
435
404
  return this.getUserAccount()
436
- .positions.filter((pos) =>
405
+ .perpPositions.filter((pos) =>
437
406
  marketIndex ? pos.marketIndex === marketIndex : true
438
407
  )
439
- .reduce((pnl, marketPosition) => {
440
- const market = this.clearingHouse.getMarketAccount(
441
- marketPosition.marketIndex
408
+ .reduce((pnl, perpPosition) => {
409
+ const market = this.clearingHouse.getPerpMarketAccount(
410
+ perpPosition.marketIndex
442
411
  );
443
- return pnl.add(calculatePositionFundingPNL(market, marketPosition));
412
+ return pnl.add(calculatePositionFundingPNL(market, perpPosition));
444
413
  }, ZERO);
445
414
  }
446
415
 
447
- public getBankLiabilityValue(
448
- bankIndex?: BN,
449
- withWeightMarginCategory?: MarginCategory
416
+ public getSpotMarketLiabilityValue(
417
+ marketIndex?: BN,
418
+ marginCategory?: MarginCategory,
419
+ liquidationBuffer?: BN,
420
+ includeOpenOrders?: boolean
450
421
  ): BN {
451
- return this.getUserAccount().bankBalances.reduce(
452
- (totalLiabilityValue, bankBalance) => {
422
+ return this.getUserAccount().spotPositions.reduce(
423
+ (totalLiabilityValue, spotPosition) => {
453
424
  if (
454
- bankBalance.balance.eq(ZERO) ||
455
- isVariant(bankBalance.balanceType, 'deposit') ||
456
- (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
425
+ isSpotPositionAvailable(spotPosition) ||
426
+ (marketIndex !== undefined &&
427
+ !spotPosition.marketIndex.eq(marketIndex))
457
428
  ) {
458
429
  return totalLiabilityValue;
459
430
  }
460
431
 
461
- // Todo this needs to account for whether it's based on initial or maintenance requirements
462
- const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
463
- bankBalance.bankIndex
464
- );
432
+ const spotMarketAccount: SpotMarketAccount =
433
+ this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
465
434
 
466
- const tokenAmount = getTokenAmount(
467
- bankBalance.balance,
468
- bankAccount,
469
- bankBalance.balanceType
435
+ if (spotPosition.marketIndex.eq(QUOTE_SPOT_MARKET_INDEX)) {
436
+ if (isVariant(spotPosition.balanceType, 'borrow')) {
437
+ const tokenAmount = getTokenAmount(
438
+ spotPosition.balance,
439
+ spotMarketAccount,
440
+ spotPosition.balanceType
441
+ );
442
+
443
+ let weight = SPOT_MARKET_WEIGHT_PRECISION;
444
+ if (marginCategory === 'Initial') {
445
+ weight = BN.max(
446
+ weight,
447
+ new BN(this.getUserAccount().customMarginRatio)
448
+ );
449
+ }
450
+
451
+ const weightedTokenValue = tokenAmount
452
+ .mul(weight)
453
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
454
+
455
+ return totalLiabilityValue.add(weightedTokenValue);
456
+ } else {
457
+ return totalLiabilityValue;
458
+ }
459
+ }
460
+
461
+ const oraclePriceData = this.getOracleDataForSpotMarket(
462
+ spotPosition.marketIndex
470
463
  );
471
464
 
472
- let liabilityValue = tokenAmount
473
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
474
- .div(MARK_PRICE_PRECISION)
475
- .div(
476
- new BN(10).pow(
477
- new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
478
- )
465
+ if (!includeOpenOrders) {
466
+ if (isVariant(spotPosition.balanceType, 'borrow')) {
467
+ const tokenAmount = getTokenAmount(
468
+ spotPosition.balance,
469
+ spotMarketAccount,
470
+ spotPosition.balanceType
471
+ );
472
+ const liabilityValue = this.getSpotLiabilityValue(
473
+ tokenAmount,
474
+ oraclePriceData,
475
+ spotMarketAccount,
476
+ marginCategory,
477
+ liquidationBuffer
478
+ );
479
+ return totalLiabilityValue.add(liabilityValue);
480
+ } else {
481
+ return totalLiabilityValue;
482
+ }
483
+ }
484
+
485
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
486
+ getWorstCaseTokenAmounts(
487
+ spotPosition,
488
+ spotMarketAccount,
489
+ this.getOracleDataForSpotMarket(spotPosition.marketIndex)
479
490
  );
480
491
 
481
- if (withWeightMarginCategory !== undefined) {
482
- const weight = calculateLiabilityWeight(
483
- tokenAmount,
484
- bankAccount,
485
- withWeightMarginCategory
492
+ let newTotalLiabilityValue = totalLiabilityValue;
493
+ if (worstCaseTokenAmount.lt(ZERO)) {
494
+ const baseLiabilityValue = this.getSpotLiabilityValue(
495
+ worstCaseTokenAmount,
496
+ oraclePriceData,
497
+ spotMarketAccount,
498
+ marginCategory,
499
+ liquidationBuffer
486
500
  );
487
- liabilityValue = liabilityValue
501
+
502
+ newTotalLiabilityValue =
503
+ newTotalLiabilityValue.add(baseLiabilityValue);
504
+ }
505
+
506
+ if (worstCaseQuoteTokenAmount.lt(ZERO)) {
507
+ let weight = SPOT_MARKET_WEIGHT_PRECISION;
508
+ if (marginCategory === 'Initial') {
509
+ weight = BN.max(
510
+ weight,
511
+ new BN(this.getUserAccount().customMarginRatio)
512
+ );
513
+ }
514
+
515
+ const weightedTokenValue = worstCaseQuoteTokenAmount
488
516
  .mul(weight)
489
- .div(BANK_WEIGHT_PRECISION);
517
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
518
+
519
+ newTotalLiabilityValue =
520
+ newTotalLiabilityValue.add(weightedTokenValue);
490
521
  }
491
522
 
492
- return totalLiabilityValue.add(liabilityValue);
523
+ return newTotalLiabilityValue;
493
524
  },
494
525
  ZERO
495
526
  );
496
527
  }
497
528
 
498
- public getBankAssetValue(
499
- bankIndex?: BN,
500
- withWeightMarginCategory?: MarginCategory
529
+ getSpotLiabilityValue(
530
+ tokenAmount: BN,
531
+ oraclePriceData: OraclePriceData,
532
+ spotMarketAccount: SpotMarketAccount,
533
+ marginCategory?: MarginCategory,
534
+ liquidationBuffer?: BN
501
535
  ): BN {
502
- return this.getUserAccount().bankBalances.reduce(
503
- (totalAssetValue, bankBalance) => {
536
+ let liabilityValue = getTokenValue(
537
+ tokenAmount,
538
+ spotMarketAccount.decimals,
539
+ oraclePriceData
540
+ );
541
+
542
+ if (marginCategory !== undefined) {
543
+ let weight = calculateLiabilityWeight(
544
+ tokenAmount,
545
+ spotMarketAccount,
546
+ marginCategory
547
+ );
548
+
549
+ if (marginCategory === 'Initial') {
550
+ weight = BN.max(
551
+ weight,
552
+ new BN(this.getUserAccount().customMarginRatio)
553
+ );
554
+ }
555
+
556
+ if (liquidationBuffer !== undefined) {
557
+ weight = weight.add(liquidationBuffer);
558
+ }
559
+
560
+ liabilityValue = liabilityValue
561
+ .mul(weight)
562
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
563
+ }
564
+
565
+ return liabilityValue;
566
+ }
567
+
568
+ public getSpotMarketAssetValue(
569
+ marketIndex?: BN,
570
+ marginCategory?: MarginCategory,
571
+ includeOpenOrders?: boolean
572
+ ): BN {
573
+ return this.getUserAccount().spotPositions.reduce(
574
+ (totalAssetValue, spotPosition) => {
504
575
  if (
505
- bankBalance.balance.eq(ZERO) ||
506
- isVariant(bankBalance.balanceType, 'borrow') ||
507
- (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
576
+ isSpotPositionAvailable(spotPosition) ||
577
+ (marketIndex !== undefined &&
578
+ !spotPosition.marketIndex.eq(marketIndex))
508
579
  ) {
509
580
  return totalAssetValue;
510
581
  }
511
582
 
512
583
  // Todo this needs to account for whether it's based on initial or maintenance requirements
513
- const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
514
- bankBalance.bankIndex
515
- );
584
+ const spotMarketAccount: SpotMarketAccount =
585
+ this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
586
+
587
+ if (spotPosition.marketIndex.eq(QUOTE_SPOT_MARKET_INDEX)) {
588
+ if (isVariant(spotPosition.balanceType, 'deposit')) {
589
+ const tokenAmount = getTokenAmount(
590
+ spotPosition.balance,
591
+ spotMarketAccount,
592
+ spotPosition.balanceType
593
+ );
516
594
 
517
- const tokenAmount = getTokenAmount(
518
- bankBalance.balance,
519
- bankAccount,
520
- bankBalance.balanceType
595
+ return totalAssetValue.add(tokenAmount);
596
+ } else {
597
+ return totalAssetValue;
598
+ }
599
+ }
600
+
601
+ const oraclePriceData = this.getOracleDataForSpotMarket(
602
+ spotPosition.marketIndex
521
603
  );
522
604
 
523
- let assetValue = tokenAmount
524
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
525
- .div(MARK_PRICE_PRECISION)
526
- .div(
527
- new BN(10).pow(
528
- new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
529
- )
605
+ if (!includeOpenOrders) {
606
+ if (isVariant(spotPosition.balanceType, 'deposit')) {
607
+ const tokenAmount = getTokenAmount(
608
+ spotPosition.balance,
609
+ spotMarketAccount,
610
+ spotPosition.balanceType
611
+ );
612
+ const assetValue = this.getSpotAssetValue(
613
+ tokenAmount,
614
+ oraclePriceData,
615
+ spotMarketAccount,
616
+ marginCategory
617
+ );
618
+ return totalAssetValue.add(assetValue);
619
+ } else {
620
+ return totalAssetValue;
621
+ }
622
+ }
623
+
624
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
625
+ getWorstCaseTokenAmounts(
626
+ spotPosition,
627
+ spotMarketAccount,
628
+ this.getOracleDataForSpotMarket(spotPosition.marketIndex)
530
629
  );
531
- if (withWeightMarginCategory !== undefined) {
532
- const weight = calculateAssetWeight(
533
- tokenAmount,
534
- bankAccount,
535
- withWeightMarginCategory
630
+
631
+ let newTotalAssetValue = totalAssetValue;
632
+ if (worstCaseTokenAmount.gt(ZERO)) {
633
+ const baseAssetValue = this.getSpotAssetValue(
634
+ worstCaseTokenAmount,
635
+ oraclePriceData,
636
+ spotMarketAccount,
637
+ marginCategory
638
+ );
639
+
640
+ newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
641
+ }
642
+
643
+ if (worstCaseQuoteTokenAmount.gt(ZERO)) {
644
+ newTotalAssetValue = newTotalAssetValue.add(
645
+ worstCaseQuoteTokenAmount
536
646
  );
537
- assetValue = assetValue.mul(weight).div(BANK_WEIGHT_PRECISION);
538
647
  }
539
648
 
540
- return totalAssetValue.add(assetValue);
649
+ return newTotalAssetValue;
541
650
  },
542
651
  ZERO
543
652
  );
544
653
  }
545
654
 
546
- public getNetBankValue(withWeightMarginCategory?: MarginCategory): BN {
547
- return this.getBankAssetValue(undefined, withWeightMarginCategory).sub(
548
- this.getBankLiabilityValue(undefined, withWeightMarginCategory)
655
+ getSpotAssetValue(
656
+ tokenAmount: BN,
657
+ oraclePriceData: OraclePriceData,
658
+ spotMarketAccount: SpotMarketAccount,
659
+ marginCategory?: MarginCategory
660
+ ): BN {
661
+ let assetValue = getTokenValue(
662
+ tokenAmount,
663
+ spotMarketAccount.decimals,
664
+ oraclePriceData
665
+ );
666
+
667
+ if (marginCategory !== undefined) {
668
+ const weight = calculateAssetWeight(
669
+ tokenAmount,
670
+ spotMarketAccount,
671
+ marginCategory
672
+ );
673
+
674
+ assetValue = assetValue.mul(weight).div(SPOT_MARKET_WEIGHT_PRECISION);
675
+ }
676
+
677
+ return assetValue;
678
+ }
679
+
680
+ public getNetSpotMarketValue(withWeightMarginCategory?: MarginCategory): BN {
681
+ return this.getSpotMarketAssetValue(
682
+ undefined,
683
+ withWeightMarginCategory
684
+ ).sub(
685
+ this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory)
549
686
  );
550
687
  }
551
688
 
@@ -554,7 +691,7 @@ export class ClearingHouseUser {
554
691
  * @returns : Precision QUOTE_PRECISION
555
692
  */
556
693
  public getTotalCollateral(marginCategory: MarginCategory = 'Initial'): BN {
557
- return this.getBankAssetValue(undefined, marginCategory).add(
694
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(
558
695
  this.getUnrealizedPNL(true, undefined, marginCategory)
559
696
  );
560
697
  }
@@ -563,19 +700,99 @@ export class ClearingHouseUser {
563
700
  * calculates sum of position value across all positions in margin system
564
701
  * @returns : Precision QUOTE_PRECISION
565
702
  */
566
- getTotalPositionValue(): BN {
567
- return this.getUserAccount().positions.reduce(
568
- (positionValue, marketPosition) => {
569
- const market = this.clearingHouse.getMarketAccount(
570
- marketPosition.marketIndex
571
- );
572
- const posVal = calculateBaseAssetValueWithOracle(
573
- market,
574
- marketPosition,
575
- this.getOracleDataForMarket(market.marketIndex)
703
+ getTotalPerpPositionValue(
704
+ marginCategory?: MarginCategory,
705
+ liquidationBuffer?: BN,
706
+ includeOpenOrders?: boolean
707
+ ): BN {
708
+ return this.getUserAccount().perpPositions.reduce(
709
+ (totalPerpValue, perpPosition) => {
710
+ const market = this.clearingHouse.getPerpMarketAccount(
711
+ perpPosition.marketIndex
576
712
  );
577
713
 
578
- return positionValue.add(posVal);
714
+ if (perpPosition.lpShares.gt(ZERO)) {
715
+ // is an lp
716
+ // clone so we dont mutate the position
717
+ perpPosition = this.getClonedPosition(perpPosition);
718
+
719
+ // settle position
720
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(
721
+ market.marketIndex
722
+ );
723
+ perpPosition.baseAssetAmount =
724
+ settledPosition.baseAssetAmount.add(dustBaa);
725
+ perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
726
+
727
+ // open orders
728
+ let openAsks;
729
+ if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
730
+ openAsks = market.amm.maxBaseAssetReserve
731
+ .sub(market.amm.baseAssetReserve)
732
+ .mul(perpPosition.lpShares)
733
+ .div(market.amm.sqrtK)
734
+ .mul(new BN(-1));
735
+ } else {
736
+ openAsks = ZERO;
737
+ }
738
+
739
+ let openBids;
740
+ if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
741
+ openBids = market.amm.baseAssetReserve
742
+ .sub(market.amm.minBaseAssetReserve)
743
+ .mul(perpPosition.lpShares)
744
+ .div(market.amm.sqrtK);
745
+ } else {
746
+ openBids = ZERO;
747
+ }
748
+
749
+ perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
750
+ perpPosition.openBids = perpPosition.openBids.add(openBids);
751
+ }
752
+
753
+ let valuationPrice = this.getOracleDataForMarket(
754
+ market.marketIndex
755
+ ).price;
756
+
757
+ if (isVariant(market.status, 'settlement')) {
758
+ valuationPrice = market.settlementPrice;
759
+ }
760
+
761
+ const baseAssetAmount = includeOpenOrders
762
+ ? calculateWorstCaseBaseAssetAmount(perpPosition)
763
+ : perpPosition.baseAssetAmount;
764
+
765
+ let baseAssetValue = baseAssetAmount
766
+ .abs()
767
+ .mul(valuationPrice)
768
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
769
+
770
+ if (marginCategory) {
771
+ let marginRatio = new BN(
772
+ calculateMarketMarginRatio(
773
+ market,
774
+ baseAssetAmount.abs(),
775
+ marginCategory
776
+ )
777
+ );
778
+
779
+ if (marginCategory === 'Initial') {
780
+ marginRatio = BN.max(
781
+ marginRatio,
782
+ new BN(this.getUserAccount().customMarginRatio)
783
+ );
784
+ }
785
+
786
+ if (liquidationBuffer !== undefined) {
787
+ marginRatio = marginRatio.add(liquidationBuffer);
788
+ }
789
+
790
+ baseAssetValue = baseAssetValue
791
+ .mul(marginRatio)
792
+ .div(MARGIN_PRECISION);
793
+ }
794
+
795
+ return totalPerpValue.add(baseAssetValue);
579
796
  },
580
797
  ZERO
581
798
  );
@@ -585,13 +802,13 @@ export class ClearingHouseUser {
585
802
  * calculates position value in margin system
586
803
  * @returns : Precision QUOTE_PRECISION
587
804
  */
588
- public getPositionValue(
805
+ public getPerpPositionValue(
589
806
  marketIndex: BN,
590
807
  oraclePriceData: OraclePriceData
591
808
  ): BN {
592
809
  const userPosition =
593
810
  this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
594
- const market = this.clearingHouse.getMarketAccount(
811
+ const market = this.clearingHouse.getPerpMarketAccount(
595
812
  userPosition.marketIndex
596
813
  );
597
814
  return calculateBaseAssetValueWithOracle(
@@ -602,7 +819,7 @@ export class ClearingHouseUser {
602
819
  }
603
820
 
604
821
  public getPositionSide(
605
- currentPosition: Pick<UserPosition, 'baseAssetAmount'>
822
+ currentPosition: Pick<PerpPosition, 'baseAssetAmount'>
606
823
  ): PositionDirection | undefined {
607
824
  if (currentPosition.baseAssetAmount.gt(ZERO)) {
608
825
  return PositionDirection.LONG;
@@ -618,11 +835,13 @@ export class ClearingHouseUser {
618
835
  * @returns : Precision MARK_PRICE_PRECISION
619
836
  */
620
837
  public getPositionEstimatedExitPriceAndPnl(
621
- position: UserPosition,
838
+ position: PerpPosition,
622
839
  amountToClose?: BN,
623
840
  useAMMClose = false
624
841
  ): [BN, BN] {
625
- const market = this.clearingHouse.getMarketAccount(position.marketIndex);
842
+ const market = this.clearingHouse.getPerpMarketAccount(
843
+ position.marketIndex
844
+ );
626
845
 
627
846
  const entryPrice = calculateEntryPrice(position);
628
847
 
@@ -637,7 +856,7 @@ export class ClearingHouseUser {
637
856
  lastCumulativeFundingRate: position.lastCumulativeFundingRate,
638
857
  marketIndex: position.marketIndex,
639
858
  quoteAssetAmount: position.quoteAssetAmount,
640
- } as UserPosition;
859
+ } as PerpPosition;
641
860
  }
642
861
 
643
862
  let baseAssetValue: BN;
@@ -677,13 +896,31 @@ export class ClearingHouseUser {
677
896
  * calculates current user leverage across all positions
678
897
  * @returns : Precision TEN_THOUSAND
679
898
  */
680
- public getLeverage(): BN {
681
- const totalCollateral = this.getTotalCollateral();
682
- const totalPositionValue = this.getTotalPositionValue();
683
- if (totalPositionValue.eq(ZERO) && totalCollateral.eq(ZERO)) {
899
+ public getLeverage(marginCategory?: MarginCategory): BN {
900
+ const totalLiabilityValue = this.getTotalPerpPositionValue(
901
+ marginCategory,
902
+ undefined,
903
+ true
904
+ ).add(
905
+ this.getSpotMarketLiabilityValue(
906
+ undefined,
907
+ marginCategory,
908
+ undefined,
909
+ true
910
+ )
911
+ );
912
+
913
+ const totalAssetValue = this.getSpotMarketAssetValue(
914
+ undefined,
915
+ marginCategory,
916
+ true
917
+ ).add(this.getUnrealizedPNL(true, undefined, marginCategory));
918
+
919
+ if (totalAssetValue.eq(ZERO) && totalLiabilityValue.eq(ZERO)) {
684
920
  return ZERO;
685
921
  }
686
- return totalPositionValue.mul(TEN_THOUSAND).div(totalCollateral);
922
+
923
+ return totalLiabilityValue.mul(TEN_THOUSAND).div(totalAssetValue);
687
924
  }
688
925
 
689
926
  /**
@@ -695,7 +932,7 @@ export class ClearingHouseUser {
695
932
  marketIndex: BN | number,
696
933
  category: MarginCategory = 'Initial'
697
934
  ): BN {
698
- const market = this.clearingHouse.getMarketAccount(marketIndex);
935
+ const market = this.clearingHouse.getPerpMarketAccount(marketIndex);
699
936
 
700
937
  const marginRatioCategory = calculateMarketMarginRatio(
701
938
  market,
@@ -713,23 +950,46 @@ export class ClearingHouseUser {
713
950
  * calculates margin ratio: total collateral / |total position value|
714
951
  * @returns : Precision TEN_THOUSAND
715
952
  */
716
- public getMarginRatio(): BN {
717
- const totalPositionValue = this.getTotalPositionValue();
953
+ public getMarginRatio(marginCategory?: MarginCategory): BN {
954
+ const totalLiabilityValue = this.getTotalPerpPositionValue(
955
+ marginCategory,
956
+ undefined,
957
+ true
958
+ ).add(
959
+ this.getSpotMarketLiabilityValue(
960
+ undefined,
961
+ marginCategory,
962
+ undefined,
963
+ true
964
+ )
965
+ );
718
966
 
719
- if (totalPositionValue.eq(ZERO)) {
967
+ if (totalLiabilityValue.eq(ZERO)) {
720
968
  return BN_MAX;
721
969
  }
722
970
 
723
- return this.getTotalCollateral().mul(TEN_THOUSAND).div(totalPositionValue);
971
+ const totalAssetValue = this.getSpotMarketAssetValue(
972
+ undefined,
973
+ marginCategory,
974
+ true
975
+ ).add(this.getUnrealizedPNL(true, undefined, marginCategory));
976
+
977
+ return totalAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
724
978
  }
725
979
 
726
- public canBeLiquidated(): [boolean, BN] {
980
+ public canBeLiquidated(): boolean {
727
981
  const totalCollateral = this.getTotalCollateral();
728
- const partialMaintenanceRequirement =
729
- this.getMaintenanceMarginRequirement();
730
- const marginRatio = this.getMarginRatio();
731
- const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
732
- return [canLiquidate, marginRatio];
982
+
983
+ // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
984
+ let liquidationBuffer = undefined;
985
+ if (this.getUserAccount().beingLiquidated) {
986
+ liquidationBuffer = new BN(
987
+ this.clearingHouse.getStateAccount().liquidationMarginBufferRatio
988
+ );
989
+ }
990
+ const maintenanceRequirement =
991
+ this.getMaintenanceMarginRequirement(liquidationBuffer);
992
+ return totalCollateral.lt(maintenanceRequirement);
733
993
  }
734
994
 
735
995
  /**
@@ -737,12 +997,12 @@ export class ClearingHouseUser {
737
997
  * @returns
738
998
  */
739
999
  public needsToSettleFundingPayment(): boolean {
740
- for (const userPosition of this.getUserAccount().positions) {
1000
+ for (const userPosition of this.getUserAccount().perpPositions) {
741
1001
  if (userPosition.baseAssetAmount.eq(ZERO)) {
742
1002
  continue;
743
1003
  }
744
1004
 
745
- const market = this.clearingHouse.getMarketAccount(
1005
+ const market = this.clearingHouse.getPerpMarketAccount(
746
1006
  userPosition.marketIndex
747
1007
  );
748
1008
  if (
@@ -763,13 +1023,13 @@ export class ClearingHouseUser {
763
1023
 
764
1024
  /**
765
1025
  * Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
766
- * @param marketPosition
1026
+ * @param PerpPosition
767
1027
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
768
1028
  * @param partial
769
1029
  * @returns Precision : MARK_PRICE_PRECISION
770
1030
  */
771
1031
  public liquidationPrice(
772
- marketPosition: Pick<UserPosition, 'marketIndex'>,
1032
+ perpPosition: Pick<PerpPosition, 'marketIndex'>,
773
1033
  positionBaseSizeChange: BN = ZERO
774
1034
  ): BN {
775
1035
  // solves formula for example canBeLiquidated below
@@ -788,22 +1048,23 @@ export class ClearingHouseUser {
788
1048
 
789
1049
  // calculate the total position value ignoring any value from the target market of the trade
790
1050
  const totalPositionValueExcludingTargetMarket =
791
- this.getTotalPositionValueExcludingMarket(marketPosition.marketIndex);
1051
+ this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
792
1052
 
793
- const currentMarketPosition =
794
- this.getUserPosition(marketPosition.marketIndex) ||
795
- this.getEmptyPosition(marketPosition.marketIndex);
1053
+ const currentPerpPosition =
1054
+ this.getUserPosition(perpPosition.marketIndex) ||
1055
+ this.getEmptyPosition(perpPosition.marketIndex);
796
1056
 
797
- const currentMarketPositionBaseSize = currentMarketPosition.baseAssetAmount;
1057
+ const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
798
1058
 
799
- const proposedBaseAssetAmount = currentMarketPositionBaseSize.add(
1059
+ const proposedBaseAssetAmount = currentPerpPositionBaseSize.add(
800
1060
  positionBaseSizeChange
801
1061
  );
802
1062
 
803
1063
  // calculate position for current market after trade
804
- const proposedMarketPosition: UserPosition = {
805
- marketIndex: marketPosition.marketIndex,
1064
+ const proposedPerpPosition: PerpPosition = {
1065
+ marketIndex: perpPosition.marketIndex,
806
1066
  baseAssetAmount: proposedBaseAssetAmount,
1067
+ remainderBaseAssetAmount: ZERO,
807
1068
  quoteAssetAmount: new BN(0),
808
1069
  lastCumulativeFundingRate: ZERO,
809
1070
  quoteEntryAmount: new BN(0),
@@ -819,25 +1080,25 @@ export class ClearingHouseUser {
819
1080
 
820
1081
  if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
821
1082
 
822
- const market = this.clearingHouse.getMarketAccount(
823
- proposedMarketPosition.marketIndex
1083
+ const market = this.clearingHouse.getPerpMarketAccount(
1084
+ proposedPerpPosition.marketIndex
824
1085
  );
825
1086
 
826
- const proposedMarketPositionValue = calculateBaseAssetValueWithOracle(
1087
+ const proposedPerpPositionValue = calculateBaseAssetValueWithOracle(
827
1088
  market,
828
- proposedMarketPosition,
1089
+ proposedPerpPosition,
829
1090
  this.getOracleDataForMarket(market.marketIndex)
830
1091
  );
831
1092
 
832
1093
  // total position value after trade
833
1094
  const totalPositionValueAfterTrade =
834
- totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
1095
+ totalPositionValueExcludingTargetMarket.add(proposedPerpPositionValue);
835
1096
 
836
1097
  const marginRequirementExcludingTargetMarket =
837
- this.getUserAccount().positions.reduce(
1098
+ this.getUserAccount().perpPositions.reduce(
838
1099
  (totalMarginRequirement, position) => {
839
- if (!position.marketIndex.eq(marketPosition.marketIndex)) {
840
- const market = this.clearingHouse.getMarketAccount(
1100
+ if (!position.marketIndex.eq(perpPosition.marketIndex)) {
1101
+ const market = this.clearingHouse.getPerpMarketAccount(
841
1102
  position.marketIndex
842
1103
  );
843
1104
  const positionValue = calculateBaseAssetValueWithOracle(
@@ -872,19 +1133,19 @@ export class ClearingHouseUser {
872
1133
  // if the position value after the trade is less than free collateral, there is no liq price
873
1134
  if (
874
1135
  totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
875
- proposedMarketPosition.baseAssetAmount.abs().gt(ZERO)
1136
+ proposedPerpPosition.baseAssetAmount.abs().gt(ZERO)
876
1137
  ) {
877
1138
  return new BN(-1);
878
1139
  }
879
1140
 
880
1141
  const marginRequirementAfterTrade =
881
1142
  marginRequirementExcludingTargetMarket.add(
882
- proposedMarketPositionValue
1143
+ proposedPerpPositionValue
883
1144
  .mul(
884
1145
  new BN(
885
1146
  calculateMarketMarginRatio(
886
1147
  market,
887
- proposedMarketPosition.baseAssetAmount.abs(),
1148
+ proposedPerpPosition.baseAssetAmount.abs(),
888
1149
  'Maintenance'
889
1150
  )
890
1151
  )
@@ -896,7 +1157,7 @@ export class ClearingHouseUser {
896
1157
  );
897
1158
 
898
1159
  const marketMaxLeverage = this.getMaxLeverage(
899
- proposedMarketPosition.marketIndex,
1160
+ proposedPerpPosition.marketIndex,
900
1161
  'Maintenance'
901
1162
  );
902
1163
 
@@ -920,8 +1181,8 @@ export class ClearingHouseUser {
920
1181
  let markPriceAfterTrade;
921
1182
  if (positionBaseSizeChange.eq(ZERO)) {
922
1183
  markPriceAfterTrade = calculateMarkPrice(
923
- this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
924
- this.getOracleDataForMarket(marketPosition.marketIndex)
1184
+ this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex),
1185
+ this.getOracleDataForMarket(perpPosition.marketIndex)
925
1186
  );
926
1187
  } else {
927
1188
  const direction = positionBaseSizeChange.gt(ZERO)
@@ -930,9 +1191,9 @@ export class ClearingHouseUser {
930
1191
  markPriceAfterTrade = calculateTradeSlippage(
931
1192
  direction,
932
1193
  positionBaseSizeChange.abs(),
933
- this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
1194
+ this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex),
934
1195
  'base',
935
- this.getOracleDataForMarket(marketPosition.marketIndex)
1196
+ this.getOracleDataForMarket(perpPosition.marketIndex)
936
1197
  )[3]; // newPrice after swap
937
1198
  }
938
1199
 
@@ -1019,7 +1280,7 @@ export class ClearingHouseUser {
1019
1280
  // add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
1020
1281
  const oppositeSizeValueUSDC = targetingSameSide
1021
1282
  ? ZERO
1022
- : this.getPositionValue(targetMarketIndex, oracleData);
1283
+ : this.getPerpPositionValue(targetMarketIndex, oracleData);
1023
1284
 
1024
1285
  let maxPositionSize = this.getBuyingPower(targetMarketIndex);
1025
1286
  if (maxPositionSize.gte(ZERO)) {
@@ -1036,21 +1297,22 @@ export class ClearingHouseUser {
1036
1297
  // current leverage is greater than max leverage - can only reduce position size
1037
1298
 
1038
1299
  if (!targetingSameSide) {
1039
- const market = this.clearingHouse.getMarketAccount(targetMarketIndex);
1040
- const marketPositionValue = this.getPositionValue(
1300
+ const market =
1301
+ this.clearingHouse.getPerpMarketAccount(targetMarketIndex);
1302
+ const perpPositionValue = this.getPerpPositionValue(
1041
1303
  targetMarketIndex,
1042
1304
  oracleData
1043
1305
  );
1044
1306
  const totalCollateral = this.getTotalCollateral();
1045
1307
  const marginRequirement = this.getInitialMarginRequirement();
1046
- const marginFreedByClosing = marketPositionValue
1308
+ const marginFreedByClosing = perpPositionValue
1047
1309
  .mul(new BN(market.marginRatioInitial))
1048
1310
  .div(MARGIN_PRECISION);
1049
1311
  const marginRequirementAfterClosing =
1050
1312
  marginRequirement.sub(marginFreedByClosing);
1051
1313
 
1052
1314
  if (marginRequirementAfterClosing.gt(totalCollateral)) {
1053
- maxPositionSize = marketPositionValue;
1315
+ maxPositionSize = perpPositionValue;
1054
1316
  } else {
1055
1317
  const freeCollateralAfterClose = totalCollateral.sub(
1056
1318
  marginRequirementAfterClosing
@@ -1058,7 +1320,7 @@ export class ClearingHouseUser {
1058
1320
  const buyingPowerAfterClose = freeCollateralAfterClose
1059
1321
  .mul(this.getMaxLeverage(targetMarketIndex))
1060
1322
  .div(TEN_THOUSAND);
1061
- maxPositionSize = marketPositionValue.add(buyingPowerAfterClose);
1323
+ maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
1062
1324
  }
1063
1325
  } else {
1064
1326
  // do nothing if targetting same side
@@ -1091,7 +1353,7 @@ export class ClearingHouseUser {
1091
1353
 
1092
1354
  const oracleData = this.getOracleDataForMarket(targetMarketIndex);
1093
1355
 
1094
- let currentPositionQuoteAmount = this.getPositionValue(
1356
+ let currentPositionQuoteAmount = this.getPerpPositionValue(
1095
1357
  targetMarketIndex,
1096
1358
  oracleData
1097
1359
  );
@@ -1107,16 +1369,16 @@ export class ClearingHouseUser {
1107
1369
  if (tradeSide === PositionDirection.SHORT)
1108
1370
  tradeQuoteAmount = tradeQuoteAmount.neg();
1109
1371
 
1110
- const currentMarketPositionAfterTrade = currentPositionQuoteAmount
1372
+ const currentPerpPositionAfterTrade = currentPositionQuoteAmount
1111
1373
  .add(tradeQuoteAmount)
1112
1374
  .abs();
1113
1375
 
1114
1376
  const totalPositionAfterTradeExcludingTargetMarket =
1115
- this.getTotalPositionValueExcludingMarket(targetMarketIndex);
1377
+ this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
1116
1378
 
1117
1379
  const totalCollateral = this.getTotalCollateral();
1118
1380
  if (totalCollateral.gt(ZERO)) {
1119
- const newLeverage = currentMarketPositionAfterTrade
1381
+ const newLeverage = currentPerpPositionAfterTrade
1120
1382
  .add(totalPositionAfterTradeExcludingTargetMarket)
1121
1383
  .abs()
1122
1384
  .mul(TEN_THOUSAND)
@@ -1133,7 +1395,7 @@ export class ClearingHouseUser {
1133
1395
  * @returns feeForQuote : Precision QUOTE_PRECISION
1134
1396
  */
1135
1397
  public calculateFeeForQuoteAmount(quoteAmount: BN): BN {
1136
- const feeStructure = this.clearingHouse.getStateAccount().feeStructure;
1398
+ const feeStructure = this.clearingHouse.getStateAccount().perpFeeStructure;
1137
1399
 
1138
1400
  return quoteAmount
1139
1401
  .mul(feeStructure.feeNumerator)
@@ -1145,34 +1407,35 @@ export class ClearingHouseUser {
1145
1407
  * @param marketToIgnore
1146
1408
  * @returns positionValue : Precision QUOTE_PRECISION
1147
1409
  */
1148
- private getTotalPositionValueExcludingMarket(marketToIgnore: BN): BN {
1149
- const currentMarketPosition =
1410
+ private getTotalPerpPositionValueExcludingMarket(marketToIgnore: BN): BN {
1411
+ const currentPerpPosition =
1150
1412
  this.getUserPosition(marketToIgnore) ||
1151
1413
  this.getEmptyPosition(marketToIgnore);
1152
1414
 
1153
1415
  const oracleData = this.getOracleDataForMarket(marketToIgnore);
1154
1416
 
1155
- let currentMarketPositionValueUSDC = ZERO;
1156
- if (currentMarketPosition) {
1157
- currentMarketPositionValueUSDC = this.getPositionValue(
1417
+ let currentPerpPositionValueUSDC = ZERO;
1418
+ if (currentPerpPosition) {
1419
+ currentPerpPositionValueUSDC = this.getPerpPositionValue(
1158
1420
  marketToIgnore,
1159
1421
  oracleData
1160
1422
  );
1161
1423
  }
1162
1424
 
1163
- return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
1425
+ return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
1164
1426
  }
1165
1427
 
1166
1428
  private getOracleDataForMarket(marketIndex: BN): OraclePriceData {
1167
1429
  const oracleKey =
1168
- this.clearingHouse.getMarketAccount(marketIndex).amm.oracle;
1430
+ this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
1169
1431
  const oracleData =
1170
1432
  this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
1171
1433
 
1172
1434
  return oracleData;
1173
1435
  }
1174
- private getOracleDataForBank(bankIndex: BN): OraclePriceData {
1175
- const oracleKey = this.clearingHouse.getBankAccount(bankIndex).oracle;
1436
+ private getOracleDataForSpotMarket(marketIndex: BN): OraclePriceData {
1437
+ const oracleKey =
1438
+ this.clearingHouse.getSpotMarketAccount(marketIndex).oracle;
1176
1439
 
1177
1440
  const oracleData =
1178
1441
  this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;