@drift-labs/sdk 0.2.0-master.2 → 0.2.0-master.22

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 (149) hide show
  1. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  2. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  3. package/lib/accounts/bulkUserSubscription.js +0 -1
  4. package/lib/accounts/fetch.d.ts +2 -1
  5. package/lib/accounts/fetch.js +9 -1
  6. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  7. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  8. package/lib/accounts/types.d.ts +14 -1
  9. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  10. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  11. package/lib/addresses/pda.d.ts +1 -0
  12. package/lib/addresses/pda.js +8 -1
  13. package/lib/admin.d.ts +9 -5
  14. package/lib/admin.js +52 -11
  15. package/lib/clearingHouse.d.ts +52 -23
  16. package/lib/clearingHouse.js +736 -200
  17. package/lib/clearingHouseConfig.d.ts +1 -0
  18. package/lib/clearingHouseUser.d.ts +24 -16
  19. package/lib/clearingHouseUser.js +223 -101
  20. package/lib/clearingHouseUserStats.d.ts +18 -0
  21. package/lib/clearingHouseUserStats.js +49 -0
  22. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  23. package/lib/clearingHouseUserStatsConfig.js +2 -0
  24. package/lib/config.js +1 -1
  25. package/lib/constants/banks.d.ts +2 -2
  26. package/lib/constants/banks.js +12 -4
  27. package/lib/constants/numericConstants.d.ts +5 -0
  28. package/lib/constants/numericConstants.js +8 -3
  29. package/lib/events/eventList.js +3 -0
  30. package/lib/events/types.d.ts +3 -1
  31. package/lib/events/types.js +2 -0
  32. package/lib/factory/bigNum.d.ts +1 -0
  33. package/lib/factory/bigNum.js +34 -10
  34. package/lib/idl/clearing_house.json +1603 -377
  35. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  36. package/lib/index.d.ts +9 -3
  37. package/lib/index.js +13 -3
  38. package/lib/math/amm.d.ts +1 -0
  39. package/lib/math/amm.js +22 -38
  40. package/lib/math/auction.js +4 -1
  41. package/lib/math/bankBalance.d.ts +7 -1
  42. package/lib/math/bankBalance.js +77 -2
  43. package/lib/math/margin.d.ts +11 -0
  44. package/lib/math/margin.js +72 -0
  45. package/lib/math/market.d.ts +4 -1
  46. package/lib/math/market.js +35 -1
  47. package/lib/math/oracles.d.ts +3 -0
  48. package/lib/math/oracles.js +25 -5
  49. package/lib/math/orders.d.ts +6 -2
  50. package/lib/math/orders.js +62 -12
  51. package/lib/math/position.d.ts +8 -0
  52. package/lib/math/position.js +45 -12
  53. package/lib/math/trade.d.ts +1 -1
  54. package/lib/math/trade.js +7 -10
  55. package/lib/orderParams.d.ts +14 -5
  56. package/lib/orderParams.js +8 -96
  57. package/lib/slot/SlotSubscriber.d.ts +7 -0
  58. package/lib/slot/SlotSubscriber.js +3 -0
  59. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  60. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  61. package/lib/tx/retryTxSender.js +9 -2
  62. package/lib/tx/utils.js +1 -1
  63. package/lib/types.d.ts +236 -26
  64. package/lib/types.js +64 -1
  65. package/lib/util/computeUnits.js +1 -1
  66. package/lib/util/getTokenAddress.d.ts +2 -0
  67. package/lib/util/getTokenAddress.js +9 -0
  68. package/package.json +3 -3
  69. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  70. package/src/accounts/bulkUserSubscription.ts +0 -1
  71. package/src/accounts/fetch.ts +27 -2
  72. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  73. package/src/accounts/types.ts +18 -0
  74. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  75. package/src/addresses/marketAddresses.js +26 -0
  76. package/src/addresses/pda.ts +13 -0
  77. package/src/admin.ts +82 -15
  78. package/src/assert/assert.js +9 -0
  79. package/src/clearingHouse.ts +1232 -323
  80. package/src/clearingHouseConfig.ts +1 -0
  81. package/src/clearingHouseUser.ts +343 -155
  82. package/src/clearingHouseUserStats.ts +75 -0
  83. package/src/clearingHouseUserStatsConfig.ts +18 -0
  84. package/src/config.ts +1 -1
  85. package/src/constants/banks.js +42 -0
  86. package/src/constants/banks.ts +14 -4
  87. package/src/constants/markets.js +42 -0
  88. package/src/constants/numericConstants.js +41 -0
  89. package/src/constants/numericConstants.ts +14 -2
  90. package/src/events/eventList.js +77 -0
  91. package/src/events/eventList.ts +3 -0
  92. package/src/events/eventSubscriber.js +139 -0
  93. package/src/events/sort.js +44 -0
  94. package/src/events/txEventCache.js +71 -0
  95. package/src/events/types.ts +6 -0
  96. package/src/examples/makeTradeExample.js +80 -0
  97. package/src/factory/bigNum.js +390 -0
  98. package/src/factory/bigNum.ts +42 -13
  99. package/src/factory/oracleClient.js +20 -0
  100. package/src/idl/clearing_house.json +1603 -377
  101. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  102. package/src/index.ts +9 -3
  103. package/src/math/amm.ts +54 -55
  104. package/src/math/auction.js +42 -0
  105. package/src/math/auction.ts +5 -1
  106. package/src/math/bankBalance.ts +148 -2
  107. package/src/math/conversion.js +11 -0
  108. package/src/math/funding.js +248 -0
  109. package/src/math/margin.ts +124 -0
  110. package/src/math/market.ts +66 -1
  111. package/src/math/oracles.js +26 -0
  112. package/src/math/oracles.ts +42 -5
  113. package/src/math/orders.ts +129 -13
  114. package/src/math/position.ts +64 -9
  115. package/src/math/repeg.js +128 -0
  116. package/src/math/state.js +15 -0
  117. package/src/math/trade.js +253 -0
  118. package/src/math/trade.ts +23 -25
  119. package/src/math/utils.js +0 -1
  120. package/src/oracles/oracleClientCache.js +19 -0
  121. package/src/oracles/pythClient.js +46 -0
  122. package/src/oracles/quoteAssetOracleClient.js +32 -0
  123. package/src/oracles/switchboardClient.js +69 -0
  124. package/src/oracles/types.js +2 -0
  125. package/src/orderParams.js +20 -0
  126. package/src/orderParams.ts +20 -141
  127. package/src/slot/SlotSubscriber.js +39 -0
  128. package/src/slot/SlotSubscriber.ts +11 -1
  129. package/src/token/index.js +38 -0
  130. package/src/tokenFaucet.js +189 -0
  131. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  132. package/src/tx/retryTxSender.ts +11 -3
  133. package/src/tx/types.js +2 -0
  134. package/src/tx/utils.js +17 -0
  135. package/src/tx/utils.ts +1 -1
  136. package/src/types.ts +239 -27
  137. package/src/userName.js +20 -0
  138. package/src/util/computeUnits.js +21 -11
  139. package/src/util/computeUnits.ts +1 -1
  140. package/src/util/getTokenAddress.js +9 -0
  141. package/src/util/getTokenAddress.ts +18 -0
  142. package/src/util/promiseTimeout.js +14 -0
  143. package/src/util/tps.js +27 -0
  144. package/src/wallet.js +35 -0
  145. package/tests/bn/test.ts +10 -0
  146. package/lib/orders.d.ts +0 -8
  147. package/lib/orders.js +0 -142
  148. package/src/orders.ts +0 -251
  149. package/src/util/computeUnits.js.map +0 -1
@@ -21,6 +21,7 @@ import {
21
21
  PRICE_TO_QUOTE_PRECISION,
22
22
  MARGIN_PRECISION,
23
23
  BANK_WEIGHT_PRECISION,
24
+ BANK_BALANCE_PRECISION_EXP,
24
25
  } from './constants/numericConstants';
25
26
  import {
26
27
  UserAccountSubscriber,
@@ -32,17 +33,26 @@ import {
32
33
  calculateBaseAssetValue,
33
34
  calculatePositionFundingPNL,
34
35
  calculatePositionPNL,
36
+ calculateUnrealizedAssetWeight,
37
+ calculateMarketMarginRatio,
35
38
  PositionDirection,
36
39
  calculateTradeSlippage,
37
40
  BN,
38
41
  BankAccount,
39
42
  } from '.';
40
- import { getTokenAmount } from './math/bankBalance';
43
+ import {
44
+ getTokenAmount,
45
+ calculateAssetWeight,
46
+ calculateLiabilityWeight,
47
+ } from './math/bankBalance';
48
+ import {
49
+ calculateMarginBaseAssetValue,
50
+ calculateWorstCaseBaseAssetAmount,
51
+ } from './math/margin';
41
52
  import { OraclePriceData } from './oracles/types';
42
53
  import { ClearingHouseUserConfig } from './clearingHouseUserConfig';
43
54
  import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
44
55
  import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
45
-
46
56
  export class ClearingHouseUser {
47
57
  clearingHouse: ClearingHouse;
48
58
  userAccountPublicKey: PublicKey;
@@ -124,12 +134,21 @@ export class ClearingHouseUser {
124
134
  quoteAssetAmount: ZERO,
125
135
  quoteEntryAmount: ZERO,
126
136
  openOrders: ZERO,
127
- unsettledPnl: ZERO,
128
137
  openBids: ZERO,
129
138
  openAsks: ZERO,
139
+ realizedPnl: ZERO,
140
+ lpShares: ZERO,
141
+ lastFeePerLp: ZERO,
142
+ lastNetBaseAssetAmountPerLp: ZERO,
143
+ lastNetQuoteAssetAmountPerLp: ZERO,
130
144
  };
131
145
  }
132
146
 
147
+ public getClonedPosition(position: UserPosition): UserPosition {
148
+ const clonedPosition = Object.assign({}, position);
149
+ return clonedPosition;
150
+ }
151
+
133
152
  /**
134
153
  * @param orderId
135
154
  * @returns Order
@@ -162,6 +181,100 @@ export class ClearingHouseUser {
162
181
  return userAccountRPCResponse.value !== null;
163
182
  }
164
183
 
184
+ /**
185
+ * calculates the market position if the lp position was settled
186
+ * @returns : userPosition
187
+ */
188
+ public getSettledLPPosition(marketIndex: BN): [UserPosition, BN, BN] {
189
+ const _position = this.getUserPosition(marketIndex);
190
+ const position = this.getClonedPosition(_position);
191
+
192
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
193
+ const nShares = position.lpShares;
194
+
195
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
196
+ .sub(position.lastNetBaseAssetAmountPerLp)
197
+ .mul(nShares)
198
+ .div(AMM_RESERVE_PRECISION);
199
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
200
+ .sub(position.lastNetQuoteAssetAmountPerLp)
201
+ .mul(nShares)
202
+ .div(AMM_RESERVE_PRECISION);
203
+
204
+ function sign(v: BN) {
205
+ const sign = { true: new BN(1), false: new BN(-1) }[
206
+ v.gte(ZERO).toString()
207
+ ];
208
+ return sign;
209
+ }
210
+
211
+ const remainder = deltaBaa
212
+ .abs()
213
+ .mod(market.amm.baseAssetAmountStepSize)
214
+ .mul(sign(deltaBaa));
215
+ const _standardizedBaa = deltaBaa.sub(remainder);
216
+
217
+ let remainderBaa;
218
+ if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
219
+ remainderBaa = remainder;
220
+ } else {
221
+ remainderBaa = deltaBaa;
222
+ }
223
+ const standardizedBaa = deltaBaa.sub(remainderBaa);
224
+
225
+ const reaminderPerLP = remainderBaa.mul(AMM_RESERVE_PRECISION).div(nShares);
226
+
227
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
228
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
229
+
230
+ position.lastNetBaseAssetAmountPerLp =
231
+ market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
232
+
233
+ let updateType;
234
+ if (position.baseAssetAmount.eq(ZERO)) {
235
+ updateType = 'open';
236
+ } else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
237
+ updateType = 'increase';
238
+ } else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
239
+ updateType = 'reduce';
240
+ } else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
241
+ updateType = 'close';
242
+ } else {
243
+ updateType = 'flip';
244
+ }
245
+
246
+ let newQuoteEntry;
247
+ let pnl;
248
+ if (updateType == 'open' || updateType == 'increase') {
249
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
250
+ pnl = 0;
251
+ } else if (updateType == 'reduce' || updateType == 'close') {
252
+ newQuoteEntry = position.quoteEntryAmount.sub(
253
+ position.quoteEntryAmount
254
+ .mul(deltaBaa.abs())
255
+ .div(position.baseAssetAmount.abs())
256
+ );
257
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry);
258
+ } else {
259
+ newQuoteEntry = deltaQaa.sub(
260
+ deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs())
261
+ );
262
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
263
+ }
264
+ position.quoteEntryAmount = newQuoteEntry;
265
+
266
+ if (position.baseAssetAmount.gt(ZERO)) {
267
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
268
+ } else if (position.baseAssetAmount.lt(ZERO)) {
269
+ position.lastCumulativeFundingRate =
270
+ market.amm.cumulativeFundingRateShort;
271
+ } else {
272
+ position.lastCumulativeFundingRate = ZERO;
273
+ }
274
+
275
+ return [position, remainderBaa, pnl];
276
+ }
277
+
165
278
  /**
166
279
  * calculates Buying Power = FC * MAX_LEVERAGE
167
280
  * @returns : Precision QUOTE_PRECISION
@@ -183,82 +296,133 @@ export class ClearingHouseUser {
183
296
  return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
184
297
  }
185
298
 
186
- public getInitialMarginRequirement(): BN {
299
+ /**
300
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
301
+ */
302
+ public getMarginRequirement(type: MarginCategory): BN {
187
303
  return this.getUserAccount()
188
304
  .positions.reduce((marginRequirement, marketPosition) => {
189
305
  const market = this.clearingHouse.getMarketAccount(
190
306
  marketPosition.marketIndex
191
307
  );
308
+
309
+ if (marketPosition.lpShares.gt(ZERO)) {
310
+ // is an lp
311
+ // clone so we dont mutate the position
312
+ marketPosition = this.getClonedPosition(marketPosition);
313
+
314
+ // settle position
315
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(
316
+ market.marketIndex
317
+ );
318
+ marketPosition.baseAssetAmount =
319
+ settledPosition.baseAssetAmount.add(dustBaa);
320
+ marketPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
321
+
322
+ // open orders
323
+ let openAsks;
324
+ if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
325
+ openAsks = market.amm.maxBaseAssetReserve
326
+ .sub(market.amm.baseAssetReserve)
327
+ .mul(marketPosition.lpShares)
328
+ .div(market.amm.sqrtK)
329
+ .mul(new BN(-1));
330
+ } else {
331
+ openAsks = ZERO;
332
+ }
333
+
334
+ let openBids;
335
+ if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
336
+ openBids = market.amm.baseAssetReserve
337
+ .sub(market.amm.minBaseAssetReserve)
338
+ .mul(marketPosition.lpShares)
339
+ .div(market.amm.sqrtK);
340
+ } else {
341
+ openBids = ZERO;
342
+ }
343
+
344
+ marketPosition.openAsks = marketPosition.openAsks.add(openAsks);
345
+ marketPosition.openBids = marketPosition.openBids.add(openBids);
346
+ }
347
+
348
+ const worstCaseBaseAssetAmount =
349
+ calculateWorstCaseBaseAssetAmount(marketPosition);
350
+
351
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
352
+ .abs()
353
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
354
+ .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(MARK_PRICE_PRECISION));
355
+
192
356
  return marginRequirement.add(
193
- calculateBaseAssetValue(
194
- market,
195
- marketPosition,
196
- this.getOracleDataForMarket(market.marketIndex)
197
- )
198
- .mul(new BN(market.marginRatioInitial))
357
+ worstCaseAssetValue
358
+ .mul(
359
+ new BN(
360
+ calculateMarketMarginRatio(
361
+ market,
362
+ worstCaseBaseAssetAmount.abs(),
363
+ type
364
+ )
365
+ )
366
+ )
199
367
  .div(MARGIN_PRECISION)
200
368
  );
201
369
  }, ZERO)
202
- .add(this.getTotalLiability());
370
+ .add(this.getBankLiabilityValue(undefined, type));
203
371
  }
204
372
 
205
373
  /**
206
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
374
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
207
375
  */
208
- public getPartialMarginRequirement(): BN {
209
- return this.getUserAccount()
210
- .positions.reduce((marginRequirement, marketPosition) => {
211
- const market = this.clearingHouse.getMarketAccount(
212
- marketPosition.marketIndex
213
- );
214
- return marginRequirement.add(
215
- calculateBaseAssetValue(
216
- market,
217
- marketPosition,
218
- this.getOracleDataForMarket(market.marketIndex)
219
- )
220
- .mul(new BN(market.marginRatioPartial))
221
- .div(MARGIN_PRECISION)
222
- );
223
- }, ZERO)
224
- .add(this.getTotalLiability());
376
+ public getInitialMarginRequirement(): BN {
377
+ return this.getMarginRequirement('Initial');
378
+ }
379
+
380
+ /**
381
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
382
+ */
383
+ public getMaintenanceMarginRequirement(): BN {
384
+ return this.getMarginRequirement('Maintenance');
225
385
  }
226
386
 
227
387
  /**
228
388
  * calculates unrealized position price pnl
229
389
  * @returns : Precision QUOTE_PRECISION
230
390
  */
231
- public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN {
391
+ public getUnrealizedPNL(
392
+ withFunding?: boolean,
393
+ marketIndex?: BN,
394
+ withWeightMarginCategory?: MarginCategory
395
+ ): BN {
232
396
  return this.getUserAccount()
233
397
  .positions.filter((pos) =>
234
398
  marketIndex ? pos.marketIndex === marketIndex : true
235
399
  )
236
- .reduce((pnl, marketPosition) => {
400
+ .reduce((unrealizedPnl, marketPosition) => {
237
401
  const market = this.clearingHouse.getMarketAccount(
238
402
  marketPosition.marketIndex
239
403
  );
240
- return pnl.add(
241
- calculatePositionPNL(
242
- market,
243
- marketPosition,
244
- withFunding,
245
- this.getOracleDataForMarket(market.marketIndex)
246
- )
404
+ let positionUnrealizedPnl = calculatePositionPNL(
405
+ market,
406
+ marketPosition,
407
+ withFunding,
408
+ this.getOracleDataForMarket(market.marketIndex)
247
409
  );
248
- }, ZERO);
249
- }
250
410
 
251
- /**
252
- * calculates unrealized position price pnl
253
- * @returns : Precision QUOTE_PRECISION
254
- */
255
- public getUnsettledPNL(marketIndex?: BN): BN {
256
- return this.getUserAccount()
257
- .positions.filter((pos) =>
258
- marketIndex ? pos.marketIndex === marketIndex : true
259
- )
260
- .reduce((pnl, marketPosition) => {
261
- return pnl.add(marketPosition.unsettledPnl);
411
+ if (withWeightMarginCategory !== undefined) {
412
+ if (positionUnrealizedPnl.gt(ZERO)) {
413
+ positionUnrealizedPnl = positionUnrealizedPnl
414
+ .mul(
415
+ calculateUnrealizedAssetWeight(
416
+ market,
417
+ positionUnrealizedPnl,
418
+ withWeightMarginCategory
419
+ )
420
+ )
421
+ .div(new BN(BANK_WEIGHT_PRECISION));
422
+ }
423
+ }
424
+
425
+ return unrealizedPnl.add(positionUnrealizedPnl);
262
426
  }, ZERO);
263
427
  }
264
428
 
@@ -279,14 +443,18 @@ export class ClearingHouseUser {
279
443
  }, ZERO);
280
444
  }
281
445
 
282
- public getTotalLiability(): BN {
446
+ public getBankLiabilityValue(
447
+ bankIndex?: BN,
448
+ withWeightMarginCategory?: MarginCategory
449
+ ): BN {
283
450
  return this.getUserAccount().bankBalances.reduce(
284
- (totalAssetValue, bankBalance) => {
451
+ (totalLiabilityValue, bankBalance) => {
285
452
  if (
286
453
  bankBalance.balance.eq(ZERO) ||
287
- isVariant(bankBalance.balanceType, 'deposit')
454
+ isVariant(bankBalance.balanceType, 'deposit') ||
455
+ (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
288
456
  ) {
289
- return totalAssetValue;
457
+ return totalLiabilityValue;
290
458
  }
291
459
 
292
460
  // Todo this needs to account for whether it's based on initial or maintenance requirements
@@ -299,23 +467,42 @@ export class ClearingHouseUser {
299
467
  bankAccount,
300
468
  bankBalance.balanceType
301
469
  );
302
- return totalAssetValue.add(
303
- tokenAmount
304
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
305
- .mul(bankAccount.initialLiabilityWeight)
306
- .div(BANK_WEIGHT_PRECISION)
307
- .div(MARK_PRICE_PRECISION)
308
- );
470
+
471
+ let liabilityValue = tokenAmount
472
+ .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
473
+ .div(MARK_PRICE_PRECISION)
474
+ .div(
475
+ new BN(10).pow(
476
+ new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
477
+ )
478
+ );
479
+
480
+ if (withWeightMarginCategory !== undefined) {
481
+ const weight = calculateLiabilityWeight(
482
+ tokenAmount,
483
+ bankAccount,
484
+ withWeightMarginCategory
485
+ );
486
+ liabilityValue = liabilityValue
487
+ .mul(weight)
488
+ .div(BANK_WEIGHT_PRECISION);
489
+ }
490
+
491
+ return totalLiabilityValue.add(liabilityValue);
309
492
  },
310
493
  ZERO
311
494
  );
312
495
  }
313
496
 
314
- public getCollateralValue(bankIndex?: BN): BN {
497
+ public getBankAssetValue(
498
+ bankIndex?: BN,
499
+ withWeightMarginCategory?: MarginCategory
500
+ ): BN {
315
501
  return this.getUserAccount().bankBalances.reduce(
316
502
  (totalAssetValue, bankBalance) => {
317
503
  if (
318
504
  bankBalance.balance.eq(ZERO) ||
505
+ isVariant(bankBalance.balanceType, 'borrow') ||
319
506
  (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
320
507
  ) {
321
508
  return totalAssetValue;
@@ -326,65 +513,53 @@ export class ClearingHouseUser {
326
513
  bankBalance.bankIndex
327
514
  );
328
515
 
329
- let tokenAmount = getTokenAmount(
516
+ const tokenAmount = getTokenAmount(
330
517
  bankBalance.balance,
331
518
  bankAccount,
332
519
  bankBalance.balanceType
333
520
  );
334
521
 
335
- if (isVariant(bankBalance.balanceType, 'borrow')) {
336
- tokenAmount = tokenAmount.mul(new BN(-1));
522
+ let assetValue = tokenAmount
523
+ .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
524
+ .div(MARK_PRICE_PRECISION)
525
+ .div(
526
+ new BN(10).pow(
527
+ new BN(bankAccount.decimals).sub(BANK_BALANCE_PRECISION_EXP)
528
+ )
529
+ );
530
+ if (withWeightMarginCategory !== undefined) {
531
+ const weight = calculateAssetWeight(
532
+ tokenAmount,
533
+ bankAccount,
534
+ withWeightMarginCategory
535
+ );
536
+ assetValue = assetValue.mul(weight).div(BANK_WEIGHT_PRECISION);
337
537
  }
338
538
 
339
- return totalAssetValue.add(
340
- tokenAmount
341
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
342
- .div(MARK_PRICE_PRECISION)
343
- );
539
+ return totalAssetValue.add(assetValue);
344
540
  },
345
541
  ZERO
346
542
  );
347
543
  }
348
544
 
545
+ public getNetBankValue(withWeightMarginCategory?: MarginCategory): BN {
546
+ return this.getBankAssetValue(undefined, withWeightMarginCategory).sub(
547
+ this.getBankLiabilityValue(undefined, withWeightMarginCategory)
548
+ );
549
+ }
550
+
349
551
  /**
350
552
  * calculates TotalCollateral: collateral + unrealized pnl
351
- * TODO: rename to total equity (for perpetuals swaps)
352
553
  * @returns : Precision QUOTE_PRECISION
353
554
  */
354
- public getTotalCollateral(): BN {
355
- return this.getUserAccount()
356
- .bankBalances.reduce((totalAssetValue, bankBalance) => {
357
- if (
358
- bankBalance.balance.eq(ZERO) ||
359
- isVariant(bankBalance.balanceType, 'borrow')
360
- ) {
361
- return totalAssetValue;
362
- }
363
-
364
- // Todo this needs to account for whether it's based on initial or maintenance requirements
365
- const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
366
- bankBalance.bankIndex
367
- );
368
-
369
- const tokenAmount = getTokenAmount(
370
- bankBalance.balance,
371
- bankAccount,
372
- bankBalance.balanceType
373
- );
374
- return totalAssetValue.add(
375
- tokenAmount
376
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
377
- .mul(bankAccount.initialAssetWeight)
378
- .div(BANK_WEIGHT_PRECISION)
379
- .div(MARK_PRICE_PRECISION)
380
- );
381
- }, ZERO)
382
- .add(this.getUnrealizedPNL(true))
383
- .add(this.getUnsettledPNL());
555
+ public getTotalCollateral(marginCategory: MarginCategory = 'Initial'): BN {
556
+ return this.getBankAssetValue(undefined, marginCategory).add(
557
+ this.getUnrealizedPNL(true, undefined, marginCategory)
558
+ );
384
559
  }
385
560
 
386
561
  /**
387
- * calculates sum of position value across all positions
562
+ * calculates sum of position value across all positions in margin system
388
563
  * @returns : Precision QUOTE_PRECISION
389
564
  */
390
565
  getTotalPositionValue(): BN {
@@ -393,20 +568,20 @@ export class ClearingHouseUser {
393
568
  const market = this.clearingHouse.getMarketAccount(
394
569
  marketPosition.marketIndex
395
570
  );
396
- return positionValue.add(
397
- calculateBaseAssetValue(
398
- market,
399
- marketPosition,
400
- this.getOracleDataForMarket(market.marketIndex)
401
- )
571
+ const posVal = calculateMarginBaseAssetValue(
572
+ market,
573
+ marketPosition,
574
+ this.getOracleDataForMarket(market.marketIndex)
402
575
  );
576
+
577
+ return positionValue.add(posVal);
403
578
  },
404
579
  ZERO
405
580
  );
406
581
  }
407
582
 
408
583
  /**
409
- * calculates position value from closing 100%
584
+ * calculates position value in margin system
410
585
  * @returns : Precision QUOTE_PRECISION
411
586
  */
412
587
  public getPositionValue(
@@ -418,7 +593,7 @@ export class ClearingHouseUser {
418
593
  const market = this.clearingHouse.getMarketAccount(
419
594
  userPosition.marketIndex
420
595
  );
421
- return calculateBaseAssetValue(market, userPosition, oraclePriceData);
596
+ return calculateMarginBaseAssetValue(market, userPosition, oraclePriceData);
422
597
  }
423
598
 
424
599
  public getPositionSide(
@@ -434,12 +609,13 @@ export class ClearingHouseUser {
434
609
  }
435
610
 
436
611
  /**
437
- * calculates average exit price for closing 100% of position
612
+ * calculates average exit price (optionally for closing up to 100% of position)
438
613
  * @returns : Precision MARK_PRICE_PRECISION
439
614
  */
440
615
  public getPositionEstimatedExitPriceAndPnl(
441
616
  position: UserPosition,
442
- amountToClose?: BN
617
+ amountToClose?: BN,
618
+ useAMMClose = false
443
619
  ): [BN, BN] {
444
620
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
445
621
 
@@ -459,11 +635,21 @@ export class ClearingHouseUser {
459
635
  } as UserPosition;
460
636
  }
461
637
 
462
- const baseAssetValue = calculateBaseAssetValue(
463
- market,
464
- position,
465
- oraclePriceData
466
- );
638
+ let baseAssetValue: BN;
639
+
640
+ if (useAMMClose) {
641
+ baseAssetValue = calculateBaseAssetValue(
642
+ market,
643
+ position,
644
+ oraclePriceData
645
+ );
646
+ } else {
647
+ baseAssetValue = calculateMarginBaseAssetValue(
648
+ market,
649
+ position,
650
+ oraclePriceData
651
+ );
652
+ }
467
653
  if (position.baseAssetAmount.eq(ZERO)) {
468
654
  return [ZERO, ZERO];
469
655
  }
@@ -505,22 +691,13 @@ export class ClearingHouseUser {
505
691
  category: MarginCategory = 'Initial'
506
692
  ): BN {
507
693
  const market = this.clearingHouse.getMarketAccount(marketIndex);
508
- let marginRatioCategory: number;
509
-
510
- switch (category) {
511
- case 'Initial':
512
- marginRatioCategory = market.marginRatioInitial;
513
- break;
514
- case 'Maintenance':
515
- marginRatioCategory = market.marginRatioMaintenance;
516
- break;
517
- case 'Partial':
518
- marginRatioCategory = market.marginRatioPartial;
519
- break;
520
- default:
521
- marginRatioCategory = market.marginRatioInitial;
522
- break;
523
- }
694
+
695
+ const marginRatioCategory = calculateMarketMarginRatio(
696
+ market,
697
+ // worstCaseBaseAssetAmount.abs(),
698
+ ZERO, // todo
699
+ category
700
+ );
524
701
  const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(
525
702
  new BN(marginRatioCategory)
526
703
  );
@@ -543,7 +720,8 @@ export class ClearingHouseUser {
543
720
 
544
721
  public canBeLiquidated(): [boolean, BN] {
545
722
  const totalCollateral = this.getTotalCollateral();
546
- const partialMaintenanceRequirement = this.getPartialMarginRequirement();
723
+ const partialMaintenanceRequirement =
724
+ this.getMaintenanceMarginRequirement();
547
725
  const marginRatio = this.getMarginRatio();
548
726
  const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
549
727
  return [canLiquidate, marginRatio];
@@ -587,8 +765,7 @@ export class ClearingHouseUser {
587
765
  */
588
766
  public liquidationPrice(
589
767
  marketPosition: Pick<UserPosition, 'marketIndex'>,
590
- positionBaseSizeChange: BN = ZERO,
591
- partial = false
768
+ positionBaseSizeChange: BN = ZERO
592
769
  ): BN {
593
770
  // solves formula for example canBeLiquidated below
594
771
 
@@ -622,14 +799,17 @@ export class ClearingHouseUser {
622
799
  const proposedMarketPosition: UserPosition = {
623
800
  marketIndex: marketPosition.marketIndex,
624
801
  baseAssetAmount: proposedBaseAssetAmount,
625
- lastCumulativeFundingRate:
626
- currentMarketPosition.lastCumulativeFundingRate,
627
802
  quoteAssetAmount: new BN(0),
803
+ lastCumulativeFundingRate: ZERO,
628
804
  quoteEntryAmount: new BN(0),
629
805
  openOrders: new BN(0),
630
- unsettledPnl: new BN(0),
631
806
  openBids: new BN(0),
632
807
  openAsks: new BN(0),
808
+ realizedPnl: ZERO,
809
+ lpShares: ZERO,
810
+ lastFeePerLp: ZERO,
811
+ lastNetBaseAssetAmountPerLp: ZERO,
812
+ lastNetQuoteAssetAmountPerLp: ZERO,
633
813
  };
634
814
 
635
815
  if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
@@ -638,7 +818,7 @@ export class ClearingHouseUser {
638
818
  proposedMarketPosition.marketIndex
639
819
  );
640
820
 
641
- const proposedMarketPositionValue = calculateBaseAssetValue(
821
+ const proposedMarketPositionValue = calculateMarginBaseAssetValue(
642
822
  market,
643
823
  proposedMarketPosition,
644
824
  this.getOracleDataForMarket(market.marketIndex)
@@ -655,16 +835,20 @@ export class ClearingHouseUser {
655
835
  const market = this.clearingHouse.getMarketAccount(
656
836
  position.marketIndex
657
837
  );
658
- const positionValue = calculateBaseAssetValue(
838
+ const positionValue = calculateMarginBaseAssetValue(
659
839
  market,
660
840
  position,
661
841
  this.getOracleDataForMarket(market.marketIndex)
662
842
  );
663
843
  const marketMarginRequirement = positionValue
664
844
  .mul(
665
- partial
666
- ? new BN(market.marginRatioPartial)
667
- : new BN(market.marginRatioMaintenance)
845
+ new BN(
846
+ calculateMarketMarginRatio(
847
+ market,
848
+ position.baseAssetAmount.abs(),
849
+ 'Maintenance'
850
+ )
851
+ )
668
852
  )
669
853
  .div(MARGIN_PRECISION);
670
854
  totalMarginRequirement = totalMarginRequirement.add(
@@ -692,9 +876,13 @@ export class ClearingHouseUser {
692
876
  marginRequirementExcludingTargetMarket.add(
693
877
  proposedMarketPositionValue
694
878
  .mul(
695
- partial
696
- ? new BN(market.marginRatioPartial)
697
- : new BN(market.marginRatioMaintenance)
879
+ new BN(
880
+ calculateMarketMarginRatio(
881
+ market,
882
+ proposedMarketPosition.baseAssetAmount.abs(),
883
+ 'Maintenance'
884
+ )
885
+ )
698
886
  )
699
887
  .div(MARGIN_PRECISION)
700
888
  );
@@ -702,9 +890,10 @@ export class ClearingHouseUser {
702
890
  marginRequirementAfterTrade
703
891
  );
704
892
 
705
- const marketMaxLeverage = partial
706
- ? this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Partial')
707
- : this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
893
+ const marketMaxLeverage = this.getMaxLeverage(
894
+ proposedMarketPosition.marketIndex,
895
+ 'Maintenance'
896
+ );
708
897
 
709
898
  let priceDelta;
710
899
  if (proposedBaseAssetAmount.lt(ZERO)) {
@@ -765,11 +954,11 @@ export class ClearingHouseUser {
765
954
 
766
955
  const closeBaseAmount = currentPosition.baseAssetAmount
767
956
  .mul(closeQuoteAmount)
768
- .div(currentPosition.quoteAssetAmount)
957
+ .div(currentPosition.quoteAssetAmount.abs())
769
958
  .add(
770
959
  currentPosition.baseAssetAmount
771
960
  .mul(closeQuoteAmount)
772
- .mod(currentPosition.quoteAssetAmount)
961
+ .mod(currentPosition.quoteAssetAmount.abs())
773
962
  )
774
963
  .neg();
775
964
 
@@ -777,8 +966,7 @@ export class ClearingHouseUser {
777
966
  {
778
967
  marketIndex: positionMarketIndex,
779
968
  },
780
- closeBaseAmount,
781
- true
969
+ closeBaseAmount
782
970
  );
783
971
  }
784
972