@drift-labs/sdk 0.2.0-temp.2 → 2.0.1

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 (278) hide show
  1. package/README.md +27 -29
  2. package/lib/accounts/bulkAccountLoader.d.ts +2 -0
  3. package/lib/accounts/bulkAccountLoader.js +36 -29
  4. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  5. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  6. package/lib/accounts/bulkUserSubscription.d.ts +2 -2
  7. package/lib/accounts/bulkUserSubscription.js +0 -1
  8. package/lib/accounts/fetch.d.ts +2 -1
  9. package/lib/accounts/fetch.js +9 -1
  10. package/lib/accounts/{pollingClearingHouseAccountSubscriber.d.ts → pollingDriftClientAccountSubscriber.d.ts} +20 -25
  11. package/lib/accounts/{pollingClearingHouseAccountSubscriber.js → pollingDriftClientAccountSubscriber.js} +45 -49
  12. package/lib/accounts/{pollingOracleSubscriber.d.ts → pollingOracleAccountSubscriber.d.ts} +2 -2
  13. package/lib/accounts/{pollingOracleSubscriber.js → pollingOracleAccountSubscriber.js} +3 -3
  14. package/lib/accounts/pollingUserAccountSubscriber.d.ts +1 -1
  15. package/lib/accounts/pollingUserAccountSubscriber.js +5 -11
  16. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  17. package/lib/accounts/pollingUserStatsAccountSubscriber.js +107 -0
  18. package/lib/accounts/types.d.ts +26 -15
  19. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +49 -0
  20. package/lib/accounts/{webSocketClearingHouseAccountSubscriber.js → webSocketDriftClientAccountSubscriber.js} +47 -45
  21. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  22. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  23. package/lib/addresses/marketAddresses.d.ts +1 -3
  24. package/lib/addresses/marketAddresses.js +1 -1
  25. package/lib/addresses/pda.d.ts +15 -9
  26. package/lib/addresses/pda.js +73 -35
  27. package/lib/adminClient.d.ts +65 -0
  28. package/lib/adminClient.js +637 -0
  29. package/lib/config.d.ts +10 -10
  30. package/lib/config.js +26 -22
  31. package/lib/constants/numericConstants.d.ts +29 -12
  32. package/lib/constants/numericConstants.js +40 -21
  33. package/lib/constants/perpMarkets.d.ts +18 -0
  34. package/lib/constants/{markets.js → perpMarkets.js} +20 -9
  35. package/lib/constants/spotMarkets.d.ts +19 -0
  36. package/lib/constants/spotMarkets.js +63 -0
  37. package/lib/dlob/DLOB.d.ts +82 -0
  38. package/lib/dlob/DLOB.js +694 -0
  39. package/lib/dlob/DLOBNode.d.ts +54 -0
  40. package/lib/dlob/DLOBNode.js +77 -0
  41. package/lib/dlob/NodeList.d.ts +27 -0
  42. package/lib/dlob/NodeList.js +144 -0
  43. package/lib/driftClient.d.ts +234 -0
  44. package/lib/driftClient.js +2108 -0
  45. package/lib/{clearingHouseConfig.d.ts → driftClientConfig.d.ts} +9 -9
  46. package/lib/{clearingHouseConfig.js → driftClientConfig.js} +0 -0
  47. package/lib/events/eventList.d.ts +0 -1
  48. package/lib/events/eventList.js +0 -7
  49. package/lib/events/eventSubscriber.d.ts +5 -2
  50. package/lib/events/eventSubscriber.js +25 -11
  51. package/lib/events/fetchLogs.d.ts +13 -2
  52. package/lib/events/fetchLogs.js +45 -14
  53. package/lib/events/pollingLogProvider.d.ts +2 -1
  54. package/lib/events/pollingLogProvider.js +7 -3
  55. package/lib/events/sort.js +8 -11
  56. package/lib/events/txEventCache.d.ts +0 -2
  57. package/lib/events/txEventCache.js +0 -14
  58. package/lib/events/types.d.ts +11 -3
  59. package/lib/events/types.js +8 -0
  60. package/lib/events/webSocketLogProvider.js +1 -1
  61. package/lib/examples/makeTradeExample.js +30 -18
  62. package/lib/factory/bigNum.d.ts +8 -4
  63. package/lib/factory/bigNum.js +109 -19
  64. package/lib/idl/drift.json +8392 -0
  65. package/lib/index.d.ts +29 -12
  66. package/lib/index.js +29 -12
  67. package/lib/math/amm.d.ts +9 -6
  68. package/lib/math/amm.js +91 -38
  69. package/lib/math/conversion.js +1 -1
  70. package/lib/math/exchangeStatus.d.ts +4 -0
  71. package/lib/math/exchangeStatus.js +18 -0
  72. package/lib/math/funding.d.ts +6 -6
  73. package/lib/math/funding.js +23 -21
  74. package/lib/math/insurance.d.ts +4 -0
  75. package/lib/math/insurance.js +27 -0
  76. package/lib/math/margin.d.ts +11 -0
  77. package/lib/math/margin.js +82 -0
  78. package/lib/math/market.d.ts +14 -9
  79. package/lib/math/market.js +70 -10
  80. package/lib/math/oracles.d.ts +4 -0
  81. package/lib/math/oracles.js +36 -8
  82. package/lib/math/orders.d.ts +16 -6
  83. package/lib/math/orders.js +97 -17
  84. package/lib/math/position.d.ts +27 -13
  85. package/lib/math/position.js +91 -37
  86. package/lib/math/repeg.js +17 -8
  87. package/lib/math/spotBalance.d.ts +22 -0
  88. package/lib/math/spotBalance.js +192 -0
  89. package/lib/math/spotMarket.d.ts +4 -0
  90. package/lib/math/spotMarket.js +8 -0
  91. package/lib/math/spotPosition.d.ts +6 -0
  92. package/lib/math/spotPosition.js +23 -0
  93. package/lib/math/trade.d.ts +10 -10
  94. package/lib/math/trade.js +27 -31
  95. package/lib/oracles/pythClient.js +1 -1
  96. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  97. package/lib/oracles/switchboardClient.js +1 -1
  98. package/lib/orderParams.d.ts +4 -4
  99. package/lib/orderParams.js +12 -4
  100. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  101. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  102. package/lib/serum/serumSubscriber.d.ts +27 -0
  103. package/lib/serum/serumSubscriber.js +56 -0
  104. package/lib/serum/types.d.ts +11 -0
  105. package/{src/oracles → lib/serum}/types.js +0 -0
  106. package/lib/tokenFaucet.d.ts +1 -0
  107. package/lib/tokenFaucet.js +23 -12
  108. package/lib/tx/retryTxSender.d.ts +1 -1
  109. package/lib/tx/retryTxSender.js +13 -4
  110. package/lib/tx/types.d.ts +1 -1
  111. package/lib/types.d.ts +631 -222
  112. package/lib/types.js +137 -24
  113. package/lib/user.d.ts +228 -0
  114. package/lib/user.js +959 -0
  115. package/lib/userConfig.d.ts +14 -0
  116. package/lib/{clearingHouseUserConfig.js → userConfig.js} +0 -0
  117. package/lib/userMap/userMap.d.ts +41 -0
  118. package/lib/userMap/userMap.js +85 -0
  119. package/lib/userMap/userStatsMap.d.ts +19 -0
  120. package/lib/userMap/userStatsMap.js +68 -0
  121. package/lib/userName.d.ts +1 -0
  122. package/lib/userName.js +3 -2
  123. package/lib/userStats.d.ts +18 -0
  124. package/lib/userStats.js +49 -0
  125. package/lib/userStatsConfig.d.ts +14 -0
  126. package/{src/clearingHouseConfig.js → lib/userStatsConfig.js} +0 -0
  127. package/lib/util/getTokenAddress.d.ts +2 -0
  128. package/lib/util/getTokenAddress.js +9 -0
  129. package/package.json +12 -5
  130. package/src/accounts/bulkAccountLoader.ts +44 -34
  131. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  132. package/src/accounts/bulkUserSubscription.ts +2 -3
  133. package/src/accounts/fetch.ts +27 -2
  134. package/src/accounts/{pollingClearingHouseAccountSubscriber.ts → pollingDriftClientAccountSubscriber.ts} +65 -75
  135. package/src/accounts/{pollingOracleSubscriber.ts → pollingOracleAccountSubscriber.ts} +2 -2
  136. package/src/accounts/pollingUserAccountSubscriber.ts +5 -12
  137. package/src/accounts/pollingUserStatsAccountSubscriber.ts +166 -0
  138. package/src/accounts/types.ts +35 -15
  139. package/src/accounts/{webSocketClearingHouseAccountSubscriber.ts → webSocketDriftClientAccountSubscriber.ts} +78 -73
  140. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  141. package/src/addresses/marketAddresses.ts +3 -4
  142. package/src/addresses/pda.ts +105 -33
  143. package/src/adminClient.ts +1207 -0
  144. package/src/config.ts +41 -34
  145. package/src/constants/numericConstants.ts +59 -26
  146. package/src/constants/{markets.ts → perpMarkets.ts} +22 -11
  147. package/src/constants/spotMarkets.ts +83 -0
  148. package/src/dlob/DLOB.ts +1120 -0
  149. package/src/dlob/DLOBNode.ts +155 -0
  150. package/src/dlob/NodeList.ts +195 -0
  151. package/src/driftClient.ts +3594 -0
  152. package/src/{clearingHouseConfig.ts → driftClientConfig.ts} +9 -8
  153. package/src/events/eventList.ts +1 -8
  154. package/src/events/eventSubscriber.ts +36 -14
  155. package/src/events/fetchLogs.ts +60 -15
  156. package/src/events/pollingLogProvider.ts +11 -3
  157. package/src/events/sort.ts +11 -15
  158. package/src/events/txEventCache.ts +0 -16
  159. package/src/events/types.ts +27 -2
  160. package/src/events/webSocketLogProvider.ts +1 -1
  161. package/src/examples/makeTradeExample.js +152 -75
  162. package/src/examples/makeTradeExample.ts +44 -28
  163. package/src/factory/bigNum.ts +150 -22
  164. package/src/idl/drift.json +8392 -0
  165. package/src/idl/pyth.json +98 -2
  166. package/src/index.ts +29 -12
  167. package/src/math/amm.ts +161 -48
  168. package/src/math/conversion.ts +2 -2
  169. package/src/math/exchangeStatus.ts +31 -0
  170. package/src/math/funding.ts +41 -31
  171. package/src/math/insurance.ts +35 -0
  172. package/src/math/margin.ts +133 -0
  173. package/src/math/market.ts +143 -14
  174. package/src/math/oracles.ts +63 -9
  175. package/src/math/orders.ts +168 -26
  176. package/src/math/position.ts +133 -59
  177. package/src/math/repeg.ts +19 -9
  178. package/src/math/spotBalance.ts +319 -0
  179. package/src/math/spotMarket.ts +9 -0
  180. package/src/math/spotPosition.ts +47 -0
  181. package/src/math/trade.ts +33 -37
  182. package/src/oracles/pythClient.ts +2 -2
  183. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  184. package/src/oracles/switchboardClient.ts +2 -2
  185. package/src/orderParams.ts +16 -8
  186. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  187. package/src/serum/serumSubscriber.ts +99 -0
  188. package/src/serum/types.ts +13 -0
  189. package/src/tokenFaucet.ts +38 -15
  190. package/src/tx/retryTxSender.ts +16 -5
  191. package/src/tx/types.ts +2 -1
  192. package/src/types.ts +594 -195
  193. package/src/user.ts +1599 -0
  194. package/src/{clearingHouseUserConfig.ts → userConfig.ts} +5 -5
  195. package/src/userMap/userMap.ts +124 -0
  196. package/src/userMap/userStatsMap.ts +108 -0
  197. package/src/userName.ts +2 -1
  198. package/src/userStats.ts +75 -0
  199. package/src/userStatsConfig.ts +18 -0
  200. package/src/util/getTokenAddress.ts +18 -0
  201. package/tests/bn/test.ts +46 -11
  202. package/tests/dlob/helpers.ts +619 -0
  203. package/tests/dlob/test.ts +4586 -0
  204. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +0 -49
  205. package/lib/admin.d.ts +0 -44
  206. package/lib/admin.js +0 -438
  207. package/lib/clearingHouse.d.ts +0 -146
  208. package/lib/clearingHouse.js +0 -1154
  209. package/lib/clearingHouseUser.d.ts +0 -187
  210. package/lib/clearingHouseUser.js +0 -634
  211. package/lib/clearingHouseUserConfig.d.ts +0 -14
  212. package/lib/constants/banks.d.ts +0 -16
  213. package/lib/constants/banks.js +0 -41
  214. package/lib/constants/markets.d.ts +0 -19
  215. package/lib/idl/clearing_house.json +0 -4464
  216. package/lib/math/bankBalance.d.ts +0 -9
  217. package/lib/math/bankBalance.js +0 -75
  218. package/lib/math/state.d.ts +0 -8
  219. package/lib/math/state.js +0 -15
  220. package/lib/orders.d.ts +0 -8
  221. package/lib/orders.js +0 -134
  222. package/src/accounts/bulkAccountLoader.js +0 -197
  223. package/src/accounts/bulkUserSubscription.js +0 -33
  224. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  225. package/src/accounts/pollingOracleSubscriber.js +0 -93
  226. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  227. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  228. package/src/accounts/types.js +0 -10
  229. package/src/accounts/utils.js +0 -7
  230. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  231. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  232. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  233. package/src/addresses/marketAddresses.js +0 -26
  234. package/src/admin.js +0 -517
  235. package/src/admin.ts +0 -730
  236. package/src/clearingHouse.ts +0 -1828
  237. package/src/clearingHouseUser.ts +0 -978
  238. package/src/clearingHouseUserConfig.js +0 -2
  239. package/src/config.js +0 -67
  240. package/src/constants/banks.js +0 -42
  241. package/src/constants/banks.ts +0 -50
  242. package/src/constants/markets.js +0 -42
  243. package/src/constants/numericConstants.js +0 -41
  244. package/src/events/eventSubscriber.js +0 -139
  245. package/src/events/fetchLogs.js +0 -50
  246. package/src/events/pollingLogProvider.js +0 -64
  247. package/src/events/sort.js +0 -44
  248. package/src/events/txEventCache.js +0 -71
  249. package/src/events/types.js +0 -20
  250. package/src/events/webSocketLogProvider.js +0 -41
  251. package/src/factory/bigNum.js +0 -390
  252. package/src/factory/oracleClient.js +0 -20
  253. package/src/idl/clearing_house.json +0 -4464
  254. package/src/index.js +0 -69
  255. package/src/math/amm.js +0 -369
  256. package/src/math/auction.js +0 -42
  257. package/src/math/bankBalance.ts +0 -112
  258. package/src/math/conversion.js +0 -11
  259. package/src/math/funding.js +0 -248
  260. package/src/math/oracles.js +0 -26
  261. package/src/math/repeg.js +0 -128
  262. package/src/math/state.js +0 -15
  263. package/src/math/state.ts +0 -14
  264. package/src/math/trade.js +0 -253
  265. package/src/math/utils.js +0 -26
  266. package/src/math/utils.js.map +0 -1
  267. package/src/mockUSDCFaucet.js +0 -280
  268. package/src/oracles/oracleClientCache.js +0 -19
  269. package/src/oracles/pythClient.js +0 -46
  270. package/src/oracles/quoteAssetOracleClient.js +0 -32
  271. package/src/oracles/switchboardClient.js +0 -69
  272. package/src/orderParams.js +0 -20
  273. package/src/orders.ts +0 -245
  274. package/src/slot/SlotSubscriber.js +0 -39
  275. package/src/tokenFaucet.js +0 -189
  276. package/src/types.js +0 -125
  277. package/src/userName.js +0 -20
  278. package/src/wallet.js +0 -35
package/lib/user.js ADDED
@@ -0,0 +1,959 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.User = void 0;
4
+ const types_1 = require("./types");
5
+ const position_1 = require("./math/position");
6
+ const numericConstants_1 = require("./constants/numericConstants");
7
+ const _1 = require(".");
8
+ const spotBalance_1 = require("./math/spotBalance");
9
+ const amm_1 = require("./math/amm");
10
+ const margin_1 = require("./math/margin");
11
+ const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSubscriber");
12
+ const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
13
+ const spotPosition_1 = require("./math/spotPosition");
14
+ class User {
15
+ constructor(config) {
16
+ var _a;
17
+ this._isSubscribed = false;
18
+ this.driftClient = config.driftClient;
19
+ this.userAccountPublicKey = config.userAccountPublicKey;
20
+ if (((_a = config.accountSubscription) === null || _a === void 0 ? void 0 : _a.type) === 'polling') {
21
+ this.accountSubscriber = new pollingUserAccountSubscriber_1.PollingUserAccountSubscriber(config.driftClient.program, config.userAccountPublicKey, config.accountSubscription.accountLoader);
22
+ }
23
+ else {
24
+ this.accountSubscriber = new webSocketUserAccountSubscriber_1.WebSocketUserAccountSubscriber(config.driftClient.program, config.userAccountPublicKey);
25
+ }
26
+ this.eventEmitter = this.accountSubscriber.eventEmitter;
27
+ }
28
+ get isSubscribed() {
29
+ return this._isSubscribed && this.accountSubscriber.isSubscribed;
30
+ }
31
+ set isSubscribed(val) {
32
+ this._isSubscribed = val;
33
+ }
34
+ /**
35
+ * Subscribe to User state accounts
36
+ * @returns SusbcriptionSuccess result
37
+ */
38
+ async subscribe() {
39
+ this.isSubscribed = await this.accountSubscriber.subscribe();
40
+ return this.isSubscribed;
41
+ }
42
+ /**
43
+ * Forces the accountSubscriber to fetch account updates from rpc
44
+ */
45
+ async fetchAccounts() {
46
+ await this.accountSubscriber.fetch();
47
+ }
48
+ async unsubscribe() {
49
+ await this.accountSubscriber.unsubscribe();
50
+ this.isSubscribed = false;
51
+ }
52
+ getUserAccount() {
53
+ return this.accountSubscriber.getUserAccountAndSlot().data;
54
+ }
55
+ getUserAccountAndSlot() {
56
+ return this.accountSubscriber.getUserAccountAndSlot();
57
+ }
58
+ /**
59
+ * Gets the user's current position for a given perp market. If the user has no position returns undefined
60
+ * @param marketIndex
61
+ * @returns userPerpPosition
62
+ */
63
+ getPerpPosition(marketIndex) {
64
+ return this.getUserAccount().perpPositions.find((position) => position.marketIndex === marketIndex);
65
+ }
66
+ /**
67
+ * Gets the user's current position for a given spot market. If the user has no position returns undefined
68
+ * @param marketIndex
69
+ * @returns userSpotPosition
70
+ */
71
+ getSpotPosition(marketIndex) {
72
+ return this.getUserAccount().spotPositions.find((position) => position.marketIndex === marketIndex);
73
+ }
74
+ getEmptyPosition(marketIndex) {
75
+ return {
76
+ baseAssetAmount: numericConstants_1.ZERO,
77
+ remainderBaseAssetAmount: 0,
78
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
79
+ marketIndex,
80
+ quoteAssetAmount: numericConstants_1.ZERO,
81
+ quoteEntryAmount: numericConstants_1.ZERO,
82
+ quoteBreakEvenAmount: numericConstants_1.ZERO,
83
+ openOrders: 0,
84
+ openBids: numericConstants_1.ZERO,
85
+ openAsks: numericConstants_1.ZERO,
86
+ settledPnl: numericConstants_1.ZERO,
87
+ lpShares: numericConstants_1.ZERO,
88
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
89
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
90
+ };
91
+ }
92
+ getClonedPosition(position) {
93
+ const clonedPosition = Object.assign({}, position);
94
+ return clonedPosition;
95
+ }
96
+ /**
97
+ * @param orderId
98
+ * @returns Order
99
+ */
100
+ getOrder(orderId) {
101
+ return this.getUserAccount().orders.find((order) => order.orderId === orderId);
102
+ }
103
+ /**
104
+ * @param userOrderId
105
+ * @returns Order
106
+ */
107
+ getOrderByUserOrderId(userOrderId) {
108
+ return this.getUserAccount().orders.find((order) => order.userOrderId === userOrderId);
109
+ }
110
+ getUserAccountPublicKey() {
111
+ return this.userAccountPublicKey;
112
+ }
113
+ async exists() {
114
+ const userAccountRPCResponse = await this.driftClient.connection.getParsedAccountInfo(this.userAccountPublicKey);
115
+ return userAccountRPCResponse.value !== null;
116
+ }
117
+ /**
118
+ * calculates the total open bids/asks in a perp market (including lps)
119
+ * @returns : open bids
120
+ * @returns : open asks
121
+ */
122
+ getPerpBidAsks(marketIndex) {
123
+ const position = this.getPerpPosition(marketIndex);
124
+ const [lpOpenBids, lpOpenAsks] = this.getLPBidAsks(marketIndex);
125
+ const totalOpenBids = lpOpenBids.add(position.openBids);
126
+ const totalOpenAsks = lpOpenAsks.add(position.openAsks);
127
+ return [totalOpenBids, totalOpenAsks];
128
+ }
129
+ /**
130
+ * calculates the open bids and asks for an lp
131
+ * @returns : lp open bids
132
+ * @returns : lp open asks
133
+ */
134
+ getLPBidAsks(marketIndex) {
135
+ const position = this.getPerpPosition(marketIndex);
136
+ if (position === undefined || position.lpShares.eq(numericConstants_1.ZERO)) {
137
+ return [numericConstants_1.ZERO, numericConstants_1.ZERO];
138
+ }
139
+ const market = this.driftClient.getPerpMarketAccount(marketIndex);
140
+ const [marketOpenBids, marketOpenAsks] = (0, amm_1.calculateMarketOpenBidAsk)(market.amm.baseAssetReserve, market.amm.minBaseAssetReserve, market.amm.maxBaseAssetReserve);
141
+ const lpOpenBids = marketOpenBids
142
+ .mul(position.lpShares)
143
+ .div(market.amm.sqrtK);
144
+ const lpOpenAsks = marketOpenAsks
145
+ .mul(position.lpShares)
146
+ .div(market.amm.sqrtK);
147
+ return [lpOpenBids, lpOpenAsks];
148
+ }
149
+ /**
150
+ * calculates the market position if the lp position was settled
151
+ * @returns : the settled userPosition
152
+ * @returns : the dust base asset amount (ie, < stepsize)
153
+ * @returns : pnl from settle
154
+ */
155
+ getSettledLPPosition(marketIndex) {
156
+ const _position = this.getPerpPosition(marketIndex);
157
+ const position = this.getClonedPosition(_position);
158
+ if (position.lpShares.eq(numericConstants_1.ZERO)) {
159
+ return [position, numericConstants_1.ZERO, numericConstants_1.ZERO];
160
+ }
161
+ const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
162
+ const nShares = position.lpShares;
163
+ const deltaBaa = market.amm.baseAssetAmountPerLp
164
+ .sub(position.lastNetBaseAssetAmountPerLp)
165
+ .mul(nShares)
166
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
167
+ const deltaQaa = market.amm.quoteAssetAmountPerLp
168
+ .sub(position.lastNetQuoteAssetAmountPerLp)
169
+ .mul(nShares)
170
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
171
+ function sign(v) {
172
+ const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
173
+ return sign;
174
+ }
175
+ function standardize(amount, stepsize) {
176
+ const remainder = amount.abs().mod(stepsize).mul(sign(amount));
177
+ const standardizedAmount = amount.sub(remainder);
178
+ return [standardizedAmount, remainder];
179
+ }
180
+ const [standardizedBaa, remainderBaa] = standardize(deltaBaa, market.amm.orderStepSize);
181
+ position.remainderBaseAssetAmount += remainderBaa.toNumber();
182
+ if (Math.abs(position.remainderBaseAssetAmount) >
183
+ market.amm.orderStepSize.toNumber()) {
184
+ const [newStandardizedBaa, newRemainderBaa] = standardize(new _1.BN(position.remainderBaseAssetAmount), market.amm.orderStepSize);
185
+ position.baseAssetAmount =
186
+ position.baseAssetAmount.add(newStandardizedBaa);
187
+ position.remainderBaseAssetAmount = newRemainderBaa.toNumber();
188
+ }
189
+ let updateType;
190
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
191
+ updateType = 'open';
192
+ }
193
+ else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
194
+ updateType = 'increase';
195
+ }
196
+ else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
197
+ updateType = 'reduce';
198
+ }
199
+ else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
200
+ updateType = 'close';
201
+ }
202
+ else {
203
+ updateType = 'flip';
204
+ }
205
+ let newQuoteEntry;
206
+ let pnl;
207
+ if (updateType == 'open' || updateType == 'increase') {
208
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
209
+ pnl = 0;
210
+ }
211
+ else if (updateType == 'reduce' || updateType == 'close') {
212
+ newQuoteEntry = position.quoteEntryAmount.sub(position.quoteEntryAmount
213
+ .mul(deltaBaa.abs())
214
+ .div(position.baseAssetAmount.abs()));
215
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry).add(deltaQaa);
216
+ }
217
+ else {
218
+ newQuoteEntry = deltaQaa.sub(deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()));
219
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
220
+ }
221
+ position.quoteEntryAmount = newQuoteEntry;
222
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
223
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
224
+ if (position.baseAssetAmount.gt(numericConstants_1.ZERO)) {
225
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
226
+ }
227
+ else if (position.baseAssetAmount.lt(numericConstants_1.ZERO)) {
228
+ position.lastCumulativeFundingRate =
229
+ market.amm.cumulativeFundingRateShort;
230
+ }
231
+ else {
232
+ position.lastCumulativeFundingRate = numericConstants_1.ZERO;
233
+ }
234
+ return [position, remainderBaa, pnl];
235
+ }
236
+ /**
237
+ * calculates Buying Power = FC * MAX_LEVERAGE
238
+ * @returns : Precision QUOTE_PRECISION
239
+ */
240
+ getBuyingPower(marketIndex) {
241
+ return this.getFreeCollateral()
242
+ .mul(this.getMaxLeverage(marketIndex, 'Initial'))
243
+ .div(numericConstants_1.TEN_THOUSAND);
244
+ }
245
+ /**
246
+ * calculates Free Collateral = Total collateral - initial margin requirement
247
+ * @returns : Precision QUOTE_PRECISION
248
+ */
249
+ getFreeCollateral() {
250
+ const totalCollateral = this.getTotalCollateral();
251
+ const initialMarginRequirement = this.getInitialMarginRequirement();
252
+ const freeCollateral = totalCollateral.sub(initialMarginRequirement);
253
+ return freeCollateral.gte(numericConstants_1.ZERO) ? freeCollateral : numericConstants_1.ZERO;
254
+ }
255
+ /**
256
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
257
+ */
258
+ getMarginRequirement(marginCategory, liquidationBuffer) {
259
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true));
260
+ }
261
+ /**
262
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
263
+ */
264
+ getInitialMarginRequirement() {
265
+ return this.getMarginRequirement('Initial');
266
+ }
267
+ /**
268
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
269
+ */
270
+ getMaintenanceMarginRequirement(liquidationBuffer) {
271
+ return this.getMarginRequirement('Maintenance', liquidationBuffer);
272
+ }
273
+ /**
274
+ * calculates unrealized position price pnl
275
+ * @returns : Precision QUOTE_PRECISION
276
+ */
277
+ getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
278
+ const quoteSpotMarket = this.driftClient.getQuoteSpotMarketAccount();
279
+ return this.getUserAccount()
280
+ .perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
281
+ .reduce((unrealizedPnl, perpPosition) => {
282
+ const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
283
+ const oraclePriceData = this.getOracleDataForPerpMarket(market.marketIndex);
284
+ let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, perpPosition, withFunding, oraclePriceData);
285
+ if (withWeightMarginCategory !== undefined) {
286
+ if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
287
+ positionUnrealizedPnl = positionUnrealizedPnl
288
+ .mul((0, _1.calculateUnrealizedAssetWeight)(market, quoteSpotMarket, positionUnrealizedPnl, withWeightMarginCategory, oraclePriceData))
289
+ .div(new _1.BN(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION));
290
+ }
291
+ }
292
+ return unrealizedPnl.add(positionUnrealizedPnl);
293
+ }, numericConstants_1.ZERO);
294
+ }
295
+ /**
296
+ * calculates unrealized funding payment pnl
297
+ * @returns : Precision QUOTE_PRECISION
298
+ */
299
+ getUnrealizedFundingPNL(marketIndex) {
300
+ return this.getUserAccount()
301
+ .perpPositions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true)
302
+ .reduce((pnl, perpPosition) => {
303
+ const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
304
+ return pnl.add((0, _1.calculatePositionFundingPNL)(market, perpPosition));
305
+ }, numericConstants_1.ZERO);
306
+ }
307
+ getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders) {
308
+ return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
309
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
310
+ (marketIndex !== undefined &&
311
+ spotPosition.marketIndex !== marketIndex)) {
312
+ return totalLiabilityValue;
313
+ }
314
+ const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
315
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
316
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
317
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
318
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
319
+ if (marginCategory === 'Initial') {
320
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
321
+ }
322
+ const weightedTokenValue = tokenAmount
323
+ .mul(weight)
324
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
325
+ return totalLiabilityValue.add(weightedTokenValue);
326
+ }
327
+ else {
328
+ return totalLiabilityValue;
329
+ }
330
+ }
331
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
332
+ if (!includeOpenOrders) {
333
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
334
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
335
+ const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
336
+ return totalLiabilityValue.add(liabilityValue);
337
+ }
338
+ else {
339
+ return totalLiabilityValue;
340
+ }
341
+ }
342
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
343
+ let newTotalLiabilityValue = totalLiabilityValue;
344
+ if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
345
+ const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount.abs(), oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
346
+ newTotalLiabilityValue =
347
+ newTotalLiabilityValue.add(baseLiabilityValue);
348
+ }
349
+ if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO)) {
350
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
351
+ if (marginCategory === 'Initial') {
352
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
353
+ }
354
+ const weightedTokenValue = worstCaseQuoteTokenAmount
355
+ .abs()
356
+ .mul(weight)
357
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
358
+ newTotalLiabilityValue =
359
+ newTotalLiabilityValue.add(weightedTokenValue);
360
+ }
361
+ return newTotalLiabilityValue;
362
+ }, numericConstants_1.ZERO);
363
+ }
364
+ getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer) {
365
+ let liabilityValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
366
+ if (marginCategory !== undefined) {
367
+ let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, marginCategory);
368
+ if (marginCategory === 'Initial') {
369
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
370
+ }
371
+ if (liquidationBuffer !== undefined) {
372
+ weight = weight.add(liquidationBuffer);
373
+ }
374
+ liabilityValue = liabilityValue
375
+ .mul(weight)
376
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
377
+ }
378
+ return liabilityValue;
379
+ }
380
+ getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders) {
381
+ return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
382
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
383
+ (marketIndex !== undefined &&
384
+ spotPosition.marketIndex !== marketIndex)) {
385
+ return totalAssetValue;
386
+ }
387
+ // Todo this needs to account for whether it's based on initial or maintenance requirements
388
+ const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
389
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
390
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
391
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
392
+ return totalAssetValue.add(tokenAmount);
393
+ }
394
+ else {
395
+ return totalAssetValue;
396
+ }
397
+ }
398
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
399
+ if (!includeOpenOrders) {
400
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
401
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
402
+ const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
403
+ return totalAssetValue.add(assetValue);
404
+ }
405
+ else {
406
+ return totalAssetValue;
407
+ }
408
+ }
409
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
410
+ let newTotalAssetValue = totalAssetValue;
411
+ if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
412
+ const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
413
+ newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
414
+ }
415
+ if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
416
+ newTotalAssetValue = newTotalAssetValue.add(worstCaseQuoteTokenAmount);
417
+ }
418
+ return newTotalAssetValue;
419
+ }, numericConstants_1.ZERO);
420
+ }
421
+ getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory) {
422
+ let assetValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
423
+ if (marginCategory !== undefined) {
424
+ const weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, spotMarketAccount, marginCategory);
425
+ assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
426
+ }
427
+ return assetValue;
428
+ }
429
+ getNetSpotMarketValue(withWeightMarginCategory) {
430
+ return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
431
+ }
432
+ /**
433
+ * calculates TotalCollateral: collateral + unrealized pnl
434
+ * @returns : Precision QUOTE_PRECISION
435
+ */
436
+ getTotalCollateral(marginCategory = 'Initial') {
437
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
438
+ }
439
+ /**
440
+ * calculates sum of position value across all positions in margin system
441
+ * @returns : Precision QUOTE_PRECISION
442
+ */
443
+ getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
444
+ return this.getUserAccount().perpPositions.reduce((totalPerpValue, perpPosition) => {
445
+ const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
446
+ if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
447
+ // is an lp
448
+ // clone so we dont mutate the position
449
+ perpPosition = this.getClonedPosition(perpPosition);
450
+ // settle position
451
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
452
+ perpPosition.baseAssetAmount =
453
+ settledPosition.baseAssetAmount.add(dustBaa);
454
+ perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
455
+ const [totalOpenBids, totalOpenAsks] = this.getPerpBidAsks(market.marketIndex);
456
+ perpPosition.openAsks = totalOpenAsks;
457
+ perpPosition.openBids = totalOpenBids;
458
+ }
459
+ let valuationPrice = this.getOracleDataForPerpMarket(market.marketIndex).price;
460
+ if ((0, types_1.isVariant)(market.status, 'settlement')) {
461
+ valuationPrice = market.expiryPrice;
462
+ }
463
+ const baseAssetAmount = includeOpenOrders
464
+ ? (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition)
465
+ : perpPosition.baseAssetAmount;
466
+ let baseAssetValue = baseAssetAmount
467
+ .abs()
468
+ .mul(valuationPrice)
469
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.PRICE_PRECISION));
470
+ if (marginCategory) {
471
+ let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory));
472
+ if (marginCategory === 'Initial') {
473
+ marginRatio = _1.BN.max(marginRatio, new _1.BN(this.getUserAccount().maxMarginRatio));
474
+ }
475
+ if (liquidationBuffer !== undefined) {
476
+ marginRatio = marginRatio.add(liquidationBuffer);
477
+ }
478
+ if ((0, types_1.isVariant)(market.status, 'settlement')) {
479
+ marginRatio = numericConstants_1.ZERO;
480
+ }
481
+ baseAssetValue = baseAssetValue
482
+ .mul(marginRatio)
483
+ .div(numericConstants_1.MARGIN_PRECISION);
484
+ }
485
+ return totalPerpValue.add(baseAssetValue);
486
+ }, numericConstants_1.ZERO);
487
+ }
488
+ /**
489
+ * calculates position value in margin system
490
+ * @returns : Precision QUOTE_PRECISION
491
+ */
492
+ getPerpPositionValue(marketIndex, oraclePriceData) {
493
+ const userPosition = this.getPerpPosition(marketIndex) || this.getEmptyPosition(marketIndex);
494
+ const market = this.driftClient.getPerpMarketAccount(userPosition.marketIndex);
495
+ return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData);
496
+ }
497
+ getPositionSide(currentPosition) {
498
+ if (currentPosition.baseAssetAmount.gt(numericConstants_1.ZERO)) {
499
+ return _1.PositionDirection.LONG;
500
+ }
501
+ else if (currentPosition.baseAssetAmount.lt(numericConstants_1.ZERO)) {
502
+ return _1.PositionDirection.SHORT;
503
+ }
504
+ else {
505
+ return undefined;
506
+ }
507
+ }
508
+ /**
509
+ * calculates average exit price (optionally for closing up to 100% of position)
510
+ * @returns : Precision PRICE_PRECISION
511
+ */
512
+ getPositionEstimatedExitPriceAndPnl(position, amountToClose, useAMMClose = false) {
513
+ const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
514
+ const entryPrice = (0, position_1.calculateEntryPrice)(position);
515
+ const oraclePriceData = this.getOracleDataForPerpMarket(position.marketIndex);
516
+ if (amountToClose) {
517
+ if (amountToClose.eq(numericConstants_1.ZERO)) {
518
+ return [(0, _1.calculateReservePrice)(market, oraclePriceData), numericConstants_1.ZERO];
519
+ }
520
+ position = {
521
+ baseAssetAmount: amountToClose,
522
+ lastCumulativeFundingRate: position.lastCumulativeFundingRate,
523
+ marketIndex: position.marketIndex,
524
+ quoteAssetAmount: position.quoteAssetAmount,
525
+ };
526
+ }
527
+ let baseAssetValue;
528
+ if (useAMMClose) {
529
+ baseAssetValue = (0, _1.calculateBaseAssetValue)(market, position, oraclePriceData);
530
+ }
531
+ else {
532
+ baseAssetValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, position, oraclePriceData);
533
+ }
534
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
535
+ return [numericConstants_1.ZERO, numericConstants_1.ZERO];
536
+ }
537
+ const exitPrice = baseAssetValue
538
+ .mul(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO)
539
+ .mul(numericConstants_1.PRICE_PRECISION)
540
+ .div(position.baseAssetAmount.abs());
541
+ const pnlPerBase = exitPrice.sub(entryPrice);
542
+ const pnl = pnlPerBase
543
+ .mul(position.baseAssetAmount)
544
+ .div(numericConstants_1.PRICE_PRECISION)
545
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
546
+ return [exitPrice, pnl];
547
+ }
548
+ /**
549
+ * calculates current user leverage across all positions
550
+ * @returns : Precision TEN_THOUSAND
551
+ */
552
+ getLeverage() {
553
+ const totalLiabilityValue = this.getTotalLiabilityValue();
554
+ const totalAssetValue = this.getTotalAssetValue();
555
+ if (totalAssetValue.eq(numericConstants_1.ZERO) && totalLiabilityValue.eq(numericConstants_1.ZERO)) {
556
+ return numericConstants_1.ZERO;
557
+ }
558
+ return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(totalAssetValue);
559
+ }
560
+ getTotalLiabilityValue() {
561
+ return this.getTotalPerpPositionValue(undefined, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, undefined, undefined, true));
562
+ }
563
+ getTotalAssetValue() {
564
+ return this.getSpotMarketAssetValue(undefined, undefined, true).add(this.getUnrealizedPNL(true, undefined, undefined));
565
+ }
566
+ /**
567
+ * calculates max allowable leverage exceeding hitting requirement category
568
+ * @params category {Initial, Maintenance}
569
+ * @returns : Precision TEN_THOUSAND
570
+ */
571
+ getMaxLeverage(marketIndex, category = 'Initial') {
572
+ const market = this.driftClient.getPerpMarketAccount(marketIndex);
573
+ const totalAssetValue = this.getTotalAssetValue();
574
+ if (totalAssetValue.eq(numericConstants_1.ZERO)) {
575
+ return numericConstants_1.ZERO;
576
+ }
577
+ const totalLiabilityValue = this.getTotalLiabilityValue();
578
+ const marginRatio = (0, _1.calculateMarketMarginRatio)(market,
579
+ // worstCaseBaseAssetAmount.abs(),
580
+ numericConstants_1.ZERO, // todo
581
+ category);
582
+ const freeCollateral = this.getFreeCollateral();
583
+ // how much more liabilities can be opened w remaining free collateral
584
+ const additionalLiabilities = freeCollateral
585
+ .mul(numericConstants_1.MARGIN_PRECISION)
586
+ .div(new _1.BN(marginRatio));
587
+ return totalLiabilityValue
588
+ .add(additionalLiabilities)
589
+ .mul(numericConstants_1.TEN_THOUSAND)
590
+ .div(totalAssetValue);
591
+ }
592
+ /**
593
+ * calculates margin ratio: total collateral / |total position value|
594
+ * @returns : Precision TEN_THOUSAND
595
+ */
596
+ getMarginRatio() {
597
+ const totalLiabilityValue = this.getTotalLiabilityValue();
598
+ if (totalLiabilityValue.eq(numericConstants_1.ZERO)) {
599
+ return numericConstants_1.BN_MAX;
600
+ }
601
+ const totalAssetValue = this.getTotalAssetValue();
602
+ return totalAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
603
+ }
604
+ canBeLiquidated() {
605
+ const totalCollateral = this.getTotalCollateral();
606
+ // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
607
+ let liquidationBuffer = undefined;
608
+ const isBeingLiquidated = (0, types_1.isVariant)(this.getUserAccount().status, 'beingLiquidated');
609
+ if (isBeingLiquidated) {
610
+ liquidationBuffer = new _1.BN(this.driftClient.getStateAccount().liquidationMarginBufferRatio);
611
+ }
612
+ const maintenanceRequirement = this.getMaintenanceMarginRequirement(liquidationBuffer);
613
+ return totalCollateral.lt(maintenanceRequirement);
614
+ }
615
+ isBeingLiquidated() {
616
+ return (0, types_1.isOneOfVariant)(this.getUserAccount().status, [
617
+ 'beingLiquidated',
618
+ 'bankrupt',
619
+ ]);
620
+ }
621
+ isBankrupt() {
622
+ return (0, types_1.isVariant)(this.getUserAccount().status, 'bankrupt');
623
+ }
624
+ /**
625
+ * Checks if any user position cumulative funding differs from respective market cumulative funding
626
+ * @returns
627
+ */
628
+ needsToSettleFundingPayment() {
629
+ for (const userPosition of this.getUserAccount().perpPositions) {
630
+ if (userPosition.baseAssetAmount.eq(numericConstants_1.ZERO)) {
631
+ continue;
632
+ }
633
+ const market = this.driftClient.getPerpMarketAccount(userPosition.marketIndex);
634
+ if (market.amm.cumulativeFundingRateLong.eq(userPosition.lastCumulativeFundingRate) ||
635
+ market.amm.cumulativeFundingRateShort.eq(userPosition.lastCumulativeFundingRate)) {
636
+ continue;
637
+ }
638
+ return true;
639
+ }
640
+ return false;
641
+ }
642
+ /**
643
+ * Calculate the liquidation price of a position, with optional parameter to calculate the liquidation price after a trade
644
+ * @param PerpPosition
645
+ * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
646
+ * @param partial
647
+ * @returns Precision : PRICE_PRECISION
648
+ */
649
+ liquidationPrice(perpPosition, positionBaseSizeChange = numericConstants_1.ZERO) {
650
+ // solves formula for example canBeLiquidated below
651
+ /* example: assume BTC price is $40k (examine 10% up/down)
652
+
653
+ if 10k deposit and levered 10x short BTC => BTC up $400 means:
654
+ 1. higher base_asset_value (+$4k)
655
+ 2. lower collateral (-$4k)
656
+ 3. (10k - 4k)/(100k + 4k) => 6k/104k => .0576
657
+
658
+ for 10x long, BTC down $400:
659
+ 3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
660
+ const totalCollateral = this.getTotalCollateral();
661
+ // calculate the total position value ignoring any value from the target market of the trade
662
+ const totalPositionValueExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
663
+ const currentPerpPosition = this.getPerpPosition(perpPosition.marketIndex) ||
664
+ this.getEmptyPosition(perpPosition.marketIndex);
665
+ const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
666
+ const proposedBaseAssetAmount = currentPerpPositionBaseSize.add(positionBaseSizeChange);
667
+ // calculate position for current market after trade
668
+ const proposedPerpPosition = {
669
+ marketIndex: perpPosition.marketIndex,
670
+ baseAssetAmount: proposedBaseAssetAmount,
671
+ remainderBaseAssetAmount: 0,
672
+ quoteAssetAmount: new _1.BN(0),
673
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
674
+ quoteBreakEvenAmount: new _1.BN(0),
675
+ quoteEntryAmount: new _1.BN(0),
676
+ openOrders: 0,
677
+ openBids: new _1.BN(0),
678
+ openAsks: new _1.BN(0),
679
+ settledPnl: numericConstants_1.ZERO,
680
+ lpShares: numericConstants_1.ZERO,
681
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
682
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
683
+ };
684
+ if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
685
+ return new _1.BN(-1);
686
+ const market = this.driftClient.getPerpMarketAccount(proposedPerpPosition.marketIndex);
687
+ const proposedPerpPositionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, proposedPerpPosition, this.getOracleDataForPerpMarket(market.marketIndex));
688
+ // total position value after trade
689
+ const totalPositionValueAfterTrade = totalPositionValueExcludingTargetMarket.add(proposedPerpPositionValue);
690
+ const marginRequirementExcludingTargetMarket = this.getUserAccount().perpPositions.reduce((totalMarginRequirement, position) => {
691
+ if (position.marketIndex !== perpPosition.marketIndex) {
692
+ const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
693
+ const positionValue = (0, margin_1.calculateBaseAssetValueWithOracle)(market, position, this.getOracleDataForPerpMarket(market.marketIndex));
694
+ const marketMarginRequirement = positionValue
695
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
696
+ .div(numericConstants_1.MARGIN_PRECISION);
697
+ totalMarginRequirement = totalMarginRequirement.add(marketMarginRequirement);
698
+ }
699
+ return totalMarginRequirement;
700
+ }, numericConstants_1.ZERO);
701
+ const freeCollateralExcludingTargetMarket = totalCollateral.sub(marginRequirementExcludingTargetMarket);
702
+ // if the position value after the trade is less than free collateral, there is no liq price
703
+ if (totalPositionValueAfterTrade.lte(freeCollateralExcludingTargetMarket) &&
704
+ proposedPerpPosition.baseAssetAmount.abs().gt(numericConstants_1.ZERO)) {
705
+ return new _1.BN(-1);
706
+ }
707
+ const marginRequirementAfterTrade = marginRequirementExcludingTargetMarket.add(proposedPerpPositionValue
708
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, proposedPerpPosition.baseAssetAmount.abs(), 'Maintenance')))
709
+ .div(numericConstants_1.MARGIN_PRECISION));
710
+ const freeCollateralAfterTrade = totalCollateral.sub(marginRequirementAfterTrade);
711
+ const marketMaxLeverage = this.getMaxLeverage(proposedPerpPosition.marketIndex, 'Maintenance');
712
+ let priceDelta;
713
+ if (proposedBaseAssetAmount.lt(numericConstants_1.ZERO)) {
714
+ priceDelta = freeCollateralAfterTrade
715
+ .mul(marketMaxLeverage) // precision is TEN_THOUSAND
716
+ .div(marketMaxLeverage.add(numericConstants_1.TEN_THOUSAND))
717
+ .mul(numericConstants_1.PRICE_TO_QUOTE_PRECISION)
718
+ .mul(numericConstants_1.AMM_RESERVE_PRECISION)
719
+ .div(proposedBaseAssetAmount);
720
+ }
721
+ else {
722
+ priceDelta = freeCollateralAfterTrade
723
+ .mul(marketMaxLeverage) // precision is TEN_THOUSAND
724
+ .div(marketMaxLeverage.sub(numericConstants_1.TEN_THOUSAND))
725
+ .mul(numericConstants_1.PRICE_TO_QUOTE_PRECISION)
726
+ .mul(numericConstants_1.AMM_RESERVE_PRECISION)
727
+ .div(proposedBaseAssetAmount);
728
+ }
729
+ let markPriceAfterTrade;
730
+ if (positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
731
+ markPriceAfterTrade = (0, _1.calculateReservePrice)(this.driftClient.getPerpMarketAccount(perpPosition.marketIndex), this.getOracleDataForPerpMarket(perpPosition.marketIndex));
732
+ }
733
+ else {
734
+ const direction = positionBaseSizeChange.gt(numericConstants_1.ZERO)
735
+ ? _1.PositionDirection.LONG
736
+ : _1.PositionDirection.SHORT;
737
+ markPriceAfterTrade = (0, _1.calculateTradeSlippage)(direction, positionBaseSizeChange.abs(), this.driftClient.getPerpMarketAccount(perpPosition.marketIndex), 'base', this.getOracleDataForPerpMarket(perpPosition.marketIndex))[3]; // newPrice after swap
738
+ }
739
+ if (priceDelta.gt(markPriceAfterTrade)) {
740
+ return new _1.BN(-1);
741
+ }
742
+ return markPriceAfterTrade.sub(priceDelta);
743
+ }
744
+ /**
745
+ * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
746
+ * @param positionMarketIndex
747
+ * @param closeQuoteAmount
748
+ * @returns : Precision PRICE_PRECISION
749
+ */
750
+ liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount) {
751
+ const currentPosition = this.getPerpPosition(positionMarketIndex) ||
752
+ this.getEmptyPosition(positionMarketIndex);
753
+ const closeBaseAmount = currentPosition.baseAssetAmount
754
+ .mul(closeQuoteAmount)
755
+ .div(currentPosition.quoteAssetAmount.abs())
756
+ .add(currentPosition.baseAssetAmount
757
+ .mul(closeQuoteAmount)
758
+ .mod(currentPosition.quoteAssetAmount.abs()))
759
+ .neg();
760
+ return this.liquidationPrice({
761
+ marketIndex: positionMarketIndex,
762
+ }, closeBaseAmount);
763
+ }
764
+ /**
765
+ * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
766
+ *
767
+ * To Calculate Max Quote Available:
768
+ *
769
+ * Case 1: SameSide
770
+ * => Remaining quote to get to maxLeverage
771
+ *
772
+ * Case 2: NOT SameSide && currentLeverage <= maxLeverage
773
+ * => Current opposite position x2 + remaining to get to maxLeverage
774
+ *
775
+ * Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
776
+ * => strictly reduce current position size
777
+ *
778
+ * Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
779
+ * => current position + remaining to get to maxLeverage
780
+ *
781
+ * @param targetMarketIndex
782
+ * @param tradeSide
783
+ * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
784
+ */
785
+ getMaxTradeSizeUSDC(targetMarketIndex, tradeSide) {
786
+ const currentPosition = this.getPerpPosition(targetMarketIndex) ||
787
+ this.getEmptyPosition(targetMarketIndex);
788
+ const targetSide = (0, types_1.isVariant)(tradeSide, 'short') ? 'short' : 'long';
789
+ const currentPositionSide = (currentPosition === null || currentPosition === void 0 ? void 0 : currentPosition.baseAssetAmount.isNeg())
790
+ ? 'short'
791
+ : 'long';
792
+ const targetingSameSide = !currentPosition
793
+ ? true
794
+ : targetSide === currentPositionSide;
795
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
796
+ // 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.
797
+ const oppositeSizeValueUSDC = targetingSameSide
798
+ ? numericConstants_1.ZERO
799
+ : this.getPerpPositionValue(targetMarketIndex, oracleData);
800
+ let maxPositionSize = this.getBuyingPower(targetMarketIndex);
801
+ if (maxPositionSize.gte(numericConstants_1.ZERO)) {
802
+ if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
803
+ // case 1 : Regular trade where current total position less than max, and no opposite position to account for
804
+ // do nothing
805
+ }
806
+ else {
807
+ // case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
808
+ maxPositionSize = maxPositionSize.add(oppositeSizeValueUSDC.mul(new _1.BN(2)));
809
+ }
810
+ }
811
+ else {
812
+ // current leverage is greater than max leverage - can only reduce position size
813
+ if (!targetingSameSide) {
814
+ const market = this.driftClient.getPerpMarketAccount(targetMarketIndex);
815
+ const perpPositionValue = this.getPerpPositionValue(targetMarketIndex, oracleData);
816
+ const totalCollateral = this.getTotalCollateral();
817
+ const marginRequirement = this.getInitialMarginRequirement();
818
+ const marginFreedByClosing = perpPositionValue
819
+ .mul(new _1.BN(market.marginRatioInitial))
820
+ .div(numericConstants_1.MARGIN_PRECISION);
821
+ const marginRequirementAfterClosing = marginRequirement.sub(marginFreedByClosing);
822
+ if (marginRequirementAfterClosing.gt(totalCollateral)) {
823
+ maxPositionSize = perpPositionValue;
824
+ }
825
+ else {
826
+ const freeCollateralAfterClose = totalCollateral.sub(marginRequirementAfterClosing);
827
+ const buyingPowerAfterClose = freeCollateralAfterClose
828
+ .mul(this.getMaxLeverage(targetMarketIndex))
829
+ .div(numericConstants_1.TEN_THOUSAND);
830
+ maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
831
+ }
832
+ }
833
+ else {
834
+ // do nothing if targetting same side
835
+ }
836
+ }
837
+ // subtract oneMillionth of maxPositionSize
838
+ // => to avoid rounding errors when taking max leverage
839
+ const oneMilli = maxPositionSize.div(numericConstants_1.QUOTE_PRECISION);
840
+ return maxPositionSize.sub(oneMilli);
841
+ }
842
+ // TODO - should this take the price impact of the trade into account for strict accuracy?
843
+ /**
844
+ * Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
845
+ * @param targetMarketIndex
846
+ * @param positionMarketIndex
847
+ * @param tradeQuoteAmount
848
+ * @returns leverageRatio : Precision TEN_THOUSAND
849
+ */
850
+ accountLeverageRatioAfterTrade(targetMarketIndex, tradeQuoteAmount, tradeSide, includeOpenOrders = true) {
851
+ const currentPosition = this.getPerpPosition(targetMarketIndex) ||
852
+ this.getEmptyPosition(targetMarketIndex);
853
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
854
+ let currentPositionQuoteAmount = this.getPerpPositionValue(targetMarketIndex, oracleData);
855
+ const currentSide = currentPosition && currentPosition.baseAssetAmount.isNeg()
856
+ ? _1.PositionDirection.SHORT
857
+ : _1.PositionDirection.LONG;
858
+ if (currentSide === _1.PositionDirection.SHORT)
859
+ currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
860
+ if (tradeSide === _1.PositionDirection.SHORT)
861
+ tradeQuoteAmount = tradeQuoteAmount.neg();
862
+ const currentPerpPositionAfterTrade = currentPositionQuoteAmount
863
+ .add(tradeQuoteAmount)
864
+ .abs();
865
+ const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex, undefined, undefined, includeOpenOrders);
866
+ const totalAssetValue = this.getTotalAssetValue();
867
+ const totalPerpPositionValue = currentPerpPositionAfterTrade
868
+ .add(totalPositionAfterTradeExcludingTargetMarket)
869
+ .abs();
870
+ const totalLiabilitiesAfterTrade = totalPerpPositionValue.add(this.getSpotMarketLiabilityValue(undefined, undefined, undefined, false));
871
+ if (totalAssetValue.eq(numericConstants_1.ZERO) && totalLiabilitiesAfterTrade.eq(numericConstants_1.ZERO)) {
872
+ return numericConstants_1.ZERO;
873
+ }
874
+ const newLeverage = totalLiabilitiesAfterTrade
875
+ .mul(numericConstants_1.TEN_THOUSAND)
876
+ .div(totalAssetValue);
877
+ return newLeverage;
878
+ }
879
+ /**
880
+ * Calculates how much fee will be taken for a given sized trade
881
+ * @param quoteAmount
882
+ * @returns feeForQuote : Precision QUOTE_PRECISION
883
+ */
884
+ calculateFeeForQuoteAmount(quoteAmount) {
885
+ const feeTier = this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
886
+ return quoteAmount
887
+ .mul(new _1.BN(feeTier.feeNumerator))
888
+ .div(new _1.BN(feeTier.feeDenominator));
889
+ }
890
+ /**
891
+ * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
892
+ * it will return the max withdrawal amount without opening a liability for the user
893
+ * @param marketIndex
894
+ * @returns withdrawalLimit : Precision is the token precision for the chosen SpotMarket
895
+ */
896
+ getWithdrawalLimit(marketIndex, reduceOnly) {
897
+ const nowTs = new _1.BN(Math.floor(Date.now() / 1000));
898
+ const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
899
+ const { borrowLimit, withdrawLimit } = (0, spotBalance_1.calculateWithdrawLimit)(spotMarket, nowTs);
900
+ const freeCollateral = this.getFreeCollateral();
901
+ const oracleData = this.getOracleDataForSpotMarket(marketIndex);
902
+ const precisionIncrease = numericConstants_1.TEN.pow(new _1.BN(spotMarket.decimals - 6));
903
+ const amountWithdrawable = freeCollateral
904
+ .mul(numericConstants_1.MARGIN_PRECISION)
905
+ .div(new _1.BN(spotMarket.initialAssetWeight))
906
+ .mul(numericConstants_1.PRICE_PRECISION)
907
+ .div(oracleData.price)
908
+ .mul(precisionIncrease);
909
+ const userSpotPosition = this.getUserAccount().spotPositions.find((spotPosition) => (0, types_1.isVariant)(spotPosition.balanceType, 'deposit') &&
910
+ spotPosition.marketIndex == marketIndex);
911
+ const userSpotBalance = userSpotPosition
912
+ ? (0, spotBalance_1.getTokenAmount)(userSpotPosition.scaledBalance, this.driftClient.getSpotMarketAccount(marketIndex), _1.SpotBalanceType.DEPOSIT)
913
+ : numericConstants_1.ZERO;
914
+ const maxWithdrawValue = _1.BN.min(_1.BN.min(amountWithdrawable, userSpotBalance), withdrawLimit.abs());
915
+ if (reduceOnly) {
916
+ return _1.BN.max(maxWithdrawValue, numericConstants_1.ZERO);
917
+ }
918
+ else {
919
+ const weightedAssetValue = this.getSpotMarketAssetValue(marketIndex, 'Initial', false);
920
+ const freeCollatAfterWithdraw = userSpotBalance.gt(numericConstants_1.ZERO)
921
+ ? freeCollateral.sub(weightedAssetValue)
922
+ : freeCollateral;
923
+ const maxLiabilityAllowed = freeCollatAfterWithdraw
924
+ .mul(numericConstants_1.MARGIN_PRECISION)
925
+ .div(new _1.BN(spotMarket.initialLiabilityWeight))
926
+ .mul(numericConstants_1.PRICE_PRECISION)
927
+ .div(oracleData.price)
928
+ .mul(precisionIncrease);
929
+ const maxBorrowValue = _1.BN.min(maxWithdrawValue.add(maxLiabilityAllowed), borrowLimit.abs());
930
+ return _1.BN.max(maxBorrowValue, numericConstants_1.ZERO);
931
+ }
932
+ }
933
+ /**
934
+ * Get the total position value, excluding any position coming from the given target market
935
+ * @param marketToIgnore
936
+ * @returns positionValue : Precision QUOTE_PRECISION
937
+ */
938
+ getTotalPerpPositionValueExcludingMarket(marketToIgnore, marginCategory, liquidationBuffer, includeOpenOrders) {
939
+ const currentPerpPosition = this.getPerpPosition(marketToIgnore) ||
940
+ this.getEmptyPosition(marketToIgnore);
941
+ const oracleData = this.getOracleDataForPerpMarket(marketToIgnore);
942
+ let currentPerpPositionValueUSDC = numericConstants_1.ZERO;
943
+ if (currentPerpPosition) {
944
+ currentPerpPositionValueUSDC = this.getPerpPositionValue(marketToIgnore, oracleData);
945
+ }
946
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders).sub(currentPerpPositionValueUSDC);
947
+ }
948
+ getOracleDataForPerpMarket(marketIndex) {
949
+ const oracleKey = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
950
+ const oracleData = this.driftClient.getOraclePriceDataAndSlot(oracleKey).data;
951
+ return oracleData;
952
+ }
953
+ getOracleDataForSpotMarket(marketIndex) {
954
+ const oracleKey = this.driftClient.getSpotMarketAccount(marketIndex).oracle;
955
+ const oracleData = this.driftClient.getOraclePriceDataAndSlot(oracleKey).data;
956
+ return oracleData;
957
+ }
958
+ }
959
+ exports.User = User;