@drift-labs/sdk 0.1.36-master.5 → 0.2.0-master.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +7 -6
  2. package/lib/accounts/bulkAccountLoader.js +83 -93
  3. package/lib/accounts/bulkUserSubscription.js +13 -57
  4. package/lib/accounts/fetch.d.ts +4 -0
  5. package/lib/accounts/fetch.js +18 -0
  6. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +34 -38
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +224 -224
  8. package/lib/accounts/pollingOracleSubscriber.d.ts +3 -3
  9. package/lib/accounts/pollingOracleSubscriber.js +37 -49
  10. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
  11. package/lib/accounts/pollingTokenAccountSubscriber.js +35 -50
  12. package/lib/accounts/pollingUserAccountSubscriber.d.ts +7 -13
  13. package/lib/accounts/pollingUserAccountSubscriber.js +71 -134
  14. package/lib/accounts/types.d.ts +34 -41
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -4
  16. package/lib/accounts/webSocketAccountSubscriber.js +39 -35
  17. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +33 -28
  18. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +153 -185
  19. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +5 -11
  20. package/lib/accounts/webSocketUserAccountSubscriber.js +22 -67
  21. package/lib/addresses/marketAddresses.d.ts +4 -0
  22. package/lib/addresses/marketAddresses.js +15 -0
  23. package/lib/addresses/pda.d.ts +12 -0
  24. package/lib/addresses/pda.js +83 -0
  25. package/lib/admin.d.ts +8 -12
  26. package/lib/admin.js +366 -490
  27. package/lib/clearingHouse.d.ts +84 -103
  28. package/lib/clearingHouse.js +779 -810
  29. package/lib/clearingHouseConfig.d.ts +34 -0
  30. package/lib/clearingHouseConfig.js +2 -0
  31. package/lib/clearingHouseUser.d.ts +19 -18
  32. package/lib/clearingHouseUser.js +157 -115
  33. package/lib/clearingHouseUserConfig.d.ts +14 -0
  34. package/lib/clearingHouseUserConfig.js +2 -0
  35. package/lib/config.d.ts +12 -0
  36. package/lib/config.js +35 -4
  37. package/lib/constants/banks.d.ts +16 -0
  38. package/lib/constants/banks.js +34 -0
  39. package/lib/constants/markets.d.ts +8 -3
  40. package/lib/constants/markets.js +13 -206
  41. package/lib/constants/numericConstants.d.ts +16 -0
  42. package/lib/constants/numericConstants.js +22 -6
  43. package/lib/events/eventList.d.ts +22 -0
  44. package/lib/events/eventList.js +77 -0
  45. package/lib/events/eventSubscriber.d.ts +34 -0
  46. package/lib/events/eventSubscriber.js +126 -0
  47. package/lib/events/fetchLogs.d.ts +13 -0
  48. package/lib/events/fetchLogs.js +39 -0
  49. package/lib/events/pollingLogProvider.d.ts +15 -0
  50. package/lib/events/pollingLogProvider.js +53 -0
  51. package/lib/events/sort.d.ts +2 -0
  52. package/lib/events/sort.js +44 -0
  53. package/lib/events/txEventCache.d.ts +24 -0
  54. package/lib/events/txEventCache.js +71 -0
  55. package/lib/events/types.d.ts +49 -0
  56. package/lib/events/types.js +20 -0
  57. package/lib/events/webSocketLogProvider.d.ts +12 -0
  58. package/lib/events/webSocketLogProvider.js +30 -0
  59. package/lib/examples/makeTradeExample.js +26 -27
  60. package/lib/factory/bigNum.d.ts +112 -0
  61. package/lib/factory/bigNum.js +356 -0
  62. package/lib/factory/oracleClient.d.ts +1 -2
  63. package/lib/factory/oracleClient.js +6 -2
  64. package/lib/idl/clearing_house.json +1739 -2287
  65. package/lib/index.d.ts +13 -4
  66. package/lib/index.js +13 -4
  67. package/lib/math/amm.d.ts +19 -29
  68. package/lib/math/amm.js +129 -179
  69. package/lib/math/auction.d.ts +5 -0
  70. package/lib/math/auction.js +39 -0
  71. package/lib/math/bankBalance.d.ts +9 -0
  72. package/lib/math/bankBalance.js +75 -0
  73. package/lib/math/conversion.d.ts +0 -1
  74. package/lib/math/conversion.js +1 -5
  75. package/lib/math/funding.d.ts +6 -6
  76. package/lib/math/funding.js +158 -175
  77. package/lib/math/market.d.ts +6 -6
  78. package/lib/math/market.js +10 -9
  79. package/lib/math/oracles.d.ts +3 -0
  80. package/lib/math/oracles.js +26 -0
  81. package/lib/math/orders.d.ts +5 -0
  82. package/lib/math/orders.js +31 -1
  83. package/lib/math/position.d.ts +7 -4
  84. package/lib/math/position.js +27 -9
  85. package/lib/math/repeg.d.ts +22 -0
  86. package/lib/math/repeg.js +128 -0
  87. package/lib/math/trade.d.ts +13 -8
  88. package/lib/math/trade.js +85 -22
  89. package/lib/mockUSDCFaucet.js +87 -116
  90. package/lib/oracles/oracleClientCache.d.ts +8 -0
  91. package/lib/oracles/oracleClientCache.js +19 -0
  92. package/lib/oracles/pythClient.d.ts +3 -5
  93. package/lib/oracles/pythClient.js +12 -30
  94. package/lib/oracles/quoteAssetOracleClient.d.ts +9 -0
  95. package/lib/oracles/quoteAssetOracleClient.js +21 -0
  96. package/lib/oracles/switchboardClient.d.ts +3 -5
  97. package/lib/oracles/switchboardClient.js +29 -47
  98. package/lib/oracles/types.d.ts +7 -1
  99. package/lib/orders.d.ts +6 -6
  100. package/lib/orders.js +10 -9
  101. package/lib/slot/SlotSubscriber.d.ts +12 -0
  102. package/lib/slot/SlotSubscriber.js +23 -0
  103. package/lib/tx/retryTxSender.d.ts +2 -2
  104. package/lib/tx/retryTxSender.js +108 -123
  105. package/lib/tx/types.d.ts +5 -1
  106. package/lib/tx/utils.d.ts +1 -1
  107. package/lib/tx/utils.js +11 -2
  108. package/lib/types.d.ts +105 -98
  109. package/lib/types.js +13 -1
  110. package/lib/userName.d.ts +4 -0
  111. package/lib/userName.js +20 -0
  112. package/lib/util/computeUnits.js +10 -21
  113. package/lib/util/tps.js +11 -22
  114. package/lib/wallet.js +7 -20
  115. package/package.json +10 -3
  116. package/src/accounts/bulkAccountLoader.ts +26 -15
  117. package/src/accounts/bulkUserSubscription.ts +1 -45
  118. package/src/accounts/fetch.ts +33 -0
  119. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +249 -214
  120. package/src/accounts/pollingOracleSubscriber.ts +16 -8
  121. package/src/accounts/pollingTokenAccountSubscriber.ts +11 -8
  122. package/src/accounts/pollingUserAccountSubscriber.ts +21 -86
  123. package/src/accounts/types.ts +41 -70
  124. package/src/accounts/webSocketAccountSubscriber.ts +33 -16
  125. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +190 -240
  126. package/src/accounts/webSocketUserAccountSubscriber.ts +11 -71
  127. package/src/addresses/marketAddresses.ts +18 -0
  128. package/src/addresses/pda.ts +118 -0
  129. package/src/admin.ts +209 -267
  130. package/src/clearingHouse.ts +921 -829
  131. package/src/clearingHouseConfig.ts +37 -0
  132. package/src/clearingHouseUser.ts +280 -127
  133. package/src/clearingHouseUserConfig.ts +18 -0
  134. package/src/config.ts +54 -1
  135. package/src/constants/banks.ts +43 -0
  136. package/src/constants/markets.ts +16 -207
  137. package/src/constants/numericConstants.ts +33 -5
  138. package/src/events/eventList.ts +94 -0
  139. package/src/events/eventSubscriber.ts +194 -0
  140. package/src/events/fetchLogs.ts +80 -0
  141. package/src/events/pollingLogProvider.ts +79 -0
  142. package/src/events/sort.ts +65 -0
  143. package/src/events/txEventCache.ts +74 -0
  144. package/src/events/types.ts +98 -0
  145. package/src/events/webSocketLogProvider.ts +38 -0
  146. package/src/examples/makeTradeExample.ts +20 -11
  147. package/src/factory/bigNum.ts +507 -0
  148. package/src/factory/oracleClient.ts +7 -4
  149. package/src/idl/clearing_house.json +1739 -2287
  150. package/src/index.ts +13 -4
  151. package/src/math/amm.ts +229 -245
  152. package/src/math/auction.ts +39 -0
  153. package/src/math/bankBalance.ts +112 -0
  154. package/src/math/conversion.ts +1 -11
  155. package/src/math/funding.ts +12 -9
  156. package/src/math/market.ts +37 -30
  157. package/src/math/oracles.ts +36 -0
  158. package/src/math/orders.ts +38 -0
  159. package/src/math/position.ts +48 -13
  160. package/src/math/repeg.ts +175 -0
  161. package/src/math/trade.ts +114 -36
  162. package/src/math/utils.js +27 -0
  163. package/src/math/utils.js.map +1 -0
  164. package/src/oracles/oracleClientCache.ts +20 -0
  165. package/src/oracles/pythClient.ts +5 -11
  166. package/src/oracles/quoteAssetOracleClient.ts +25 -0
  167. package/src/oracles/switchboardClient.ts +16 -24
  168. package/src/oracles/types.ts +8 -1
  169. package/src/orders.ts +35 -20
  170. package/src/slot/SlotSubscriber.ts +32 -0
  171. package/src/tx/retryTxSender.ts +6 -4
  172. package/src/tx/types.ts +6 -1
  173. package/src/tx/utils.ts +22 -3
  174. package/src/types.ts +108 -110
  175. package/src/userName.ts +20 -0
  176. package/src/util/computeUnits.js +17 -0
  177. package/src/util/computeUnits.js.map +1 -0
  178. package/tests/bn/test.ts +255 -0
  179. package/tsconfig.json +12 -12
  180. package/lib/addresses.d.ts +0 -9
  181. package/lib/addresses.js +0 -87
  182. package/lib/constants/accounts.d.ts +0 -15
  183. package/lib/constants/accounts.js +0 -18
  184. package/lib/factory/clearingHouse.d.ts +0 -35
  185. package/lib/factory/clearingHouse.js +0 -81
  186. package/lib/factory/clearingHouseUser.d.ts +0 -19
  187. package/lib/factory/clearingHouseUser.js +0 -34
  188. package/lib/math/insuranceFund.d.ts +0 -15
  189. package/lib/math/insuranceFund.js +0 -33
  190. package/lib/tx/defaultTxSender.d.ts +0 -8
  191. package/lib/tx/defaultTxSender.js +0 -12
  192. package/src/addresses.ts +0 -71
  193. package/src/constants/accounts.ts +0 -26
  194. package/src/factory/clearingHouse.ts +0 -173
  195. package/src/factory/clearingHouseUser.ts +0 -73
  196. package/src/math/insuranceFund.ts +0 -29
  197. package/src/tx/defaultTxSender.ts +0 -24
@@ -7,9 +7,7 @@ import {
7
7
  MarginCategory,
8
8
  Order,
9
9
  UserAccount,
10
- UserOrdersAccount,
11
10
  UserPosition,
12
- UserPositionsAccount,
13
11
  } from './types';
14
12
  import { calculateEntryPrice } from './math/position';
15
13
  import {
@@ -22,30 +20,33 @@ import {
22
20
  AMM_RESERVE_PRECISION,
23
21
  PRICE_TO_QUOTE_PRECISION,
24
22
  MARGIN_PRECISION,
23
+ BANK_WEIGHT_PRECISION,
25
24
  } from './constants/numericConstants';
26
- import { UserAccountSubscriber, UserAccountEvents } from './accounts/types';
25
+ import {
26
+ UserAccountSubscriber,
27
+ UserAccountEvents,
28
+ DataAndSlot,
29
+ } from './accounts/types';
27
30
  import {
28
31
  calculateMarkPrice,
29
32
  calculateBaseAssetValue,
30
33
  calculatePositionFundingPNL,
31
34
  calculatePositionPNL,
32
35
  PositionDirection,
33
- getUserOrdersAccountPublicKey,
34
36
  calculateTradeSlippage,
35
37
  BN,
38
+ BankAccount,
36
39
  } from '.';
37
- import { getUserAccountPublicKey } from './addresses';
38
- import {
39
- getClearingHouseUser,
40
- getWebSocketClearingHouseUserConfig,
41
- } from './factory/clearingHouseUser';
40
+ import { getTokenAmount } from './math/bankBalance';
41
+ import { OraclePriceData } from './oracles/types';
42
+ import { ClearingHouseUserConfig } from './clearingHouseUserConfig';
43
+ import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
44
+ import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
42
45
 
43
46
  export class ClearingHouseUser {
44
47
  clearingHouse: ClearingHouse;
45
- authority: PublicKey;
48
+ userAccountPublicKey: PublicKey;
46
49
  accountSubscriber: UserAccountSubscriber;
47
- userAccountPublicKey?: PublicKey;
48
- userOrdersAccountPublicKey?: PublicKey;
49
50
  _isSubscribed = false;
50
51
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
51
52
 
@@ -57,34 +58,21 @@ export class ClearingHouseUser {
57
58
  this._isSubscribed = val;
58
59
  }
59
60
 
60
- /**
61
- * @deprecated You should use getClearingHouseUser factory method instead
62
- * @param clearingHouse
63
- * @param authority
64
- * @returns
65
- */
66
- public static from(
67
- clearingHouse: ClearingHouse,
68
- authority: PublicKey
69
- ): ClearingHouseUser {
70
- if (clearingHouse.accountSubscriber.type !== 'websocket')
71
- throw 'This method only works for clearing houses with a websocket account listener. Try using the getClearingHouseUser factory method to initialize a ClearingHouseUser instead';
72
-
73
- const config = getWebSocketClearingHouseUserConfig(
74
- clearingHouse,
75
- authority
76
- );
77
- return getClearingHouseUser(config);
78
- }
79
-
80
- public constructor(
81
- clearingHouse: ClearingHouse,
82
- authority: PublicKey,
83
- accountSubscriber: UserAccountSubscriber
84
- ) {
85
- this.clearingHouse = clearingHouse;
86
- this.authority = authority;
87
- this.accountSubscriber = accountSubscriber;
61
+ public constructor(config: ClearingHouseUserConfig) {
62
+ this.clearingHouse = config.clearingHouse;
63
+ this.userAccountPublicKey = config.userAccountPublicKey;
64
+ if (config.accountSubscription?.type === 'polling') {
65
+ this.accountSubscriber = new PollingUserAccountSubscriber(
66
+ config.clearingHouse.program,
67
+ config.userAccountPublicKey,
68
+ config.accountSubscription.accountLoader
69
+ );
70
+ } else {
71
+ this.accountSubscriber = new WebSocketUserAccountSubscriber(
72
+ config.clearingHouse.program,
73
+ config.userAccountPublicKey
74
+ );
75
+ }
88
76
  this.eventEmitter = this.accountSubscriber.eventEmitter;
89
77
  }
90
78
 
@@ -93,9 +81,6 @@ export class ClearingHouseUser {
93
81
  * @returns SusbcriptionSuccess result
94
82
  */
95
83
  public async subscribe(): Promise<boolean> {
96
- // Clearing house should already be subscribed, but await for the subscription just incase to avoid race condition
97
- await this.clearingHouse.subscribe();
98
-
99
84
  this.isSubscribed = await this.accountSubscriber.subscribe();
100
85
  return this.isSubscribed;
101
86
  }
@@ -113,15 +98,11 @@ export class ClearingHouseUser {
113
98
  }
114
99
 
115
100
  public getUserAccount(): UserAccount {
116
- return this.accountSubscriber.getUserAccount();
101
+ return this.accountSubscriber.getUserAccountAndSlot().data;
117
102
  }
118
103
 
119
- public getUserPositionsAccount(): UserPositionsAccount {
120
- return this.accountSubscriber.getUserPositionsAccount();
121
- }
122
-
123
- public getUserOrdersAccount(): UserOrdersAccount | undefined {
124
- return this.accountSubscriber.getUserOrdersAccount();
104
+ public getUserAccountAndSlot(): DataAndSlot<UserAccount> | undefined {
105
+ return this.accountSubscriber.getUserAccountAndSlot();
125
106
  }
126
107
 
127
108
  /**
@@ -130,7 +111,7 @@ export class ClearingHouseUser {
130
111
  * @returns userPosition
131
112
  */
132
113
  public getUserPosition(marketIndex: BN): UserPosition | undefined {
133
- return this.getUserPositionsAccount().positions.find((position) =>
114
+ return this.getUserAccount().positions.find((position) =>
134
115
  position.marketIndex.eq(marketIndex)
135
116
  );
136
117
  }
@@ -141,7 +122,11 @@ export class ClearingHouseUser {
141
122
  lastCumulativeFundingRate: ZERO,
142
123
  marketIndex,
143
124
  quoteAssetAmount: ZERO,
125
+ quoteEntryAmount: ZERO,
144
126
  openOrders: ZERO,
127
+ unsettledPnl: ZERO,
128
+ openBids: ZERO,
129
+ openAsks: ZERO,
145
130
  };
146
131
  }
147
132
 
@@ -150,7 +135,7 @@ export class ClearingHouseUser {
150
135
  * @returns Order
151
136
  */
152
137
  public getOrder(orderId: BN): Order | undefined {
153
- return this.getUserOrdersAccount().orders.find((order) =>
138
+ return this.getUserAccount().orders.find((order) =>
154
139
  order.orderId.eq(orderId)
155
140
  );
156
141
  }
@@ -160,40 +145,19 @@ export class ClearingHouseUser {
160
145
  * @returns Order
161
146
  */
162
147
  public getOrderByUserOrderId(userOrderId: number): Order | undefined {
163
- return this.getUserOrdersAccount().orders.find(
148
+ return this.getUserAccount().orders.find(
164
149
  (order) => order.userOrderId === userOrderId
165
150
  );
166
151
  }
167
152
 
168
- public async getUserAccountPublicKey(): Promise<PublicKey> {
169
- if (this.userAccountPublicKey) {
170
- return this.userAccountPublicKey;
171
- }
172
-
173
- this.userAccountPublicKey = await getUserAccountPublicKey(
174
- this.clearingHouse.program.programId,
175
- this.authority
176
- );
153
+ public getUserAccountPublicKey(): PublicKey {
177
154
  return this.userAccountPublicKey;
178
155
  }
179
156
 
180
- public async getUserOrdersAccountPublicKey(): Promise<PublicKey> {
181
- if (this.userOrdersAccountPublicKey) {
182
- return this.userOrdersAccountPublicKey;
183
- }
184
-
185
- this.userOrdersAccountPublicKey = await getUserOrdersAccountPublicKey(
186
- this.clearingHouse.program.programId,
187
- await this.getUserAccountPublicKey()
188
- );
189
- return this.userOrdersAccountPublicKey;
190
- }
191
-
192
157
  public async exists(): Promise<boolean> {
193
- const userAccountPublicKey = await this.getUserAccountPublicKey();
194
158
  const userAccountRPCResponse =
195
159
  await this.clearingHouse.connection.getParsedAccountInfo(
196
- userAccountPublicKey
160
+ this.userAccountPublicKey
197
161
  );
198
162
  return userAccountRPCResponse.value !== null;
199
163
  }
@@ -220,34 +184,44 @@ export class ClearingHouseUser {
220
184
  }
221
185
 
222
186
  public getInitialMarginRequirement(): BN {
223
- return this.getUserPositionsAccount().positions.reduce(
224
- (marginRequirement, marketPosition) => {
225
- const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
187
+ return this.getUserAccount()
188
+ .positions.reduce((marginRequirement, marketPosition) => {
189
+ const market = this.clearingHouse.getMarketAccount(
190
+ marketPosition.marketIndex
191
+ );
226
192
  return marginRequirement.add(
227
- calculateBaseAssetValue(market, marketPosition)
193
+ calculateBaseAssetValue(
194
+ market,
195
+ marketPosition,
196
+ this.getOracleDataForMarket(market.marketIndex)
197
+ )
228
198
  .mul(new BN(market.marginRatioInitial))
229
199
  .div(MARGIN_PRECISION)
230
200
  );
231
- },
232
- ZERO
233
- );
201
+ }, ZERO)
202
+ .add(this.getTotalLiability());
234
203
  }
235
204
 
236
205
  /**
237
206
  * @returns The partial margin requirement in USDC. : QUOTE_PRECISION
238
207
  */
239
208
  public getPartialMarginRequirement(): BN {
240
- return this.getUserPositionsAccount().positions.reduce(
241
- (marginRequirement, marketPosition) => {
242
- const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
209
+ return this.getUserAccount()
210
+ .positions.reduce((marginRequirement, marketPosition) => {
211
+ const market = this.clearingHouse.getMarketAccount(
212
+ marketPosition.marketIndex
213
+ );
243
214
  return marginRequirement.add(
244
- calculateBaseAssetValue(market, marketPosition)
215
+ calculateBaseAssetValue(
216
+ market,
217
+ marketPosition,
218
+ this.getOracleDataForMarket(market.marketIndex)
219
+ )
245
220
  .mul(new BN(market.marginRatioPartial))
246
221
  .div(MARGIN_PRECISION)
247
222
  );
248
- },
249
- ZERO
250
- );
223
+ }, ZERO)
224
+ .add(this.getTotalLiability());
251
225
  }
252
226
 
253
227
  /**
@@ -255,42 +229,158 @@ export class ClearingHouseUser {
255
229
  * @returns : Precision QUOTE_PRECISION
256
230
  */
257
231
  public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN {
258
- return this.getUserPositionsAccount()
232
+ return this.getUserAccount()
259
233
  .positions.filter((pos) =>
260
234
  marketIndex ? pos.marketIndex === marketIndex : true
261
235
  )
262
236
  .reduce((pnl, marketPosition) => {
263
- const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
237
+ const market = this.clearingHouse.getMarketAccount(
238
+ marketPosition.marketIndex
239
+ );
264
240
  return pnl.add(
265
- calculatePositionPNL(market, marketPosition, withFunding)
241
+ calculatePositionPNL(
242
+ market,
243
+ marketPosition,
244
+ withFunding,
245
+ this.getOracleDataForMarket(market.marketIndex)
246
+ )
266
247
  );
267
248
  }, ZERO);
268
249
  }
269
250
 
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);
262
+ }, ZERO);
263
+ }
264
+
270
265
  /**
271
266
  * calculates unrealized funding payment pnl
272
267
  * @returns : Precision QUOTE_PRECISION
273
268
  */
274
269
  public getUnrealizedFundingPNL(marketIndex?: BN): BN {
275
- return this.getUserPositionsAccount()
270
+ return this.getUserAccount()
276
271
  .positions.filter((pos) =>
277
272
  marketIndex ? pos.marketIndex === marketIndex : true
278
273
  )
279
274
  .reduce((pnl, marketPosition) => {
280
- const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
275
+ const market = this.clearingHouse.getMarketAccount(
276
+ marketPosition.marketIndex
277
+ );
281
278
  return pnl.add(calculatePositionFundingPNL(market, marketPosition));
282
279
  }, ZERO);
283
280
  }
284
281
 
282
+ public getTotalLiability(): BN {
283
+ return this.getUserAccount().bankBalances.reduce(
284
+ (totalAssetValue, bankBalance) => {
285
+ if (
286
+ bankBalance.balance.eq(ZERO) ||
287
+ isVariant(bankBalance.balanceType, 'deposit')
288
+ ) {
289
+ return totalAssetValue;
290
+ }
291
+
292
+ // Todo this needs to account for whether it's based on initial or maintenance requirements
293
+ const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
294
+ bankBalance.bankIndex
295
+ );
296
+
297
+ const tokenAmount = getTokenAmount(
298
+ bankBalance.balance,
299
+ bankAccount,
300
+ bankBalance.balanceType
301
+ );
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
+ );
309
+ },
310
+ ZERO
311
+ );
312
+ }
313
+
314
+ public getCollateralValue(bankIndex?: BN): BN {
315
+ return this.getUserAccount().bankBalances.reduce(
316
+ (totalAssetValue, bankBalance) => {
317
+ if (
318
+ bankBalance.balance.eq(ZERO) ||
319
+ (bankIndex !== undefined && !bankBalance.bankIndex.eq(bankIndex))
320
+ ) {
321
+ return totalAssetValue;
322
+ }
323
+
324
+ // Todo this needs to account for whether it's based on initial or maintenance requirements
325
+ const bankAccount: BankAccount = this.clearingHouse.getBankAccount(
326
+ bankBalance.bankIndex
327
+ );
328
+
329
+ let tokenAmount = getTokenAmount(
330
+ bankBalance.balance,
331
+ bankAccount,
332
+ bankBalance.balanceType
333
+ );
334
+
335
+ if (isVariant(bankBalance.balanceType, 'borrow')) {
336
+ tokenAmount = tokenAmount.mul(new BN(-1));
337
+ }
338
+
339
+ return totalAssetValue.add(
340
+ tokenAmount
341
+ .mul(this.getOracleDataForBank(bankAccount.bankIndex).price)
342
+ .div(MARK_PRICE_PRECISION)
343
+ );
344
+ },
345
+ ZERO
346
+ );
347
+ }
348
+
285
349
  /**
286
350
  * calculates TotalCollateral: collateral + unrealized pnl
351
+ * TODO: rename to total equity (for perpetuals swaps)
287
352
  * @returns : Precision QUOTE_PRECISION
288
353
  */
289
354
  public getTotalCollateral(): BN {
290
- return (
291
- this.getUserAccount().collateral.add(this.getUnrealizedPNL(true)) ??
292
- new BN(0)
293
- );
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());
294
384
  }
295
385
 
296
386
  /**
@@ -298,11 +388,17 @@ export class ClearingHouseUser {
298
388
  * @returns : Precision QUOTE_PRECISION
299
389
  */
300
390
  getTotalPositionValue(): BN {
301
- return this.getUserPositionsAccount().positions.reduce(
391
+ return this.getUserAccount().positions.reduce(
302
392
  (positionValue, marketPosition) => {
303
- const market = this.clearingHouse.getMarket(marketPosition.marketIndex);
393
+ const market = this.clearingHouse.getMarketAccount(
394
+ marketPosition.marketIndex
395
+ );
304
396
  return positionValue.add(
305
- calculateBaseAssetValue(market, marketPosition)
397
+ calculateBaseAssetValue(
398
+ market,
399
+ marketPosition,
400
+ this.getOracleDataForMarket(market.marketIndex)
401
+ )
306
402
  );
307
403
  },
308
404
  ZERO
@@ -313,11 +409,16 @@ export class ClearingHouseUser {
313
409
  * calculates position value from closing 100%
314
410
  * @returns : Precision QUOTE_PRECISION
315
411
  */
316
- public getPositionValue(marketIndex: BN): BN {
412
+ public getPositionValue(
413
+ marketIndex: BN,
414
+ oraclePriceData: OraclePriceData
415
+ ): BN {
317
416
  const userPosition =
318
417
  this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
319
- const market = this.clearingHouse.getMarket(userPosition.marketIndex);
320
- return calculateBaseAssetValue(market, userPosition);
418
+ const market = this.clearingHouse.getMarketAccount(
419
+ userPosition.marketIndex
420
+ );
421
+ return calculateBaseAssetValue(market, userPosition, oraclePriceData);
321
422
  }
322
423
 
323
424
  public getPositionSide(
@@ -340,13 +441,15 @@ export class ClearingHouseUser {
340
441
  position: UserPosition,
341
442
  amountToClose?: BN
342
443
  ): [BN, BN] {
343
- const market = this.clearingHouse.getMarket(position.marketIndex);
444
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
344
445
 
345
446
  const entryPrice = calculateEntryPrice(position);
346
447
 
448
+ const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
449
+
347
450
  if (amountToClose) {
348
451
  if (amountToClose.eq(ZERO)) {
349
- return [calculateMarkPrice(market), ZERO];
452
+ return [calculateMarkPrice(market, oraclePriceData), ZERO];
350
453
  }
351
454
  position = {
352
455
  baseAssetAmount: amountToClose,
@@ -356,7 +459,11 @@ export class ClearingHouseUser {
356
459
  } as UserPosition;
357
460
  }
358
461
 
359
- const baseAssetValue = calculateBaseAssetValue(market, position);
462
+ const baseAssetValue = calculateBaseAssetValue(
463
+ market,
464
+ position,
465
+ oraclePriceData
466
+ );
360
467
  if (position.baseAssetAmount.eq(ZERO)) {
361
468
  return [ZERO, ZERO];
362
469
  }
@@ -397,7 +504,7 @@ export class ClearingHouseUser {
397
504
  marketIndex: BN | number,
398
505
  category: MarginCategory = 'Initial'
399
506
  ): BN {
400
- const market = this.clearingHouse.getMarket(marketIndex);
507
+ const market = this.clearingHouse.getMarketAccount(marketIndex);
401
508
  let marginRatioCategory: number;
402
509
 
403
510
  switch (category) {
@@ -447,14 +554,14 @@ export class ClearingHouseUser {
447
554
  * @returns
448
555
  */
449
556
  public needsToSettleFundingPayment(): boolean {
450
- const marketsAccount = this.clearingHouse.getMarketsAccount();
451
- for (const userPosition of this.getUserPositionsAccount().positions) {
557
+ for (const userPosition of this.getUserAccount().positions) {
452
558
  if (userPosition.baseAssetAmount.eq(ZERO)) {
453
559
  continue;
454
560
  }
455
561
 
456
- const market =
457
- marketsAccount.markets[userPosition.marketIndex.toNumber()];
562
+ const market = this.clearingHouse.getMarketAccount(
563
+ userPosition.marketIndex
564
+ );
458
565
  if (
459
566
  market.amm.cumulativeFundingRateLong.eq(
460
567
  userPosition.lastCumulativeFundingRate
@@ -518,18 +625,23 @@ export class ClearingHouseUser {
518
625
  lastCumulativeFundingRate:
519
626
  currentMarketPosition.lastCumulativeFundingRate,
520
627
  quoteAssetAmount: new BN(0),
628
+ quoteEntryAmount: new BN(0),
521
629
  openOrders: new BN(0),
630
+ unsettledPnl: new BN(0),
631
+ openBids: new BN(0),
632
+ openAsks: new BN(0),
522
633
  };
523
634
 
524
635
  if (proposedBaseAssetAmount.eq(ZERO)) return new BN(-1);
525
636
 
526
- const market = this.clearingHouse.getMarket(
637
+ const market = this.clearingHouse.getMarketAccount(
527
638
  proposedMarketPosition.marketIndex
528
639
  );
529
640
 
530
641
  const proposedMarketPositionValue = calculateBaseAssetValue(
531
642
  market,
532
- proposedMarketPosition
643
+ proposedMarketPosition,
644
+ this.getOracleDataForMarket(market.marketIndex)
533
645
  );
534
646
 
535
647
  // total position value after trade
@@ -537,11 +649,17 @@ export class ClearingHouseUser {
537
649
  totalPositionValueExcludingTargetMarket.add(proposedMarketPositionValue);
538
650
 
539
651
  const marginRequirementExcludingTargetMarket =
540
- this.getUserPositionsAccount().positions.reduce(
652
+ this.getUserAccount().positions.reduce(
541
653
  (totalMarginRequirement, position) => {
542
654
  if (!position.marketIndex.eq(marketPosition.marketIndex)) {
543
- const market = this.clearingHouse.getMarket(position.marketIndex);
544
- const positionValue = calculateBaseAssetValue(market, position);
655
+ const market = this.clearingHouse.getMarketAccount(
656
+ position.marketIndex
657
+ );
658
+ const positionValue = calculateBaseAssetValue(
659
+ market,
660
+ position,
661
+ this.getOracleDataForMarket(market.marketIndex)
662
+ );
545
663
  const marketMarginRequirement = positionValue
546
664
  .mul(
547
665
  partial
@@ -608,7 +726,8 @@ export class ClearingHouseUser {
608
726
  let markPriceAfterTrade;
609
727
  if (positionBaseSizeChange.eq(ZERO)) {
610
728
  markPriceAfterTrade = calculateMarkPrice(
611
- this.clearingHouse.getMarket(marketPosition.marketIndex)
729
+ this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
730
+ this.getOracleDataForMarket(marketPosition.marketIndex)
612
731
  );
613
732
  } else {
614
733
  const direction = positionBaseSizeChange.gt(ZERO)
@@ -617,8 +736,9 @@ export class ClearingHouseUser {
617
736
  markPriceAfterTrade = calculateTradeSlippage(
618
737
  direction,
619
738
  positionBaseSizeChange.abs(),
620
- this.clearingHouse.getMarket(marketPosition.marketIndex),
621
- 'base'
739
+ this.clearingHouse.getMarketAccount(marketPosition.marketIndex),
740
+ 'base',
741
+ this.getOracleDataForMarket(marketPosition.marketIndex)
622
742
  )[3]; // newPrice after swap
623
743
  }
624
744
 
@@ -657,7 +777,8 @@ export class ClearingHouseUser {
657
777
  {
658
778
  marketIndex: positionMarketIndex,
659
779
  },
660
- closeBaseAmount
780
+ closeBaseAmount,
781
+ true
661
782
  );
662
783
  }
663
784
 
@@ -700,10 +821,12 @@ export class ClearingHouseUser {
700
821
  ? true
701
822
  : targetSide === currentPositionSide;
702
823
 
824
+ const oracleData = this.getOracleDataForMarket(targetMarketIndex);
825
+
703
826
  // add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
704
827
  const oppositeSizeValueUSDC = targetingSameSide
705
828
  ? ZERO
706
- : this.getPositionValue(targetMarketIndex);
829
+ : this.getPositionValue(targetMarketIndex, oracleData);
707
830
 
708
831
  let maxPositionSize = this.getBuyingPower(targetMarketIndex);
709
832
  if (maxPositionSize.gte(ZERO)) {
@@ -720,8 +843,11 @@ export class ClearingHouseUser {
720
843
  // current leverage is greater than max leverage - can only reduce position size
721
844
 
722
845
  if (!targetingSameSide) {
723
- const market = this.clearingHouse.getMarket(targetMarketIndex);
724
- const marketPositionValue = this.getPositionValue(targetMarketIndex);
846
+ const market = this.clearingHouse.getMarketAccount(targetMarketIndex);
847
+ const marketPositionValue = this.getPositionValue(
848
+ targetMarketIndex,
849
+ oracleData
850
+ );
725
851
  const totalCollateral = this.getTotalCollateral();
726
852
  const marginRequirement = this.getInitialMarginRequirement();
727
853
  const marginFreedByClosing = marketPositionValue
@@ -770,7 +896,12 @@ export class ClearingHouseUser {
770
896
  this.getUserPosition(targetMarketIndex) ||
771
897
  this.getEmptyPosition(targetMarketIndex);
772
898
 
773
- let currentPositionQuoteAmount = this.getPositionValue(targetMarketIndex);
899
+ const oracleData = this.getOracleDataForMarket(targetMarketIndex);
900
+
901
+ let currentPositionQuoteAmount = this.getPositionValue(
902
+ targetMarketIndex,
903
+ oracleData
904
+ );
774
905
 
775
906
  const currentSide =
776
907
  currentPosition && currentPosition.baseAssetAmount.isNeg()
@@ -826,11 +957,33 @@ export class ClearingHouseUser {
826
957
  this.getUserPosition(marketToIgnore) ||
827
958
  this.getEmptyPosition(marketToIgnore);
828
959
 
960
+ const oracleData = this.getOracleDataForMarket(marketToIgnore);
961
+
829
962
  let currentMarketPositionValueUSDC = ZERO;
830
963
  if (currentMarketPosition) {
831
- currentMarketPositionValueUSDC = this.getPositionValue(marketToIgnore);
964
+ currentMarketPositionValueUSDC = this.getPositionValue(
965
+ marketToIgnore,
966
+ oracleData
967
+ );
832
968
  }
833
969
 
834
970
  return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
835
971
  }
972
+
973
+ private getOracleDataForMarket(marketIndex: BN): OraclePriceData {
974
+ const oracleKey =
975
+ this.clearingHouse.getMarketAccount(marketIndex).amm.oracle;
976
+ const oracleData =
977
+ this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
978
+
979
+ return oracleData;
980
+ }
981
+ private getOracleDataForBank(bankIndex: BN): OraclePriceData {
982
+ const oracleKey = this.clearingHouse.getBankAccount(bankIndex).oracle;
983
+
984
+ const oracleData =
985
+ this.clearingHouse.getOraclePriceDataAndSlot(oracleKey).data;
986
+
987
+ return oracleData;
988
+ }
836
989
  }
@@ -0,0 +1,18 @@
1
+ import { ClearingHouse } from './clearingHouse';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
+
5
+ export type ClearingHouseUserConfig = {
6
+ accountSubscription?: ClearingHouseUserAccountSubscriptionConfig;
7
+ clearingHouse: ClearingHouse;
8
+ userAccountPublicKey: PublicKey;
9
+ };
10
+
11
+ export type ClearingHouseUserAccountSubscriptionConfig =
12
+ | {
13
+ type: 'websocket';
14
+ }
15
+ | {
16
+ type: 'polling';
17
+ accountLoader: BulkAccountLoader;
18
+ };