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

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 (152) 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 +727 -197
  17. package/lib/clearingHouseConfig.d.ts +1 -0
  18. package/lib/clearingHouseUser.d.ts +17 -17
  19. package/lib/clearingHouseUser.js +186 -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 +1609 -388
  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 +5 -2
  50. package/lib/math/orders.js +53 -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 +233 -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 +1224 -319
  80. package/src/clearingHouseConfig.ts +1 -0
  81. package/src/clearingHouseUser.ts +311 -148
  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/fetchLogs.js +50 -0
  94. package/src/events/pollingLogProvider.js +64 -0
  95. package/src/events/sort.js +44 -0
  96. package/src/events/txEventCache.js +71 -0
  97. package/src/events/types.ts +6 -0
  98. package/src/events/webSocketLogProvider.js +41 -0
  99. package/src/examples/makeTradeExample.js +80 -0
  100. package/src/factory/bigNum.js +390 -0
  101. package/src/factory/bigNum.ts +42 -13
  102. package/src/factory/oracleClient.js +20 -0
  103. package/src/idl/clearing_house.json +1609 -388
  104. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  105. package/src/index.ts +9 -3
  106. package/src/math/amm.ts +54 -55
  107. package/src/math/auction.js +42 -0
  108. package/src/math/auction.ts +5 -1
  109. package/src/math/bankBalance.ts +148 -2
  110. package/src/math/conversion.js +11 -0
  111. package/src/math/funding.js +248 -0
  112. package/src/math/margin.ts +124 -0
  113. package/src/math/market.ts +66 -1
  114. package/src/math/oracles.js +26 -0
  115. package/src/math/oracles.ts +42 -5
  116. package/src/math/orders.ts +112 -13
  117. package/src/math/position.ts +64 -9
  118. package/src/math/repeg.js +128 -0
  119. package/src/math/state.js +15 -0
  120. package/src/math/trade.js +253 -0
  121. package/src/math/trade.ts +23 -25
  122. package/src/math/utils.js +0 -1
  123. package/src/oracles/oracleClientCache.js +19 -0
  124. package/src/oracles/pythClient.js +46 -0
  125. package/src/oracles/quoteAssetOracleClient.js +32 -0
  126. package/src/oracles/switchboardClient.js +69 -0
  127. package/src/oracles/types.js +2 -0
  128. package/src/orderParams.js +20 -0
  129. package/src/orderParams.ts +20 -141
  130. package/src/slot/SlotSubscriber.js +39 -0
  131. package/src/slot/SlotSubscriber.ts +11 -1
  132. package/src/token/index.js +38 -0
  133. package/src/tokenFaucet.js +189 -0
  134. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  135. package/src/tx/retryTxSender.ts +11 -3
  136. package/src/tx/types.js +2 -0
  137. package/src/tx/utils.js +17 -0
  138. package/src/tx/utils.ts +1 -1
  139. package/src/types.ts +236 -27
  140. package/src/userName.js +20 -0
  141. package/src/util/computeUnits.js +21 -11
  142. package/src/util/computeUnits.ts +1 -1
  143. package/src/util/getTokenAddress.js +9 -0
  144. package/src/util/getTokenAddress.ts +18 -0
  145. package/src/util/promiseTimeout.js +14 -0
  146. package/src/util/tps.js +27 -0
  147. package/src/wallet.js +35 -0
  148. package/tests/bn/test.ts +10 -0
  149. package/lib/orders.d.ts +0 -8
  150. package/lib/orders.js +0 -142
  151. package/src/orders.ts +0 -251
  152. 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';
@@ -49,6 +49,11 @@ export declare class ClearingHouseUser {
49
49
  getOrderByUserOrderId(userOrderId: number): Order | undefined;
50
50
  getUserAccountPublicKey(): PublicKey;
51
51
  exists(): Promise<boolean>;
52
+ /**
53
+ * calculates the market position if the lp position was settled
54
+ * @returns : userPosition
55
+ */
56
+ getSettledLPPosition(marketIndex: BN): [UserPosition, BN];
52
57
  /**
53
58
  * calculates Buying Power = FC * MAX_LEVERAGE
54
59
  * @returns : Precision QUOTE_PRECISION
@@ -61,48 +66,43 @@ export declare class ClearingHouseUser {
61
66
  getFreeCollateral(): BN;
62
67
  getInitialMarginRequirement(): BN;
63
68
  /**
64
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
65
- */
66
- getPartialMarginRequirement(): BN;
67
- /**
68
- * calculates unrealized position price pnl
69
- * @returns : Precision QUOTE_PRECISION
69
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
70
70
  */
71
- getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN;
71
+ getMaintenanceMarginRequirement(): BN;
72
72
  /**
73
73
  * calculates unrealized position price pnl
74
74
  * @returns : Precision QUOTE_PRECISION
75
75
  */
76
- getUnsettledPNL(marketIndex?: BN): BN;
76
+ getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
77
77
  /**
78
78
  * calculates unrealized funding payment pnl
79
79
  * @returns : Precision QUOTE_PRECISION
80
80
  */
81
81
  getUnrealizedFundingPNL(marketIndex?: BN): BN;
82
- getTotalLiability(): BN;
83
- getCollateralValue(bankIndex?: BN): BN;
82
+ getBankLiabilityValue(bankIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
83
+ getBankAssetValue(bankIndex?: BN, withWeightMarginCategory?: MarginCategory): BN;
84
+ getNetBankValue(withWeightMarginCategory?: MarginCategory): BN;
84
85
  /**
85
86
  * calculates TotalCollateral: collateral + unrealized pnl
86
- * TODO: rename to total equity (for perpetuals swaps)
87
87
  * @returns : Precision QUOTE_PRECISION
88
88
  */
89
- getTotalCollateral(): BN;
89
+ getTotalCollateral(marginCategory?: MarginCategory): BN;
90
90
  /**
91
- * calculates sum of position value across all positions
91
+ * calculates sum of position value across all positions in margin system
92
92
  * @returns : Precision QUOTE_PRECISION
93
93
  */
94
94
  getTotalPositionValue(): BN;
95
95
  /**
96
- * calculates position value from closing 100%
96
+ * calculates position value in margin system
97
97
  * @returns : Precision QUOTE_PRECISION
98
98
  */
99
99
  getPositionValue(marketIndex: BN, oraclePriceData: OraclePriceData): BN;
100
100
  getPositionSide(currentPosition: Pick<UserPosition, 'baseAssetAmount'>): PositionDirection | undefined;
101
101
  /**
102
- * calculates average exit price for closing 100% of position
102
+ * calculates average exit price (optionally for closing up to 100% of position)
103
103
  * @returns : Precision MARK_PRICE_PRECISION
104
104
  */
105
- getPositionEstimatedExitPriceAndPnl(position: UserPosition, amountToClose?: BN): [BN, BN];
105
+ getPositionEstimatedExitPriceAndPnl(position: UserPosition, amountToClose?: BN, useAMMClose?: boolean): [BN, BN];
106
106
  /**
107
107
  * calculates current user leverage across all positions
108
108
  * @returns : Precision TEN_THOUSAND
@@ -132,7 +132,7 @@ export declare class ClearingHouseUser {
132
132
  * @param partial
133
133
  * @returns Precision : MARK_PRICE_PRECISION
134
134
  */
135
- liquidationPrice(marketPosition: Pick<UserPosition, 'marketIndex'>, positionBaseSizeChange?: BN, partial?: boolean): BN;
135
+ liquidationPrice(marketPosition: Pick<UserPosition, 'marketIndex'>, positionBaseSizeChange?: BN): BN;
136
136
  /**
137
137
  * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
138
138
  * @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,9 +69,13 @@ 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
  }
76
81
  /**
@@ -94,6 +99,89 @@ class ClearingHouseUser {
94
99
  const userAccountRPCResponse = await this.clearingHouse.connection.getParsedAccountInfo(this.userAccountPublicKey);
95
100
  return userAccountRPCResponse.value !== null;
96
101
  }
102
+ /**
103
+ * calculates the market position if the lp position was settled
104
+ * @returns : userPosition
105
+ */
106
+ getSettledLPPosition(marketIndex) {
107
+ const position = this.getUserPosition(marketIndex);
108
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
109
+ const nShares = position.lpShares;
110
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
111
+ .sub(position.lastNetBaseAssetAmountPerLp)
112
+ .mul(nShares)
113
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
114
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
115
+ .sub(position.lastNetQuoteAssetAmountPerLp)
116
+ .mul(nShares)
117
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
118
+ function sign(v) {
119
+ const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
120
+ return sign;
121
+ }
122
+ const remainder = deltaBaa
123
+ .abs()
124
+ .mod(market.amm.baseAssetAmountStepSize)
125
+ .mul(sign(deltaBaa));
126
+ const _standardizedBaa = deltaBaa.sub(remainder);
127
+ let remainderBaa;
128
+ if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
129
+ remainderBaa = remainder;
130
+ }
131
+ else {
132
+ remainderBaa = deltaBaa;
133
+ }
134
+ const standardizedBaa = deltaBaa.sub(remainderBaa);
135
+ const reaminderPerLP = remainderBaa.mul(numericConstants_1.AMM_RESERVE_PRECISION).div(nShares);
136
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
137
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
138
+ position.lastNetBaseAssetAmountPerLp =
139
+ market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
140
+ let updateType;
141
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
142
+ updateType = 'open';
143
+ }
144
+ else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
145
+ updateType = 'increase';
146
+ }
147
+ else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
148
+ updateType = 'reduce';
149
+ }
150
+ else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
151
+ updateType = 'close';
152
+ }
153
+ else {
154
+ updateType = 'flip';
155
+ }
156
+ let newQuoteEntry;
157
+ let pnl;
158
+ if (updateType == 'open' || updateType == 'increase') {
159
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
160
+ pnl = 0;
161
+ }
162
+ else if (updateType == 'reduce' || updateType == 'close') {
163
+ newQuoteEntry = position.quoteEntryAmount.sub(position.quoteEntryAmount
164
+ .mul(deltaBaa.abs())
165
+ .div(position.baseAssetAmount.abs()));
166
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry);
167
+ }
168
+ else {
169
+ newQuoteEntry = deltaQaa.sub(deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()));
170
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
171
+ }
172
+ position.quoteEntryAmount = newQuoteEntry;
173
+ if (position.baseAssetAmount.gt(numericConstants_1.ZERO)) {
174
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
175
+ }
176
+ else if (position.baseAssetAmount.lt(numericConstants_1.ZERO)) {
177
+ position.lastCumulativeFundingRate =
178
+ market.amm.cumulativeFundingRateShort;
179
+ }
180
+ else {
181
+ position.lastCumulativeFundingRate = numericConstants_1.ZERO;
182
+ }
183
+ return [position, pnl];
184
+ }
97
185
  /**
98
186
  * calculates Buying Power = FC * MAX_LEVERAGE
99
187
  * @returns : Precision QUOTE_PRECISION
@@ -114,49 +202,55 @@ class ClearingHouseUser {
114
202
  return freeCollateral.gte(numericConstants_1.ZERO) ? freeCollateral : numericConstants_1.ZERO;
115
203
  }
116
204
  getInitialMarginRequirement() {
117
- return this.getUserAccount()
118
- .positions.reduce((marginRequirement, marketPosition) => {
205
+ const postionMarginRequirement = this.getUserAccount().positions.reduce((marginRequirement, marketPosition) => {
119
206
  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))
122
- .div(numericConstants_1.MARGIN_PRECISION));
123
- }, numericConstants_1.ZERO)
124
- .add(this.getTotalLiability());
207
+ const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(marketPosition);
208
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
209
+ .abs()
210
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
211
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
212
+ const marketMarginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), 'Initial'));
213
+ return marginRequirement.add(worstCaseAssetValue.mul(marketMarginRatio).div(numericConstants_1.MARGIN_PRECISION));
214
+ }, numericConstants_1.ZERO);
215
+ const bankMarginRequirement = this.getBankLiabilityValue(undefined, 'Initial');
216
+ return bankMarginRequirement.add(postionMarginRequirement);
125
217
  }
126
218
  /**
127
- * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
219
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
128
220
  */
129
- getPartialMarginRequirement() {
221
+ getMaintenanceMarginRequirement() {
130
222
  return this.getUserAccount()
131
223
  .positions.reduce((marginRequirement, marketPosition) => {
132
224
  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))
225
+ const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(marketPosition);
226
+ const worstCaseAssetValue = worstCaseBaseAssetAmount
227
+ .abs()
228
+ .mul(this.getOracleDataForMarket(market.marketIndex).price)
229
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
230
+ return marginRequirement.add(worstCaseAssetValue
231
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), 'Maintenance')))
135
232
  .div(numericConstants_1.MARGIN_PRECISION));
136
233
  }, numericConstants_1.ZERO)
137
- .add(this.getTotalLiability());
234
+ .add(this.getBankLiabilityValue(undefined, 'Maintenance'));
138
235
  }
139
236
  /**
140
237
  * calculates unrealized position price pnl
141
238
  * @returns : Precision QUOTE_PRECISION
142
239
  */
143
- getUnrealizedPNL(withFunding, marketIndex) {
240
+ getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
144
241
  return this.getUserAccount()
145
242
  .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
146
- .reduce((pnl, marketPosition) => {
243
+ .reduce((unrealizedPnl, marketPosition) => {
147
244
  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);
150
- }
151
- /**
152
- * calculates unrealized position price pnl
153
- * @returns : Precision QUOTE_PRECISION
154
- */
155
- getUnsettledPNL(marketIndex) {
156
- return this.getUserAccount()
157
- .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
158
- .reduce((pnl, marketPosition) => {
159
- return pnl.add(marketPosition.unsettledPnl);
245
+ let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, marketPosition, withFunding, this.getOracleDataForMarket(market.marketIndex));
246
+ if (withWeightMarginCategory !== undefined) {
247
+ if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
248
+ positionUnrealizedPnl = positionUnrealizedPnl
249
+ .mul((0, _1.calculateUnrealizedAssetWeight)(market, positionUnrealizedPnl, withWeightMarginCategory))
250
+ .div(new _1.BN(numericConstants_1.BANK_WEIGHT_PRECISION));
251
+ }
252
+ }
253
+ return unrealizedPnl.add(positionUnrealizedPnl);
160
254
  }, numericConstants_1.ZERO);
161
255
  }
162
256
  /**
@@ -171,81 +265,79 @@ class ClearingHouseUser {
171
265
  return pnl.add((0, _1.calculatePositionFundingPNL)(market, marketPosition));
172
266
  }, numericConstants_1.ZERO);
173
267
  }
174
- getTotalLiability() {
175
- return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
268
+ getBankLiabilityValue(bankIndex, withWeightMarginCategory) {
269
+ return this.getUserAccount().bankBalances.reduce((totalLiabilityValue, bankBalance) => {
176
270
  if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
177
- (0, types_1.isVariant)(bankBalance.balanceType, 'deposit')) {
178
- return totalAssetValue;
271
+ (0, types_1.isVariant)(bankBalance.balanceType, 'deposit') ||
272
+ (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))) {
273
+ return totalLiabilityValue;
179
274
  }
180
275
  // Todo this needs to account for whether it's based on initial or maintenance requirements
181
276
  const bankAccount = this.clearingHouse.getBankAccount(bankBalance.bankIndex);
182
277
  const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
183
- return totalAssetValue.add(tokenAmount
278
+ let liabilityValue = tokenAmount
184
279
  .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
185
- .mul(bankAccount.initialLiabilityWeight)
186
- .div(numericConstants_1.BANK_WEIGHT_PRECISION)
187
- .div(numericConstants_1.MARK_PRICE_PRECISION));
280
+ .div(numericConstants_1.MARK_PRICE_PRECISION)
281
+ .div(new _1.BN(10).pow(new _1.BN(bankAccount.decimals).sub(numericConstants_1.BANK_BALANCE_PRECISION_EXP)));
282
+ if (withWeightMarginCategory !== undefined) {
283
+ const weight = (0, bankBalance_1.calculateLiabilityWeight)(tokenAmount, bankAccount, withWeightMarginCategory);
284
+ liabilityValue = liabilityValue
285
+ .mul(weight)
286
+ .div(numericConstants_1.BANK_WEIGHT_PRECISION);
287
+ }
288
+ return totalLiabilityValue.add(liabilityValue);
188
289
  }, numericConstants_1.ZERO);
189
290
  }
190
- getCollateralValue(bankIndex) {
291
+ getBankAssetValue(bankIndex, withWeightMarginCategory) {
191
292
  return this.getUserAccount().bankBalances.reduce((totalAssetValue, bankBalance) => {
192
293
  if (bankBalance.balance.eq(numericConstants_1.ZERO) ||
294
+ (0, types_1.isVariant)(bankBalance.balanceType, 'borrow') ||
193
295
  (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))) {
194
296
  return totalAssetValue;
195
297
  }
196
298
  // Todo this needs to account for whether it's based on initial or maintenance requirements
197
299
  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
300
+ const tokenAmount = (0, bankBalance_1.getTokenAmount)(bankBalance.balance, bankAccount, bankBalance.balanceType);
301
+ let assetValue = tokenAmount
203
302
  .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
204
- .div(numericConstants_1.MARK_PRICE_PRECISION));
303
+ .div(numericConstants_1.MARK_PRICE_PRECISION)
304
+ .div(new _1.BN(10).pow(new _1.BN(bankAccount.decimals).sub(numericConstants_1.BANK_BALANCE_PRECISION_EXP)));
305
+ if (withWeightMarginCategory !== undefined) {
306
+ const weight = (0, bankBalance_1.calculateAssetWeight)(tokenAmount, bankAccount, withWeightMarginCategory);
307
+ assetValue = assetValue.mul(weight).div(numericConstants_1.BANK_WEIGHT_PRECISION);
308
+ }
309
+ return totalAssetValue.add(assetValue);
205
310
  }, numericConstants_1.ZERO);
206
311
  }
312
+ getNetBankValue(withWeightMarginCategory) {
313
+ return this.getBankAssetValue(undefined, withWeightMarginCategory).sub(this.getBankLiabilityValue(undefined, withWeightMarginCategory));
314
+ }
207
315
  /**
208
316
  * calculates TotalCollateral: collateral + unrealized pnl
209
- * TODO: rename to total equity (for perpetuals swaps)
210
317
  * @returns : Precision QUOTE_PRECISION
211
318
  */
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());
319
+ getTotalCollateral(marginCategory = 'Initial') {
320
+ return this.getBankAssetValue(undefined, marginCategory).add(this.getUnrealizedPNL(true, undefined, marginCategory));
230
321
  }
231
322
  /**
232
- * calculates sum of position value across all positions
323
+ * calculates sum of position value across all positions in margin system
233
324
  * @returns : Precision QUOTE_PRECISION
234
325
  */
235
326
  getTotalPositionValue() {
236
327
  return this.getUserAccount().positions.reduce((positionValue, marketPosition) => {
237
328
  const market = this.clearingHouse.getMarketAccount(marketPosition.marketIndex);
238
- return positionValue.add((0, _1.calculateBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex)));
329
+ const posVal = (0, margin_1.calculateMarginBaseAssetValue)(market, marketPosition, this.getOracleDataForMarket(market.marketIndex));
330
+ return positionValue.add(posVal);
239
331
  }, numericConstants_1.ZERO);
240
332
  }
241
333
  /**
242
- * calculates position value from closing 100%
334
+ * calculates position value in margin system
243
335
  * @returns : Precision QUOTE_PRECISION
244
336
  */
245
337
  getPositionValue(marketIndex, oraclePriceData) {
246
338
  const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
247
339
  const market = this.clearingHouse.getMarketAccount(userPosition.marketIndex);
248
- return (0, _1.calculateBaseAssetValue)(market, userPosition, oraclePriceData);
340
+ return (0, margin_1.calculateMarginBaseAssetValue)(market, userPosition, oraclePriceData);
249
341
  }
250
342
  getPositionSide(currentPosition) {
251
343
  if (currentPosition.baseAssetAmount.gt(numericConstants_1.ZERO)) {
@@ -259,10 +351,10 @@ class ClearingHouseUser {
259
351
  }
260
352
  }
261
353
  /**
262
- * calculates average exit price for closing 100% of position
354
+ * calculates average exit price (optionally for closing up to 100% of position)
263
355
  * @returns : Precision MARK_PRICE_PRECISION
264
356
  */
265
- getPositionEstimatedExitPriceAndPnl(position, amountToClose) {
357
+ getPositionEstimatedExitPriceAndPnl(position, amountToClose, useAMMClose = false) {
266
358
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
267
359
  const entryPrice = (0, position_1.calculateEntryPrice)(position);
268
360
  const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
@@ -277,7 +369,13 @@ class ClearingHouseUser {
277
369
  quoteAssetAmount: position.quoteAssetAmount,
278
370
  };
279
371
  }
280
- const baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
372
+ let baseAssetValue;
373
+ if (useAMMClose) {
374
+ baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
375
+ }
376
+ else {
377
+ baseAssetValue = (0, margin_1.calculateMarginBaseAssetValue)(market, position, oraclePriceData);
378
+ }
281
379
  if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
282
380
  return [numericConstants_1.ZERO, numericConstants_1.ZERO];
283
381
  }
@@ -311,21 +409,10 @@ class ClearingHouseUser {
311
409
  */
312
410
  getMaxLeverage(marketIndex, category = 'Initial') {
313
411
  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
- }
412
+ const marginRatioCategory = (0, _1.calculateMarketMarginRatio)(market,
413
+ // worstCaseBaseAssetAmount.abs(),
414
+ numericConstants_1.ZERO, // todo
415
+ category);
329
416
  const maxLeverage = numericConstants_1.TEN_THOUSAND.mul(numericConstants_1.TEN_THOUSAND).div(new _1.BN(marginRatioCategory));
330
417
  return maxLeverage;
331
418
  }
@@ -342,7 +429,7 @@ class ClearingHouseUser {
342
429
  }
343
430
  canBeLiquidated() {
344
431
  const totalCollateral = this.getTotalCollateral();
345
- const partialMaintenanceRequirement = this.getPartialMarginRequirement();
432
+ const partialMaintenanceRequirement = this.getMaintenanceMarginRequirement();
346
433
  const marginRatio = this.getMarginRatio();
347
434
  const canLiquidate = totalCollateral.lt(partialMaintenanceRequirement);
348
435
  return [canLiquidate, marginRatio];
@@ -372,7 +459,7 @@ class ClearingHouseUser {
372
459
  * @param partial
373
460
  * @returns Precision : MARK_PRICE_PRECISION
374
461
  */
375
- liquidationPrice(marketPosition, positionBaseSizeChange = numericConstants_1.ZERO, partial = false) {
462
+ liquidationPrice(marketPosition, positionBaseSizeChange = numericConstants_1.ZERO) {
376
463
  // solves formula for example canBeLiquidated below
377
464
  /* example: assume BTC price is $40k (examine 10% up/down)
378
465
 
@@ -394,28 +481,30 @@ class ClearingHouseUser {
394
481
  const proposedMarketPosition = {
395
482
  marketIndex: marketPosition.marketIndex,
396
483
  baseAssetAmount: proposedBaseAssetAmount,
397
- lastCumulativeFundingRate: currentMarketPosition.lastCumulativeFundingRate,
398
484
  quoteAssetAmount: new _1.BN(0),
485
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
399
486
  quoteEntryAmount: new _1.BN(0),
400
487
  openOrders: new _1.BN(0),
401
- unsettledPnl: new _1.BN(0),
402
488
  openBids: new _1.BN(0),
403
489
  openAsks: new _1.BN(0),
490
+ realizedPnl: numericConstants_1.ZERO,
491
+ lpShares: numericConstants_1.ZERO,
492
+ lastFeePerLp: numericConstants_1.ZERO,
493
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
494
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
404
495
  };
405
496
  if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
406
497
  return new _1.BN(-1);
407
498
  const market = this.clearingHouse.getMarketAccount(proposedMarketPosition.marketIndex);
408
- const proposedMarketPositionValue = (0, _1.calculateBaseAssetValue)(market, proposedMarketPosition, this.getOracleDataForMarket(market.marketIndex));
499
+ const proposedMarketPositionValue = (0, margin_1.calculateMarginBaseAssetValue)(market, proposedMarketPosition, this.getOracleDataForMarket(market.marketIndex));
409
500
  // total position value after trade
410
501
  const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
411
502
  const marginRequirementExcludingTargetMarket = this.getUserAccount().positions.reduce((totalMarginRequirement, position) => {
412
503
  if (!position.marketIndex.eq(marketPosition.marketIndex)) {
413
504
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
414
- const positionValue = (0, _1.calculateBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
505
+ const positionValue = (0, margin_1.calculateMarginBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
415
506
  const marketMarginRequirement = positionValue
416
- .mul(partial
417
- ? new _1.BN(market.marginRatioPartial)
418
- : new _1.BN(market.marginRatioMaintenance))
507
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
419
508
  .div(numericConstants_1.MARGIN_PRECISION);
420
509
  totalMarginRequirement = totalMarginRequirement.add(marketMarginRequirement);
421
510
  }
@@ -428,14 +517,10 @@ class ClearingHouseUser {
428
517
  return new _1.BN(-1);
429
518
  }
430
519
  const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedMarketPositionValue
431
- .mul(partial
432
- ? new _1.BN(market.marginRatioPartial)
433
- : new _1.BN(market.marginRatioMaintenance))
520
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, proposedMarketPosition.baseAssetAmount.abs(), 'Maintenance')))
434
521
  .div(numericConstants_1.MARGIN_PRECISION));
435
522
  const freeCollateralAfterTrade = totalCollateral.sub(marginRequirementAfterTrade);
436
- const marketMaxLeverage = partial
437
- ? this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Partial')
438
- : this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
523
+ const marketMaxLeverage = this.getMaxLeverage(proposedMarketPosition.marketIndex, 'Maintenance');
439
524
  let priceDelta;
440
525
  if (proposedBaseAssetAmount.lt(numericConstants_1.ZERO)) {
441
526
  priceDelta = freeCollateralAfterTrade
@@ -479,14 +564,14 @@ class ClearingHouseUser {
479
564
  this.getEmptyPosition(positionMarketIndex);
480
565
  const closeBaseAmount = currentPosition.baseAssetAmount
481
566
  .mul(closeQuoteAmount)
482
- .div(currentPosition.quoteAssetAmount)
567
+ .div(currentPosition.quoteAssetAmount.abs())
483
568
  .add(currentPosition.baseAssetAmount
484
569
  .mul(closeQuoteAmount)
485
- .mod(currentPosition.quoteAssetAmount))
570
+ .mod(currentPosition.quoteAssetAmount.abs()))
486
571
  .neg();
487
572
  return this.liquidationPrice({
488
573
  marketIndex: positionMarketIndex,
489
- }, closeBaseAmount, true);
574
+ }, closeBaseAmount);
490
575
  }
491
576
  /**
492
577
  * 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
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClearingHouseUserStats = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const pollingUserStatsAccountSubscriber_1 = require("./accounts/pollingUserStatsAccountSubscriber");
6
+ const webSocketUserStatsAccountSubsriber_1 = require("./accounts/webSocketUserStatsAccountSubsriber");
7
+ const pda_1 = require("./addresses/pda");
8
+ class ClearingHouseUserStats {
9
+ constructor(config) {
10
+ var _a;
11
+ this.clearingHouse = config.clearingHouse;
12
+ this.userStatsAccountPublicKey = config.userStatsAccountPublicKey;
13
+ if (((_a = config.accountSubscription) === null || _a === void 0 ? void 0 : _a.type) === 'polling') {
14
+ this.accountSubscriber = new pollingUserStatsAccountSubscriber_1.PollingUserStatsAccountSubscriber(config.clearingHouse.program, config.userStatsAccountPublicKey, config.accountSubscription.accountLoader);
15
+ }
16
+ else {
17
+ this.accountSubscriber = new webSocketUserStatsAccountSubsriber_1.WebSocketUserStatsAccountSubscriber(config.clearingHouse.program, config.userStatsAccountPublicKey);
18
+ }
19
+ }
20
+ async subscribe() {
21
+ this.isSubscribed = await this.accountSubscriber.subscribe();
22
+ return this.isSubscribed;
23
+ }
24
+ async fetchAccounts() {
25
+ await this.accountSubscriber.fetch();
26
+ }
27
+ async unsubscribe() {
28
+ await this.accountSubscriber.unsubscribe();
29
+ this.isSubscribed = false;
30
+ }
31
+ getAccountAndSlot() {
32
+ return this.accountSubscriber.getUserStatsAccountAndSlot();
33
+ }
34
+ getAccount() {
35
+ return this.accountSubscriber.getUserStatsAccountAndSlot().data;
36
+ }
37
+ getReferrerInfo() {
38
+ if (this.getAccount().referrer.equals(web3_js_1.PublicKey.default)) {
39
+ return undefined;
40
+ }
41
+ else {
42
+ return {
43
+ referrer: (0, pda_1.getUserAccountPublicKeySync)(this.clearingHouse.program.programId, this.getAccount().referrer, 0),
44
+ referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.clearingHouse.program.programId, this.getAccount().referrer),
45
+ };
46
+ }
47
+ }
48
+ }
49
+ exports.ClearingHouseUserStats = ClearingHouseUserStats;
@@ -0,0 +1,14 @@
1
+ import { ClearingHouse } from './clearingHouse';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
+ export declare type ClearingHouseUserStatsConfig = {
5
+ accountSubscription?: ClearingHouseUserStatsAccountSubscriptionConfig;
6
+ clearingHouse: ClearingHouse;
7
+ userStatsAccountPublicKey: PublicKey;
8
+ };
9
+ export declare type ClearingHouseUserStatsAccountSubscriptionConfig = {
10
+ type: 'websocket';
11
+ } | {
12
+ type: 'polling';
13
+ accountLoader: BulkAccountLoader;
14
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/config.js CHANGED
@@ -7,7 +7,7 @@ exports.configs = {
7
7
  devnet: {
8
8
  ENV: 'devnet',
9
9
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
10
- CLEARING_HOUSE_PROGRAM_ID: '9jwr5nC2f9yAraXrg4UzHXmCX3vi9FQkjD6p9e8bRqNa',
10
+ CLEARING_HOUSE_PROGRAM_ID: '65sz7dRiWDRPZjiRxcTxPM7AE6VK4Nag9HEK6oBJXhJn',
11
11
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
12
12
  MARKETS: markets_1.DevnetMarkets,
13
13
  BANKS: banks_1.DevnetBanks,