@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
@@ -18,6 +18,7 @@ export declare type ClearingHouseConfig = {
18
18
  bankIndexes?: BN[];
19
19
  oracleInfos?: OracleInfo[];
20
20
  env?: DriftEnv;
21
+ userStats?: boolean;
21
22
  };
22
23
  declare type ClearingHouseSubscriptionConfig = {
23
24
  type: 'websocket';
@@ -37,6 +37,7 @@ export declare class ClearingHouseUser {
37
37
  */
38
38
  getUserPosition(marketIndex: BN): UserPosition | undefined;
39
39
  getEmptyPosition(marketIndex: BN): UserPosition;
40
+ getClonedPosition(position: UserPosition): UserPosition;
40
41
  /**
41
42
  * @param orderId
42
43
  * @returns Order
@@ -49,6 +50,11 @@ export declare class ClearingHouseUser {
49
50
  getOrderByUserOrderId(userOrderId: number): Order | undefined;
50
51
  getUserAccountPublicKey(): PublicKey;
51
52
  exists(): Promise<boolean>;
53
+ /**
54
+ * calculates the market position if the lp position was settled
55
+ * @returns : userPosition
56
+ */
57
+ getSettledLPPosition(marketIndex: BN): [UserPosition, BN, BN];
52
58
  /**
53
59
  * calculates Buying Power = FC * MAX_LEVERAGE
54
60
  * @returns : Precision QUOTE_PRECISION
@@ -59,50 +65,52 @@ export declare class ClearingHouseUser {
59
65
  * @returns : Precision QUOTE_PRECISION
60
66
  */
61
67
  getFreeCollateral(): BN;
62
- getInitialMarginRequirement(): BN;
63
68
  /**
64
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
69
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
65
70
  */
66
- getPartialMarginRequirement(): BN;
71
+ getMarginRequirement(type: MarginCategory): BN;
67
72
  /**
68
- * calculates unrealized position price pnl
69
- * @returns : Precision QUOTE_PRECISION
73
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
74
+ */
75
+ getInitialMarginRequirement(): BN;
76
+ /**
77
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
70
78
  */
71
- getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN;
79
+ getMaintenanceMarginRequirement(): BN;
72
80
  /**
73
81
  * calculates unrealized position price pnl
74
82
  * @returns : Precision QUOTE_PRECISION
75
83
  */
76
- getUnsettledPNL(marketIndex?: BN): BN;
84
+ getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
77
85
  /**
78
86
  * calculates unrealized funding payment pnl
79
87
  * @returns : Precision QUOTE_PRECISION
80
88
  */
81
89
  getUnrealizedFundingPNL(marketIndex?: BN): BN;
82
- getTotalLiability(): BN;
83
- getCollateralValue(bankIndex?: BN): BN;
90
+ getBankLiabilityValue(bankIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
91
+ getBankAssetValue(bankIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
92
+ getNetBankValue(withWeightMarginCategory?: MarginCategory): BN;
84
93
  /**
85
94
  * calculates TotalCollateral: collateral + unrealized pnl
86
- * TODO: rename to total equity (for perpetuals swaps)
87
95
  * @returns : Precision QUOTE_PRECISION
88
96
  */
89
- getTotalCollateral(): BN;
97
+ getTotalCollateral(marginCategory?: MarginCategory): BN;
90
98
  /**
91
- * calculates sum of position value across all positions
99
+ * calculates sum of position value across all positions in margin system
92
100
  * @returns : Precision QUOTE_PRECISION
93
101
  */
94
102
  getTotalPositionValue(): BN;
95
103
  /**
96
- * calculates position value from closing 100%
104
+ * calculates position value in margin system
97
105
  * @returns : Precision QUOTE_PRECISION
98
106
  */
99
107
  getPositionValue(marketIndex: BN, oraclePriceData: OraclePriceData): BN;
100
108
  getPositionSide(currentPosition: Pick<UserPosition, 'baseAssetAmount'>): PositionDirection | undefined;
101
109
  /**
102
- * calculates average exit price for closing 100% of position
110
+ * calculates average exit price (optionally for closing up to 100% of position)
103
111
  * @returns : Precision MARK_PRICE_PRECISION
104
112
  */
105
- getPositionEstimatedExitPriceAndPnl(position: UserPosition, amountToClose?: BN): [BN, BN];
113
+ getPositionEstimatedExitPriceAndPnl(position: UserPosition, amountToClose?: BN, useAMMClose?: boolean): [BN, BN];
106
114
  /**
107
115
  * calculates current user leverage across all positions
108
116
  * @returns : Precision TEN_THOUSAND
@@ -132,7 +140,7 @@ export declare class ClearingHouseUser {
132
140
  * @param partial
133
141
  * @returns Precision : MARK_PRICE_PRECISION
134
142
  */
135
- liquidationPrice(marketPosition: Pick<UserPosition, 'marketIndex'>, positionBaseSizeChange?: BN, partial?: boolean): BN;
143
+ liquidationPrice(marketPosition: Pick<UserPosition, 'marketIndex'>, positionBaseSizeChange?: BN): BN;
136
144
  /**
137
145
  * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
138
146
  * @param positionMarketIndex
@@ -6,6 +6,7 @@ const position_1 = require("./math/position");
6
6
  const numericConstants_1 = require("./constants/numericConstants");
7
7
  const _1 = require(".");
8
8
  const bankBalance_1 = require("./math/bankBalance");
9
+ const margin_1 = require("./math/margin");
9
10
  const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSubscriber");
10
11
  const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
11
12
  class ClearingHouseUser {
@@ -68,11 +69,19 @@ class ClearingHouseUser {
68
69
  quoteAssetAmount: numericConstants_1.ZERO,
69
70
  quoteEntryAmount: numericConstants_1.ZERO,
70
71
  openOrders: numericConstants_1.ZERO,
71
- unsettledPnl: numericConstants_1.ZERO,
72
72
  openBids: numericConstants_1.ZERO,
73
73
  openAsks: numericConstants_1.ZERO,
74
+ realizedPnl: numericConstants_1.ZERO,
75
+ lpShares: numericConstants_1.ZERO,
76
+ lastFeePerLp: numericConstants_1.ZERO,
77
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
78
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
74
79
  };
75
80
  }
81
+ getClonedPosition(position) {
82
+ const clonedPosition = Object.assign({}, position);
83
+ return clonedPosition;
84
+ }
76
85
  /**
77
86
  * @param orderId
78
87
  * @returns Order
@@ -94,6 +103,90 @@ class ClearingHouseUser {
94
103
  const userAccountRPCResponse = await this.clearingHouse.connection.getParsedAccountInfo(this.userAccountPublicKey);
95
104
  return userAccountRPCResponse.value !== null;
96
105
  }
106
+ /**
107
+ * calculates the market position if the lp position was settled
108
+ * @returns : userPosition
109
+ */
110
+ getSettledLPPosition(marketIndex) {
111
+ const _position = this.getUserPosition(marketIndex);
112
+ const position = this.getClonedPosition(_position);
113
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
114
+ const nShares = position.lpShares;
115
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
116
+ .sub(position.lastNetBaseAssetAmountPerLp)
117
+ .mul(nShares)
118
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
119
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
120
+ .sub(position.lastNetQuoteAssetAmountPerLp)
121
+ .mul(nShares)
122
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
123
+ function sign(v) {
124
+ const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
125
+ return sign;
126
+ }
127
+ const remainder = deltaBaa
128
+ .abs()
129
+ .mod(market.amm.baseAssetAmountStepSize)
130
+ .mul(sign(deltaBaa));
131
+ const _standardizedBaa = deltaBaa.sub(remainder);
132
+ let remainderBaa;
133
+ if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
134
+ remainderBaa = remainder;
135
+ }
136
+ else {
137
+ remainderBaa = deltaBaa;
138
+ }
139
+ const standardizedBaa = deltaBaa.sub(remainderBaa);
140
+ const reaminderPerLP = remainderBaa.mul(numericConstants_1.AMM_RESERVE_PRECISION).div(nShares);
141
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
142
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
143
+ position.lastNetBaseAssetAmountPerLp =
144
+ market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
145
+ let updateType;
146
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
147
+ updateType = 'open';
148
+ }
149
+ else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
150
+ updateType = 'increase';
151
+ }
152
+ else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
153
+ updateType = 'reduce';
154
+ }
155
+ else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
156
+ updateType = 'close';
157
+ }
158
+ else {
159
+ updateType = 'flip';
160
+ }
161
+ let newQuoteEntry;
162
+ let pnl;
163
+ if (updateType == 'open' || updateType == 'increase') {
164
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
165
+ pnl = 0;
166
+ }
167
+ else if (updateType == 'reduce' || updateType == 'close') {
168
+ newQuoteEntry = position.quoteEntryAmount.sub(position.quoteEntryAmount
169
+ .mul(deltaBaa.abs())
170
+ .div(position.baseAssetAmount.abs()));
171
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry);
172
+ }
173
+ else {
174
+ newQuoteEntry = deltaQaa.sub(deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()));
175
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
176
+ }
177
+ position.quoteEntryAmount = newQuoteEntry;
178
+ if (position.baseAssetAmount.gt(numericConstants_1.ZERO)) {
179
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
180
+ }
181
+ else if (position.baseAssetAmount.lt(numericConstants_1.ZERO)) {
182
+ position.lastCumulativeFundingRate =
183
+ market.amm.cumulativeFundingRateShort;
184
+ }
185
+ else {
186
+ position.lastCumulativeFundingRate = numericConstants_1.ZERO;
187
+ }
188
+ return [position, remainderBaa, pnl];
189
+ }
97
190
  /**
98
191
  * calculates Buying Power = FC * MAX_LEVERAGE
99
192
  * @returns : Precision QUOTE_PRECISION
@@ -113,50 +206,88 @@ class ClearingHouseUser {
113
206
  const freeCollateral = totalCollateral.sub(initialMarginRequirement);
114
207
  return freeCollateral.gte(numericConstants_1.ZERO) ? freeCollateral : numericConstants_1.ZERO;
115
208
  }
116
- getInitialMarginRequirement() {
209
+ /**
210
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
211
+ */
212
+ getMarginRequirement(type) {
117
213
  return this.getUserAccount()
118
214
  .positions.reduce((marginRequirement, marketPosition) => {
119
215
  const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
120
- return marginRequirement.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex))
121
- .mul(new _1.BN(market.marginRatioInitial))
216
+ if (marketPosition.lpShares.gt(numericConstants_1.ZERO)) {
217
+ // is an lp
218
+ // clone so we dont mutate the position
219
+ marketPosition = this.getClonedPosition(marketPosition);
220
+ // settle position
221
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
222
+ marketPosition.baseAssetAmount =
223
+ settledPosition.baseAssetAmount.add(dustBaa);
224
+ marketPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
225
+ // open orders
226
+ let openAsks;
227
+ if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
228
+ openAsks = market.amm.maxBaseAssetReserve
229
+ .sub(market.amm.baseAssetReserve)
230
+ .mul(marketPosition.lpShares)
231
+ .div(market.amm.sqrtK)
232
+ .mul(new _1.BN(-1));
233
+ }
234
+ else {
235
+ openAsks = numericConstants_1.ZERO;
236
+ }
237
+ let openBids;
238
+ if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
239
+ openBids = market.amm.baseAssetReserve
240
+ .sub(market.amm.minBaseAssetReserve)
241
+ .mul(marketPosition.lpShares)
242
+ .div(market.amm.sqrtK);
243
+ }
244
+ else {
245
+ openBids = numericConstants_1.ZERO;
246
+ }
247
+ marketPosition.openAsks = marketPosition.openAsks.add(openAsks);
248
+ marketPosition.openBids = marketPosition.openBids.add(openBids);
249
+ }
250
+ const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(marketPosition);
251
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
252
+ .abs()
253
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
254
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
255
+ return marginRequirement.add(worstCaseAssetValue
256
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), type)))
122
257
  .div(numericConstants_1.MARGIN_PRECISION));
123
258
  }, numericConstants_1.ZERO)
124
- .add(this.getTotalLiability());
259
+ .add(this.getBankLiabilityValue(undefined, type));
125
260
  }
126
261
  /**
127
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
262
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
128
263
  */
129
- getPartialMarginRequirement() {
130
- return this.getUserAccount()
131
- .positions.reduce((marginRequirement, marketPosition) => {
132
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
133
- return marginRequirement.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex))
134
- .mul(new _1.BN(market.marginRatioPartial))
135
- .div(numericConstants_1.MARGIN_PRECISION));
136
- }, numericConstants_1.ZERO)
137
- .add(this.getTotalLiability());
264
+ getInitialMarginRequirement() {
265
+ return this.getMarginRequirement('Initial');
138
266
  }
139
267
  /**
140
- * calculates unrealized position price pnl
141
- * @returns : Precision QUOTE_PRECISION
268
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
142
269
  */
143
- getUnrealizedPNL(withFunding, marketIndex) {
144
- return this.getUserAccount()
145
- .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
146
- .reduce((pnl, marketPosition) => {
147
- const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
148
- return pnl.add((0, _1.calculatePositionPNL)(market, marketPosition, withFunding, this.getOracleDataForMarket(market.marketIndex)));
149
- }, numericConstants_1.ZERO);
270
+ getMaintenanceMarginRequirement() {
271
+ return this.getMarginRequirement('Maintenance');
150
272
  }
151
273
  /**
152
274
  * calculates unrealized position price pnl
153
275
  * @returns : Precision QUOTE_PRECISION
154
276
  */
155
- getUnsettledPNL(marketIndex) {
277
+ getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
156
278
  return this.getUserAccount()
157
279
  .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
158
- .reduce((pnl, marketPosition) => {
159
- return pnl.add(marketPosition.unsettledPnl);
280
+ .reduce((unrealizedPnl, marketPosition) => {
281
+ const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
282
+ let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, marketPosition, withFunding, this.getOracleDataForMarket(market.marketIndex));
283
+ if (withWeightMarginCategory !== undefined) {
284
+ if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
285
+ positionUnrealizedPnl = positionUnrealizedPnl
286
+ .mul((0, _1.calculateUnrealizedAssetWeight)(market, positionUnrealizedPnl, withWeightMarginCategory))
287
+ .div(new _1.BN(numericConstants_1.BANK_WEIGHT_PRECISION));
288
+ }
289
+ }
290
+ return unrealizedPnl.add(positionUnrealizedPnl);
160
291
  }, numericConstants_1.ZERO);
161
292
  }
162
293
  /**
@@ -171,81 +302,79 @@ class ClearingHouseUser {
171
302
  return pnl.add((0, _1.calculatePositionFundingPNL)(market, marketPosition));
172
303
  }, numericConstants_1.ZERO);
173
304
  }
174
- getTotalLiability() {
175
- return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
305
+ getBankLiabilityValue(bankIndex, withWeightMarginCategory) {
306
+ return this.getUserAccount().bankBalances.reduce((totalLiabilityValue, bankBalance) => {
176
307
  if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
177
- (0, types_1.isVariant)(bankBalance.balanceType, 'deposit')) {
178
- return totalAssetValue;
308
+ (0, types_1.isVariant)(bankBalance.balanceType, 'deposit') ||
309
+ (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))) {
310
+ return totalLiabilityValue;
179
311
  }
180
312
  // Todo this needs to account for whether it's based on initial or maintenance requirements
181
313
  const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
182
314
  const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
183
- return totalAssetValue.add(tokenAmount
315
+ let liabilityValue = tokenAmount
184
316
  .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
185
- .mul(bankAccount.initialLiabilityWeight)
186
- .div(numericConstants_1.BANK_WEIGHT_PRECISION)
187
- .div(numericConstants_1.MARK_PRICE_PRECISION));
317
+ .div(numericConstants_1.MARK_PRICE_PRECISION)
318
+ .div(new _1.BN(10).pow(new _1.BN(bankAccount.decimals).sub(numericConstants_1.BANK_BALANCE_PRECISION_EXP)));
319
+ if (withWeightMarginCategory !== undefined) {
320
+ const weight = (0, bankBalance_1.calculateLiabilityWeight)(tokenAmount, bankAccount, withWeightMarginCategory);
321
+ liabilityValue = liabilityValue
322
+ .mul(weight)
323
+ .div(numericConstants_1.BANK_WEIGHT_PRECISION);
324
+ }
325
+ return totalLiabilityValue.add(liabilityValue);
188
326
  }, numericConstants_1.ZERO);
189
327
  }
190
- getCollateralValue(bankIndex) {
328
+ getBankAssetValue(bankIndex, withWeightMarginCategory) {
191
329
  return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
192
330
  if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
331
+ (0, types_1.isVariant)(bankBalance.balanceType, 'borrow') ||
193
332
  (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))) {
194
333
  return totalAssetValue;
195
334
  }
196
335
  // Todo this needs to account for whether it's based on initial or maintenance requirements
197
336
  const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
198
- let tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
199
- if ((0, types_1.isVariant)(bankBalance.balanceType, 'borrow')) {
200
- tokenAmount = tokenAmount.mul(new _1.BN(-1));
201
- }
202
- return totalAssetValue.add(tokenAmount
337
+ const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
338
+ let assetValue = tokenAmount
203
339
  .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
204
- .div(numericConstants_1.MARK_PRICE_PRECISION));
340
+ .div(numericConstants_1.MARK_PRICE_PRECISION)
341
+ .div(new _1.BN(10).pow(new _1.BN(bankAccount.decimals).sub(numericConstants_1.BANK_BALANCE_PRECISION_EXP)));
342
+ if (withWeightMarginCategory !== undefined) {
343
+ const weight = (0, bankBalance_1.calculateAssetWeight)(tokenAmount, bankAccount, withWeightMarginCategory);
344
+ assetValue = assetValue.mul(weight).div(numericConstants_1.BANK_WEIGHT_PRECISION);
345
+ }
346
+ return totalAssetValue.add(assetValue);
205
347
  }, numericConstants_1.ZERO);
206
348
  }
349
+ getNetBankValue(withWeightMarginCategory) {
350
+ return this.getBankAssetValue(undefined, withWeightMarginCategory).sub(this.getBankLiabilityValue(undefined, withWeightMarginCategory));
351
+ }
207
352
  /**
208
353
  * calculates TotalCollateral: collateral + unrealized pnl
209
- * TODO: rename to total equity (for perpetuals swaps)
210
354
  * @returns : Precision QUOTE_PRECISION
211
355
  */
212
- getTotalCollateral() {
213
- return this.getUserAccount()
214
- .bankBalances.reduce((totalAssetValue, bankBalance) => {
215
- if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
216
- (0, types_1.isVariant)(bankBalance.balanceType, 'borrow')) {
217
- return totalAssetValue;
218
- }
219
- // Todo this needs to account for whether it's based on initial or maintenance requirements
220
- const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
221
- const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
222
- return totalAssetValue.add(tokenAmount
223
- .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
224
- .mul(bankAccount.initialAssetWeight)
225
- .div(numericConstants_1.BANK_WEIGHT_PRECISION)
226
- .div(numericConstants_1.MARK_PRICE_PRECISION));
227
- }, numericConstants_1.ZERO)
228
- .add(this.getUnrealizedPNL(true))
229
- .add(this.getUnsettledPNL());
356
+ getTotalCollateral(marginCategory = 'Initial') {
357
+ return this.getBankAssetValue(undefined, marginCategory).add(this.getUnrealizedPNL(true, undefined, marginCategory));
230
358
  }
231
359
  /**
232
- * calculates sum of position value across all positions
360
+ * calculates sum of position value across all positions in margin system
233
361
  * @returns : Precision QUOTE_PRECISION
234
362
  */
235
363
  getTotalPositionValue() {
236
364
  return this.getUserAccount().positions.reduce((positionValue, marketPosition) => {
237
365
  const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
238
- return positionValue.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex)));
366
+ const posVal = (0, margin_1.calculateMarginBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex));
367
+ return positionValue.add(posVal);
239
368
  }, numericConstants_1.ZERO);
240
369
  }
241
370
  /**
242
- * calculates position value from closing 100%
371
+ * calculates position value in margin system
243
372
  * @returns : Precision QUOTE_PRECISION
244
373
  */
245
374
  getPositionValue(marketIndex, oraclePriceData) {
246
375
  const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
247
376
  const market = this.clearingHouse.getMarketAccount(userPosition.marketIndex);
248
- return (0, _1.calculateBaseAssetValue)(market, userPosition, oraclePriceData);
377
+ return (0, margin_1.calculateMarginBaseAssetValue)(market, userPosition, oraclePriceData);
249
378
  }
250
379
  getPositionSide(currentPosition) {
251
380
  if (currentPosition.baseAssetAmount.gt(numericConstants_1.ZERO)) {
@@ -259,10 +388,10 @@ class ClearingHouseUser {
259
388
  }
260
389
  }
261
390
  /**
262
- * calculates average exit price for closing 100% of position
391
+ * calculates average exit price (optionally for closing up to 100% of position)
263
392
  * @returns : Precision MARK_PRICE_PRECISION
264
393
  */
265
- getPositionEstimatedExitPriceAndPnl(position, amountToClose) {
394
+ getPositionEstimatedExitPriceAndPnl(position, amountToClose, useAMMClose = false) {
266
395
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
267
396
  const entryPrice = (0, position_1.calculateEntryPrice)(position);
268
397
  const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
@@ -277,7 +406,13 @@ class ClearingHouseUser {
277
406
  quoteAssetAmount: position.quoteAssetAmount,
278
407
  };
279
408
  }
280
- const baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
409
+ let baseAssetValue;
410
+ if (useAMMClose) {
411
+ baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
412
+ }
413
+ else {
414
+ baseAssetValue = (0, margin_1.calculateMarginBaseAssetValue)(market, position, oraclePriceData);
415
+ }
281
416
  if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
282
417
  return [numericConstants_1.ZERO, numericConstants_1.ZERO];
283
418
  }
@@ -311,21 +446,10 @@ class ClearingHouseUser {
311
446
  */
312
447
  getMaxLeverage(marketIndex, category = 'Initial') {
313
448
  const market = this.clearingHouse.getMarketAccount(marketIndex);
314
- let marginRatioCategory;
315
- switch (category) {
316
- case 'Initial':
317
- marginRatioCategory = market.marginRatioInitial;
318
- break;
319
- case 'Maintenance':
320
- marginRatioCategory = market.marginRatioMaintenance;
321
- break;
322
- case 'Partial':
323
- marginRatioCategory = market.marginRatioPartial;
324
- break;
325
- default:
326
- marginRatioCategory = market.marginRatioInitial;
327
- break;
328
- }
449
+ const marginRatioCategory = (0, _1.calculateMarketMarginRatio)(market,
450
+ // worstCaseBaseAssetAmount.abs(),
451
+ numericConstants_1.ZERO, // todo
452
+ category);
329
453
  const maxLeverage = numericConstants_1.TEN_THOUSAND.mul(numericConstants_1.TEN_THOUSAND).div(new _1.BN(marginRatioCategory));
330
454
  return maxLeverage;
331
455
  }
@@ -342,7 +466,7 @@ class ClearingHouseUser {
342
466
  }
343
467
  canBeLiquidated() {
344
468
  const totalCollateral = this.getTotalCollateral();
345
- const partialMaintenanceRequirement = this.getPartialMarginRequirement();
469
+ const partialMaintenanceRequirement = this.getMaintenanceMarginRequirement();
346
470
  const marginRatio = this.getMarginRatio();
347
471
  const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
348
472
  return [canLiquidate, marginRatio];
@@ -372,7 +496,7 @@ class ClearingHouseUser {
372
496
  * @param partial
373
497
  * @returns Precision : MARK_PRICE_PRECISION
374
498
  */
375
- liquidationPrice(marketPosition, positionBaseSizeChange = numericConstants_1.ZERO, partial = false) {
499
+ liquidationPrice(marketPosition, positionBaseSizeChange = numericConstants_1.ZERO) {
376
500
  // solves formula for example canBeLiquidated below
377
501
  /* example: assume BTC price is $40k (examine 10% up/down)
378
502
 
@@ -394,28 +518,30 @@ class ClearingHouseUser {
394
518
  const proposedMarketPosition = {
395
519
  marketIndex: marketPosition.marketIndex,
396
520
  baseAssetAmount: proposedBaseAssetAmount,
397
- lastCumulativeFundingRate: currentMarketPosition.lastCumulativeFundingRate,
398
521
  quoteAssetAmount: new _1.BN(0),
522
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
399
523
  quoteEntryAmount: new _1.BN(0),
400
524
  openOrders: new _1.BN(0),
401
- unsettledPnl: new _1.BN(0),
402
525
  openBids: new _1.BN(0),
403
526
  openAsks: new _1.BN(0),
527
+ realizedPnl: numericConstants_1.ZERO,
528
+ lpShares: numericConstants_1.ZERO,
529
+ lastFeePerLp: numericConstants_1.ZERO,
530
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
531
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
404
532
  };
405
533
  if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
406
534
  return new _1.BN(-1);
407
535
  const market = this.clearingHouse.getMarketAccount(proposedMarketPosition.marketIndex);
408
- const proposedMarketPositionValue = (0, _1.calculateBaseAssetValue)(market, proposedMarketPosition, this.getOracleDataForMarket(market.marketIndex));
536
+ const proposedMarketPositionValue = (0, margin_1.calculateMarginBaseAssetValue)(market, proposedMarketPosition, this.getOracleDataForMarket(market.marketIndex));
409
537
  // total position value after trade
410
538
  const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
411
539
  const marginRequirementExcludingTargetMarket = this.getUserAccount().positions.reduce((totalMarginRequirement, position) => {
412
540
  if (!position.marketIndex.eq(marketPosition.marketIndex)) {
413
541
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
414
- const positionValue = (0, _1.calculateBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
542
+ const positionValue = (0, margin_1.calculateMarginBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
415
543
  const marketMarginRequirement = positionValue
416
- .mul(partial
417
- ? new _1.BN(market.marginRatioPartial)
418
- : new _1.BN(market.marginRatioMaintenance))
544
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
419
545
  .div(numericConstants_1.MARGIN_PRECISION);
420
546
  totalMarginRequirement = totalMarginRequirement.add(marketMarginRequirement);
421
547
  }
@@ -428,14 +554,10 @@ class ClearingHouseUser {
428
554
  return new _1.BN(-1);
429
555
  }
430
556
  const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedMarketPositionValue
431
- .mul(partial
432
- ? new _1.BN(market.marginRatioPartial)
433
- : new _1.BN(market.marginRatioMaintenance))
557
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, proposedMarketPosition.baseAssetAmount.abs(), 'Maintenance')))
434
558
  .div(numericConstants_1.MARGIN_PRECISION));
435
559
  const freeCollateralAfterTrade = totalCollateral.sub(marginRequirementAfterTrade);
436
- const marketMaxLeverage = partial
437
- ? this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Partial')
438
- : this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
560
+ const marketMaxLeverage = this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
439
561
  let priceDelta;
440
562
  if (proposedBaseAssetAmount.lt(numericConstants_1.ZERO)) {
441
563
  priceDelta = freeCollateralAfterTrade
@@ -479,14 +601,14 @@ class ClearingHouseUser {
479
601
  this.getEmptyPosition(positionMarketIndex);
480
602
  const closeBaseAmount = currentPosition.baseAssetAmount
481
603
  .mul(closeQuoteAmount)
482
- .div(currentPosition.quoteAssetAmount)
604
+ .div(currentPosition.quoteAssetAmount.abs())
483
605
  .add(currentPosition.baseAssetAmount
484
606
  .mul(closeQuoteAmount)
485
- .mod(currentPosition.quoteAssetAmount))
607
+ .mod(currentPosition.quoteAssetAmount.abs()))
486
608
  .neg();
487
609
  return this.liquidationPrice({
488
610
  marketIndex: positionMarketIndex,
489
- }, closeBaseAmount, true);
611
+ }, closeBaseAmount);
490
612
  }
491
613
  /**
492
614
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
@@ -0,0 +1,18 @@
1
+ import { ClearingHouse } from './clearingHouse';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { DataAndSlot, UserStatsAccountSubscriber } from './accounts/types';
4
+ import { ClearingHouseUserStatsConfig } from './clearingHouseUserStatsConfig';
5
+ import { ReferrerInfo, UserStatsAccount } from './types';
6
+ export declare class ClearingHouseUserStats {
7
+ clearingHouse: ClearingHouse;
8
+ userStatsAccountPublicKey: PublicKey;
9
+ accountSubscriber: UserStatsAccountSubscriber;
10
+ isSubscribed: boolean;
11
+ constructor(config: ClearingHouseUserStatsConfig);
12
+ subscribe(): Promise<boolean>;
13
+ fetchAccounts(): Promise<void>;
14
+ unsubscribe(): Promise<void>;
15
+ getAccountAndSlot(): DataAndSlot<UserStatsAccount>;
16
+ getAccount(): UserStatsAccount;
17
+ getReferrerInfo(): ReferrerInfo | undefined;
18
+ }