@drift-labs/sdk-browser 2.104.0-beta.21

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 (935) hide show
  1. package/README.md +276 -0
  2. package/VERSION +1 -0
  3. package/bun.lockb +0 -0
  4. package/get_events.ts +47 -0
  5. package/lib/browser/accounts/basicUserAccountSubscriber.d.ts +27 -0
  6. package/lib/browser/accounts/basicUserAccountSubscriber.js +38 -0
  7. package/lib/browser/accounts/bulkAccountLoader.d.ts +37 -0
  8. package/lib/browser/accounts/bulkAccountLoader.js +222 -0
  9. package/lib/browser/accounts/bulkUserStatsSubscription.d.ts +7 -0
  10. package/lib/browser/accounts/bulkUserStatsSubscription.js +21 -0
  11. package/lib/browser/accounts/bulkUserSubscription.d.ts +7 -0
  12. package/lib/browser/accounts/bulkUserSubscription.js +21 -0
  13. package/lib/browser/accounts/fetch.d.ts +6 -0
  14. package/lib/browser/accounts/fetch.js +30 -0
  15. package/lib/browser/accounts/grpcAccountSubscriber.d.ts +16 -0
  16. package/lib/browser/accounts/grpcAccountSubscriber.js +154 -0
  17. package/lib/browser/accounts/grpcDriftClientAccountSubscriber.d.ts +12 -0
  18. package/lib/browser/accounts/grpcDriftClientAccountSubscriber.js +98 -0
  19. package/lib/browser/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +10 -0
  20. package/lib/browser/accounts/grpcInsuranceFundStakeAccountSubscriber.js +30 -0
  21. package/lib/browser/accounts/grpcProgramAccountSubscriber.d.ts +18 -0
  22. package/lib/browser/accounts/grpcProgramAccountSubscriber.js +171 -0
  23. package/lib/browser/accounts/grpcUserAccountSubscriber.d.ts +10 -0
  24. package/lib/browser/accounts/grpcUserAccountSubscriber.js +28 -0
  25. package/lib/browser/accounts/grpcUserStatsAccountSubscriber.d.ts +10 -0
  26. package/lib/browser/accounts/grpcUserStatsAccountSubscriber.js +28 -0
  27. package/lib/browser/accounts/oneShotUserAccountSubscriber.d.ts +18 -0
  28. package/lib/browser/accounts/oneShotUserAccountSubscriber.js +48 -0
  29. package/lib/browser/accounts/pollingDriftClientAccountSubscriber.d.ts +69 -0
  30. package/lib/browser/accounts/pollingDriftClientAccountSubscriber.js +418 -0
  31. package/lib/browser/accounts/pollingHighLeverageModeConfigAccountSubscriber.d.ts +29 -0
  32. package/lib/browser/accounts/pollingHighLeverageModeConfigAccountSubscriber.js +111 -0
  33. package/lib/browser/accounts/pollingInsuranceFundStakeAccountSubscriber.d.ts +29 -0
  34. package/lib/browser/accounts/pollingInsuranceFundStakeAccountSubscriber.js +110 -0
  35. package/lib/browser/accounts/pollingOracleAccountSubscriber.d.ts +27 -0
  36. package/lib/browser/accounts/pollingOracleAccountSubscriber.js +78 -0
  37. package/lib/browser/accounts/pollingTokenAccountSubscriber.d.ts +26 -0
  38. package/lib/browser/accounts/pollingTokenAccountSubscriber.js +78 -0
  39. package/lib/browser/accounts/pollingUserAccountSubscriber.d.ts +29 -0
  40. package/lib/browser/accounts/pollingUserAccountSubscriber.js +102 -0
  41. package/lib/browser/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  42. package/lib/browser/accounts/pollingUserStatsAccountSubscriber.js +94 -0
  43. package/lib/browser/accounts/testBulkAccountLoader.d.ts +4 -0
  44. package/lib/browser/accounts/testBulkAccountLoader.js +45 -0
  45. package/lib/browser/accounts/types.d.ts +168 -0
  46. package/lib/browser/accounts/types.js +16 -0
  47. package/lib/browser/accounts/utils.d.ts +8 -0
  48. package/lib/browser/accounts/utils.js +49 -0
  49. package/lib/browser/accounts/webSocketAccountSubscriber.d.ts +29 -0
  50. package/lib/browser/accounts/webSocketAccountSubscriber.js +149 -0
  51. package/lib/browser/accounts/webSocketDriftClientAccountSubscriber.d.ts +66 -0
  52. package/lib/browser/accounts/webSocketDriftClientAccountSubscriber.js +358 -0
  53. package/lib/browser/accounts/webSocketHighLeverageModeConfigAccountSubscriber.d.ts +23 -0
  54. package/lib/browser/accounts/webSocketHighLeverageModeConfigAccountSubscriber.js +69 -0
  55. package/lib/browser/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +23 -0
  56. package/lib/browser/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +67 -0
  57. package/lib/browser/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
  58. package/lib/browser/accounts/webSocketProgramAccountSubscriber.js +120 -0
  59. package/lib/browser/accounts/webSocketUserAccountSubscriber.d.ts +23 -0
  60. package/lib/browser/accounts/webSocketUserAccountSubscriber.js +61 -0
  61. package/lib/browser/accounts/webSocketUserStatsAccountSubsriber.d.ts +22 -0
  62. package/lib/browser/accounts/webSocketUserStatsAccountSubsriber.js +52 -0
  63. package/lib/browser/addresses/marketAddresses.d.ts +2 -0
  64. package/lib/browser/addresses/marketAddresses.js +15 -0
  65. package/lib/browser/addresses/pda.d.ts +32 -0
  66. package/lib/browser/addresses/pda.js +211 -0
  67. package/lib/browser/adminClient.d.ts +206 -0
  68. package/lib/browser/adminClient.js +1858 -0
  69. package/lib/browser/assert/assert.d.ts +1 -0
  70. package/lib/browser/assert/assert.js +9 -0
  71. package/lib/browser/auctionSubscriber/auctionSubscriber.d.ts +14 -0
  72. package/lib/browser/auctionSubscriber/auctionSubscriber.js +32 -0
  73. package/lib/browser/auctionSubscriber/auctionSubscriberGrpc.d.ts +15 -0
  74. package/lib/browser/auctionSubscriber/auctionSubscriberGrpc.js +32 -0
  75. package/lib/browser/auctionSubscriber/index.d.ts +3 -0
  76. package/lib/browser/auctionSubscriber/index.js +19 -0
  77. package/lib/browser/auctionSubscriber/types.d.ts +14 -0
  78. package/lib/browser/auctionSubscriber/types.js +2 -0
  79. package/lib/browser/bankrun/bankrunConnection.d.ts +75 -0
  80. package/lib/browser/bankrun/bankrunConnection.js +332 -0
  81. package/lib/browser/blockhashSubscriber/BlockhashSubscriber.d.ts +27 -0
  82. package/lib/browser/blockhashSubscriber/BlockhashSubscriber.js +89 -0
  83. package/lib/browser/blockhashSubscriber/index.d.ts +1 -0
  84. package/lib/browser/blockhashSubscriber/index.js +17 -0
  85. package/lib/browser/blockhashSubscriber/types.d.ts +7 -0
  86. package/lib/browser/blockhashSubscriber/types.js +2 -0
  87. package/lib/browser/clock/clockSubscriber.d.ts +31 -0
  88. package/lib/browser/clock/clockSubscriber.js +80 -0
  89. package/lib/browser/config.d.ts +60 -0
  90. package/lib/browser/config.js +130 -0
  91. package/lib/browser/constants/numericConstants.d.ts +71 -0
  92. package/lib/browser/constants/numericConstants.js +75 -0
  93. package/lib/browser/constants/perpMarkets.d.ts +19 -0
  94. package/lib/browser/constants/perpMarkets.js +997 -0
  95. package/lib/browser/constants/spotMarkets.d.ts +24 -0
  96. package/lib/browser/constants/spotMarkets.js +470 -0
  97. package/lib/browser/constants/txConstants.d.ts +1 -0
  98. package/lib/browser/constants/txConstants.js +4 -0
  99. package/lib/browser/decode/phoenix.d.ts +7 -0
  100. package/lib/browser/decode/phoenix.js +159 -0
  101. package/lib/browser/decode/user.d.ts +4 -0
  102. package/lib/browser/decode/user.js +339 -0
  103. package/lib/browser/dlob/DLOB.d.ts +186 -0
  104. package/lib/browser/dlob/DLOB.js +1039 -0
  105. package/lib/browser/dlob/DLOBNode.d.ts +68 -0
  106. package/lib/browser/dlob/DLOBNode.js +100 -0
  107. package/lib/browser/dlob/DLOBSubscriber.d.ts +54 -0
  108. package/lib/browser/dlob/DLOBSubscriber.js +139 -0
  109. package/lib/browser/dlob/NodeList.d.ts +25 -0
  110. package/lib/browser/dlob/NodeList.js +126 -0
  111. package/lib/browser/dlob/orderBookLevels.d.ts +72 -0
  112. package/lib/browser/dlob/orderBookLevels.js +438 -0
  113. package/lib/browser/dlob/types.d.ts +18 -0
  114. package/lib/browser/dlob/types.js +2 -0
  115. package/lib/browser/driftClient.d.ts +861 -0
  116. package/lib/browser/driftClient.js +4768 -0
  117. package/lib/browser/driftClientConfig.d.ts +49 -0
  118. package/lib/browser/driftClientConfig.js +2 -0
  119. package/lib/browser/events/eventList.d.ts +22 -0
  120. package/lib/browser/events/eventList.js +80 -0
  121. package/lib/browser/events/eventSubscriber.d.ts +46 -0
  122. package/lib/browser/events/eventSubscriber.js +223 -0
  123. package/lib/browser/events/eventsServerLogProvider.d.ts +21 -0
  124. package/lib/browser/events/eventsServerLogProvider.js +121 -0
  125. package/lib/browser/events/fetchLogs.d.ts +25 -0
  126. package/lib/browser/events/fetchLogs.js +99 -0
  127. package/lib/browser/events/parse.d.ts +6 -0
  128. package/lib/browser/events/parse.js +106 -0
  129. package/lib/browser/events/pollingLogProvider.d.ts +17 -0
  130. package/lib/browser/events/pollingLogProvider.js +58 -0
  131. package/lib/browser/events/sort.d.ts +2 -0
  132. package/lib/browser/events/sort.js +24 -0
  133. package/lib/browser/events/txEventCache.d.ts +24 -0
  134. package/lib/browser/events/txEventCache.js +71 -0
  135. package/lib/browser/events/types.d.ts +79 -0
  136. package/lib/browser/events/types.js +32 -0
  137. package/lib/browser/events/webSocketLogProvider.d.ts +24 -0
  138. package/lib/browser/events/webSocketLogProvider.js +96 -0
  139. package/lib/browser/factory/bigNum.d.ts +122 -0
  140. package/lib/browser/factory/bigNum.js +457 -0
  141. package/lib/browser/factory/oracleClient.d.ts +5 -0
  142. package/lib/browser/factory/oracleClient.js +56 -0
  143. package/lib/browser/idl/drift.json +14440 -0
  144. package/lib/browser/idl/openbook.json +3854 -0
  145. package/lib/browser/idl/pyth_solana_receiver.json +628 -0
  146. package/lib/browser/idl/switchboard.json +8354 -0
  147. package/lib/browser/idl/switchboard_on_demand_30.json +4546 -0
  148. package/lib/browser/idl/token_faucet.json +142 -0
  149. package/lib/browser/index.d.ts +125 -0
  150. package/lib/browser/index.js +147 -0
  151. package/lib/browser/isomorphic/grpc.browser.d.ts +1 -0
  152. package/lib/browser/isomorphic/grpc.browser.js +8 -0
  153. package/lib/browser/isomorphic/grpc.d.ts +1 -0
  154. package/lib/browser/isomorphic/grpc.js +8 -0
  155. package/lib/browser/jupiter/jupiterClient.d.ts +302 -0
  156. package/lib/browser/jupiter/jupiterClient.js +178 -0
  157. package/lib/browser/keypair.d.ts +2 -0
  158. package/lib/browser/keypair.js +28 -0
  159. package/lib/browser/marinade/index.d.ts +12 -0
  160. package/lib/browser/marinade/index.js +36 -0
  161. package/lib/browser/marinade/types.d.ts +1963 -0
  162. package/lib/browser/marinade/types.js +1965 -0
  163. package/lib/browser/math/amm.d.ts +98 -0
  164. package/lib/browser/math/amm.js +626 -0
  165. package/lib/browser/math/auction.d.ts +23 -0
  166. package/lib/browser/math/auction.js +130 -0
  167. package/lib/browser/math/bankruptcy.d.ts +2 -0
  168. package/lib/browser/math/bankruptcy.js +31 -0
  169. package/lib/browser/math/conversion.d.ts +2 -0
  170. package/lib/browser/math/conversion.js +11 -0
  171. package/lib/browser/math/exchangeStatus.d.ts +6 -0
  172. package/lib/browser/math/exchangeStatus.js +77 -0
  173. package/lib/browser/math/fuel.d.ts +6 -0
  174. package/lib/browser/math/fuel.js +55 -0
  175. package/lib/browser/math/funding.d.ts +34 -0
  176. package/lib/browser/math/funding.js +209 -0
  177. package/lib/browser/math/insurance.d.ts +7 -0
  178. package/lib/browser/math/insurance.js +73 -0
  179. package/lib/browser/math/margin.d.ts +39 -0
  180. package/lib/browser/math/margin.js +184 -0
  181. package/lib/browser/math/market.d.ts +39 -0
  182. package/lib/browser/math/market.js +163 -0
  183. package/lib/browser/math/oracles.d.ts +14 -0
  184. package/lib/browser/math/oracles.js +134 -0
  185. package/lib/browser/math/orders.d.ts +23 -0
  186. package/lib/browser/math/orders.js +216 -0
  187. package/lib/browser/math/position.d.ts +70 -0
  188. package/lib/browser/math/position.js +225 -0
  189. package/lib/browser/math/repeg.d.ts +22 -0
  190. package/lib/browser/math/repeg.js +164 -0
  191. package/lib/browser/math/spotBalance.d.ts +83 -0
  192. package/lib/browser/math/spotBalance.js +373 -0
  193. package/lib/browser/math/spotMarket.d.ts +11 -0
  194. package/lib/browser/math/spotMarket.js +49 -0
  195. package/lib/browser/math/spotPosition.d.ts +19 -0
  196. package/lib/browser/math/spotPosition.js +78 -0
  197. package/lib/browser/math/state.d.ts +5 -0
  198. package/lib/browser/math/state.js +30 -0
  199. package/lib/browser/math/superStake.d.ts +167 -0
  200. package/lib/browser/math/superStake.js +306 -0
  201. package/lib/browser/math/tiers.d.ts +4 -0
  202. package/lib/browser/math/tiers.js +52 -0
  203. package/lib/browser/math/trade.d.ts +117 -0
  204. package/lib/browser/math/trade.js +637 -0
  205. package/lib/browser/math/userStatus.d.ts +2 -0
  206. package/lib/browser/math/userStatus.js +8 -0
  207. package/lib/browser/math/utils.d.ts +23 -0
  208. package/lib/browser/math/utils.js +112 -0
  209. package/lib/browser/memcmp.d.ts +11 -0
  210. package/lib/browser/memcmp.js +99 -0
  211. package/lib/browser/openbook/openbookV2FulfillmentConfigMap.d.ts +10 -0
  212. package/lib/browser/openbook/openbookV2FulfillmentConfigMap.js +17 -0
  213. package/lib/browser/openbook/openbookV2Subscriber.d.ts +36 -0
  214. package/lib/browser/openbook/openbookV2Subscriber.js +104 -0
  215. package/lib/browser/oracles/oracleClientCache.d.ts +9 -0
  216. package/lib/browser/oracles/oracleClientCache.js +19 -0
  217. package/lib/browser/oracles/oracleId.d.ts +4 -0
  218. package/lib/browser/oracles/oracleId.js +38 -0
  219. package/lib/browser/oracles/prelaunchOracleClient.d.ts +12 -0
  220. package/lib/browser/oracles/prelaunchOracleClient.js +24 -0
  221. package/lib/browser/oracles/pythClient.d.ts +14 -0
  222. package/lib/browser/oracles/pythClient.js +51 -0
  223. package/lib/browser/oracles/pythLazerClient.d.ts +16 -0
  224. package/lib/browser/oracles/pythLazerClient.js +61 -0
  225. package/lib/browser/oracles/pythPullClient.d.ts +19 -0
  226. package/lib/browser/oracles/pythPullClient.js +60 -0
  227. package/lib/browser/oracles/quoteAssetOracleClient.d.ts +10 -0
  228. package/lib/browser/oracles/quoteAssetOracleClient.js +21 -0
  229. package/lib/browser/oracles/strictOraclePrice.d.ts +9 -0
  230. package/lib/browser/oracles/strictOraclePrice.js +17 -0
  231. package/lib/browser/oracles/switchboardClient.d.ts +12 -0
  232. package/lib/browser/oracles/switchboardClient.js +40 -0
  233. package/lib/browser/oracles/switchboardOnDemandClient.d.ts +12 -0
  234. package/lib/browser/oracles/switchboardOnDemandClient.js +32 -0
  235. package/lib/browser/oracles/types.d.ts +23 -0
  236. package/lib/browser/oracles/types.js +2 -0
  237. package/lib/browser/orderParams.d.ts +29 -0
  238. package/lib/browser/orderParams.js +44 -0
  239. package/lib/browser/orderSubscriber/OrderSubscriber.d.ts +42 -0
  240. package/lib/browser/orderSubscriber/OrderSubscriber.js +172 -0
  241. package/lib/browser/orderSubscriber/PollingSubscription.d.ts +12 -0
  242. package/lib/browser/orderSubscriber/PollingSubscription.js +23 -0
  243. package/lib/browser/orderSubscriber/WebsocketSubscription.d.ts +23 -0
  244. package/lib/browser/orderSubscriber/WebsocketSubscription.js +67 -0
  245. package/lib/browser/orderSubscriber/grpcSubscription.d.ts +22 -0
  246. package/lib/browser/orderSubscriber/grpcSubscription.js +66 -0
  247. package/lib/browser/orderSubscriber/index.d.ts +2 -0
  248. package/lib/browser/orderSubscriber/index.js +18 -0
  249. package/lib/browser/orderSubscriber/types.d.ts +34 -0
  250. package/lib/browser/orderSubscriber/types.js +2 -0
  251. package/lib/browser/phoenix/phoenixFulfillmentConfigMap.d.ts +10 -0
  252. package/lib/browser/phoenix/phoenixFulfillmentConfigMap.js +17 -0
  253. package/lib/browser/phoenix/phoenixSubscriber.d.ts +41 -0
  254. package/lib/browser/phoenix/phoenixSubscriber.js +152 -0
  255. package/lib/browser/priorityFee/averageOverSlotsStrategy.d.ts +5 -0
  256. package/lib/browser/priorityFee/averageOverSlotsStrategy.js +16 -0
  257. package/lib/browser/priorityFee/averageStrategy.d.ts +5 -0
  258. package/lib/browser/priorityFee/averageStrategy.js +11 -0
  259. package/lib/browser/priorityFee/driftPriorityFeeMethod.d.ts +13 -0
  260. package/lib/browser/priorityFee/driftPriorityFeeMethod.js +26 -0
  261. package/lib/browser/priorityFee/ewmaStrategy.d.ts +11 -0
  262. package/lib/browser/priorityFee/ewmaStrategy.js +33 -0
  263. package/lib/browser/priorityFee/heliusPriorityFeeMethod.d.ts +20 -0
  264. package/lib/browser/priorityFee/heliusPriorityFeeMethod.js +46 -0
  265. package/lib/browser/priorityFee/index.d.ts +11 -0
  266. package/lib/browser/priorityFee/index.js +27 -0
  267. package/lib/browser/priorityFee/maxOverSlotsStrategy.d.ts +5 -0
  268. package/lib/browser/priorityFee/maxOverSlotsStrategy.js +17 -0
  269. package/lib/browser/priorityFee/maxStrategy.d.ts +7 -0
  270. package/lib/browser/priorityFee/maxStrategy.js +9 -0
  271. package/lib/browser/priorityFee/priorityFeeSubscriber.d.ts +46 -0
  272. package/lib/browser/priorityFee/priorityFeeSubscriber.js +188 -0
  273. package/lib/browser/priorityFee/priorityFeeSubscriberMap.d.ts +48 -0
  274. package/lib/browser/priorityFee/priorityFeeSubscriberMap.js +88 -0
  275. package/lib/browser/priorityFee/solanaPriorityFeeMethod.d.ts +6 -0
  276. package/lib/browser/priorityFee/solanaPriorityFeeMethod.js +21 -0
  277. package/lib/browser/priorityFee/types.d.ts +31 -0
  278. package/lib/browser/priorityFee/types.js +10 -0
  279. package/lib/browser/serum/serumFulfillmentConfigMap.d.ts +10 -0
  280. package/lib/browser/serum/serumFulfillmentConfigMap.js +17 -0
  281. package/lib/browser/serum/serumSubscriber.d.ts +32 -0
  282. package/lib/browser/serum/serumSubscriber.js +107 -0
  283. package/lib/browser/serum/types.d.ts +13 -0
  284. package/lib/browser/serum/types.js +2 -0
  285. package/lib/browser/slot/SlotSubscriber.d.ts +27 -0
  286. package/lib/browser/slot/SlotSubscriber.js +71 -0
  287. package/lib/browser/slot/SlothashSubscriber.d.ts +26 -0
  288. package/lib/browser/slot/SlothashSubscriber.js +85 -0
  289. package/lib/browser/testClient.d.ts +8 -0
  290. package/lib/browser/testClient.js +23 -0
  291. package/lib/browser/token/index.d.ts +5 -0
  292. package/lib/browser/token/index.js +15 -0
  293. package/lib/browser/tokenFaucet.d.ts +41 -0
  294. package/lib/browser/tokenFaucet.js +188 -0
  295. package/lib/browser/tx/baseTxSender.d.ts +59 -0
  296. package/lib/browser/tx/baseTxSender.js +294 -0
  297. package/lib/browser/tx/blockhashFetcher/baseBlockhashFetcher.d.ts +8 -0
  298. package/lib/browser/tx/blockhashFetcher/baseBlockhashFetcher.js +13 -0
  299. package/lib/browser/tx/blockhashFetcher/cachedBlockhashFetcher.d.ts +28 -0
  300. package/lib/browser/tx/blockhashFetcher/cachedBlockhashFetcher.js +73 -0
  301. package/lib/browser/tx/blockhashFetcher/types.d.ts +4 -0
  302. package/lib/browser/tx/blockhashFetcher/types.js +2 -0
  303. package/lib/browser/tx/fastSingleTxSender.d.ts +41 -0
  304. package/lib/browser/tx/fastSingleTxSender.js +86 -0
  305. package/lib/browser/tx/forwardOnlyTxSender.d.ts +37 -0
  306. package/lib/browser/tx/forwardOnlyTxSender.js +92 -0
  307. package/lib/browser/tx/priorityFeeCalculator.d.ts +44 -0
  308. package/lib/browser/tx/priorityFeeCalculator.js +85 -0
  309. package/lib/browser/tx/reportTransactionError.d.ts +20 -0
  310. package/lib/browser/tx/reportTransactionError.js +103 -0
  311. package/lib/browser/tx/retryTxSender.d.ts +37 -0
  312. package/lib/browser/tx/retryTxSender.js +86 -0
  313. package/lib/browser/tx/txHandler.d.ts +154 -0
  314. package/lib/browser/tx/txHandler.js +453 -0
  315. package/lib/browser/tx/txParamProcessor.d.ts +25 -0
  316. package/lib/browser/tx/txParamProcessor.js +88 -0
  317. package/lib/browser/tx/types.d.ts +29 -0
  318. package/lib/browser/tx/types.js +20 -0
  319. package/lib/browser/tx/utils.d.ts +2 -0
  320. package/lib/browser/tx/utils.js +10 -0
  321. package/lib/browser/tx/whileValidTxSender.d.ts +45 -0
  322. package/lib/browser/tx/whileValidTxSender.js +167 -0
  323. package/lib/browser/types.d.ts +1385 -0
  324. package/lib/browser/types.js +366 -0
  325. package/lib/browser/user.d.ts +411 -0
  326. package/lib/browser/user.js +2151 -0
  327. package/lib/browser/userConfig.d.ts +26 -0
  328. package/lib/browser/userConfig.js +2 -0
  329. package/lib/browser/userMap/PollingSubscription.d.ts +16 -0
  330. package/lib/browser/userMap/PollingSubscription.js +30 -0
  331. package/lib/browser/userMap/WebsocketSubscription.d.ts +27 -0
  332. package/lib/browser/userMap/WebsocketSubscription.js +45 -0
  333. package/lib/browser/userMap/grpcSubscription.d.ts +27 -0
  334. package/lib/browser/userMap/grpcSubscription.js +44 -0
  335. package/lib/browser/userMap/referrerMap.d.ts +45 -0
  336. package/lib/browser/userMap/referrerMap.js +181 -0
  337. package/lib/browser/userMap/userMap.d.ts +90 -0
  338. package/lib/browser/userMap/userMap.js +467 -0
  339. package/lib/browser/userMap/userMapConfig.d.ts +39 -0
  340. package/lib/browser/userMap/userMapConfig.js +2 -0
  341. package/lib/browser/userMap/userStatsMap.d.ts +46 -0
  342. package/lib/browser/userMap/userStatsMap.js +165 -0
  343. package/lib/browser/userName.d.ts +5 -0
  344. package/lib/browser/userName.js +21 -0
  345. package/lib/browser/userStats.d.ts +22 -0
  346. package/lib/browser/userStats.js +91 -0
  347. package/lib/browser/userStatsConfig.d.ts +25 -0
  348. package/lib/browser/userStatsConfig.js +2 -0
  349. package/lib/browser/util/TransactionConfirmationManager.d.ts +16 -0
  350. package/lib/browser/util/TransactionConfirmationManager.js +174 -0
  351. package/lib/browser/util/chainClock.d.ts +17 -0
  352. package/lib/browser/util/chainClock.js +29 -0
  353. package/lib/browser/util/computeUnits.d.ts +8 -0
  354. package/lib/browser/util/computeUnits.js +48 -0
  355. package/lib/browser/util/digest.d.ts +4 -0
  356. package/lib/browser/util/digest.js +14 -0
  357. package/lib/browser/util/promiseTimeout.d.ts +1 -0
  358. package/lib/browser/util/promiseTimeout.js +14 -0
  359. package/lib/browser/util/pythOracleUtils.d.ts +17 -0
  360. package/lib/browser/util/pythOracleUtils.js +107 -0
  361. package/lib/browser/util/tps.d.ts +2 -0
  362. package/lib/browser/util/tps.js +16 -0
  363. package/lib/browser/wallet.d.ts +11 -0
  364. package/lib/browser/wallet.js +32 -0
  365. package/lib/node/accounts/basicUserAccountSubscriber.d.ts +27 -0
  366. package/lib/node/accounts/basicUserAccountSubscriber.js +38 -0
  367. package/lib/node/accounts/bulkAccountLoader.d.ts +37 -0
  368. package/lib/node/accounts/bulkAccountLoader.js +222 -0
  369. package/lib/node/accounts/bulkUserStatsSubscription.d.ts +7 -0
  370. package/lib/node/accounts/bulkUserStatsSubscription.js +21 -0
  371. package/lib/node/accounts/bulkUserSubscription.d.ts +7 -0
  372. package/lib/node/accounts/bulkUserSubscription.js +21 -0
  373. package/lib/node/accounts/fetch.d.ts +6 -0
  374. package/lib/node/accounts/fetch.js +30 -0
  375. package/lib/node/accounts/grpcAccountSubscriber.d.ts +16 -0
  376. package/lib/node/accounts/grpcAccountSubscriber.js +154 -0
  377. package/lib/node/accounts/grpcDriftClientAccountSubscriber.d.ts +12 -0
  378. package/lib/node/accounts/grpcDriftClientAccountSubscriber.js +98 -0
  379. package/lib/node/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +10 -0
  380. package/lib/node/accounts/grpcInsuranceFundStakeAccountSubscriber.js +30 -0
  381. package/lib/node/accounts/grpcProgramAccountSubscriber.d.ts +18 -0
  382. package/lib/node/accounts/grpcProgramAccountSubscriber.js +171 -0
  383. package/lib/node/accounts/grpcUserAccountSubscriber.d.ts +10 -0
  384. package/lib/node/accounts/grpcUserAccountSubscriber.js +28 -0
  385. package/lib/node/accounts/grpcUserStatsAccountSubscriber.d.ts +10 -0
  386. package/lib/node/accounts/grpcUserStatsAccountSubscriber.js +28 -0
  387. package/lib/node/accounts/oneShotUserAccountSubscriber.d.ts +18 -0
  388. package/lib/node/accounts/oneShotUserAccountSubscriber.js +48 -0
  389. package/lib/node/accounts/pollingDriftClientAccountSubscriber.d.ts +69 -0
  390. package/lib/node/accounts/pollingDriftClientAccountSubscriber.js +418 -0
  391. package/lib/node/accounts/pollingHighLeverageModeConfigAccountSubscriber.d.ts +29 -0
  392. package/lib/node/accounts/pollingHighLeverageModeConfigAccountSubscriber.js +111 -0
  393. package/lib/node/accounts/pollingInsuranceFundStakeAccountSubscriber.d.ts +29 -0
  394. package/lib/node/accounts/pollingInsuranceFundStakeAccountSubscriber.js +110 -0
  395. package/lib/node/accounts/pollingOracleAccountSubscriber.d.ts +27 -0
  396. package/lib/node/accounts/pollingOracleAccountSubscriber.js +78 -0
  397. package/lib/node/accounts/pollingTokenAccountSubscriber.d.ts +26 -0
  398. package/lib/node/accounts/pollingTokenAccountSubscriber.js +78 -0
  399. package/lib/node/accounts/pollingUserAccountSubscriber.d.ts +29 -0
  400. package/lib/node/accounts/pollingUserAccountSubscriber.js +102 -0
  401. package/lib/node/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  402. package/lib/node/accounts/pollingUserStatsAccountSubscriber.js +94 -0
  403. package/lib/node/accounts/testBulkAccountLoader.d.ts +4 -0
  404. package/lib/node/accounts/testBulkAccountLoader.js +45 -0
  405. package/lib/node/accounts/types.d.ts +168 -0
  406. package/lib/node/accounts/types.js +16 -0
  407. package/lib/node/accounts/utils.d.ts +8 -0
  408. package/lib/node/accounts/utils.js +49 -0
  409. package/lib/node/accounts/webSocketAccountSubscriber.d.ts +29 -0
  410. package/lib/node/accounts/webSocketAccountSubscriber.js +149 -0
  411. package/lib/node/accounts/webSocketDriftClientAccountSubscriber.d.ts +66 -0
  412. package/lib/node/accounts/webSocketDriftClientAccountSubscriber.js +358 -0
  413. package/lib/node/accounts/webSocketHighLeverageModeConfigAccountSubscriber.d.ts +23 -0
  414. package/lib/node/accounts/webSocketHighLeverageModeConfigAccountSubscriber.js +69 -0
  415. package/lib/node/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +23 -0
  416. package/lib/node/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +67 -0
  417. package/lib/node/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
  418. package/lib/node/accounts/webSocketProgramAccountSubscriber.js +120 -0
  419. package/lib/node/accounts/webSocketUserAccountSubscriber.d.ts +23 -0
  420. package/lib/node/accounts/webSocketUserAccountSubscriber.js +61 -0
  421. package/lib/node/accounts/webSocketUserStatsAccountSubsriber.d.ts +22 -0
  422. package/lib/node/accounts/webSocketUserStatsAccountSubsriber.js +52 -0
  423. package/lib/node/addresses/marketAddresses.d.ts +2 -0
  424. package/lib/node/addresses/marketAddresses.js +15 -0
  425. package/lib/node/addresses/pda.d.ts +32 -0
  426. package/lib/node/addresses/pda.js +211 -0
  427. package/lib/node/adminClient.d.ts +206 -0
  428. package/lib/node/adminClient.js +1858 -0
  429. package/lib/node/assert/assert.d.ts +1 -0
  430. package/lib/node/assert/assert.js +9 -0
  431. package/lib/node/auctionSubscriber/auctionSubscriber.d.ts +14 -0
  432. package/lib/node/auctionSubscriber/auctionSubscriber.js +32 -0
  433. package/lib/node/auctionSubscriber/auctionSubscriberGrpc.d.ts +15 -0
  434. package/lib/node/auctionSubscriber/auctionSubscriberGrpc.js +32 -0
  435. package/lib/node/auctionSubscriber/index.d.ts +3 -0
  436. package/lib/node/auctionSubscriber/index.js +19 -0
  437. package/lib/node/auctionSubscriber/types.d.ts +14 -0
  438. package/lib/node/auctionSubscriber/types.js +2 -0
  439. package/lib/node/bankrun/bankrunConnection.d.ts +75 -0
  440. package/lib/node/bankrun/bankrunConnection.js +332 -0
  441. package/lib/node/blockhashSubscriber/BlockhashSubscriber.d.ts +27 -0
  442. package/lib/node/blockhashSubscriber/BlockhashSubscriber.js +89 -0
  443. package/lib/node/blockhashSubscriber/index.d.ts +1 -0
  444. package/lib/node/blockhashSubscriber/index.js +17 -0
  445. package/lib/node/blockhashSubscriber/types.d.ts +7 -0
  446. package/lib/node/blockhashSubscriber/types.js +2 -0
  447. package/lib/node/clock/clockSubscriber.d.ts +31 -0
  448. package/lib/node/clock/clockSubscriber.js +80 -0
  449. package/lib/node/config.d.ts +60 -0
  450. package/lib/node/config.js +130 -0
  451. package/lib/node/constants/numericConstants.d.ts +71 -0
  452. package/lib/node/constants/numericConstants.js +75 -0
  453. package/lib/node/constants/perpMarkets.d.ts +19 -0
  454. package/lib/node/constants/perpMarkets.js +997 -0
  455. package/lib/node/constants/spotMarkets.d.ts +24 -0
  456. package/lib/node/constants/spotMarkets.js +470 -0
  457. package/lib/node/constants/txConstants.d.ts +1 -0
  458. package/lib/node/constants/txConstants.js +4 -0
  459. package/lib/node/decode/phoenix.d.ts +7 -0
  460. package/lib/node/decode/phoenix.js +159 -0
  461. package/lib/node/decode/user.d.ts +4 -0
  462. package/lib/node/decode/user.js +339 -0
  463. package/lib/node/dlob/DLOB.d.ts +186 -0
  464. package/lib/node/dlob/DLOB.js +1039 -0
  465. package/lib/node/dlob/DLOBNode.d.ts +68 -0
  466. package/lib/node/dlob/DLOBNode.js +100 -0
  467. package/lib/node/dlob/DLOBSubscriber.d.ts +54 -0
  468. package/lib/node/dlob/DLOBSubscriber.js +139 -0
  469. package/lib/node/dlob/NodeList.d.ts +25 -0
  470. package/lib/node/dlob/NodeList.js +126 -0
  471. package/lib/node/dlob/orderBookLevels.d.ts +72 -0
  472. package/lib/node/dlob/orderBookLevels.js +438 -0
  473. package/lib/node/dlob/types.d.ts +18 -0
  474. package/lib/node/dlob/types.js +2 -0
  475. package/lib/node/driftClient.d.ts +861 -0
  476. package/lib/node/driftClient.js +4768 -0
  477. package/lib/node/driftClientConfig.d.ts +49 -0
  478. package/lib/node/driftClientConfig.js +2 -0
  479. package/lib/node/events/eventList.d.ts +22 -0
  480. package/lib/node/events/eventList.js +80 -0
  481. package/lib/node/events/eventSubscriber.d.ts +46 -0
  482. package/lib/node/events/eventSubscriber.js +223 -0
  483. package/lib/node/events/eventsServerLogProvider.d.ts +21 -0
  484. package/lib/node/events/eventsServerLogProvider.js +121 -0
  485. package/lib/node/events/fetchLogs.d.ts +25 -0
  486. package/lib/node/events/fetchLogs.js +99 -0
  487. package/lib/node/events/parse.d.ts +6 -0
  488. package/lib/node/events/parse.js +106 -0
  489. package/lib/node/events/pollingLogProvider.d.ts +17 -0
  490. package/lib/node/events/pollingLogProvider.js +58 -0
  491. package/lib/node/events/sort.d.ts +2 -0
  492. package/lib/node/events/sort.js +24 -0
  493. package/lib/node/events/txEventCache.d.ts +24 -0
  494. package/lib/node/events/txEventCache.js +71 -0
  495. package/lib/node/events/types.d.ts +79 -0
  496. package/lib/node/events/types.js +32 -0
  497. package/lib/node/events/webSocketLogProvider.d.ts +24 -0
  498. package/lib/node/events/webSocketLogProvider.js +96 -0
  499. package/lib/node/factory/bigNum.d.ts +122 -0
  500. package/lib/node/factory/bigNum.js +457 -0
  501. package/lib/node/factory/oracleClient.d.ts +5 -0
  502. package/lib/node/factory/oracleClient.js +56 -0
  503. package/lib/node/idl/drift.json +14440 -0
  504. package/lib/node/idl/openbook.json +3854 -0
  505. package/lib/node/idl/pyth_solana_receiver.json +628 -0
  506. package/lib/node/idl/switchboard.json +8354 -0
  507. package/lib/node/idl/switchboard_on_demand_30.json +4546 -0
  508. package/lib/node/idl/token_faucet.json +142 -0
  509. package/lib/node/index.d.ts +125 -0
  510. package/lib/node/index.js +147 -0
  511. package/lib/node/isomorphic/grpc.browser.d.ts +1 -0
  512. package/lib/node/isomorphic/grpc.browser.js +8 -0
  513. package/lib/node/isomorphic/grpc.d.ts +1 -0
  514. package/lib/node/isomorphic/grpc.js +8 -0
  515. package/lib/node/jupiter/jupiterClient.d.ts +302 -0
  516. package/lib/node/jupiter/jupiterClient.js +178 -0
  517. package/lib/node/keypair.d.ts +2 -0
  518. package/lib/node/keypair.js +28 -0
  519. package/lib/node/marinade/index.d.ts +12 -0
  520. package/lib/node/marinade/index.js +36 -0
  521. package/lib/node/marinade/types.d.ts +1963 -0
  522. package/lib/node/marinade/types.js +1965 -0
  523. package/lib/node/math/amm.d.ts +98 -0
  524. package/lib/node/math/amm.js +626 -0
  525. package/lib/node/math/auction.d.ts +23 -0
  526. package/lib/node/math/auction.js +130 -0
  527. package/lib/node/math/bankruptcy.d.ts +2 -0
  528. package/lib/node/math/bankruptcy.js +31 -0
  529. package/lib/node/math/conversion.d.ts +2 -0
  530. package/lib/node/math/conversion.js +11 -0
  531. package/lib/node/math/exchangeStatus.d.ts +6 -0
  532. package/lib/node/math/exchangeStatus.js +77 -0
  533. package/lib/node/math/fuel.d.ts +6 -0
  534. package/lib/node/math/fuel.js +55 -0
  535. package/lib/node/math/funding.d.ts +34 -0
  536. package/lib/node/math/funding.js +209 -0
  537. package/lib/node/math/insurance.d.ts +7 -0
  538. package/lib/node/math/insurance.js +73 -0
  539. package/lib/node/math/margin.d.ts +39 -0
  540. package/lib/node/math/margin.js +184 -0
  541. package/lib/node/math/market.d.ts +39 -0
  542. package/lib/node/math/market.js +163 -0
  543. package/lib/node/math/oracles.d.ts +14 -0
  544. package/lib/node/math/oracles.js +134 -0
  545. package/lib/node/math/orders.d.ts +23 -0
  546. package/lib/node/math/orders.js +216 -0
  547. package/lib/node/math/position.d.ts +70 -0
  548. package/lib/node/math/position.js +225 -0
  549. package/lib/node/math/repeg.d.ts +22 -0
  550. package/lib/node/math/repeg.js +164 -0
  551. package/lib/node/math/spotBalance.d.ts +83 -0
  552. package/lib/node/math/spotBalance.js +373 -0
  553. package/lib/node/math/spotMarket.d.ts +11 -0
  554. package/lib/node/math/spotMarket.js +49 -0
  555. package/lib/node/math/spotPosition.d.ts +19 -0
  556. package/lib/node/math/spotPosition.js +78 -0
  557. package/lib/node/math/state.d.ts +5 -0
  558. package/lib/node/math/state.js +30 -0
  559. package/lib/node/math/superStake.d.ts +167 -0
  560. package/lib/node/math/superStake.js +306 -0
  561. package/lib/node/math/tiers.d.ts +4 -0
  562. package/lib/node/math/tiers.js +52 -0
  563. package/lib/node/math/trade.d.ts +117 -0
  564. package/lib/node/math/trade.js +637 -0
  565. package/lib/node/math/userStatus.d.ts +2 -0
  566. package/lib/node/math/userStatus.js +8 -0
  567. package/lib/node/math/utils.d.ts +23 -0
  568. package/lib/node/math/utils.js +112 -0
  569. package/lib/node/memcmp.d.ts +11 -0
  570. package/lib/node/memcmp.js +99 -0
  571. package/lib/node/openbook/openbookV2FulfillmentConfigMap.d.ts +10 -0
  572. package/lib/node/openbook/openbookV2FulfillmentConfigMap.js +17 -0
  573. package/lib/node/openbook/openbookV2Subscriber.d.ts +36 -0
  574. package/lib/node/openbook/openbookV2Subscriber.js +104 -0
  575. package/lib/node/oracles/oracleClientCache.d.ts +9 -0
  576. package/lib/node/oracles/oracleClientCache.js +19 -0
  577. package/lib/node/oracles/oracleId.d.ts +4 -0
  578. package/lib/node/oracles/oracleId.js +38 -0
  579. package/lib/node/oracles/prelaunchOracleClient.d.ts +12 -0
  580. package/lib/node/oracles/prelaunchOracleClient.js +24 -0
  581. package/lib/node/oracles/pythClient.d.ts +14 -0
  582. package/lib/node/oracles/pythClient.js +51 -0
  583. package/lib/node/oracles/pythLazerClient.d.ts +16 -0
  584. package/lib/node/oracles/pythLazerClient.js +61 -0
  585. package/lib/node/oracles/pythPullClient.d.ts +19 -0
  586. package/lib/node/oracles/pythPullClient.js +60 -0
  587. package/lib/node/oracles/quoteAssetOracleClient.d.ts +10 -0
  588. package/lib/node/oracles/quoteAssetOracleClient.js +21 -0
  589. package/lib/node/oracles/strictOraclePrice.d.ts +9 -0
  590. package/lib/node/oracles/strictOraclePrice.js +17 -0
  591. package/lib/node/oracles/switchboardClient.d.ts +12 -0
  592. package/lib/node/oracles/switchboardClient.js +40 -0
  593. package/lib/node/oracles/switchboardOnDemandClient.d.ts +12 -0
  594. package/lib/node/oracles/switchboardOnDemandClient.js +32 -0
  595. package/lib/node/oracles/types.d.ts +23 -0
  596. package/lib/node/oracles/types.js +2 -0
  597. package/lib/node/orderParams.d.ts +29 -0
  598. package/lib/node/orderParams.js +44 -0
  599. package/lib/node/orderSubscriber/OrderSubscriber.d.ts +42 -0
  600. package/lib/node/orderSubscriber/OrderSubscriber.js +172 -0
  601. package/lib/node/orderSubscriber/PollingSubscription.d.ts +12 -0
  602. package/lib/node/orderSubscriber/PollingSubscription.js +23 -0
  603. package/lib/node/orderSubscriber/WebsocketSubscription.d.ts +23 -0
  604. package/lib/node/orderSubscriber/WebsocketSubscription.js +67 -0
  605. package/lib/node/orderSubscriber/grpcSubscription.d.ts +22 -0
  606. package/lib/node/orderSubscriber/grpcSubscription.js +66 -0
  607. package/lib/node/orderSubscriber/index.d.ts +2 -0
  608. package/lib/node/orderSubscriber/index.js +18 -0
  609. package/lib/node/orderSubscriber/types.d.ts +34 -0
  610. package/lib/node/orderSubscriber/types.js +2 -0
  611. package/lib/node/phoenix/phoenixFulfillmentConfigMap.d.ts +10 -0
  612. package/lib/node/phoenix/phoenixFulfillmentConfigMap.js +17 -0
  613. package/lib/node/phoenix/phoenixSubscriber.d.ts +41 -0
  614. package/lib/node/phoenix/phoenixSubscriber.js +152 -0
  615. package/lib/node/priorityFee/averageOverSlotsStrategy.d.ts +5 -0
  616. package/lib/node/priorityFee/averageOverSlotsStrategy.js +16 -0
  617. package/lib/node/priorityFee/averageStrategy.d.ts +5 -0
  618. package/lib/node/priorityFee/averageStrategy.js +11 -0
  619. package/lib/node/priorityFee/driftPriorityFeeMethod.d.ts +13 -0
  620. package/lib/node/priorityFee/driftPriorityFeeMethod.js +26 -0
  621. package/lib/node/priorityFee/ewmaStrategy.d.ts +11 -0
  622. package/lib/node/priorityFee/ewmaStrategy.js +33 -0
  623. package/lib/node/priorityFee/heliusPriorityFeeMethod.d.ts +20 -0
  624. package/lib/node/priorityFee/heliusPriorityFeeMethod.js +46 -0
  625. package/lib/node/priorityFee/index.d.ts +11 -0
  626. package/lib/node/priorityFee/index.js +27 -0
  627. package/lib/node/priorityFee/maxOverSlotsStrategy.d.ts +5 -0
  628. package/lib/node/priorityFee/maxOverSlotsStrategy.js +17 -0
  629. package/lib/node/priorityFee/maxStrategy.d.ts +7 -0
  630. package/lib/node/priorityFee/maxStrategy.js +9 -0
  631. package/lib/node/priorityFee/priorityFeeSubscriber.d.ts +46 -0
  632. package/lib/node/priorityFee/priorityFeeSubscriber.js +188 -0
  633. package/lib/node/priorityFee/priorityFeeSubscriberMap.d.ts +48 -0
  634. package/lib/node/priorityFee/priorityFeeSubscriberMap.js +88 -0
  635. package/lib/node/priorityFee/solanaPriorityFeeMethod.d.ts +6 -0
  636. package/lib/node/priorityFee/solanaPriorityFeeMethod.js +21 -0
  637. package/lib/node/priorityFee/types.d.ts +31 -0
  638. package/lib/node/priorityFee/types.js +10 -0
  639. package/lib/node/serum/serumFulfillmentConfigMap.d.ts +10 -0
  640. package/lib/node/serum/serumFulfillmentConfigMap.js +17 -0
  641. package/lib/node/serum/serumSubscriber.d.ts +32 -0
  642. package/lib/node/serum/serumSubscriber.js +107 -0
  643. package/lib/node/serum/types.d.ts +13 -0
  644. package/lib/node/serum/types.js +2 -0
  645. package/lib/node/slot/SlotSubscriber.d.ts +27 -0
  646. package/lib/node/slot/SlotSubscriber.js +71 -0
  647. package/lib/node/slot/SlothashSubscriber.d.ts +26 -0
  648. package/lib/node/slot/SlothashSubscriber.js +85 -0
  649. package/lib/node/testClient.d.ts +8 -0
  650. package/lib/node/testClient.js +23 -0
  651. package/lib/node/token/index.d.ts +5 -0
  652. package/lib/node/token/index.js +15 -0
  653. package/lib/node/tokenFaucet.d.ts +41 -0
  654. package/lib/node/tokenFaucet.js +188 -0
  655. package/lib/node/tx/baseTxSender.d.ts +59 -0
  656. package/lib/node/tx/baseTxSender.js +294 -0
  657. package/lib/node/tx/blockhashFetcher/baseBlockhashFetcher.d.ts +8 -0
  658. package/lib/node/tx/blockhashFetcher/baseBlockhashFetcher.js +13 -0
  659. package/lib/node/tx/blockhashFetcher/cachedBlockhashFetcher.d.ts +28 -0
  660. package/lib/node/tx/blockhashFetcher/cachedBlockhashFetcher.js +73 -0
  661. package/lib/node/tx/blockhashFetcher/types.d.ts +4 -0
  662. package/lib/node/tx/blockhashFetcher/types.js +2 -0
  663. package/lib/node/tx/fastSingleTxSender.d.ts +41 -0
  664. package/lib/node/tx/fastSingleTxSender.js +86 -0
  665. package/lib/node/tx/forwardOnlyTxSender.d.ts +37 -0
  666. package/lib/node/tx/forwardOnlyTxSender.js +92 -0
  667. package/lib/node/tx/priorityFeeCalculator.d.ts +44 -0
  668. package/lib/node/tx/priorityFeeCalculator.js +85 -0
  669. package/lib/node/tx/reportTransactionError.d.ts +20 -0
  670. package/lib/node/tx/reportTransactionError.js +103 -0
  671. package/lib/node/tx/retryTxSender.d.ts +37 -0
  672. package/lib/node/tx/retryTxSender.js +86 -0
  673. package/lib/node/tx/txHandler.d.ts +154 -0
  674. package/lib/node/tx/txHandler.js +453 -0
  675. package/lib/node/tx/txParamProcessor.d.ts +25 -0
  676. package/lib/node/tx/txParamProcessor.js +88 -0
  677. package/lib/node/tx/types.d.ts +29 -0
  678. package/lib/node/tx/types.js +20 -0
  679. package/lib/node/tx/utils.d.ts +2 -0
  680. package/lib/node/tx/utils.js +10 -0
  681. package/lib/node/tx/whileValidTxSender.d.ts +45 -0
  682. package/lib/node/tx/whileValidTxSender.js +167 -0
  683. package/lib/node/types.d.ts +1385 -0
  684. package/lib/node/types.js +366 -0
  685. package/lib/node/user.d.ts +411 -0
  686. package/lib/node/user.js +2151 -0
  687. package/lib/node/userConfig.d.ts +26 -0
  688. package/lib/node/userConfig.js +2 -0
  689. package/lib/node/userMap/PollingSubscription.d.ts +16 -0
  690. package/lib/node/userMap/PollingSubscription.js +30 -0
  691. package/lib/node/userMap/WebsocketSubscription.d.ts +27 -0
  692. package/lib/node/userMap/WebsocketSubscription.js +45 -0
  693. package/lib/node/userMap/grpcSubscription.d.ts +27 -0
  694. package/lib/node/userMap/grpcSubscription.js +44 -0
  695. package/lib/node/userMap/referrerMap.d.ts +45 -0
  696. package/lib/node/userMap/referrerMap.js +181 -0
  697. package/lib/node/userMap/userMap.d.ts +90 -0
  698. package/lib/node/userMap/userMap.js +467 -0
  699. package/lib/node/userMap/userMapConfig.d.ts +39 -0
  700. package/lib/node/userMap/userMapConfig.js +2 -0
  701. package/lib/node/userMap/userStatsMap.d.ts +46 -0
  702. package/lib/node/userMap/userStatsMap.js +165 -0
  703. package/lib/node/userName.d.ts +5 -0
  704. package/lib/node/userName.js +21 -0
  705. package/lib/node/userStats.d.ts +22 -0
  706. package/lib/node/userStats.js +91 -0
  707. package/lib/node/userStatsConfig.d.ts +25 -0
  708. package/lib/node/userStatsConfig.js +2 -0
  709. package/lib/node/util/TransactionConfirmationManager.d.ts +16 -0
  710. package/lib/node/util/TransactionConfirmationManager.js +174 -0
  711. package/lib/node/util/chainClock.d.ts +17 -0
  712. package/lib/node/util/chainClock.js +29 -0
  713. package/lib/node/util/computeUnits.d.ts +8 -0
  714. package/lib/node/util/computeUnits.js +48 -0
  715. package/lib/node/util/digest.d.ts +4 -0
  716. package/lib/node/util/digest.js +14 -0
  717. package/lib/node/util/promiseTimeout.d.ts +1 -0
  718. package/lib/node/util/promiseTimeout.js +14 -0
  719. package/lib/node/util/pythOracleUtils.d.ts +17 -0
  720. package/lib/node/util/pythOracleUtils.js +107 -0
  721. package/lib/node/util/tps.d.ts +2 -0
  722. package/lib/node/util/tps.js +16 -0
  723. package/lib/node/wallet.d.ts +11 -0
  724. package/lib/node/wallet.js +32 -0
  725. package/package.json +92 -0
  726. package/scripts/postbuild.js +95 -0
  727. package/scripts/updateVersion.js +28 -0
  728. package/src/accounts/basicUserAccountSubscriber.ts +59 -0
  729. package/src/accounts/bulkAccountLoader.ts +294 -0
  730. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  731. package/src/accounts/bulkUserSubscription.ts +33 -0
  732. package/src/accounts/fetch.ts +66 -0
  733. package/src/accounts/grpcAccountSubscriber.ts +160 -0
  734. package/src/accounts/grpcDriftClientAccountSubscriber.ts +203 -0
  735. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +56 -0
  736. package/src/accounts/grpcProgramAccountSubscriber.ts +190 -0
  737. package/src/accounts/grpcUserAccountSubscriber.ts +48 -0
  738. package/src/accounts/grpcUserStatsAccountSubscriber.ts +50 -0
  739. package/src/accounts/oneShotUserAccountSubscriber.ts +68 -0
  740. package/src/accounts/pollingDriftClientAccountSubscriber.ts +644 -0
  741. package/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts +189 -0
  742. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +185 -0
  743. package/src/accounts/pollingOracleAccountSubscriber.ts +125 -0
  744. package/src/accounts/pollingTokenAccountSubscriber.ts +118 -0
  745. package/src/accounts/pollingUserAccountSubscriber.ts +160 -0
  746. package/src/accounts/pollingUserStatsAccountSubscriber.ts +156 -0
  747. package/src/accounts/testBulkAccountLoader.ts +53 -0
  748. package/src/accounts/types.ts +245 -0
  749. package/src/accounts/utils.ts +62 -0
  750. package/src/accounts/webSocketAccountSubscriber.ts +205 -0
  751. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +621 -0
  752. package/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts +131 -0
  753. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +129 -0
  754. package/src/accounts/webSocketProgramAccountSubscriber.ts +182 -0
  755. package/src/accounts/webSocketUserAccountSubscriber.ts +104 -0
  756. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +98 -0
  757. package/src/addresses/marketAddresses.ts +17 -0
  758. package/src/addresses/pda.ts +355 -0
  759. package/src/adminClient.ts +4030 -0
  760. package/src/assert/assert.ts +5 -0
  761. package/src/auctionSubscriber/auctionSubscriber.ts +66 -0
  762. package/src/auctionSubscriber/auctionSubscriberGrpc.ts +70 -0
  763. package/src/auctionSubscriber/index.ts +3 -0
  764. package/src/auctionSubscriber/types.ts +20 -0
  765. package/src/bankrun/bankrunConnection.ts +534 -0
  766. package/src/blockhashSubscriber/BlockhashSubscriber.ts +126 -0
  767. package/src/blockhashSubscriber/index.ts +1 -0
  768. package/src/blockhashSubscriber/types.ts +12 -0
  769. package/src/clock/clockSubscriber.ts +121 -0
  770. package/src/config.ts +212 -0
  771. package/src/constants/numericConstants.ts +113 -0
  772. package/src/constants/perpMarkets.ts +1084 -0
  773. package/src/constants/spotMarkets.ts +565 -0
  774. package/src/constants/txConstants.ts +1 -0
  775. package/src/decode/phoenix.ts +207 -0
  776. package/src/decode/user.ts +368 -0
  777. package/src/dlob/DLOB.ts +1897 -0
  778. package/src/dlob/DLOBNode.ts +197 -0
  779. package/src/dlob/DLOBSubscriber.ts +201 -0
  780. package/src/dlob/NodeList.ts +173 -0
  781. package/src/dlob/orderBookLevels.ts +643 -0
  782. package/src/dlob/types.ts +22 -0
  783. package/src/driftClient.ts +9032 -0
  784. package/src/driftClientConfig.ts +60 -0
  785. package/src/events/eventList.ts +97 -0
  786. package/src/events/eventSubscriber.ts +364 -0
  787. package/src/events/eventsServerLogProvider.ts +152 -0
  788. package/src/events/fetchLogs.ts +169 -0
  789. package/src/events/parse.ts +133 -0
  790. package/src/events/pollingLogProvider.ts +89 -0
  791. package/src/events/sort.ts +39 -0
  792. package/src/events/txEventCache.ts +74 -0
  793. package/src/events/types.ts +185 -0
  794. package/src/events/webSocketLogProvider.ts +121 -0
  795. package/src/factory/bigNum.ts +660 -0
  796. package/src/factory/oracleClient.ts +72 -0
  797. package/src/idl/drift.json +14440 -0
  798. package/src/idl/openbook.json +3854 -0
  799. package/src/idl/pyth.json +142 -0
  800. package/src/idl/pyth_solana_receiver.json +628 -0
  801. package/src/idl/switchboard.json +8354 -0
  802. package/src/idl/switchboard_on_demand_30.json +4546 -0
  803. package/src/idl/token_faucet.json +142 -0
  804. package/src/index.ts +127 -0
  805. package/src/isomorphic/README.md +19 -0
  806. package/src/isomorphic/grpc.browser.ts +4 -0
  807. package/src/isomorphic/grpc.node.ts +23 -0
  808. package/src/isomorphic/grpc.ts +1 -0
  809. package/src/jupiter/jupiterClient.ts +510 -0
  810. package/src/keypair.ts +24 -0
  811. package/src/marinade/idl/idl.json +1962 -0
  812. package/src/marinade/index.ts +64 -0
  813. package/src/marinade/types.ts +3925 -0
  814. package/src/math/amm.ts +1162 -0
  815. package/src/math/auction.ts +173 -0
  816. package/src/math/bankruptcy.ts +34 -0
  817. package/src/math/conversion.ts +13 -0
  818. package/src/math/exchangeStatus.ts +121 -0
  819. package/src/math/fuel.ts +70 -0
  820. package/src/math/funding.ts +342 -0
  821. package/src/math/insurance.ts +110 -0
  822. package/src/math/margin.ts +340 -0
  823. package/src/math/market.ts +336 -0
  824. package/src/math/oracles.ts +228 -0
  825. package/src/math/orders.ts +343 -0
  826. package/src/math/position.ts +324 -0
  827. package/src/math/repeg.ts +214 -0
  828. package/src/math/spotBalance.ts +630 -0
  829. package/src/math/spotMarket.ts +82 -0
  830. package/src/math/spotPosition.ts +184 -0
  831. package/src/math/state.ts +29 -0
  832. package/src/math/superStake.ts +525 -0
  833. package/src/math/tiers.ts +44 -0
  834. package/src/math/trade.ts +993 -0
  835. package/src/math/userStatus.ts +5 -0
  836. package/src/math/utils.ts +120 -0
  837. package/src/memcmp.ts +94 -0
  838. package/src/openbook/openbookV2FulfillmentConfigMap.ts +29 -0
  839. package/src/openbook/openbookV2Subscriber.ts +165 -0
  840. package/src/oracles/oracleClientCache.ts +25 -0
  841. package/src/oracles/oracleId.ts +28 -0
  842. package/src/oracles/prelaunchOracleClient.ts +37 -0
  843. package/src/oracles/pythClient.ts +85 -0
  844. package/src/oracles/pythLazerClient.ts +102 -0
  845. package/src/oracles/pythPullClient.ts +111 -0
  846. package/src/oracles/quoteAssetOracleClient.ts +25 -0
  847. package/src/oracles/strictOraclePrice.ts +19 -0
  848. package/src/oracles/switchboardClient.ts +77 -0
  849. package/src/oracles/switchboardOnDemandClient.ts +56 -0
  850. package/src/oracles/types.ts +23 -0
  851. package/src/orderParams.ts +79 -0
  852. package/src/orderSubscriber/OrderSubscriber.ts +249 -0
  853. package/src/orderSubscriber/PollingSubscription.ts +39 -0
  854. package/src/orderSubscriber/WebsocketSubscription.ts +119 -0
  855. package/src/orderSubscriber/grpcSubscription.ts +121 -0
  856. package/src/orderSubscriber/index.ts +2 -0
  857. package/src/orderSubscriber/types.ts +54 -0
  858. package/src/phoenix/phoenixFulfillmentConfigMap.ts +26 -0
  859. package/src/phoenix/phoenixSubscriber.ts +235 -0
  860. package/src/priorityFee/averageOverSlotsStrategy.ts +16 -0
  861. package/src/priorityFee/averageStrategy.ts +12 -0
  862. package/src/priorityFee/driftPriorityFeeMethod.ts +42 -0
  863. package/src/priorityFee/ewmaStrategy.ts +41 -0
  864. package/src/priorityFee/heliusPriorityFeeMethod.ts +57 -0
  865. package/src/priorityFee/index.ts +11 -0
  866. package/src/priorityFee/maxOverSlotsStrategy.ts +17 -0
  867. package/src/priorityFee/maxStrategy.ts +7 -0
  868. package/src/priorityFee/priorityFeeSubscriber.ts +251 -0
  869. package/src/priorityFee/priorityFeeSubscriberMap.ts +112 -0
  870. package/src/priorityFee/solanaPriorityFeeMethod.ts +34 -0
  871. package/src/priorityFee/types.ts +60 -0
  872. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  873. package/src/serum/serumSubscriber.ts +169 -0
  874. package/src/serum/types.ts +17 -0
  875. package/src/slot/SlotSubscriber.ts +101 -0
  876. package/src/slot/SlothashSubscriber.ts +126 -0
  877. package/src/testClient.ts +41 -0
  878. package/src/token/index.ts +13 -0
  879. package/src/tokenFaucet.ts +269 -0
  880. package/src/tx/baseTxSender.ts +477 -0
  881. package/src/tx/blockhashFetcher/baseBlockhashFetcher.ts +19 -0
  882. package/src/tx/blockhashFetcher/cachedBlockhashFetcher.ts +90 -0
  883. package/src/tx/blockhashFetcher/types.ts +5 -0
  884. package/src/tx/fastSingleTxSender.ts +142 -0
  885. package/src/tx/forwardOnlyTxSender.ts +145 -0
  886. package/src/tx/priorityFeeCalculator.ts +117 -0
  887. package/src/tx/reportTransactionError.ts +159 -0
  888. package/src/tx/retryTxSender.ts +135 -0
  889. package/src/tx/txHandler.ts +737 -0
  890. package/src/tx/txParamProcessor.ts +155 -0
  891. package/src/tx/types.ts +71 -0
  892. package/src/tx/utils.ts +11 -0
  893. package/src/tx/whileValidTxSender.ts +265 -0
  894. package/src/types.ts +1386 -0
  895. package/src/user.ts +4054 -0
  896. package/src/userConfig.ts +32 -0
  897. package/src/userMap/PollingSubscription.ts +47 -0
  898. package/src/userMap/WebsocketSubscription.ts +84 -0
  899. package/src/userMap/grpcSubscription.ts +85 -0
  900. package/src/userMap/referrerMap.ts +267 -0
  901. package/src/userMap/userMap.ts +654 -0
  902. package/src/userMap/userMapConfig.ts +63 -0
  903. package/src/userMap/userStatsMap.ts +218 -0
  904. package/src/userName.ts +21 -0
  905. package/src/userStats.ts +174 -0
  906. package/src/userStatsConfig.ts +31 -0
  907. package/src/util/TransactionConfirmationManager.ts +292 -0
  908. package/src/util/chainClock.ts +41 -0
  909. package/src/util/computeUnits.ts +65 -0
  910. package/src/util/digest.ts +11 -0
  911. package/src/util/promiseTimeout.ts +14 -0
  912. package/src/util/pythOracleUtils.ts +136 -0
  913. package/src/util/tps.ts +27 -0
  914. package/src/wallet.ts +43 -0
  915. package/tests/amm/test.ts +2092 -0
  916. package/tests/auctions/test.ts +81 -0
  917. package/tests/bn/test.ts +341 -0
  918. package/tests/ci/idl.ts +101 -0
  919. package/tests/ci/verifyConstants.ts +278 -0
  920. package/tests/decode/phoenix.ts +71 -0
  921. package/tests/decode/test.ts +266 -0
  922. package/tests/decode/userAccountBufferStrings.ts +102 -0
  923. package/tests/dlob/helpers.ts +749 -0
  924. package/tests/dlob/test.ts +6623 -0
  925. package/tests/insurance/test.ts +40 -0
  926. package/tests/spot/test.ts +226 -0
  927. package/tests/subscriber/openbook.ts +62 -0
  928. package/tests/tx/TransactionConfirmationManager.test.ts +305 -0
  929. package/tests/tx/cachedBlockhashFetcher.test.ts +96 -0
  930. package/tests/tx/priorityFeeCalculator.ts +77 -0
  931. package/tests/tx/priorityFeeStrategy.ts +95 -0
  932. package/tests/user/helpers.ts +92 -0
  933. package/tests/user/test.ts +517 -0
  934. package/tsconfig.browser.json +13 -0
  935. package/tsconfig.json +13 -0
package/src/user.ts ADDED
@@ -0,0 +1,4054 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { EventEmitter } from 'events';
3
+ import StrictEventEmitter from 'strict-event-emitter-types';
4
+ import { DriftClient } from './driftClient';
5
+ import {
6
+ HealthComponent,
7
+ HealthComponents,
8
+ isVariant,
9
+ MarginCategory,
10
+ Order,
11
+ PerpMarketAccount,
12
+ PerpPosition,
13
+ SpotPosition,
14
+ UserAccount,
15
+ UserStatus,
16
+ UserStatsAccount,
17
+ } from './types';
18
+ import {
19
+ calculateEntryPrice,
20
+ calculateUnsettledFundingPnl,
21
+ positionIsAvailable,
22
+ } from './math/position';
23
+ import {
24
+ AMM_RESERVE_PRECISION,
25
+ AMM_RESERVE_PRECISION_EXP,
26
+ AMM_TO_QUOTE_PRECISION_RATIO,
27
+ BASE_PRECISION,
28
+ BN_MAX,
29
+ DUST_POSITION_SIZE,
30
+ FIVE_MINUTE,
31
+ MARGIN_PRECISION,
32
+ ONE,
33
+ OPEN_ORDER_MARGIN_REQUIREMENT,
34
+ PRICE_PRECISION,
35
+ QUOTE_PRECISION,
36
+ QUOTE_PRECISION_EXP,
37
+ QUOTE_SPOT_MARKET_INDEX,
38
+ SPOT_MARKET_WEIGHT_PRECISION,
39
+ TEN,
40
+ TEN_THOUSAND,
41
+ TWO,
42
+ ZERO,
43
+ FUEL_START_TS,
44
+ } from './constants/numericConstants';
45
+ import {
46
+ DataAndSlot,
47
+ UserAccountEvents,
48
+ UserAccountSubscriber,
49
+ } from './accounts/types';
50
+ import {
51
+ BigNum,
52
+ BN,
53
+ calculateBaseAssetValue,
54
+ calculateMarketMarginRatio,
55
+ calculatePerpLiabilityValue,
56
+ calculatePositionPNL,
57
+ calculateReservePrice,
58
+ calculateSpotMarketMarginRatio,
59
+ calculateUnrealizedAssetWeight,
60
+ calculateWorstCasePerpLiabilityValue,
61
+ divCeil,
62
+ getBalance,
63
+ getSignedTokenAmount,
64
+ getStrictTokenValue,
65
+ getTokenValue,
66
+ getUser30dRollingVolumeEstimate,
67
+ MarketType,
68
+ PositionDirection,
69
+ sigNum,
70
+ SpotBalanceType,
71
+ SpotMarketAccount,
72
+ standardizeBaseAssetAmount,
73
+ } from '.';
74
+ import {
75
+ calculateAssetWeight,
76
+ calculateLiabilityWeight,
77
+ calculateWithdrawLimit,
78
+ getTokenAmount,
79
+ } from './math/spotBalance';
80
+ import { calculateMarketOpenBidAsk } from './math/amm';
81
+ import {
82
+ calculateBaseAssetValueWithOracle,
83
+ calculateCollateralDepositRequiredForTrade,
84
+ calculateMarginUSDCRequiredForTrade,
85
+ calculateWorstCaseBaseAssetAmount,
86
+ } from './math/margin';
87
+ import { OraclePriceData } from './oracles/types';
88
+ import { UserConfig } from './userConfig';
89
+ import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber';
90
+ import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber';
91
+ import {
92
+ calculateWeightedTokenValue,
93
+ getWorstCaseTokenAmounts,
94
+ isSpotPositionAvailable,
95
+ } from './math/spotPosition';
96
+ import { calculateLiveOracleTwap } from './math/oracles';
97
+ import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers';
98
+ import { StrictOraclePrice } from './oracles/strictOraclePrice';
99
+
100
+ import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel';
101
+ import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber';
102
+
103
+ export class User {
104
+ driftClient: DriftClient;
105
+ userAccountPublicKey: PublicKey;
106
+ accountSubscriber: UserAccountSubscriber;
107
+ _isSubscribed = false;
108
+ eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
109
+
110
+ public get isSubscribed() {
111
+ return this._isSubscribed && this.accountSubscriber.isSubscribed;
112
+ }
113
+
114
+ public set isSubscribed(val: boolean) {
115
+ this._isSubscribed = val;
116
+ }
117
+
118
+ public constructor(config: UserConfig) {
119
+ this.driftClient = config.driftClient;
120
+ this.userAccountPublicKey = config.userAccountPublicKey;
121
+ if (config.accountSubscription?.type === 'polling') {
122
+ this.accountSubscriber = new PollingUserAccountSubscriber(
123
+ config.driftClient.connection,
124
+ config.userAccountPublicKey,
125
+ config.accountSubscription.accountLoader,
126
+ this.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(
127
+ this.driftClient.program.account.user.coder.accounts
128
+ )
129
+ );
130
+ } else if (config.accountSubscription?.type === 'custom') {
131
+ this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
132
+ } else if (config.accountSubscription?.type === 'grpc') {
133
+ this.accountSubscriber = new grpcUserAccountSubscriber(
134
+ config.accountSubscription.grpcConfigs,
135
+ config.driftClient.program,
136
+ config.userAccountPublicKey,
137
+ {
138
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
139
+ logResubMessages: config.accountSubscription?.logResubMessages,
140
+ }
141
+ );
142
+ } else {
143
+ this.accountSubscriber = new WebSocketUserAccountSubscriber(
144
+ config.driftClient.program,
145
+ config.userAccountPublicKey,
146
+ {
147
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
148
+ logResubMessages: config.accountSubscription?.logResubMessages,
149
+ },
150
+ config.accountSubscription?.commitment
151
+ );
152
+ }
153
+ this.eventEmitter = this.accountSubscriber.eventEmitter;
154
+ }
155
+
156
+ /**
157
+ * Subscribe to User state accounts
158
+ * @returns SusbcriptionSuccess result
159
+ */
160
+ public async subscribe(userAccount?: UserAccount): Promise<boolean> {
161
+ this.isSubscribed = await this.accountSubscriber.subscribe(userAccount);
162
+ return this.isSubscribed;
163
+ }
164
+
165
+ /**
166
+ * Forces the accountSubscriber to fetch account updates from rpc
167
+ */
168
+ public async fetchAccounts(): Promise<void> {
169
+ await this.accountSubscriber.fetch();
170
+ }
171
+
172
+ public async unsubscribe(): Promise<void> {
173
+ await this.accountSubscriber.unsubscribe();
174
+ this.isSubscribed = false;
175
+ }
176
+
177
+ public getUserAccount(): UserAccount {
178
+ return this.accountSubscriber.getUserAccountAndSlot().data;
179
+ }
180
+
181
+ public async forceGetUserAccount(): Promise<UserAccount> {
182
+ await this.fetchAccounts();
183
+ return this.accountSubscriber.getUserAccountAndSlot().data;
184
+ }
185
+
186
+ public getUserAccountAndSlot(): DataAndSlot<UserAccount> | undefined {
187
+ return this.accountSubscriber.getUserAccountAndSlot();
188
+ }
189
+
190
+ public getPerpPositionForUserAccount(
191
+ userAccount: UserAccount,
192
+ marketIndex: number
193
+ ): PerpPosition | undefined {
194
+ return this.getActivePerpPositionsForUserAccount(userAccount).find(
195
+ (position) => position.marketIndex === marketIndex
196
+ );
197
+ }
198
+
199
+ /**
200
+ * Gets the user's current position for a given perp market. If the user has no position returns undefined
201
+ * @param marketIndex
202
+ * @returns userPerpPosition
203
+ */
204
+ public getPerpPosition(marketIndex: number): PerpPosition | undefined {
205
+ const userAccount = this.getUserAccount();
206
+ return this.getPerpPositionForUserAccount(userAccount, marketIndex);
207
+ }
208
+
209
+ public getPerpPositionAndSlot(
210
+ marketIndex: number
211
+ ): DataAndSlot<PerpPosition | undefined> {
212
+ const userAccount = this.getUserAccountAndSlot();
213
+ const perpPosition = this.getPerpPositionForUserAccount(
214
+ userAccount.data,
215
+ marketIndex
216
+ );
217
+ return {
218
+ data: perpPosition,
219
+ slot: userAccount.slot,
220
+ };
221
+ }
222
+
223
+ public getSpotPositionForUserAccount(
224
+ userAccount: UserAccount,
225
+ marketIndex: number
226
+ ): SpotPosition | undefined {
227
+ return userAccount.spotPositions.find(
228
+ (position) => position.marketIndex === marketIndex
229
+ );
230
+ }
231
+
232
+ /**
233
+ * Gets the user's current position for a given spot market. If the user has no position returns undefined
234
+ * @param marketIndex
235
+ * @returns userSpotPosition
236
+ */
237
+ public getSpotPosition(marketIndex: number): SpotPosition | undefined {
238
+ const userAccount = this.getUserAccount();
239
+ return this.getSpotPositionForUserAccount(userAccount, marketIndex);
240
+ }
241
+
242
+ public getSpotPositionAndSlot(
243
+ marketIndex: number
244
+ ): DataAndSlot<SpotPosition | undefined> {
245
+ const userAccount = this.getUserAccountAndSlot();
246
+ const spotPosition = this.getSpotPositionForUserAccount(
247
+ userAccount.data,
248
+ marketIndex
249
+ );
250
+ return {
251
+ data: spotPosition,
252
+ slot: userAccount.slot,
253
+ };
254
+ }
255
+
256
+ getEmptySpotPosition(marketIndex: number): SpotPosition {
257
+ return {
258
+ marketIndex,
259
+ scaledBalance: ZERO,
260
+ balanceType: SpotBalanceType.DEPOSIT,
261
+ cumulativeDeposits: ZERO,
262
+ openAsks: ZERO,
263
+ openBids: ZERO,
264
+ openOrders: 0,
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
270
+ * Positive if it is a deposit, negative if it is a borrow.
271
+ *
272
+ * @param marketIndex
273
+ */
274
+ public getTokenAmount(marketIndex: number): BN {
275
+ const spotPosition = this.getSpotPosition(marketIndex);
276
+ if (spotPosition === undefined) {
277
+ return ZERO;
278
+ }
279
+ const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
280
+ return getSignedTokenAmount(
281
+ getTokenAmount(
282
+ spotPosition.scaledBalance,
283
+ spotMarket,
284
+ spotPosition.balanceType
285
+ ),
286
+ spotPosition.balanceType
287
+ );
288
+ }
289
+
290
+ public getEmptyPosition(marketIndex: number): PerpPosition {
291
+ return {
292
+ baseAssetAmount: ZERO,
293
+ remainderBaseAssetAmount: 0,
294
+ lastCumulativeFundingRate: ZERO,
295
+ marketIndex,
296
+ quoteAssetAmount: ZERO,
297
+ quoteEntryAmount: ZERO,
298
+ quoteBreakEvenAmount: ZERO,
299
+ openOrders: 0,
300
+ openBids: ZERO,
301
+ openAsks: ZERO,
302
+ settledPnl: ZERO,
303
+ lpShares: ZERO,
304
+ lastBaseAssetAmountPerLp: ZERO,
305
+ lastQuoteAssetAmountPerLp: ZERO,
306
+ perLpBase: 0,
307
+ };
308
+ }
309
+
310
+ public getClonedPosition(position: PerpPosition): PerpPosition {
311
+ const clonedPosition = Object.assign({}, position);
312
+ return clonedPosition;
313
+ }
314
+
315
+ public getOrderForUserAccount(
316
+ userAccount: UserAccount,
317
+ orderId: number
318
+ ): Order | undefined {
319
+ return userAccount.orders.find((order) => order.orderId === orderId);
320
+ }
321
+
322
+ /**
323
+ * @param orderId
324
+ * @returns Order
325
+ */
326
+ public getOrder(orderId: number): Order | undefined {
327
+ const userAccount = this.getUserAccount();
328
+ return this.getOrderForUserAccount(userAccount, orderId);
329
+ }
330
+
331
+ public getOrderAndSlot(orderId: number): DataAndSlot<Order | undefined> {
332
+ const userAccount = this.getUserAccountAndSlot();
333
+ const order = this.getOrderForUserAccount(userAccount.data, orderId);
334
+ return {
335
+ data: order,
336
+ slot: userAccount.slot,
337
+ };
338
+ }
339
+
340
+ public getOrderByUserIdForUserAccount(
341
+ userAccount: UserAccount,
342
+ userOrderId: number
343
+ ): Order | undefined {
344
+ return userAccount.orders.find(
345
+ (order) => order.userOrderId === userOrderId
346
+ );
347
+ }
348
+
349
+ /**
350
+ * @param userOrderId
351
+ * @returns Order
352
+ */
353
+ public getOrderByUserOrderId(userOrderId: number): Order | undefined {
354
+ const userAccount = this.getUserAccount();
355
+ return this.getOrderByUserIdForUserAccount(userAccount, userOrderId);
356
+ }
357
+
358
+ public getOrderByUserOrderIdAndSlot(
359
+ userOrderId: number
360
+ ): DataAndSlot<Order | undefined> {
361
+ const userAccount = this.getUserAccountAndSlot();
362
+ const order = this.getOrderByUserIdForUserAccount(
363
+ userAccount.data,
364
+ userOrderId
365
+ );
366
+ return {
367
+ data: order,
368
+ slot: userAccount.slot,
369
+ };
370
+ }
371
+
372
+ public getOpenOrdersForUserAccount(userAccount?: UserAccount): Order[] {
373
+ return userAccount?.orders.filter((order) =>
374
+ isVariant(order.status, 'open')
375
+ );
376
+ }
377
+
378
+ public getOpenOrders(): Order[] {
379
+ const userAccount = this.getUserAccount();
380
+ return this.getOpenOrdersForUserAccount(userAccount);
381
+ }
382
+
383
+ public getOpenOrdersAndSlot(): DataAndSlot<Order[]> {
384
+ const userAccount = this.getUserAccountAndSlot();
385
+ const openOrders = this.getOpenOrdersForUserAccount(userAccount.data);
386
+ return {
387
+ data: openOrders,
388
+ slot: userAccount.slot,
389
+ };
390
+ }
391
+
392
+ public getUserAccountPublicKey(): PublicKey {
393
+ return this.userAccountPublicKey;
394
+ }
395
+
396
+ public async exists(): Promise<boolean> {
397
+ const userAccountRPCResponse =
398
+ await this.driftClient.connection.getParsedAccountInfo(
399
+ this.userAccountPublicKey
400
+ );
401
+ return userAccountRPCResponse.value !== null;
402
+ }
403
+
404
+ /**
405
+ * calculates the total open bids/asks in a perp market (including lps)
406
+ * @returns : open bids
407
+ * @returns : open asks
408
+ */
409
+ public getPerpBidAsks(marketIndex: number): [BN, BN] {
410
+ const position = this.getPerpPosition(marketIndex);
411
+
412
+ const [lpOpenBids, lpOpenAsks] = this.getLPBidAsks(marketIndex);
413
+
414
+ const totalOpenBids = lpOpenBids.add(position.openBids);
415
+ const totalOpenAsks = lpOpenAsks.add(position.openAsks);
416
+
417
+ return [totalOpenBids, totalOpenAsks];
418
+ }
419
+
420
+ /**
421
+ * calculates the open bids and asks for an lp
422
+ * optionally pass in lpShares to see what bid/asks a user *would* take on
423
+ * @returns : lp open bids
424
+ * @returns : lp open asks
425
+ */
426
+ public getLPBidAsks(marketIndex: number, lpShares?: BN): [BN, BN] {
427
+ const position = this.getPerpPosition(marketIndex);
428
+
429
+ const lpSharesToCalc = lpShares ?? position?.lpShares;
430
+
431
+ if (!lpSharesToCalc || lpSharesToCalc.eq(ZERO)) {
432
+ return [ZERO, ZERO];
433
+ }
434
+
435
+ const market = this.driftClient.getPerpMarketAccount(marketIndex);
436
+ const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk(
437
+ market.amm.baseAssetReserve,
438
+ market.amm.minBaseAssetReserve,
439
+ market.amm.maxBaseAssetReserve,
440
+ market.amm.orderStepSize
441
+ );
442
+
443
+ const lpOpenBids = marketOpenBids.mul(lpSharesToCalc).div(market.amm.sqrtK);
444
+ const lpOpenAsks = marketOpenAsks.mul(lpSharesToCalc).div(market.amm.sqrtK);
445
+
446
+ return [lpOpenBids, lpOpenAsks];
447
+ }
448
+
449
+ /**
450
+ * calculates the market position if the lp position was settled
451
+ * @returns : the settled userPosition
452
+ * @returns : the dust base asset amount (ie, < stepsize)
453
+ * @returns : pnl from settle
454
+ */
455
+ public getPerpPositionWithLPSettle(
456
+ marketIndex: number,
457
+ originalPosition?: PerpPosition,
458
+ burnLpShares = false,
459
+ includeRemainderInBaseAmount = false
460
+ ): [PerpPosition, BN, BN] {
461
+ originalPosition =
462
+ originalPosition ??
463
+ this.getPerpPosition(marketIndex) ??
464
+ this.getEmptyPosition(marketIndex);
465
+
466
+ if (originalPosition.lpShares.eq(ZERO)) {
467
+ return [originalPosition, ZERO, ZERO];
468
+ }
469
+
470
+ const position = this.getClonedPosition(originalPosition);
471
+ const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
472
+
473
+ if (market.amm.perLpBase != position.perLpBase) {
474
+ // perLpBase = 1 => per 10 LP shares, perLpBase = -1 => per 0.1 LP shares
475
+ const expoDiff = market.amm.perLpBase - position.perLpBase;
476
+ const marketPerLpRebaseScalar = new BN(10 ** Math.abs(expoDiff));
477
+
478
+ if (expoDiff > 0) {
479
+ position.lastBaseAssetAmountPerLp =
480
+ position.lastBaseAssetAmountPerLp.mul(marketPerLpRebaseScalar);
481
+ position.lastQuoteAssetAmountPerLp =
482
+ position.lastQuoteAssetAmountPerLp.mul(marketPerLpRebaseScalar);
483
+ } else {
484
+ position.lastBaseAssetAmountPerLp =
485
+ position.lastBaseAssetAmountPerLp.div(marketPerLpRebaseScalar);
486
+ position.lastQuoteAssetAmountPerLp =
487
+ position.lastQuoteAssetAmountPerLp.div(marketPerLpRebaseScalar);
488
+ }
489
+
490
+ position.perLpBase = position.perLpBase + expoDiff;
491
+ }
492
+
493
+ const nShares = position.lpShares;
494
+
495
+ // incorp unsettled funding on pre settled position
496
+ const quoteFundingPnl = calculateUnsettledFundingPnl(market, position);
497
+
498
+ let baseUnit = AMM_RESERVE_PRECISION;
499
+ if (market.amm.perLpBase == position.perLpBase) {
500
+ if (
501
+ position.perLpBase >= 0 &&
502
+ position.perLpBase <= AMM_RESERVE_PRECISION_EXP.toNumber()
503
+ ) {
504
+ const marketPerLpRebase = new BN(10 ** market.amm.perLpBase);
505
+ baseUnit = baseUnit.mul(marketPerLpRebase);
506
+ } else if (
507
+ position.perLpBase < 0 &&
508
+ position.perLpBase >= -AMM_RESERVE_PRECISION_EXP.toNumber()
509
+ ) {
510
+ const marketPerLpRebase = new BN(10 ** Math.abs(market.amm.perLpBase));
511
+ baseUnit = baseUnit.div(marketPerLpRebase);
512
+ } else {
513
+ throw 'cannot calc';
514
+ }
515
+ } else {
516
+ throw 'market.amm.perLpBase != position.perLpBase';
517
+ }
518
+
519
+ const deltaBaa = market.amm.baseAssetAmountPerLp
520
+ .sub(position.lastBaseAssetAmountPerLp)
521
+ .mul(nShares)
522
+ .div(baseUnit);
523
+ const deltaQaa = market.amm.quoteAssetAmountPerLp
524
+ .sub(position.lastQuoteAssetAmountPerLp)
525
+ .mul(nShares)
526
+ .div(baseUnit);
527
+
528
+ function sign(v: BN) {
529
+ return v.isNeg() ? new BN(-1) : new BN(1);
530
+ }
531
+
532
+ function standardize(amount: BN, stepSize: BN) {
533
+ const remainder = amount.abs().mod(stepSize).mul(sign(amount));
534
+ const standardizedAmount = amount.sub(remainder);
535
+ return [standardizedAmount, remainder];
536
+ }
537
+
538
+ const [standardizedBaa, remainderBaa] = standardize(
539
+ deltaBaa,
540
+ market.amm.orderStepSize
541
+ );
542
+
543
+ position.remainderBaseAssetAmount += remainderBaa.toNumber();
544
+
545
+ if (
546
+ Math.abs(position.remainderBaseAssetAmount) >
547
+ market.amm.orderStepSize.toNumber()
548
+ ) {
549
+ const [newStandardizedBaa, newRemainderBaa] = standardize(
550
+ new BN(position.remainderBaseAssetAmount),
551
+ market.amm.orderStepSize
552
+ );
553
+ position.baseAssetAmount =
554
+ position.baseAssetAmount.add(newStandardizedBaa);
555
+ position.remainderBaseAssetAmount = newRemainderBaa.toNumber();
556
+ }
557
+
558
+ let dustBaseAssetValue = ZERO;
559
+ if (burnLpShares && position.remainderBaseAssetAmount != 0) {
560
+ const oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
561
+ position.marketIndex
562
+ );
563
+ dustBaseAssetValue = new BN(Math.abs(position.remainderBaseAssetAmount))
564
+ .mul(oraclePriceData.price)
565
+ .div(AMM_RESERVE_PRECISION)
566
+ .add(ONE);
567
+ }
568
+
569
+ let updateType;
570
+ if (position.baseAssetAmount.eq(ZERO)) {
571
+ updateType = 'open';
572
+ } else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
573
+ updateType = 'increase';
574
+ } else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
575
+ updateType = 'reduce';
576
+ } else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
577
+ updateType = 'close';
578
+ } else {
579
+ updateType = 'flip';
580
+ }
581
+
582
+ let newQuoteEntry;
583
+ let pnl;
584
+ if (updateType == 'open' || updateType == 'increase') {
585
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
586
+ pnl = ZERO;
587
+ } else if (updateType == 'reduce' || updateType == 'close') {
588
+ newQuoteEntry = position.quoteEntryAmount.sub(
589
+ position.quoteEntryAmount
590
+ .mul(deltaBaa.abs())
591
+ .div(position.baseAssetAmount.abs())
592
+ );
593
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry).add(deltaQaa);
594
+ } else {
595
+ newQuoteEntry = deltaQaa.sub(
596
+ deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs())
597
+ );
598
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
599
+ }
600
+ position.quoteEntryAmount = newQuoteEntry;
601
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
602
+ position.quoteAssetAmount = position.quoteAssetAmount
603
+ .add(deltaQaa)
604
+ .add(quoteFundingPnl)
605
+ .sub(dustBaseAssetValue);
606
+ position.quoteBreakEvenAmount = position.quoteBreakEvenAmount
607
+ .add(deltaQaa)
608
+ .add(quoteFundingPnl)
609
+ .sub(dustBaseAssetValue);
610
+
611
+ // update open bids/asks
612
+ const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk(
613
+ market.amm.baseAssetReserve,
614
+ market.amm.minBaseAssetReserve,
615
+ market.amm.maxBaseAssetReserve,
616
+ market.amm.orderStepSize
617
+ );
618
+ const lpOpenBids = marketOpenBids
619
+ .mul(position.lpShares)
620
+ .div(market.amm.sqrtK);
621
+ const lpOpenAsks = marketOpenAsks
622
+ .mul(position.lpShares)
623
+ .div(market.amm.sqrtK);
624
+ position.openBids = lpOpenBids.add(position.openBids);
625
+ position.openAsks = lpOpenAsks.add(position.openAsks);
626
+
627
+ // eliminate counting funding on settled position
628
+ if (position.baseAssetAmount.gt(ZERO)) {
629
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
630
+ } else if (position.baseAssetAmount.lt(ZERO)) {
631
+ position.lastCumulativeFundingRate =
632
+ market.amm.cumulativeFundingRateShort;
633
+ } else {
634
+ position.lastCumulativeFundingRate = ZERO;
635
+ }
636
+
637
+ const remainderBeforeRemoval = new BN(position.remainderBaseAssetAmount);
638
+
639
+ if (includeRemainderInBaseAmount) {
640
+ position.baseAssetAmount = position.baseAssetAmount.add(
641
+ remainderBeforeRemoval
642
+ );
643
+ position.remainderBaseAssetAmount = 0;
644
+ }
645
+
646
+ return [position, remainderBeforeRemoval, pnl];
647
+ }
648
+
649
+ /**
650
+ * calculates Buying Power = free collateral / initial margin ratio
651
+ * @returns : Precision QUOTE_PRECISION
652
+ */
653
+ public getPerpBuyingPower(marketIndex: number, collateralBuffer = ZERO): BN {
654
+ const perpPosition = this.getPerpPositionWithLPSettle(
655
+ marketIndex,
656
+ undefined,
657
+ true
658
+ )[0];
659
+
660
+ const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex);
661
+ const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex);
662
+ const worstCaseBaseAssetAmount = perpPosition
663
+ ? calculateWorstCaseBaseAssetAmount(
664
+ perpPosition,
665
+ perpMarket,
666
+ oraclePriceData.price
667
+ )
668
+ : ZERO;
669
+
670
+ const freeCollateral = this.getFreeCollateral().sub(collateralBuffer);
671
+
672
+ return this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(
673
+ marketIndex,
674
+ freeCollateral,
675
+ worstCaseBaseAssetAmount
676
+ );
677
+ }
678
+
679
+ getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(
680
+ marketIndex: number,
681
+ freeCollateral: BN,
682
+ baseAssetAmount: BN
683
+ ): BN {
684
+ const marginRatio = calculateMarketMarginRatio(
685
+ this.driftClient.getPerpMarketAccount(marketIndex),
686
+ baseAssetAmount,
687
+ 'Initial',
688
+ this.getUserAccount().maxMarginRatio,
689
+ this.isHighLeverageMode()
690
+ );
691
+
692
+ return freeCollateral.mul(MARGIN_PRECISION).div(new BN(marginRatio));
693
+ }
694
+
695
+ /**
696
+ * calculates Free Collateral = Total collateral - margin requirement
697
+ * @returns : Precision QUOTE_PRECISION
698
+ */
699
+ public getFreeCollateral(marginCategory: MarginCategory = 'Initial'): BN {
700
+ const totalCollateral = this.getTotalCollateral(marginCategory, true);
701
+ const marginRequirement =
702
+ marginCategory === 'Initial'
703
+ ? this.getInitialMarginRequirement()
704
+ : this.getMaintenanceMarginRequirement();
705
+ const freeCollateral = totalCollateral.sub(marginRequirement);
706
+ return freeCollateral.gte(ZERO) ? freeCollateral : ZERO;
707
+ }
708
+
709
+ /**
710
+ * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
711
+ */
712
+ public getMarginRequirement(
713
+ marginCategory: MarginCategory,
714
+ liquidationBuffer?: BN,
715
+ strict = false,
716
+ includeOpenOrders = true
717
+ ): BN {
718
+ return this.getTotalPerpPositionLiability(
719
+ marginCategory,
720
+ liquidationBuffer,
721
+ includeOpenOrders,
722
+ strict
723
+ ).add(
724
+ this.getSpotMarketLiabilityValue(
725
+ undefined,
726
+ marginCategory,
727
+ liquidationBuffer,
728
+ includeOpenOrders,
729
+ strict
730
+ )
731
+ );
732
+ }
733
+
734
+ /**
735
+ * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
736
+ */
737
+ public getInitialMarginRequirement(): BN {
738
+ return this.getMarginRequirement('Initial', undefined, true);
739
+ }
740
+
741
+ /**
742
+ * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
743
+ */
744
+ public getMaintenanceMarginRequirement(): BN {
745
+ // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
746
+ let liquidationBuffer = undefined;
747
+ if (this.isBeingLiquidated()) {
748
+ liquidationBuffer = new BN(
749
+ this.driftClient.getStateAccount().liquidationMarginBufferRatio
750
+ );
751
+ }
752
+
753
+ return this.getMarginRequirement('Maintenance', liquidationBuffer);
754
+ }
755
+
756
+ public getActivePerpPositionsForUserAccount(
757
+ userAccount: UserAccount
758
+ ): PerpPosition[] {
759
+ return userAccount.perpPositions.filter(
760
+ (pos) =>
761
+ !pos.baseAssetAmount.eq(ZERO) ||
762
+ !pos.quoteAssetAmount.eq(ZERO) ||
763
+ !(pos.openOrders == 0) ||
764
+ !pos.lpShares.eq(ZERO)
765
+ );
766
+ }
767
+
768
+ public getActivePerpPositions(): PerpPosition[] {
769
+ const userAccount = this.getUserAccount();
770
+ return this.getActivePerpPositionsForUserAccount(userAccount);
771
+ }
772
+ public getActivePerpPositionsAndSlot(): DataAndSlot<PerpPosition[]> {
773
+ const userAccount = this.getUserAccountAndSlot();
774
+ const positions = this.getActivePerpPositionsForUserAccount(
775
+ userAccount.data
776
+ );
777
+ return {
778
+ data: positions,
779
+ slot: userAccount.slot,
780
+ };
781
+ }
782
+
783
+ public getActiveSpotPositionsForUserAccount(
784
+ userAccount: UserAccount
785
+ ): SpotPosition[] {
786
+ return userAccount.spotPositions.filter(
787
+ (pos) => !isSpotPositionAvailable(pos)
788
+ );
789
+ }
790
+
791
+ public getActiveSpotPositions(): SpotPosition[] {
792
+ const userAccount = this.getUserAccount();
793
+ return this.getActiveSpotPositionsForUserAccount(userAccount);
794
+ }
795
+ public getActiveSpotPositionsAndSlot(): DataAndSlot<SpotPosition[]> {
796
+ const userAccount = this.getUserAccountAndSlot();
797
+ const positions = this.getActiveSpotPositionsForUserAccount(
798
+ userAccount.data
799
+ );
800
+ return {
801
+ data: positions,
802
+ slot: userAccount.slot,
803
+ };
804
+ }
805
+
806
+ /**
807
+ * calculates unrealized position price pnl
808
+ * @returns : Precision QUOTE_PRECISION
809
+ */
810
+ public getUnrealizedPNL(
811
+ withFunding?: boolean,
812
+ marketIndex?: number,
813
+ withWeightMarginCategory?: MarginCategory,
814
+ strict = false
815
+ ): BN {
816
+ return this.getActivePerpPositions()
817
+ .filter((pos) =>
818
+ marketIndex !== undefined ? pos.marketIndex === marketIndex : true
819
+ )
820
+ .reduce((unrealizedPnl, perpPosition) => {
821
+ const market = this.driftClient.getPerpMarketAccount(
822
+ perpPosition.marketIndex
823
+ );
824
+ const oraclePriceData = this.getOracleDataForPerpMarket(
825
+ market.marketIndex
826
+ );
827
+
828
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
829
+ market.quoteSpotMarketIndex
830
+ );
831
+ const quoteOraclePriceData = this.getOracleDataForSpotMarket(
832
+ market.quoteSpotMarketIndex
833
+ );
834
+
835
+ if (perpPosition.lpShares.gt(ZERO)) {
836
+ perpPosition = this.getPerpPositionWithLPSettle(
837
+ perpPosition.marketIndex,
838
+ undefined,
839
+ !!withWeightMarginCategory
840
+ )[0];
841
+ }
842
+
843
+ let positionUnrealizedPnl = calculatePositionPNL(
844
+ market,
845
+ perpPosition,
846
+ withFunding,
847
+ oraclePriceData
848
+ );
849
+
850
+ let quotePrice;
851
+ if (strict && positionUnrealizedPnl.gt(ZERO)) {
852
+ quotePrice = BN.min(
853
+ quoteOraclePriceData.price,
854
+ quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
855
+ );
856
+ } else if (strict && positionUnrealizedPnl.lt(ZERO)) {
857
+ quotePrice = BN.max(
858
+ quoteOraclePriceData.price,
859
+ quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
860
+ );
861
+ } else {
862
+ quotePrice = quoteOraclePriceData.price;
863
+ }
864
+
865
+ positionUnrealizedPnl = positionUnrealizedPnl
866
+ .mul(quotePrice)
867
+ .div(PRICE_PRECISION);
868
+
869
+ if (withWeightMarginCategory !== undefined) {
870
+ if (positionUnrealizedPnl.gt(ZERO)) {
871
+ positionUnrealizedPnl = positionUnrealizedPnl
872
+ .mul(
873
+ calculateUnrealizedAssetWeight(
874
+ market,
875
+ quoteSpotMarket,
876
+ positionUnrealizedPnl,
877
+ withWeightMarginCategory,
878
+ oraclePriceData
879
+ )
880
+ )
881
+ .div(new BN(SPOT_MARKET_WEIGHT_PRECISION));
882
+ }
883
+ }
884
+
885
+ return unrealizedPnl.add(positionUnrealizedPnl);
886
+ }, ZERO);
887
+ }
888
+
889
+ /**
890
+ * calculates unrealized funding payment pnl
891
+ * @returns : Precision QUOTE_PRECISION
892
+ */
893
+ public getUnrealizedFundingPNL(marketIndex?: number): BN {
894
+ return this.getUserAccount()
895
+ .perpPositions.filter((pos) =>
896
+ marketIndex !== undefined ? pos.marketIndex === marketIndex : true
897
+ )
898
+ .reduce((pnl, perpPosition) => {
899
+ const market = this.driftClient.getPerpMarketAccount(
900
+ perpPosition.marketIndex
901
+ );
902
+ return pnl.add(calculateUnsettledFundingPnl(market, perpPosition));
903
+ }, ZERO);
904
+ }
905
+
906
+ public getFuelBonus(
907
+ now: BN,
908
+ includeSettled = true,
909
+ includeUnsettled = true
910
+ ): {
911
+ depositFuel: BN;
912
+ borrowFuel: BN;
913
+ positionFuel: BN;
914
+ takerFuel: BN;
915
+ makerFuel: BN;
916
+ insuranceFuel: BN;
917
+ } {
918
+ const userAccount: UserAccount = this.getUserAccount();
919
+
920
+ const result = {
921
+ insuranceFuel: ZERO,
922
+ takerFuel: ZERO,
923
+ makerFuel: ZERO,
924
+ depositFuel: ZERO,
925
+ borrowFuel: ZERO,
926
+ positionFuel: ZERO,
927
+ };
928
+
929
+ const userStats = this.driftClient.getUserStats();
930
+ const userStatsAccount: UserStatsAccount = userStats.getAccount();
931
+
932
+ if (includeSettled) {
933
+ result.takerFuel = result.takerFuel.add(
934
+ new BN(userStatsAccount.fuelTaker)
935
+ );
936
+ result.makerFuel = result.makerFuel.add(
937
+ new BN(userStatsAccount.fuelMaker)
938
+ );
939
+ result.depositFuel = result.depositFuel.add(
940
+ new BN(userStatsAccount.fuelDeposits)
941
+ );
942
+ result.borrowFuel = result.borrowFuel.add(
943
+ new BN(userStatsAccount.fuelBorrows)
944
+ );
945
+ result.positionFuel = result.positionFuel.add(
946
+ new BN(userStatsAccount.fuelPositions)
947
+ );
948
+ }
949
+
950
+ if (includeUnsettled) {
951
+ const fuelBonusNumerator = BN.max(
952
+ now.sub(
953
+ BN.max(new BN(userAccount.lastFuelBonusUpdateTs), FUEL_START_TS)
954
+ ),
955
+ ZERO
956
+ );
957
+
958
+ if (fuelBonusNumerator.gt(ZERO)) {
959
+ for (const spotPosition of this.getActiveSpotPositions()) {
960
+ const spotMarketAccount: SpotMarketAccount =
961
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
962
+
963
+ const tokenAmount = this.getTokenAmount(spotPosition.marketIndex);
964
+ const oraclePriceData = this.getOracleDataForSpotMarket(
965
+ spotPosition.marketIndex
966
+ );
967
+
968
+ const twap5min = calculateLiveOracleTwap(
969
+ spotMarketAccount.historicalOracleData,
970
+ oraclePriceData,
971
+ now,
972
+ FIVE_MINUTE // 5MIN
973
+ );
974
+ const strictOraclePrice = new StrictOraclePrice(
975
+ oraclePriceData.price,
976
+ twap5min
977
+ );
978
+
979
+ const signedTokenValue = getStrictTokenValue(
980
+ tokenAmount,
981
+ spotMarketAccount.decimals,
982
+ strictOraclePrice
983
+ );
984
+
985
+ if (signedTokenValue.gt(ZERO)) {
986
+ result.depositFuel = result.depositFuel.add(
987
+ calculateSpotFuelBonus(
988
+ spotMarketAccount,
989
+ signedTokenValue,
990
+ fuelBonusNumerator
991
+ )
992
+ );
993
+ } else {
994
+ result.borrowFuel = result.borrowFuel.add(
995
+ calculateSpotFuelBonus(
996
+ spotMarketAccount,
997
+ signedTokenValue,
998
+ fuelBonusNumerator
999
+ )
1000
+ );
1001
+ }
1002
+ }
1003
+
1004
+ for (const perpPosition of this.getActivePerpPositions()) {
1005
+ const oraclePriceData = this.getOracleDataForPerpMarket(
1006
+ perpPosition.marketIndex
1007
+ );
1008
+
1009
+ const perpMarketAccount = this.driftClient.getPerpMarketAccount(
1010
+ perpPosition.marketIndex
1011
+ );
1012
+
1013
+ const baseAssetValue = this.getPerpPositionValue(
1014
+ perpPosition.marketIndex,
1015
+ oraclePriceData,
1016
+ false
1017
+ );
1018
+
1019
+ result.positionFuel = result.positionFuel.add(
1020
+ calculatePerpFuelBonus(
1021
+ perpMarketAccount,
1022
+ baseAssetValue,
1023
+ fuelBonusNumerator
1024
+ )
1025
+ );
1026
+ }
1027
+ }
1028
+ }
1029
+
1030
+ result.insuranceFuel = userStats.getInsuranceFuelBonus(
1031
+ now,
1032
+ includeSettled,
1033
+ includeUnsettled
1034
+ );
1035
+
1036
+ return result;
1037
+ }
1038
+
1039
+ public getSpotMarketAssetAndLiabilityValue(
1040
+ marketIndex?: number,
1041
+ marginCategory?: MarginCategory,
1042
+ liquidationBuffer?: BN,
1043
+ includeOpenOrders?: boolean,
1044
+ strict = false,
1045
+ now?: BN
1046
+ ): { totalAssetValue: BN; totalLiabilityValue: BN } {
1047
+ now = now || new BN(new Date().getTime() / 1000);
1048
+ let netQuoteValue = ZERO;
1049
+ let totalAssetValue = ZERO;
1050
+ let totalLiabilityValue = ZERO;
1051
+ for (const spotPosition of this.getUserAccount().spotPositions) {
1052
+ const countForBase =
1053
+ marketIndex === undefined || spotPosition.marketIndex === marketIndex;
1054
+
1055
+ const countForQuote =
1056
+ marketIndex === undefined ||
1057
+ marketIndex === QUOTE_SPOT_MARKET_INDEX ||
1058
+ (includeOpenOrders && spotPosition.openOrders !== 0);
1059
+ if (
1060
+ isSpotPositionAvailable(spotPosition) ||
1061
+ (!countForBase && !countForQuote)
1062
+ ) {
1063
+ continue;
1064
+ }
1065
+
1066
+ const spotMarketAccount: SpotMarketAccount =
1067
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
1068
+
1069
+ const oraclePriceData = this.getOracleDataForSpotMarket(
1070
+ spotPosition.marketIndex
1071
+ );
1072
+
1073
+ let twap5min;
1074
+ if (strict) {
1075
+ twap5min = calculateLiveOracleTwap(
1076
+ spotMarketAccount.historicalOracleData,
1077
+ oraclePriceData,
1078
+ now,
1079
+ FIVE_MINUTE // 5MIN
1080
+ );
1081
+ }
1082
+ const strictOraclePrice = new StrictOraclePrice(
1083
+ oraclePriceData.price,
1084
+ twap5min
1085
+ );
1086
+
1087
+ if (
1088
+ spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX &&
1089
+ countForQuote
1090
+ ) {
1091
+ const tokenAmount = getSignedTokenAmount(
1092
+ getTokenAmount(
1093
+ spotPosition.scaledBalance,
1094
+ spotMarketAccount,
1095
+ spotPosition.balanceType
1096
+ ),
1097
+ spotPosition.balanceType
1098
+ );
1099
+
1100
+ if (isVariant(spotPosition.balanceType, 'borrow')) {
1101
+ const weightedTokenValue = this.getSpotLiabilityValue(
1102
+ tokenAmount,
1103
+ strictOraclePrice,
1104
+ spotMarketAccount,
1105
+ marginCategory,
1106
+ liquidationBuffer
1107
+ ).abs();
1108
+
1109
+ netQuoteValue = netQuoteValue.sub(weightedTokenValue);
1110
+ } else {
1111
+ const weightedTokenValue = this.getSpotAssetValue(
1112
+ tokenAmount,
1113
+ strictOraclePrice,
1114
+ spotMarketAccount,
1115
+ marginCategory
1116
+ );
1117
+
1118
+ netQuoteValue = netQuoteValue.add(weightedTokenValue);
1119
+ }
1120
+
1121
+ continue;
1122
+ }
1123
+
1124
+ if (!includeOpenOrders && countForBase) {
1125
+ if (isVariant(spotPosition.balanceType, 'borrow')) {
1126
+ const tokenAmount = getSignedTokenAmount(
1127
+ getTokenAmount(
1128
+ spotPosition.scaledBalance,
1129
+ spotMarketAccount,
1130
+ spotPosition.balanceType
1131
+ ),
1132
+ SpotBalanceType.BORROW
1133
+ );
1134
+ const liabilityValue = this.getSpotLiabilityValue(
1135
+ tokenAmount,
1136
+ strictOraclePrice,
1137
+ spotMarketAccount,
1138
+ marginCategory,
1139
+ liquidationBuffer
1140
+ ).abs();
1141
+ totalLiabilityValue = totalLiabilityValue.add(liabilityValue);
1142
+
1143
+ continue;
1144
+ } else {
1145
+ const tokenAmount = getTokenAmount(
1146
+ spotPosition.scaledBalance,
1147
+ spotMarketAccount,
1148
+ spotPosition.balanceType
1149
+ );
1150
+ const assetValue = this.getSpotAssetValue(
1151
+ tokenAmount,
1152
+ strictOraclePrice,
1153
+ spotMarketAccount,
1154
+ marginCategory
1155
+ );
1156
+ totalAssetValue = totalAssetValue.add(assetValue);
1157
+
1158
+ continue;
1159
+ }
1160
+ }
1161
+
1162
+ const {
1163
+ tokenAmount: worstCaseTokenAmount,
1164
+ ordersValue: worstCaseQuoteTokenAmount,
1165
+ } = getWorstCaseTokenAmounts(
1166
+ spotPosition,
1167
+ spotMarketAccount,
1168
+ strictOraclePrice,
1169
+ marginCategory,
1170
+ this.getUserAccount().maxMarginRatio
1171
+ );
1172
+
1173
+ if (worstCaseTokenAmount.gt(ZERO) && countForBase) {
1174
+ const baseAssetValue = this.getSpotAssetValue(
1175
+ worstCaseTokenAmount,
1176
+ strictOraclePrice,
1177
+ spotMarketAccount,
1178
+ marginCategory
1179
+ );
1180
+
1181
+ totalAssetValue = totalAssetValue.add(baseAssetValue);
1182
+ }
1183
+
1184
+ if (worstCaseTokenAmount.lt(ZERO) && countForBase) {
1185
+ const baseLiabilityValue = this.getSpotLiabilityValue(
1186
+ worstCaseTokenAmount,
1187
+ strictOraclePrice,
1188
+ spotMarketAccount,
1189
+ marginCategory,
1190
+ liquidationBuffer
1191
+ ).abs();
1192
+
1193
+ totalLiabilityValue = totalLiabilityValue.add(baseLiabilityValue);
1194
+ }
1195
+
1196
+ if (worstCaseQuoteTokenAmount.gt(ZERO) && countForQuote) {
1197
+ netQuoteValue = netQuoteValue.add(worstCaseQuoteTokenAmount);
1198
+ }
1199
+
1200
+ if (worstCaseQuoteTokenAmount.lt(ZERO) && countForQuote) {
1201
+ let weight = SPOT_MARKET_WEIGHT_PRECISION;
1202
+ if (marginCategory === 'Initial') {
1203
+ weight = BN.max(weight, new BN(this.getUserAccount().maxMarginRatio));
1204
+ }
1205
+
1206
+ const weightedTokenValue = worstCaseQuoteTokenAmount
1207
+ .abs()
1208
+ .mul(weight)
1209
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
1210
+
1211
+ netQuoteValue = netQuoteValue.sub(weightedTokenValue);
1212
+ }
1213
+
1214
+ totalLiabilityValue = totalLiabilityValue.add(
1215
+ new BN(spotPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
1216
+ );
1217
+ }
1218
+
1219
+ if (marketIndex === undefined || marketIndex === QUOTE_SPOT_MARKET_INDEX) {
1220
+ if (netQuoteValue.gt(ZERO)) {
1221
+ totalAssetValue = totalAssetValue.add(netQuoteValue);
1222
+ } else {
1223
+ totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
1224
+ }
1225
+ }
1226
+
1227
+ return { totalAssetValue, totalLiabilityValue };
1228
+ }
1229
+
1230
+ public getSpotMarketLiabilityValue(
1231
+ marketIndex?: number,
1232
+ marginCategory?: MarginCategory,
1233
+ liquidationBuffer?: BN,
1234
+ includeOpenOrders?: boolean,
1235
+ strict = false,
1236
+ now?: BN
1237
+ ): BN {
1238
+ const { totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(
1239
+ marketIndex,
1240
+ marginCategory,
1241
+ liquidationBuffer,
1242
+ includeOpenOrders,
1243
+ strict,
1244
+ now
1245
+ );
1246
+ return totalLiabilityValue;
1247
+ }
1248
+
1249
+ getSpotLiabilityValue(
1250
+ tokenAmount: BN,
1251
+ strictOraclePrice: StrictOraclePrice,
1252
+ spotMarketAccount: SpotMarketAccount,
1253
+ marginCategory?: MarginCategory,
1254
+ liquidationBuffer?: BN
1255
+ ): BN {
1256
+ let liabilityValue = getStrictTokenValue(
1257
+ tokenAmount,
1258
+ spotMarketAccount.decimals,
1259
+ strictOraclePrice
1260
+ );
1261
+
1262
+ if (marginCategory !== undefined) {
1263
+ let weight = calculateLiabilityWeight(
1264
+ tokenAmount,
1265
+ spotMarketAccount,
1266
+ marginCategory
1267
+ );
1268
+
1269
+ if (
1270
+ marginCategory === 'Initial' &&
1271
+ spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
1272
+ ) {
1273
+ weight = BN.max(
1274
+ weight,
1275
+ SPOT_MARKET_WEIGHT_PRECISION.addn(
1276
+ this.getUserAccount().maxMarginRatio
1277
+ )
1278
+ );
1279
+ }
1280
+
1281
+ if (liquidationBuffer !== undefined) {
1282
+ weight = weight.add(liquidationBuffer);
1283
+ }
1284
+
1285
+ liabilityValue = liabilityValue
1286
+ .mul(weight)
1287
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
1288
+ }
1289
+
1290
+ return liabilityValue;
1291
+ }
1292
+
1293
+ public getSpotMarketAssetValue(
1294
+ marketIndex?: number,
1295
+ marginCategory?: MarginCategory,
1296
+ includeOpenOrders?: boolean,
1297
+ strict = false,
1298
+ now?: BN
1299
+ ): BN {
1300
+ const { totalAssetValue } = this.getSpotMarketAssetAndLiabilityValue(
1301
+ marketIndex,
1302
+ marginCategory,
1303
+ undefined,
1304
+ includeOpenOrders,
1305
+ strict,
1306
+ now
1307
+ );
1308
+ return totalAssetValue;
1309
+ }
1310
+
1311
+ getSpotAssetValue(
1312
+ tokenAmount: BN,
1313
+ strictOraclePrice: StrictOraclePrice,
1314
+ spotMarketAccount: SpotMarketAccount,
1315
+ marginCategory?: MarginCategory
1316
+ ): BN {
1317
+ let assetValue = getStrictTokenValue(
1318
+ tokenAmount,
1319
+ spotMarketAccount.decimals,
1320
+ strictOraclePrice
1321
+ );
1322
+
1323
+ if (marginCategory !== undefined) {
1324
+ let weight = calculateAssetWeight(
1325
+ tokenAmount,
1326
+ strictOraclePrice.current,
1327
+ spotMarketAccount,
1328
+ marginCategory
1329
+ );
1330
+
1331
+ if (
1332
+ marginCategory === 'Initial' &&
1333
+ spotMarketAccount.marketIndex !== QUOTE_SPOT_MARKET_INDEX
1334
+ ) {
1335
+ const userCustomAssetWeight = BN.max(
1336
+ ZERO,
1337
+ SPOT_MARKET_WEIGHT_PRECISION.subn(
1338
+ this.getUserAccount().maxMarginRatio
1339
+ )
1340
+ );
1341
+ weight = BN.min(weight, userCustomAssetWeight);
1342
+ }
1343
+
1344
+ assetValue = assetValue.mul(weight).div(SPOT_MARKET_WEIGHT_PRECISION);
1345
+ }
1346
+
1347
+ return assetValue;
1348
+ }
1349
+
1350
+ public getSpotPositionValue(
1351
+ marketIndex: number,
1352
+ marginCategory?: MarginCategory,
1353
+ includeOpenOrders?: boolean,
1354
+ strict = false,
1355
+ now?: BN
1356
+ ): BN {
1357
+ const { totalAssetValue, totalLiabilityValue } =
1358
+ this.getSpotMarketAssetAndLiabilityValue(
1359
+ marketIndex,
1360
+ marginCategory,
1361
+ undefined,
1362
+ includeOpenOrders,
1363
+ strict,
1364
+ now
1365
+ );
1366
+
1367
+ return totalAssetValue.sub(totalLiabilityValue);
1368
+ }
1369
+
1370
+ public getNetSpotMarketValue(withWeightMarginCategory?: MarginCategory): BN {
1371
+ const { totalAssetValue, totalLiabilityValue } =
1372
+ this.getSpotMarketAssetAndLiabilityValue(
1373
+ undefined,
1374
+ withWeightMarginCategory
1375
+ );
1376
+
1377
+ return totalAssetValue.sub(totalLiabilityValue);
1378
+ }
1379
+
1380
+ /**
1381
+ * calculates TotalCollateral: collateral + unrealized pnl
1382
+ * @returns : Precision QUOTE_PRECISION
1383
+ */
1384
+ public getTotalCollateral(
1385
+ marginCategory: MarginCategory = 'Initial',
1386
+ strict = false,
1387
+ includeOpenOrders = true
1388
+ ): BN {
1389
+ return this.getSpotMarketAssetValue(
1390
+ undefined,
1391
+ marginCategory,
1392
+ includeOpenOrders,
1393
+ strict
1394
+ ).add(this.getUnrealizedPNL(true, undefined, marginCategory, strict));
1395
+ }
1396
+
1397
+ /**
1398
+ * calculates User Health by comparing total collateral and maint. margin requirement
1399
+ * @returns : number (value from [0, 100])
1400
+ */
1401
+ public getHealth(): number {
1402
+ if (this.isBeingLiquidated()) {
1403
+ return 0;
1404
+ }
1405
+
1406
+ const totalCollateral = this.getTotalCollateral('Maintenance');
1407
+ const maintenanceMarginReq = this.getMaintenanceMarginRequirement();
1408
+
1409
+ let health: number;
1410
+
1411
+ if (maintenanceMarginReq.eq(ZERO) && totalCollateral.gte(ZERO)) {
1412
+ health = 100;
1413
+ } else if (totalCollateral.lte(ZERO)) {
1414
+ health = 0;
1415
+ } else {
1416
+ health = Math.round(
1417
+ Math.min(
1418
+ 100,
1419
+ Math.max(
1420
+ 0,
1421
+ (1 - maintenanceMarginReq.toNumber() / totalCollateral.toNumber()) *
1422
+ 100
1423
+ )
1424
+ )
1425
+ );
1426
+ }
1427
+
1428
+ return health;
1429
+ }
1430
+
1431
+ calculateWeightedPerpPositionLiability(
1432
+ perpPosition: PerpPosition,
1433
+ marginCategory?: MarginCategory,
1434
+ liquidationBuffer?: BN,
1435
+ includeOpenOrders?: boolean,
1436
+ strict = false
1437
+ ): BN {
1438
+ const market = this.driftClient.getPerpMarketAccount(
1439
+ perpPosition.marketIndex
1440
+ );
1441
+
1442
+ if (perpPosition.lpShares.gt(ZERO)) {
1443
+ // is an lp, clone so we dont mutate the position
1444
+ perpPosition = this.getPerpPositionWithLPSettle(
1445
+ market.marketIndex,
1446
+ this.getClonedPosition(perpPosition),
1447
+ !!marginCategory
1448
+ )[0];
1449
+ }
1450
+
1451
+ let valuationPrice = this.getOracleDataForPerpMarket(
1452
+ market.marketIndex
1453
+ ).price;
1454
+
1455
+ if (isVariant(market.status, 'settlement')) {
1456
+ valuationPrice = market.expiryPrice;
1457
+ }
1458
+
1459
+ let baseAssetAmount: BN;
1460
+ let liabilityValue;
1461
+ if (includeOpenOrders) {
1462
+ const { worstCaseBaseAssetAmount, worstCaseLiabilityValue } =
1463
+ calculateWorstCasePerpLiabilityValue(
1464
+ perpPosition,
1465
+ market,
1466
+ valuationPrice
1467
+ );
1468
+ baseAssetAmount = worstCaseBaseAssetAmount;
1469
+ liabilityValue = worstCaseLiabilityValue;
1470
+ } else {
1471
+ baseAssetAmount = perpPosition.baseAssetAmount;
1472
+ liabilityValue = calculatePerpLiabilityValue(
1473
+ baseAssetAmount,
1474
+ valuationPrice,
1475
+ isVariant(market.contractType, 'prediction')
1476
+ );
1477
+ }
1478
+
1479
+ if (marginCategory) {
1480
+ let marginRatio = new BN(
1481
+ calculateMarketMarginRatio(
1482
+ market,
1483
+ baseAssetAmount.abs(),
1484
+ marginCategory,
1485
+ this.getUserAccount().maxMarginRatio,
1486
+ this.isHighLeverageMode()
1487
+ )
1488
+ );
1489
+
1490
+ if (liquidationBuffer !== undefined) {
1491
+ marginRatio = marginRatio.add(liquidationBuffer);
1492
+ }
1493
+
1494
+ if (isVariant(market.status, 'settlement')) {
1495
+ marginRatio = ZERO;
1496
+ }
1497
+
1498
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
1499
+ market.quoteSpotMarketIndex
1500
+ );
1501
+ const quoteOraclePriceData = this.driftClient.getOracleDataForSpotMarket(
1502
+ QUOTE_SPOT_MARKET_INDEX
1503
+ );
1504
+
1505
+ let quotePrice;
1506
+ if (strict) {
1507
+ quotePrice = BN.max(
1508
+ quoteOraclePriceData.price,
1509
+ quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
1510
+ );
1511
+ } else {
1512
+ quotePrice = quoteOraclePriceData.price;
1513
+ }
1514
+
1515
+ liabilityValue = liabilityValue
1516
+ .mul(quotePrice)
1517
+ .div(PRICE_PRECISION)
1518
+ .mul(marginRatio)
1519
+ .div(MARGIN_PRECISION);
1520
+
1521
+ if (includeOpenOrders) {
1522
+ liabilityValue = liabilityValue.add(
1523
+ new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
1524
+ );
1525
+
1526
+ if (perpPosition.lpShares.gt(ZERO)) {
1527
+ liabilityValue = liabilityValue.add(
1528
+ BN.max(
1529
+ QUOTE_PRECISION,
1530
+ valuationPrice
1531
+ .mul(market.amm.orderStepSize)
1532
+ .mul(QUOTE_PRECISION)
1533
+ .div(AMM_RESERVE_PRECISION)
1534
+ .div(PRICE_PRECISION)
1535
+ )
1536
+ );
1537
+ }
1538
+ }
1539
+ }
1540
+
1541
+ return liabilityValue;
1542
+ }
1543
+
1544
+ /**
1545
+ * calculates position value of a single perp market in margin system
1546
+ * @returns : Precision QUOTE_PRECISION
1547
+ */
1548
+ public getPerpMarketLiabilityValue(
1549
+ marketIndex: number,
1550
+ marginCategory?: MarginCategory,
1551
+ liquidationBuffer?: BN,
1552
+ includeOpenOrders?: boolean,
1553
+ strict = false
1554
+ ): BN {
1555
+ const perpPosition = this.getPerpPosition(marketIndex);
1556
+ return this.calculateWeightedPerpPositionLiability(
1557
+ perpPosition,
1558
+ marginCategory,
1559
+ liquidationBuffer,
1560
+ includeOpenOrders,
1561
+ strict
1562
+ );
1563
+ }
1564
+
1565
+ /**
1566
+ * calculates sum of position value across all positions in margin system
1567
+ * @returns : Precision QUOTE_PRECISION
1568
+ */
1569
+ getTotalPerpPositionLiability(
1570
+ marginCategory?: MarginCategory,
1571
+ liquidationBuffer?: BN,
1572
+ includeOpenOrders?: boolean,
1573
+ strict = false
1574
+ ): BN {
1575
+ return this.getActivePerpPositions().reduce(
1576
+ (totalPerpValue, perpPosition) => {
1577
+ const baseAssetValue = this.calculateWeightedPerpPositionLiability(
1578
+ perpPosition,
1579
+ marginCategory,
1580
+ liquidationBuffer,
1581
+ includeOpenOrders,
1582
+ strict
1583
+ );
1584
+ return totalPerpValue.add(baseAssetValue);
1585
+ },
1586
+ ZERO
1587
+ );
1588
+ }
1589
+
1590
+ /**
1591
+ * calculates position value based on oracle
1592
+ * @returns : Precision QUOTE_PRECISION
1593
+ */
1594
+ public getPerpPositionValue(
1595
+ marketIndex: number,
1596
+ oraclePriceData: OraclePriceData,
1597
+ includeOpenOrders = false
1598
+ ): BN {
1599
+ const userPosition =
1600
+ this.getPerpPositionWithLPSettle(
1601
+ marketIndex,
1602
+ undefined,
1603
+ false,
1604
+ true
1605
+ )[0] || this.getEmptyPosition(marketIndex);
1606
+ const market = this.driftClient.getPerpMarketAccount(
1607
+ userPosition.marketIndex
1608
+ );
1609
+ return calculateBaseAssetValueWithOracle(
1610
+ market,
1611
+ userPosition,
1612
+ oraclePriceData,
1613
+ includeOpenOrders
1614
+ );
1615
+ }
1616
+
1617
+ /**
1618
+ * calculates position liabiltiy value in margin system
1619
+ * @returns : Precision QUOTE_PRECISION
1620
+ */
1621
+ public getPerpLiabilityValue(
1622
+ marketIndex: number,
1623
+ oraclePriceData: OraclePriceData,
1624
+ includeOpenOrders = false
1625
+ ): BN {
1626
+ const userPosition =
1627
+ this.getPerpPositionWithLPSettle(
1628
+ marketIndex,
1629
+ undefined,
1630
+ false,
1631
+ true
1632
+ )[0] || this.getEmptyPosition(marketIndex);
1633
+ const market = this.driftClient.getPerpMarketAccount(
1634
+ userPosition.marketIndex
1635
+ );
1636
+
1637
+ if (includeOpenOrders) {
1638
+ return calculateWorstCasePerpLiabilityValue(
1639
+ userPosition,
1640
+ market,
1641
+ oraclePriceData.price
1642
+ ).worstCaseLiabilityValue;
1643
+ } else {
1644
+ return calculatePerpLiabilityValue(
1645
+ userPosition.baseAssetAmount,
1646
+ oraclePriceData.price,
1647
+ isVariant(market.contractType, 'prediction')
1648
+ );
1649
+ }
1650
+ }
1651
+
1652
+ public getPositionSide(
1653
+ currentPosition: Pick<PerpPosition, 'baseAssetAmount'>
1654
+ ): PositionDirection | undefined {
1655
+ if (currentPosition.baseAssetAmount.gt(ZERO)) {
1656
+ return PositionDirection.LONG;
1657
+ } else if (currentPosition.baseAssetAmount.lt(ZERO)) {
1658
+ return PositionDirection.SHORT;
1659
+ } else {
1660
+ return undefined;
1661
+ }
1662
+ }
1663
+
1664
+ /**
1665
+ * calculates average exit price (optionally for closing up to 100% of position)
1666
+ * @returns : Precision PRICE_PRECISION
1667
+ */
1668
+ public getPositionEstimatedExitPriceAndPnl(
1669
+ position: PerpPosition,
1670
+ amountToClose?: BN,
1671
+ useAMMClose = false
1672
+ ): [BN, BN] {
1673
+ const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
1674
+
1675
+ const entryPrice = calculateEntryPrice(position);
1676
+
1677
+ const oraclePriceData = this.getOracleDataForPerpMarket(
1678
+ position.marketIndex
1679
+ );
1680
+
1681
+ if (amountToClose) {
1682
+ if (amountToClose.eq(ZERO)) {
1683
+ return [calculateReservePrice(market, oraclePriceData), ZERO];
1684
+ }
1685
+ position = {
1686
+ baseAssetAmount: amountToClose,
1687
+ lastCumulativeFundingRate: position.lastCumulativeFundingRate,
1688
+ marketIndex: position.marketIndex,
1689
+ quoteAssetAmount: position.quoteAssetAmount,
1690
+ } as PerpPosition;
1691
+ }
1692
+
1693
+ let baseAssetValue: BN;
1694
+
1695
+ if (useAMMClose) {
1696
+ baseAssetValue = calculateBaseAssetValue(
1697
+ market,
1698
+ position,
1699
+ oraclePriceData
1700
+ );
1701
+ } else {
1702
+ baseAssetValue = calculateBaseAssetValueWithOracle(
1703
+ market,
1704
+ position,
1705
+ oraclePriceData
1706
+ );
1707
+ }
1708
+ if (position.baseAssetAmount.eq(ZERO)) {
1709
+ return [ZERO, ZERO];
1710
+ }
1711
+
1712
+ const exitPrice = baseAssetValue
1713
+ .mul(AMM_TO_QUOTE_PRECISION_RATIO)
1714
+ .mul(PRICE_PRECISION)
1715
+ .div(position.baseAssetAmount.abs());
1716
+
1717
+ const pnlPerBase = exitPrice.sub(entryPrice);
1718
+ const pnl = pnlPerBase
1719
+ .mul(position.baseAssetAmount)
1720
+ .div(PRICE_PRECISION)
1721
+ .div(AMM_TO_QUOTE_PRECISION_RATIO);
1722
+
1723
+ return [exitPrice, pnl];
1724
+ }
1725
+
1726
+ /**
1727
+ * calculates current user leverage which is (total liability size) / (net asset value)
1728
+ * @returns : Precision TEN_THOUSAND
1729
+ */
1730
+ public getLeverage(includeOpenOrders = true): BN {
1731
+ return this.calculateLeverageFromComponents(
1732
+ this.getLeverageComponents(includeOpenOrders)
1733
+ );
1734
+ }
1735
+
1736
+ calculateLeverageFromComponents({
1737
+ perpLiabilityValue,
1738
+ perpPnl,
1739
+ spotAssetValue,
1740
+ spotLiabilityValue,
1741
+ }: {
1742
+ perpLiabilityValue: BN;
1743
+ perpPnl: BN;
1744
+ spotAssetValue: BN;
1745
+ spotLiabilityValue: BN;
1746
+ }): BN {
1747
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
1748
+ const totalAssetValue = spotAssetValue.add(perpPnl);
1749
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
1750
+
1751
+ if (netAssetValue.eq(ZERO)) {
1752
+ return ZERO;
1753
+ }
1754
+
1755
+ return totalLiabilityValue.mul(TEN_THOUSAND).div(netAssetValue);
1756
+ }
1757
+
1758
+ getLeverageComponents(
1759
+ includeOpenOrders = true,
1760
+ marginCategory: MarginCategory = undefined
1761
+ ): {
1762
+ perpLiabilityValue: BN;
1763
+ perpPnl: BN;
1764
+ spotAssetValue: BN;
1765
+ spotLiabilityValue: BN;
1766
+ } {
1767
+ const perpLiability = this.getTotalPerpPositionLiability(
1768
+ marginCategory,
1769
+ undefined,
1770
+ includeOpenOrders
1771
+ );
1772
+ const perpPnl = this.getUnrealizedPNL(true, undefined, marginCategory);
1773
+
1774
+ const {
1775
+ totalAssetValue: spotAssetValue,
1776
+ totalLiabilityValue: spotLiabilityValue,
1777
+ } = this.getSpotMarketAssetAndLiabilityValue(
1778
+ undefined,
1779
+ marginCategory,
1780
+ undefined,
1781
+ includeOpenOrders
1782
+ );
1783
+
1784
+ return {
1785
+ perpLiabilityValue: perpLiability,
1786
+ perpPnl,
1787
+ spotAssetValue,
1788
+ spotLiabilityValue,
1789
+ };
1790
+ }
1791
+
1792
+ isDustDepositPosition(spotMarketAccount: SpotMarketAccount): boolean {
1793
+ const marketIndex = spotMarketAccount.marketIndex;
1794
+
1795
+ const spotPosition = this.getSpotPosition(spotMarketAccount.marketIndex);
1796
+
1797
+ if (isSpotPositionAvailable(spotPosition)) {
1798
+ return false;
1799
+ }
1800
+
1801
+ const depositAmount = this.getTokenAmount(spotMarketAccount.marketIndex);
1802
+
1803
+ if (depositAmount.lte(ZERO)) {
1804
+ return false;
1805
+ }
1806
+
1807
+ const oraclePriceData = this.getOracleDataForSpotMarket(marketIndex);
1808
+
1809
+ const strictOraclePrice = new StrictOraclePrice(
1810
+ oraclePriceData.price,
1811
+ oraclePriceData.twap
1812
+ );
1813
+
1814
+ const balanceValue = this.getSpotAssetValue(
1815
+ depositAmount,
1816
+ strictOraclePrice,
1817
+ spotMarketAccount
1818
+ );
1819
+
1820
+ if (balanceValue.lt(DUST_POSITION_SIZE)) {
1821
+ return true;
1822
+ }
1823
+
1824
+ return false;
1825
+ }
1826
+
1827
+ getSpotMarketAccountsWithDustPosition() {
1828
+ const spotMarketAccounts = this.driftClient.getSpotMarketAccounts();
1829
+
1830
+ const dustPositionAccounts: SpotMarketAccount[] = [];
1831
+
1832
+ for (const spotMarketAccount of spotMarketAccounts) {
1833
+ const isDust = this.isDustDepositPosition(spotMarketAccount);
1834
+ if (isDust) {
1835
+ dustPositionAccounts.push(spotMarketAccount);
1836
+ }
1837
+ }
1838
+
1839
+ return dustPositionAccounts;
1840
+ }
1841
+
1842
+ getTotalLiabilityValue(marginCategory?: MarginCategory): BN {
1843
+ return this.getTotalPerpPositionLiability(
1844
+ marginCategory,
1845
+ undefined,
1846
+ true
1847
+ ).add(
1848
+ this.getSpotMarketLiabilityValue(
1849
+ undefined,
1850
+ marginCategory,
1851
+ undefined,
1852
+ true
1853
+ )
1854
+ );
1855
+ }
1856
+
1857
+ getTotalAssetValue(marginCategory?: MarginCategory): BN {
1858
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(
1859
+ this.getUnrealizedPNL(true, undefined, marginCategory)
1860
+ );
1861
+ }
1862
+
1863
+ getNetUsdValue(): BN {
1864
+ const netSpotValue = this.getNetSpotMarketValue();
1865
+ const unrealizedPnl = this.getUnrealizedPNL(true, undefined, undefined);
1866
+ return netSpotValue.add(unrealizedPnl);
1867
+ }
1868
+
1869
+ /**
1870
+ * Calculates the all time P&L of the user.
1871
+ *
1872
+ * Net withdraws + Net spot market value + Net unrealized P&L -
1873
+ */
1874
+ getTotalAllTimePnl(): BN {
1875
+ const netUsdValue = this.getNetUsdValue();
1876
+ const totalDeposits = this.getUserAccount().totalDeposits;
1877
+ const totalWithdraws = this.getUserAccount().totalWithdraws;
1878
+
1879
+ const totalPnl = netUsdValue.add(totalWithdraws).sub(totalDeposits);
1880
+
1881
+ return totalPnl;
1882
+ }
1883
+
1884
+ /**
1885
+ * calculates max allowable leverage exceeding hitting requirement category
1886
+ * for large sizes where imf factor activates, result is a lower bound
1887
+ * @param marginCategory {Initial, Maintenance}
1888
+ * @param isLp if calculating max leveraging for adding lp, need to add buffer
1889
+ * @returns : Precision TEN_THOUSAND
1890
+ */
1891
+ public getMaxLeverageForPerp(
1892
+ perpMarketIndex: number,
1893
+ marginCategory: MarginCategory = 'Initial',
1894
+ isLp = false
1895
+ ): BN {
1896
+ const market = this.driftClient.getPerpMarketAccount(perpMarketIndex);
1897
+ const marketPrice =
1898
+ this.driftClient.getOracleDataForPerpMarket(perpMarketIndex).price;
1899
+
1900
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
1901
+ this.getLeverageComponents();
1902
+
1903
+ const totalAssetValue = spotAssetValue.add(perpPnl);
1904
+
1905
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
1906
+
1907
+ if (netAssetValue.eq(ZERO)) {
1908
+ return ZERO;
1909
+ }
1910
+
1911
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
1912
+
1913
+ const lpBuffer = isLp
1914
+ ? marketPrice.mul(market.amm.orderStepSize).div(AMM_RESERVE_PRECISION)
1915
+ : ZERO;
1916
+
1917
+ const freeCollateral = this.getFreeCollateral().sub(lpBuffer);
1918
+
1919
+ let rawMarginRatio;
1920
+
1921
+ switch (marginCategory) {
1922
+ case 'Initial':
1923
+ rawMarginRatio = Math.max(
1924
+ market.marginRatioInitial,
1925
+ this.getUserAccount().maxMarginRatio
1926
+ );
1927
+ break;
1928
+ case 'Maintenance':
1929
+ rawMarginRatio = market.marginRatioMaintenance;
1930
+ break;
1931
+ default:
1932
+ rawMarginRatio = market.marginRatioInitial;
1933
+ break;
1934
+ }
1935
+
1936
+ // absolute max fesible size (upper bound)
1937
+ const maxSize = BN.max(
1938
+ ZERO,
1939
+ freeCollateral
1940
+ .mul(MARGIN_PRECISION)
1941
+ .div(new BN(rawMarginRatio))
1942
+ .mul(PRICE_PRECISION)
1943
+ .div(marketPrice)
1944
+ );
1945
+
1946
+ // margin ratio incorporting upper bound on size
1947
+ let marginRatio = calculateMarketMarginRatio(
1948
+ market,
1949
+ maxSize,
1950
+ marginCategory,
1951
+ this.getUserAccount().maxMarginRatio,
1952
+ this.isHighLeverageMode()
1953
+ );
1954
+
1955
+ // use more fesible size since imf factor activated
1956
+ let attempts = 0;
1957
+ while (marginRatio > rawMarginRatio + 1e-4 && attempts < 10) {
1958
+ // more fesible size (upper bound)
1959
+ const targetSize = BN.max(
1960
+ ZERO,
1961
+ freeCollateral
1962
+ .mul(MARGIN_PRECISION)
1963
+ .div(new BN(marginRatio))
1964
+ .mul(PRICE_PRECISION)
1965
+ .div(marketPrice)
1966
+ );
1967
+
1968
+ // margin ratio incorporting more fesible target size
1969
+ marginRatio = calculateMarketMarginRatio(
1970
+ market,
1971
+ targetSize,
1972
+ marginCategory,
1973
+ this.getUserAccount().maxMarginRatio,
1974
+ this.isHighLeverageMode()
1975
+ );
1976
+ attempts += 1;
1977
+ }
1978
+
1979
+ // how much more liabilities can be opened w remaining free collateral
1980
+ const additionalLiabilities = freeCollateral
1981
+ .mul(MARGIN_PRECISION)
1982
+ .div(new BN(marginRatio));
1983
+
1984
+ return totalLiabilityValue
1985
+ .add(additionalLiabilities)
1986
+ .mul(TEN_THOUSAND)
1987
+ .div(netAssetValue);
1988
+ }
1989
+
1990
+ /**
1991
+ * calculates max allowable leverage exceeding hitting requirement category
1992
+ * @param spotMarketIndex
1993
+ * @param direction
1994
+ * @returns : Precision TEN_THOUSAND
1995
+ */
1996
+ public getMaxLeverageForSpot(
1997
+ spotMarketIndex: number,
1998
+ direction: PositionDirection
1999
+ ): BN {
2000
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
2001
+ this.getLeverageComponents();
2002
+
2003
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
2004
+ const totalAssetValue = spotAssetValue.add(perpPnl);
2005
+
2006
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
2007
+
2008
+ if (netAssetValue.eq(ZERO)) {
2009
+ return ZERO;
2010
+ }
2011
+
2012
+ const currentQuoteAssetValue = this.getSpotMarketAssetValue(
2013
+ QUOTE_SPOT_MARKET_INDEX
2014
+ );
2015
+ const currentQuoteLiabilityValue = this.getSpotMarketLiabilityValue(
2016
+ QUOTE_SPOT_MARKET_INDEX
2017
+ );
2018
+ const currentQuoteValue = currentQuoteAssetValue.sub(
2019
+ currentQuoteLiabilityValue
2020
+ );
2021
+
2022
+ const currentSpotMarketAssetValue =
2023
+ this.getSpotMarketAssetValue(spotMarketIndex);
2024
+ const currentSpotMarketLiabilityValue =
2025
+ this.getSpotMarketLiabilityValue(spotMarketIndex);
2026
+ const currentSpotMarketNetValue = currentSpotMarketAssetValue.sub(
2027
+ currentSpotMarketLiabilityValue
2028
+ );
2029
+
2030
+ const tradeQuoteAmount = this.getMaxTradeSizeUSDCForSpot(
2031
+ spotMarketIndex,
2032
+ direction,
2033
+ currentQuoteAssetValue,
2034
+ currentSpotMarketNetValue
2035
+ );
2036
+
2037
+ let assetValueToAdd = ZERO;
2038
+ let liabilityValueToAdd = ZERO;
2039
+
2040
+ const newQuoteNetValue = isVariant(direction, 'short')
2041
+ ? currentQuoteValue.add(tradeQuoteAmount)
2042
+ : currentQuoteValue.sub(tradeQuoteAmount);
2043
+ const newQuoteAssetValue = BN.max(newQuoteNetValue, ZERO);
2044
+ const newQuoteLiabilityValue = BN.min(newQuoteNetValue, ZERO).abs();
2045
+
2046
+ assetValueToAdd = assetValueToAdd.add(
2047
+ newQuoteAssetValue.sub(currentQuoteAssetValue)
2048
+ );
2049
+ liabilityValueToAdd = liabilityValueToAdd.add(
2050
+ newQuoteLiabilityValue.sub(currentQuoteLiabilityValue)
2051
+ );
2052
+
2053
+ const newSpotMarketNetValue = isVariant(direction, 'long')
2054
+ ? currentSpotMarketNetValue.add(tradeQuoteAmount)
2055
+ : currentSpotMarketNetValue.sub(tradeQuoteAmount);
2056
+ const newSpotMarketAssetValue = BN.max(newSpotMarketNetValue, ZERO);
2057
+ const newSpotMarketLiabilityValue = BN.min(
2058
+ newSpotMarketNetValue,
2059
+ ZERO
2060
+ ).abs();
2061
+
2062
+ assetValueToAdd = assetValueToAdd.add(
2063
+ newSpotMarketAssetValue.sub(currentSpotMarketAssetValue)
2064
+ );
2065
+ liabilityValueToAdd = liabilityValueToAdd.add(
2066
+ newSpotMarketLiabilityValue.sub(currentSpotMarketLiabilityValue)
2067
+ );
2068
+
2069
+ const finalTotalAssetValue = totalAssetValue.add(assetValueToAdd);
2070
+ const finalTotalSpotLiability = spotLiabilityValue.add(liabilityValueToAdd);
2071
+
2072
+ const finalTotalLiabilityValue =
2073
+ totalLiabilityValue.add(liabilityValueToAdd);
2074
+
2075
+ const finalNetAssetValue = finalTotalAssetValue.sub(
2076
+ finalTotalSpotLiability
2077
+ );
2078
+
2079
+ return finalTotalLiabilityValue.mul(TEN_THOUSAND).div(finalNetAssetValue);
2080
+ }
2081
+
2082
+ /**
2083
+ * calculates margin ratio: 1 / leverage
2084
+ * @returns : Precision TEN_THOUSAND
2085
+ */
2086
+ public getMarginRatio(): BN {
2087
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
2088
+ this.getLeverageComponents();
2089
+
2090
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
2091
+ const totalAssetValue = spotAssetValue.add(perpPnl);
2092
+
2093
+ if (totalLiabilityValue.eq(ZERO)) {
2094
+ return BN_MAX;
2095
+ }
2096
+
2097
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
2098
+
2099
+ return netAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
2100
+ }
2101
+
2102
+ public canBeLiquidated(): {
2103
+ canBeLiquidated: boolean;
2104
+ marginRequirement: BN;
2105
+ totalCollateral: BN;
2106
+ } {
2107
+ const totalCollateral = this.getTotalCollateral('Maintenance');
2108
+
2109
+ const marginRequirement = this.getMaintenanceMarginRequirement();
2110
+ const canBeLiquidated = totalCollateral.lt(marginRequirement);
2111
+
2112
+ return {
2113
+ canBeLiquidated,
2114
+ marginRequirement,
2115
+ totalCollateral,
2116
+ };
2117
+ }
2118
+
2119
+ public isBeingLiquidated(): boolean {
2120
+ return (
2121
+ (this.getUserAccount().status &
2122
+ (UserStatus.BEING_LIQUIDATED | UserStatus.BANKRUPT)) >
2123
+ 0
2124
+ );
2125
+ }
2126
+
2127
+ public hasStatus(status: UserStatus): boolean {
2128
+ return (this.getUserAccount().status & status) > 0;
2129
+ }
2130
+
2131
+ public isBankrupt(): boolean {
2132
+ return (this.getUserAccount().status & UserStatus.BANKRUPT) > 0;
2133
+ }
2134
+
2135
+ public isHighLeverageMode(): boolean {
2136
+ return isVariant(this.getUserAccount().marginMode, 'highLeverage');
2137
+ }
2138
+
2139
+ /**
2140
+ * Checks if any user position cumulative funding differs from respective market cumulative funding
2141
+ * @returns
2142
+ */
2143
+ public needsToSettleFundingPayment(): boolean {
2144
+ for (const userPosition of this.getUserAccount().perpPositions) {
2145
+ if (userPosition.baseAssetAmount.eq(ZERO)) {
2146
+ continue;
2147
+ }
2148
+
2149
+ const market = this.driftClient.getPerpMarketAccount(
2150
+ userPosition.marketIndex
2151
+ );
2152
+ if (
2153
+ market.amm.cumulativeFundingRateLong.eq(
2154
+ userPosition.lastCumulativeFundingRate
2155
+ ) ||
2156
+ market.amm.cumulativeFundingRateShort.eq(
2157
+ userPosition.lastCumulativeFundingRate
2158
+ )
2159
+ ) {
2160
+ continue;
2161
+ }
2162
+
2163
+ return true;
2164
+ }
2165
+ return false;
2166
+ }
2167
+
2168
+ /**
2169
+ * Calculate the liquidation price of a spot position
2170
+ * @param marketIndex
2171
+ * @returns Precision : PRICE_PRECISION
2172
+ */
2173
+ public spotLiquidationPrice(
2174
+ marketIndex: number,
2175
+ positionBaseSizeChange: BN = ZERO
2176
+ ): BN {
2177
+ const currentSpotPosition = this.getSpotPosition(marketIndex);
2178
+
2179
+ if (!currentSpotPosition) {
2180
+ return new BN(-1);
2181
+ }
2182
+
2183
+ const totalCollateral = this.getTotalCollateral('Maintenance');
2184
+ const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
2185
+ const freeCollateral = BN.max(
2186
+ ZERO,
2187
+ totalCollateral.sub(maintenanceMarginRequirement)
2188
+ );
2189
+
2190
+ const market = this.driftClient.getSpotMarketAccount(marketIndex);
2191
+ let signedTokenAmount = getSignedTokenAmount(
2192
+ getTokenAmount(
2193
+ currentSpotPosition.scaledBalance,
2194
+ market,
2195
+ currentSpotPosition.balanceType
2196
+ ),
2197
+ currentSpotPosition.balanceType
2198
+ );
2199
+ signedTokenAmount = signedTokenAmount.add(positionBaseSizeChange);
2200
+
2201
+ if (signedTokenAmount.eq(ZERO)) {
2202
+ return new BN(-1);
2203
+ }
2204
+
2205
+ let freeCollateralDelta = this.calculateFreeCollateralDeltaForSpot(
2206
+ market,
2207
+ signedTokenAmount
2208
+ );
2209
+
2210
+ const oracle = market.oracle;
2211
+ const perpMarketWithSameOracle = this.driftClient
2212
+ .getPerpMarketAccounts()
2213
+ .find((market) => market.amm.oracle.equals(oracle));
2214
+ const oraclePrice =
2215
+ this.driftClient.getOracleDataForSpotMarket(marketIndex).price;
2216
+ if (perpMarketWithSameOracle) {
2217
+ const perpPosition = this.getPerpPositionWithLPSettle(
2218
+ perpMarketWithSameOracle.marketIndex,
2219
+ undefined,
2220
+ true
2221
+ )[0];
2222
+ if (perpPosition) {
2223
+ const freeCollateralDeltaForPerp =
2224
+ this.calculateFreeCollateralDeltaForPerp(
2225
+ perpMarketWithSameOracle,
2226
+ perpPosition,
2227
+ ZERO,
2228
+ oraclePrice
2229
+ );
2230
+
2231
+ freeCollateralDelta = freeCollateralDelta.add(
2232
+ freeCollateralDeltaForPerp || ZERO
2233
+ );
2234
+ }
2235
+ }
2236
+
2237
+ if (freeCollateralDelta.eq(ZERO)) {
2238
+ return new BN(-1);
2239
+ }
2240
+
2241
+ const liqPriceDelta = freeCollateral
2242
+ .mul(QUOTE_PRECISION)
2243
+ .div(freeCollateralDelta);
2244
+
2245
+ const liqPrice = oraclePrice.sub(liqPriceDelta);
2246
+
2247
+ if (liqPrice.lt(ZERO)) {
2248
+ return new BN(-1);
2249
+ }
2250
+
2251
+ return liqPrice;
2252
+ }
2253
+
2254
+ /**
2255
+ * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
2256
+ * @param marketIndex
2257
+ * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^9
2258
+ * @param estimatedEntryPrice
2259
+ * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
2260
+ * @param includeOpenOrders
2261
+ * @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral)
2262
+ * @returns Precision : PRICE_PRECISION
2263
+ */
2264
+ public liquidationPrice(
2265
+ marketIndex: number,
2266
+ positionBaseSizeChange: BN = ZERO,
2267
+ estimatedEntryPrice: BN = ZERO,
2268
+ marginCategory: MarginCategory = 'Maintenance',
2269
+ includeOpenOrders = false,
2270
+ offsetCollateral = ZERO
2271
+ ): BN {
2272
+ const totalCollateral = this.getTotalCollateral(
2273
+ marginCategory,
2274
+ false,
2275
+ includeOpenOrders
2276
+ );
2277
+ const marginRequirement = this.getMarginRequirement(
2278
+ marginCategory,
2279
+ undefined,
2280
+ false,
2281
+ includeOpenOrders
2282
+ );
2283
+ let freeCollateral = BN.max(
2284
+ ZERO,
2285
+ totalCollateral.sub(marginRequirement)
2286
+ ).add(offsetCollateral);
2287
+
2288
+ const oracle =
2289
+ this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
2290
+
2291
+ const oraclePrice =
2292
+ this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
2293
+
2294
+ const market = this.driftClient.getPerpMarketAccount(marketIndex);
2295
+ const currentPerpPosition =
2296
+ this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
2297
+ this.getEmptyPosition(marketIndex);
2298
+
2299
+ positionBaseSizeChange = standardizeBaseAssetAmount(
2300
+ positionBaseSizeChange,
2301
+ market.amm.orderStepSize
2302
+ );
2303
+
2304
+ const freeCollateralChangeFromNewPosition =
2305
+ this.calculateEntriesEffectOnFreeCollateral(
2306
+ market,
2307
+ oraclePrice,
2308
+ currentPerpPosition,
2309
+ positionBaseSizeChange,
2310
+ estimatedEntryPrice,
2311
+ includeOpenOrders
2312
+ );
2313
+
2314
+ freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
2315
+
2316
+ let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(
2317
+ market,
2318
+ currentPerpPosition,
2319
+ positionBaseSizeChange,
2320
+ oraclePrice,
2321
+ marginCategory,
2322
+ includeOpenOrders
2323
+ );
2324
+
2325
+ if (!freeCollateralDelta) {
2326
+ return new BN(-1);
2327
+ }
2328
+
2329
+ const spotMarketWithSameOracle = this.driftClient
2330
+ .getSpotMarketAccounts()
2331
+ .find((market) => market.oracle.equals(oracle));
2332
+ if (spotMarketWithSameOracle) {
2333
+ const spotPosition = this.getSpotPosition(
2334
+ spotMarketWithSameOracle.marketIndex
2335
+ );
2336
+ if (spotPosition) {
2337
+ const signedTokenAmount = getSignedTokenAmount(
2338
+ getTokenAmount(
2339
+ spotPosition.scaledBalance,
2340
+ spotMarketWithSameOracle,
2341
+ spotPosition.balanceType
2342
+ ),
2343
+ spotPosition.balanceType
2344
+ );
2345
+
2346
+ const spotFreeCollateralDelta =
2347
+ this.calculateFreeCollateralDeltaForSpot(
2348
+ spotMarketWithSameOracle,
2349
+ signedTokenAmount,
2350
+ marginCategory
2351
+ );
2352
+ freeCollateralDelta = freeCollateralDelta.add(
2353
+ spotFreeCollateralDelta || ZERO
2354
+ );
2355
+ }
2356
+ }
2357
+
2358
+ if (freeCollateralDelta.eq(ZERO)) {
2359
+ return new BN(-1);
2360
+ }
2361
+
2362
+ const liqPriceDelta = freeCollateral
2363
+ .mul(QUOTE_PRECISION)
2364
+ .div(freeCollateralDelta);
2365
+
2366
+ const liqPrice = oraclePrice.sub(liqPriceDelta);
2367
+
2368
+ if (liqPrice.lt(ZERO)) {
2369
+ return new BN(-1);
2370
+ }
2371
+
2372
+ return liqPrice;
2373
+ }
2374
+
2375
+ calculateEntriesEffectOnFreeCollateral(
2376
+ market: PerpMarketAccount,
2377
+ oraclePrice: BN,
2378
+ perpPosition: PerpPosition,
2379
+ positionBaseSizeChange: BN,
2380
+ estimatedEntryPrice: BN,
2381
+ includeOpenOrders: boolean
2382
+ ): BN {
2383
+ let freeCollateralChange = ZERO;
2384
+
2385
+ // update free collateral to account for change in pnl from new position
2386
+ if (!estimatedEntryPrice.eq(ZERO) && !positionBaseSizeChange.eq(ZERO)) {
2387
+ const costBasis = oraclePrice
2388
+ .mul(positionBaseSizeChange.abs())
2389
+ .div(BASE_PRECISION);
2390
+ const newPositionValue = estimatedEntryPrice
2391
+ .mul(positionBaseSizeChange.abs())
2392
+ .div(BASE_PRECISION);
2393
+ if (positionBaseSizeChange.gt(ZERO)) {
2394
+ freeCollateralChange = costBasis.sub(newPositionValue);
2395
+ } else {
2396
+ freeCollateralChange = newPositionValue.sub(costBasis);
2397
+ }
2398
+
2399
+ // assume worst fee tier
2400
+ const takerFeeTier =
2401
+ this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
2402
+ const takerFee = newPositionValue
2403
+ .muln(takerFeeTier.feeNumerator)
2404
+ .divn(takerFeeTier.feeDenominator);
2405
+ freeCollateralChange = freeCollateralChange.sub(takerFee);
2406
+ }
2407
+
2408
+ const calculateMarginRequirement = (perpPosition: PerpPosition) => {
2409
+ let baseAssetAmount: BN;
2410
+ let liabilityValue: BN;
2411
+ if (includeOpenOrders) {
2412
+ const { worstCaseBaseAssetAmount, worstCaseLiabilityValue } =
2413
+ calculateWorstCasePerpLiabilityValue(
2414
+ perpPosition,
2415
+ market,
2416
+ oraclePrice
2417
+ );
2418
+ baseAssetAmount = worstCaseBaseAssetAmount;
2419
+ liabilityValue = worstCaseLiabilityValue;
2420
+ } else {
2421
+ baseAssetAmount = perpPosition.baseAssetAmount;
2422
+ liabilityValue = calculatePerpLiabilityValue(
2423
+ baseAssetAmount,
2424
+ oraclePrice,
2425
+ isVariant(market.contractType, 'prediction')
2426
+ );
2427
+ }
2428
+
2429
+ const marginRatio = calculateMarketMarginRatio(
2430
+ market,
2431
+ baseAssetAmount.abs(),
2432
+ 'Maintenance',
2433
+ this.getUserAccount().maxMarginRatio,
2434
+ this.isHighLeverageMode()
2435
+ );
2436
+
2437
+ return liabilityValue.mul(new BN(marginRatio)).div(MARGIN_PRECISION);
2438
+ };
2439
+
2440
+ const freeCollateralConsumptionBefore =
2441
+ calculateMarginRequirement(perpPosition);
2442
+
2443
+ const perpPositionAfter = Object.assign({}, perpPosition);
2444
+ perpPositionAfter.baseAssetAmount = perpPositionAfter.baseAssetAmount.add(
2445
+ positionBaseSizeChange
2446
+ );
2447
+
2448
+ const freeCollateralConsumptionAfter =
2449
+ calculateMarginRequirement(perpPositionAfter);
2450
+
2451
+ return freeCollateralChange.sub(
2452
+ freeCollateralConsumptionAfter.sub(freeCollateralConsumptionBefore)
2453
+ );
2454
+ }
2455
+
2456
+ calculateFreeCollateralDeltaForPerp(
2457
+ market: PerpMarketAccount,
2458
+ perpPosition: PerpPosition,
2459
+ positionBaseSizeChange: BN,
2460
+ oraclePrice: BN,
2461
+ marginCategory: MarginCategory = 'Maintenance',
2462
+ includeOpenOrders = false
2463
+ ): BN | undefined {
2464
+ const baseAssetAmount = includeOpenOrders
2465
+ ? calculateWorstCaseBaseAssetAmount(perpPosition, market, oraclePrice)
2466
+ : perpPosition.baseAssetAmount;
2467
+
2468
+ // zero if include orders == false
2469
+ const orderBaseAssetAmount = baseAssetAmount.sub(
2470
+ perpPosition.baseAssetAmount
2471
+ );
2472
+
2473
+ const proposedBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange);
2474
+
2475
+ const marginRatio = calculateMarketMarginRatio(
2476
+ market,
2477
+ proposedBaseAssetAmount.abs(),
2478
+ marginCategory,
2479
+ this.getUserAccount().maxMarginRatio,
2480
+ this.isHighLeverageMode()
2481
+ );
2482
+ const marginRatioQuotePrecision = new BN(marginRatio)
2483
+ .mul(QUOTE_PRECISION)
2484
+ .div(MARGIN_PRECISION);
2485
+
2486
+ if (proposedBaseAssetAmount.eq(ZERO)) {
2487
+ return undefined;
2488
+ }
2489
+
2490
+ let freeCollateralDelta = ZERO;
2491
+ if (isVariant(market.contractType, 'prediction')) {
2492
+ // for prediction market, increase in pnl and margin requirement will net out for position
2493
+ // open order margin requirement will change with price though
2494
+ if (orderBaseAssetAmount.gt(ZERO)) {
2495
+ freeCollateralDelta = marginRatioQuotePrecision.neg();
2496
+ } else if (orderBaseAssetAmount.lt(ZERO)) {
2497
+ freeCollateralDelta = marginRatioQuotePrecision;
2498
+ }
2499
+ } else {
2500
+ if (proposedBaseAssetAmount.gt(ZERO)) {
2501
+ freeCollateralDelta = QUOTE_PRECISION.sub(marginRatioQuotePrecision)
2502
+ .mul(proposedBaseAssetAmount)
2503
+ .div(BASE_PRECISION);
2504
+ } else {
2505
+ freeCollateralDelta = QUOTE_PRECISION.neg()
2506
+ .sub(marginRatioQuotePrecision)
2507
+ .mul(proposedBaseAssetAmount.abs())
2508
+ .div(BASE_PRECISION);
2509
+ }
2510
+
2511
+ if (!orderBaseAssetAmount.eq(ZERO)) {
2512
+ freeCollateralDelta = freeCollateralDelta.sub(
2513
+ marginRatioQuotePrecision
2514
+ .mul(orderBaseAssetAmount.abs())
2515
+ .div(BASE_PRECISION)
2516
+ );
2517
+ }
2518
+ }
2519
+
2520
+ return freeCollateralDelta;
2521
+ }
2522
+
2523
+ calculateFreeCollateralDeltaForSpot(
2524
+ market: SpotMarketAccount,
2525
+ signedTokenAmount: BN,
2526
+ marginCategory: MarginCategory = 'Maintenance'
2527
+ ): BN {
2528
+ const tokenPrecision = new BN(Math.pow(10, market.decimals));
2529
+
2530
+ if (signedTokenAmount.gt(ZERO)) {
2531
+ const assetWeight = calculateAssetWeight(
2532
+ signedTokenAmount,
2533
+ this.driftClient.getOracleDataForSpotMarket(market.marketIndex).price,
2534
+ market,
2535
+ marginCategory
2536
+ );
2537
+
2538
+ return QUOTE_PRECISION.mul(assetWeight)
2539
+ .div(SPOT_MARKET_WEIGHT_PRECISION)
2540
+ .mul(signedTokenAmount)
2541
+ .div(tokenPrecision);
2542
+ } else {
2543
+ const liabilityWeight = calculateLiabilityWeight(
2544
+ signedTokenAmount.abs(),
2545
+ market,
2546
+ marginCategory
2547
+ );
2548
+
2549
+ return QUOTE_PRECISION.neg()
2550
+ .mul(liabilityWeight)
2551
+ .div(SPOT_MARKET_WEIGHT_PRECISION)
2552
+ .mul(signedTokenAmount.abs())
2553
+ .div(tokenPrecision);
2554
+ }
2555
+ }
2556
+
2557
+ /**
2558
+ * Calculates the estimated liquidation price for a position after closing a quote amount of the position.
2559
+ * @param positionMarketIndex
2560
+ * @param closeQuoteAmount
2561
+ * @returns : Precision PRICE_PRECISION
2562
+ */
2563
+ public liquidationPriceAfterClose(
2564
+ positionMarketIndex: number,
2565
+ closeQuoteAmount: BN,
2566
+ estimatedEntryPrice: BN = ZERO
2567
+ ): BN {
2568
+ const currentPosition =
2569
+ this.getPerpPositionWithLPSettle(
2570
+ positionMarketIndex,
2571
+ undefined,
2572
+ true
2573
+ )[0] || this.getEmptyPosition(positionMarketIndex);
2574
+
2575
+ const closeBaseAmount = currentPosition.baseAssetAmount
2576
+ .mul(closeQuoteAmount)
2577
+ .div(currentPosition.quoteAssetAmount.abs())
2578
+ .add(
2579
+ currentPosition.baseAssetAmount
2580
+ .mul(closeQuoteAmount)
2581
+ .mod(currentPosition.quoteAssetAmount.abs())
2582
+ )
2583
+ .neg();
2584
+
2585
+ return this.liquidationPrice(
2586
+ positionMarketIndex,
2587
+ closeBaseAmount,
2588
+ estimatedEntryPrice
2589
+ );
2590
+ }
2591
+
2592
+ public getMarginUSDCRequiredForTrade(
2593
+ targetMarketIndex: number,
2594
+ baseSize: BN,
2595
+ estEntryPrice?: BN
2596
+ ): BN {
2597
+ return calculateMarginUSDCRequiredForTrade(
2598
+ this.driftClient,
2599
+ targetMarketIndex,
2600
+ baseSize,
2601
+ this.getUserAccount().maxMarginRatio,
2602
+ undefined,
2603
+ estEntryPrice
2604
+ );
2605
+ }
2606
+
2607
+ public getCollateralDepositRequiredForTrade(
2608
+ targetMarketIndex: number,
2609
+ baseSize: BN,
2610
+ collateralIndex: number
2611
+ ): BN {
2612
+ return calculateCollateralDepositRequiredForTrade(
2613
+ this.driftClient,
2614
+ targetMarketIndex,
2615
+ baseSize,
2616
+ collateralIndex,
2617
+ this.getUserAccount().maxMarginRatio,
2618
+ false // assume user cant be high leverage if they havent created user account ?
2619
+ );
2620
+ }
2621
+
2622
+ /**
2623
+ * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
2624
+ *
2625
+ * To Calculate Max Quote Available:
2626
+ *
2627
+ * Case 1: SameSide
2628
+ * => Remaining quote to get to maxLeverage
2629
+ *
2630
+ * Case 2: NOT SameSide && currentLeverage <= maxLeverage
2631
+ * => Current opposite position x2 + remaining to get to maxLeverage
2632
+ *
2633
+ * Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
2634
+ * => strictly reduce current position size
2635
+ *
2636
+ * Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
2637
+ * => current position + remaining to get to maxLeverage
2638
+ *
2639
+ * @param targetMarketIndex
2640
+ * @param tradeSide
2641
+ * @param isLp
2642
+ * @returns { tradeSize: BN, oppositeSideTradeSize: BN} : Precision QUOTE_PRECISION
2643
+ */
2644
+ public getMaxTradeSizeUSDCForPerp(
2645
+ targetMarketIndex: number,
2646
+ tradeSide: PositionDirection,
2647
+ isLp = false
2648
+ ): { tradeSize: BN; oppositeSideTradeSize: BN } {
2649
+ let tradeSize = ZERO;
2650
+ let oppositeSideTradeSize = ZERO;
2651
+ const currentPosition =
2652
+ this.getPerpPositionWithLPSettle(targetMarketIndex, undefined, true)[0] ||
2653
+ this.getEmptyPosition(targetMarketIndex);
2654
+
2655
+ const targetSide = isVariant(tradeSide, 'short') ? 'short' : 'long';
2656
+
2657
+ const currentPositionSide = currentPosition?.baseAssetAmount.isNeg()
2658
+ ? 'short'
2659
+ : 'long';
2660
+
2661
+ const targetingSameSide = !currentPosition
2662
+ ? true
2663
+ : targetSide === currentPositionSide;
2664
+
2665
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
2666
+
2667
+ const marketAccount =
2668
+ this.driftClient.getPerpMarketAccount(targetMarketIndex);
2669
+
2670
+ const lpBuffer = isLp
2671
+ ? oracleData.price
2672
+ .mul(marketAccount.amm.orderStepSize)
2673
+ .div(AMM_RESERVE_PRECISION)
2674
+ : ZERO;
2675
+
2676
+ // 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.
2677
+ const oppositeSizeLiabilityValue = targetingSameSide
2678
+ ? ZERO
2679
+ : calculatePerpLiabilityValue(
2680
+ currentPosition.baseAssetAmount,
2681
+ oracleData.price,
2682
+ isVariant(marketAccount.contractType, 'prediction')
2683
+ );
2684
+
2685
+ const maxPositionSize = this.getPerpBuyingPower(
2686
+ targetMarketIndex,
2687
+ lpBuffer
2688
+ );
2689
+
2690
+ if (maxPositionSize.gte(ZERO)) {
2691
+ if (oppositeSizeLiabilityValue.eq(ZERO)) {
2692
+ // case 1 : Regular trade where current total position less than max, and no opposite position to account for
2693
+ // do nothing
2694
+ tradeSize = maxPositionSize;
2695
+ } else {
2696
+ // case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
2697
+ tradeSize = maxPositionSize.add(oppositeSizeLiabilityValue);
2698
+ oppositeSideTradeSize = oppositeSizeLiabilityValue;
2699
+ }
2700
+ } else {
2701
+ // current leverage is greater than max leverage - can only reduce position size
2702
+
2703
+ if (!targetingSameSide) {
2704
+ const market = this.driftClient.getPerpMarketAccount(targetMarketIndex);
2705
+ const perpLiabilityValue = calculatePerpLiabilityValue(
2706
+ currentPosition.baseAssetAmount,
2707
+ oracleData.price,
2708
+ isVariant(market.contractType, 'prediction')
2709
+ );
2710
+ const totalCollateral = this.getTotalCollateral();
2711
+ const marginRequirement = this.getInitialMarginRequirement();
2712
+ const marginFreedByClosing = perpLiabilityValue
2713
+ .mul(new BN(market.marginRatioInitial))
2714
+ .div(MARGIN_PRECISION);
2715
+ const marginRequirementAfterClosing =
2716
+ marginRequirement.sub(marginFreedByClosing);
2717
+
2718
+ if (marginRequirementAfterClosing.gt(totalCollateral)) {
2719
+ oppositeSideTradeSize = perpLiabilityValue;
2720
+ } else {
2721
+ const freeCollateralAfterClose = totalCollateral.sub(
2722
+ marginRequirementAfterClosing
2723
+ );
2724
+
2725
+ const buyingPowerAfterClose =
2726
+ this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(
2727
+ targetMarketIndex,
2728
+ freeCollateralAfterClose,
2729
+ ZERO
2730
+ );
2731
+ oppositeSideTradeSize = perpLiabilityValue;
2732
+ tradeSize = buyingPowerAfterClose;
2733
+ }
2734
+ } else {
2735
+ // do nothing if targetting same side
2736
+ tradeSize = maxPositionSize;
2737
+ }
2738
+ }
2739
+
2740
+ return { tradeSize, oppositeSideTradeSize };
2741
+ }
2742
+
2743
+ /**
2744
+ * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
2745
+ *
2746
+ * @param targetMarketIndex
2747
+ * @param direction
2748
+ * @param currentQuoteAssetValue
2749
+ * @param currentSpotMarketNetValue
2750
+ * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
2751
+ */
2752
+ public getMaxTradeSizeUSDCForSpot(
2753
+ targetMarketIndex: number,
2754
+ direction: PositionDirection,
2755
+ currentQuoteAssetValue?: BN,
2756
+ currentSpotMarketNetValue?: BN
2757
+ ): BN {
2758
+ const market = this.driftClient.getSpotMarketAccount(targetMarketIndex);
2759
+ const oraclePrice =
2760
+ this.driftClient.getOracleDataForSpotMarket(targetMarketIndex).price;
2761
+
2762
+ currentQuoteAssetValue = this.getSpotMarketAssetValue(
2763
+ QUOTE_SPOT_MARKET_INDEX
2764
+ );
2765
+
2766
+ currentSpotMarketNetValue =
2767
+ currentSpotMarketNetValue ?? this.getSpotPositionValue(targetMarketIndex);
2768
+
2769
+ let freeCollateral = this.getFreeCollateral();
2770
+ const marginRatio = calculateSpotMarketMarginRatio(
2771
+ market,
2772
+ oraclePrice,
2773
+ 'Initial',
2774
+ ZERO,
2775
+ isVariant(direction, 'long')
2776
+ ? SpotBalanceType.DEPOSIT
2777
+ : SpotBalanceType.BORROW,
2778
+ this.getUserAccount().maxMarginRatio
2779
+ );
2780
+
2781
+ let tradeAmount = ZERO;
2782
+ if (this.getUserAccount().isMarginTradingEnabled) {
2783
+ // if the user is buying/selling and already short/long, need to account for closing out short/long
2784
+ if (isVariant(direction, 'long') && currentSpotMarketNetValue.lt(ZERO)) {
2785
+ tradeAmount = currentSpotMarketNetValue.abs();
2786
+ const marginRatio = calculateSpotMarketMarginRatio(
2787
+ market,
2788
+ oraclePrice,
2789
+ 'Initial',
2790
+ this.getTokenAmount(targetMarketIndex).abs(),
2791
+ SpotBalanceType.BORROW,
2792
+ this.getUserAccount().maxMarginRatio
2793
+ );
2794
+ freeCollateral = freeCollateral.add(
2795
+ tradeAmount.mul(new BN(marginRatio)).div(MARGIN_PRECISION)
2796
+ );
2797
+ } else if (
2798
+ isVariant(direction, 'short') &&
2799
+ currentSpotMarketNetValue.gt(ZERO)
2800
+ ) {
2801
+ tradeAmount = currentSpotMarketNetValue;
2802
+ const marginRatio = calculateSpotMarketMarginRatio(
2803
+ market,
2804
+ oraclePrice,
2805
+ 'Initial',
2806
+ this.getTokenAmount(targetMarketIndex),
2807
+ SpotBalanceType.DEPOSIT,
2808
+ this.getUserAccount().maxMarginRatio
2809
+ );
2810
+ freeCollateral = freeCollateral.add(
2811
+ tradeAmount.mul(new BN(marginRatio)).div(MARGIN_PRECISION)
2812
+ );
2813
+ }
2814
+
2815
+ tradeAmount = tradeAmount.add(
2816
+ freeCollateral.mul(MARGIN_PRECISION).div(new BN(marginRatio))
2817
+ );
2818
+ } else if (isVariant(direction, 'long')) {
2819
+ tradeAmount = BN.min(
2820
+ currentQuoteAssetValue,
2821
+ freeCollateral.mul(MARGIN_PRECISION).div(new BN(marginRatio))
2822
+ );
2823
+ } else {
2824
+ tradeAmount = BN.max(ZERO, currentSpotMarketNetValue);
2825
+ }
2826
+
2827
+ return tradeAmount;
2828
+ }
2829
+
2830
+ /**
2831
+ * Calculates the max amount of token that can be swapped from inMarket to outMarket
2832
+ * Assumes swap happens at oracle price
2833
+ *
2834
+ * @param inMarketIndex
2835
+ * @param outMarketIndex
2836
+ * @param calculateSwap function to similate in to out swa
2837
+ * @param iterationLimit how long to run appromixation before erroring out
2838
+ */
2839
+ public getMaxSwapAmount({
2840
+ inMarketIndex,
2841
+ outMarketIndex,
2842
+ calculateSwap,
2843
+ iterationLimit = 1000,
2844
+ }: {
2845
+ inMarketIndex: number;
2846
+ outMarketIndex: number;
2847
+ calculateSwap?: (inAmount: BN) => BN;
2848
+ iterationLimit?: number;
2849
+ }): { inAmount: BN; outAmount: BN; leverage: BN } {
2850
+ const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
2851
+ const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
2852
+
2853
+ const inOraclePriceData = this.getOracleDataForSpotMarket(inMarketIndex);
2854
+ const inOraclePrice = inOraclePriceData.price;
2855
+ const outOraclePriceData = this.getOracleDataForSpotMarket(outMarketIndex);
2856
+ const outOraclePrice = outOraclePriceData.price;
2857
+
2858
+ const inStrictOraclePrice = new StrictOraclePrice(inOraclePrice);
2859
+ const outStrictOraclePrice = new StrictOraclePrice(outOraclePrice);
2860
+
2861
+ const inPrecision = new BN(10 ** inMarket.decimals);
2862
+ const outPrecision = new BN(10 ** outMarket.decimals);
2863
+
2864
+ const inSpotPosition =
2865
+ this.getSpotPosition(inMarketIndex) ||
2866
+ this.getEmptySpotPosition(inMarketIndex);
2867
+ const outSpotPosition =
2868
+ this.getSpotPosition(outMarketIndex) ||
2869
+ this.getEmptySpotPosition(outMarketIndex);
2870
+
2871
+ const freeCollateral = this.getFreeCollateral();
2872
+
2873
+ const inContributionInitial =
2874
+ this.calculateSpotPositionFreeCollateralContribution(
2875
+ inSpotPosition,
2876
+ inStrictOraclePrice
2877
+ );
2878
+ const {
2879
+ totalAssetValue: inTotalAssetValueInitial,
2880
+ totalLiabilityValue: inTotalLiabilityValueInitial,
2881
+ } = this.calculateSpotPositionLeverageContribution(
2882
+ inSpotPosition,
2883
+ inStrictOraclePrice
2884
+ );
2885
+ const outContributionInitial =
2886
+ this.calculateSpotPositionFreeCollateralContribution(
2887
+ outSpotPosition,
2888
+ outStrictOraclePrice
2889
+ );
2890
+ const {
2891
+ totalAssetValue: outTotalAssetValueInitial,
2892
+ totalLiabilityValue: outTotalLiabilityValueInitial,
2893
+ } = this.calculateSpotPositionLeverageContribution(
2894
+ outSpotPosition,
2895
+ outStrictOraclePrice
2896
+ );
2897
+ const initialContribution = inContributionInitial.add(
2898
+ outContributionInitial
2899
+ );
2900
+
2901
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
2902
+ this.getLeverageComponents();
2903
+
2904
+ if (!calculateSwap) {
2905
+ calculateSwap = (inSwap: BN) => {
2906
+ return inSwap
2907
+ .mul(outPrecision)
2908
+ .mul(inOraclePrice)
2909
+ .div(outOraclePrice)
2910
+ .div(inPrecision);
2911
+ };
2912
+ }
2913
+
2914
+ let inSwap = ZERO;
2915
+ let outSwap = ZERO;
2916
+ const inTokenAmount = this.getTokenAmount(inMarketIndex);
2917
+ const outTokenAmount = this.getTokenAmount(outMarketIndex);
2918
+
2919
+ const inAssetWeight = calculateAssetWeight(
2920
+ inTokenAmount,
2921
+ inOraclePriceData.price,
2922
+ inMarket,
2923
+ 'Initial'
2924
+ );
2925
+ const outAssetWeight = calculateAssetWeight(
2926
+ outTokenAmount,
2927
+ outOraclePriceData.price,
2928
+ outMarket,
2929
+ 'Initial'
2930
+ );
2931
+
2932
+ const outSaferThanIn =
2933
+ // selling asset to close borrow
2934
+ (inTokenAmount.gt(ZERO) && outTokenAmount.lt(ZERO)) ||
2935
+ // buying asset with higher initial asset weight
2936
+ inAssetWeight.lt(outAssetWeight);
2937
+
2938
+ if (freeCollateral.lt(PRICE_PRECISION.divn(100))) {
2939
+ if (outSaferThanIn && inTokenAmount.gt(ZERO)) {
2940
+ inSwap = inTokenAmount;
2941
+ outSwap = calculateSwap(inSwap);
2942
+ }
2943
+ } else {
2944
+ let minSwap = ZERO;
2945
+ let maxSwap = BN.max(
2946
+ freeCollateral.mul(inPrecision).mul(new BN(100)).div(inOraclePrice), // 100x current free collateral
2947
+ inTokenAmount.abs().mul(new BN(10)) // 10x current position
2948
+ );
2949
+ inSwap = maxSwap.div(TWO);
2950
+ const error = freeCollateral.div(new BN(10000));
2951
+
2952
+ let i = 0;
2953
+ let freeCollateralAfter = freeCollateral;
2954
+ while (freeCollateralAfter.gt(error) || freeCollateralAfter.isNeg()) {
2955
+ outSwap = calculateSwap(inSwap);
2956
+
2957
+ const inPositionAfter = this.cloneAndUpdateSpotPosition(
2958
+ inSpotPosition,
2959
+ inSwap.neg(),
2960
+ inMarket
2961
+ );
2962
+ const outPositionAfter = this.cloneAndUpdateSpotPosition(
2963
+ outSpotPosition,
2964
+ outSwap,
2965
+ outMarket
2966
+ );
2967
+
2968
+ const inContributionAfter =
2969
+ this.calculateSpotPositionFreeCollateralContribution(
2970
+ inPositionAfter,
2971
+ inStrictOraclePrice
2972
+ );
2973
+ const outContributionAfter =
2974
+ this.calculateSpotPositionFreeCollateralContribution(
2975
+ outPositionAfter,
2976
+ outStrictOraclePrice
2977
+ );
2978
+
2979
+ const contributionAfter = inContributionAfter.add(outContributionAfter);
2980
+
2981
+ const contributionDelta = contributionAfter.sub(initialContribution);
2982
+
2983
+ freeCollateralAfter = freeCollateral.add(contributionDelta);
2984
+
2985
+ if (freeCollateralAfter.gt(error)) {
2986
+ minSwap = inSwap;
2987
+ inSwap = minSwap.add(maxSwap).div(TWO);
2988
+ } else if (freeCollateralAfter.isNeg()) {
2989
+ maxSwap = inSwap;
2990
+ inSwap = minSwap.add(maxSwap).div(TWO);
2991
+ }
2992
+
2993
+ if (i++ > iterationLimit) {
2994
+ console.log('getMaxSwapAmount iteration limit reached');
2995
+ break;
2996
+ }
2997
+ }
2998
+ }
2999
+
3000
+ const inPositionAfter = this.cloneAndUpdateSpotPosition(
3001
+ inSpotPosition,
3002
+ inSwap.neg(),
3003
+ inMarket
3004
+ );
3005
+ const outPositionAfter = this.cloneAndUpdateSpotPosition(
3006
+ outSpotPosition,
3007
+ outSwap,
3008
+ outMarket
3009
+ );
3010
+
3011
+ const {
3012
+ totalAssetValue: inTotalAssetValueAfter,
3013
+ totalLiabilityValue: inTotalLiabilityValueAfter,
3014
+ } = this.calculateSpotPositionLeverageContribution(
3015
+ inPositionAfter,
3016
+ inStrictOraclePrice
3017
+ );
3018
+
3019
+ const {
3020
+ totalAssetValue: outTotalAssetValueAfter,
3021
+ totalLiabilityValue: outTotalLiabilityValueAfter,
3022
+ } = this.calculateSpotPositionLeverageContribution(
3023
+ outPositionAfter,
3024
+ outStrictOraclePrice
3025
+ );
3026
+
3027
+ const spotAssetValueDelta = inTotalAssetValueAfter
3028
+ .add(outTotalAssetValueAfter)
3029
+ .sub(inTotalAssetValueInitial)
3030
+ .sub(outTotalAssetValueInitial);
3031
+ const spotLiabilityValueDelta = inTotalLiabilityValueAfter
3032
+ .add(outTotalLiabilityValueAfter)
3033
+ .sub(inTotalLiabilityValueInitial)
3034
+ .sub(outTotalLiabilityValueInitial);
3035
+
3036
+ const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
3037
+ const spotLiabilityValueAfter = spotLiabilityValue.add(
3038
+ spotLiabilityValueDelta
3039
+ );
3040
+
3041
+ const leverage = this.calculateLeverageFromComponents({
3042
+ perpLiabilityValue,
3043
+ perpPnl,
3044
+ spotAssetValue: spotAssetValueAfter,
3045
+ spotLiabilityValue: spotLiabilityValueAfter,
3046
+ });
3047
+
3048
+ return { inAmount: inSwap, outAmount: outSwap, leverage };
3049
+ }
3050
+
3051
+ public cloneAndUpdateSpotPosition(
3052
+ position: SpotPosition,
3053
+ tokenAmount: BN,
3054
+ market: SpotMarketAccount
3055
+ ): SpotPosition {
3056
+ const clonedPosition = Object.assign({}, position);
3057
+ if (tokenAmount.eq(ZERO)) {
3058
+ return clonedPosition;
3059
+ }
3060
+
3061
+ const preTokenAmount = getSignedTokenAmount(
3062
+ getTokenAmount(position.scaledBalance, market, position.balanceType),
3063
+ position.balanceType
3064
+ );
3065
+
3066
+ if (sigNum(preTokenAmount).eq(sigNum(tokenAmount))) {
3067
+ const scaledBalanceDelta = getBalance(
3068
+ tokenAmount.abs(),
3069
+ market,
3070
+ position.balanceType
3071
+ );
3072
+ clonedPosition.scaledBalance =
3073
+ clonedPosition.scaledBalance.add(scaledBalanceDelta);
3074
+ return clonedPosition;
3075
+ }
3076
+
3077
+ const updateDirection = tokenAmount.isNeg()
3078
+ ? SpotBalanceType.BORROW
3079
+ : SpotBalanceType.DEPOSIT;
3080
+
3081
+ if (tokenAmount.abs().gte(preTokenAmount.abs())) {
3082
+ clonedPosition.scaledBalance = getBalance(
3083
+ tokenAmount.abs().sub(preTokenAmount.abs()),
3084
+ market,
3085
+ updateDirection
3086
+ );
3087
+ clonedPosition.balanceType = updateDirection;
3088
+ } else {
3089
+ const scaledBalanceDelta = getBalance(
3090
+ tokenAmount.abs(),
3091
+ market,
3092
+ position.balanceType
3093
+ );
3094
+
3095
+ clonedPosition.scaledBalance =
3096
+ clonedPosition.scaledBalance.sub(scaledBalanceDelta);
3097
+ }
3098
+ return clonedPosition;
3099
+ }
3100
+
3101
+ calculateSpotPositionFreeCollateralContribution(
3102
+ spotPosition: SpotPosition,
3103
+ strictOraclePrice: StrictOraclePrice
3104
+ ): BN {
3105
+ const marginCategory = 'Initial';
3106
+
3107
+ const spotMarketAccount: SpotMarketAccount =
3108
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
3109
+
3110
+ const { freeCollateralContribution } = getWorstCaseTokenAmounts(
3111
+ spotPosition,
3112
+ spotMarketAccount,
3113
+ strictOraclePrice,
3114
+ marginCategory,
3115
+ this.getUserAccount().maxMarginRatio
3116
+ );
3117
+
3118
+ return freeCollateralContribution;
3119
+ }
3120
+
3121
+ calculateSpotPositionLeverageContribution(
3122
+ spotPosition: SpotPosition,
3123
+ strictOraclePrice: StrictOraclePrice
3124
+ ): {
3125
+ totalAssetValue: BN;
3126
+ totalLiabilityValue: BN;
3127
+ } {
3128
+ let totalAssetValue = ZERO;
3129
+ let totalLiabilityValue = ZERO;
3130
+
3131
+ const spotMarketAccount: SpotMarketAccount =
3132
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
3133
+
3134
+ const { tokenValue, ordersValue } = getWorstCaseTokenAmounts(
3135
+ spotPosition,
3136
+ spotMarketAccount,
3137
+ strictOraclePrice,
3138
+ 'Initial',
3139
+ this.getUserAccount().maxMarginRatio
3140
+ );
3141
+
3142
+ if (tokenValue.gte(ZERO)) {
3143
+ totalAssetValue = tokenValue;
3144
+ } else {
3145
+ totalLiabilityValue = tokenValue.abs();
3146
+ }
3147
+
3148
+ if (ordersValue.gt(ZERO)) {
3149
+ totalAssetValue = totalAssetValue.add(ordersValue);
3150
+ } else {
3151
+ totalLiabilityValue = totalLiabilityValue.add(ordersValue.abs());
3152
+ }
3153
+
3154
+ return {
3155
+ totalAssetValue,
3156
+ totalLiabilityValue,
3157
+ };
3158
+ }
3159
+
3160
+ /**
3161
+ * Estimates what the user leverage will be after swap
3162
+ * @param inMarketIndex
3163
+ * @param outMarketIndex
3164
+ * @param inAmount
3165
+ * @param outAmount
3166
+ */
3167
+ public accountLeverageAfterSwap({
3168
+ inMarketIndex,
3169
+ outMarketIndex,
3170
+ inAmount,
3171
+ outAmount,
3172
+ }: {
3173
+ inMarketIndex: number;
3174
+ outMarketIndex: number;
3175
+ inAmount: BN;
3176
+ outAmount: BN;
3177
+ }): BN {
3178
+ const inMarket = this.driftClient.getSpotMarketAccount(inMarketIndex);
3179
+ const outMarket = this.driftClient.getSpotMarketAccount(outMarketIndex);
3180
+
3181
+ const inOraclePriceData = this.getOracleDataForSpotMarket(inMarketIndex);
3182
+ const inOraclePrice = inOraclePriceData.price;
3183
+ const outOraclePriceData = this.getOracleDataForSpotMarket(outMarketIndex);
3184
+ const outOraclePrice = outOraclePriceData.price;
3185
+ const inStrictOraclePrice = new StrictOraclePrice(inOraclePrice);
3186
+ const outStrictOraclePrice = new StrictOraclePrice(outOraclePrice);
3187
+
3188
+ const inSpotPosition =
3189
+ this.getSpotPosition(inMarketIndex) ||
3190
+ this.getEmptySpotPosition(inMarketIndex);
3191
+ const outSpotPosition =
3192
+ this.getSpotPosition(outMarketIndex) ||
3193
+ this.getEmptySpotPosition(outMarketIndex);
3194
+
3195
+ const {
3196
+ totalAssetValue: inTotalAssetValueInitial,
3197
+ totalLiabilityValue: inTotalLiabilityValueInitial,
3198
+ } = this.calculateSpotPositionLeverageContribution(
3199
+ inSpotPosition,
3200
+ inStrictOraclePrice
3201
+ );
3202
+ const {
3203
+ totalAssetValue: outTotalAssetValueInitial,
3204
+ totalLiabilityValue: outTotalLiabilityValueInitial,
3205
+ } = this.calculateSpotPositionLeverageContribution(
3206
+ outSpotPosition,
3207
+ outStrictOraclePrice
3208
+ );
3209
+
3210
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } =
3211
+ this.getLeverageComponents();
3212
+
3213
+ const inPositionAfter = this.cloneAndUpdateSpotPosition(
3214
+ inSpotPosition,
3215
+ inAmount.abs().neg(),
3216
+ inMarket
3217
+ );
3218
+ const outPositionAfter = this.cloneAndUpdateSpotPosition(
3219
+ outSpotPosition,
3220
+ outAmount.abs(),
3221
+ outMarket
3222
+ );
3223
+
3224
+ const {
3225
+ totalAssetValue: inTotalAssetValueAfter,
3226
+ totalLiabilityValue: inTotalLiabilityValueAfter,
3227
+ } = this.calculateSpotPositionLeverageContribution(
3228
+ inPositionAfter,
3229
+ inStrictOraclePrice
3230
+ );
3231
+
3232
+ const {
3233
+ totalAssetValue: outTotalAssetValueAfter,
3234
+ totalLiabilityValue: outTotalLiabilityValueAfter,
3235
+ } = this.calculateSpotPositionLeverageContribution(
3236
+ outPositionAfter,
3237
+ outStrictOraclePrice
3238
+ );
3239
+
3240
+ const spotAssetValueDelta = inTotalAssetValueAfter
3241
+ .add(outTotalAssetValueAfter)
3242
+ .sub(inTotalAssetValueInitial)
3243
+ .sub(outTotalAssetValueInitial);
3244
+ const spotLiabilityValueDelta = inTotalLiabilityValueAfter
3245
+ .add(outTotalLiabilityValueAfter)
3246
+ .sub(inTotalLiabilityValueInitial)
3247
+ .sub(outTotalLiabilityValueInitial);
3248
+
3249
+ const spotAssetValueAfter = spotAssetValue.add(spotAssetValueDelta);
3250
+ const spotLiabilityValueAfter = spotLiabilityValue.add(
3251
+ spotLiabilityValueDelta
3252
+ );
3253
+
3254
+ return this.calculateLeverageFromComponents({
3255
+ perpLiabilityValue,
3256
+ perpPnl,
3257
+ spotAssetValue: spotAssetValueAfter,
3258
+ spotLiabilityValue: spotLiabilityValueAfter,
3259
+ });
3260
+ }
3261
+
3262
+ // TODO - should this take the price impact of the trade into account for strict accuracy?
3263
+
3264
+ /**
3265
+ * Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
3266
+ * @param targetMarketIndex
3267
+ * @param: targetMarketType
3268
+ * @param tradeQuoteAmount
3269
+ * @param tradeSide
3270
+ * @param includeOpenOrders
3271
+ * @returns leverageRatio : Precision TEN_THOUSAND
3272
+ */
3273
+ public accountLeverageRatioAfterTrade(
3274
+ targetMarketIndex: number,
3275
+ targetMarketType: MarketType,
3276
+ tradeQuoteAmount: BN,
3277
+ tradeSide: PositionDirection,
3278
+ includeOpenOrders = true
3279
+ ): BN {
3280
+ const tradeIsPerp = isVariant(targetMarketType, 'perp');
3281
+
3282
+ if (!tradeIsPerp) {
3283
+ // calculate new asset/liability values for base and quote market to find new account leverage
3284
+ const totalLiabilityValue = this.getTotalLiabilityValue();
3285
+ const totalAssetValue = this.getTotalAssetValue();
3286
+ const spotLiabilityValue = this.getSpotMarketLiabilityValue(
3287
+ undefined,
3288
+ undefined,
3289
+ undefined,
3290
+ includeOpenOrders
3291
+ );
3292
+
3293
+ const currentQuoteAssetValue = this.getSpotMarketAssetValue(
3294
+ QUOTE_SPOT_MARKET_INDEX,
3295
+ undefined,
3296
+ includeOpenOrders
3297
+ );
3298
+ const currentQuoteLiabilityValue = this.getSpotMarketLiabilityValue(
3299
+ QUOTE_SPOT_MARKET_INDEX,
3300
+ undefined,
3301
+ undefined,
3302
+ includeOpenOrders
3303
+ );
3304
+ const currentQuoteValue = currentQuoteAssetValue.sub(
3305
+ currentQuoteLiabilityValue
3306
+ );
3307
+
3308
+ const currentSpotMarketAssetValue = this.getSpotMarketAssetValue(
3309
+ targetMarketIndex,
3310
+ undefined,
3311
+ includeOpenOrders
3312
+ );
3313
+ const currentSpotMarketLiabilityValue = this.getSpotMarketLiabilityValue(
3314
+ targetMarketIndex,
3315
+ undefined,
3316
+ undefined,
3317
+ includeOpenOrders
3318
+ );
3319
+ const currentSpotMarketNetValue = currentSpotMarketAssetValue.sub(
3320
+ currentSpotMarketLiabilityValue
3321
+ );
3322
+
3323
+ let assetValueToAdd = ZERO;
3324
+ let liabilityValueToAdd = ZERO;
3325
+
3326
+ const newQuoteNetValue =
3327
+ tradeSide == PositionDirection.SHORT
3328
+ ? currentQuoteValue.add(tradeQuoteAmount)
3329
+ : currentQuoteValue.sub(tradeQuoteAmount);
3330
+ const newQuoteAssetValue = BN.max(newQuoteNetValue, ZERO);
3331
+ const newQuoteLiabilityValue = BN.min(newQuoteNetValue, ZERO).abs();
3332
+
3333
+ assetValueToAdd = assetValueToAdd.add(
3334
+ newQuoteAssetValue.sub(currentQuoteAssetValue)
3335
+ );
3336
+ liabilityValueToAdd = liabilityValueToAdd.add(
3337
+ newQuoteLiabilityValue.sub(currentQuoteLiabilityValue)
3338
+ );
3339
+
3340
+ const newSpotMarketNetValue =
3341
+ tradeSide == PositionDirection.LONG
3342
+ ? currentSpotMarketNetValue.add(tradeQuoteAmount)
3343
+ : currentSpotMarketNetValue.sub(tradeQuoteAmount);
3344
+ const newSpotMarketAssetValue = BN.max(newSpotMarketNetValue, ZERO);
3345
+ const newSpotMarketLiabilityValue = BN.min(
3346
+ newSpotMarketNetValue,
3347
+ ZERO
3348
+ ).abs();
3349
+
3350
+ assetValueToAdd = assetValueToAdd.add(
3351
+ newSpotMarketAssetValue.sub(currentSpotMarketAssetValue)
3352
+ );
3353
+ liabilityValueToAdd = liabilityValueToAdd.add(
3354
+ newSpotMarketLiabilityValue.sub(currentSpotMarketLiabilityValue)
3355
+ );
3356
+
3357
+ const totalAssetValueAfterTrade = totalAssetValue.add(assetValueToAdd);
3358
+ const totalSpotLiabilityValueAfterTrade =
3359
+ spotLiabilityValue.add(liabilityValueToAdd);
3360
+
3361
+ const totalLiabilityValueAfterTrade =
3362
+ totalLiabilityValue.add(liabilityValueToAdd);
3363
+
3364
+ const netAssetValueAfterTrade = totalAssetValueAfterTrade.sub(
3365
+ totalSpotLiabilityValueAfterTrade
3366
+ );
3367
+
3368
+ if (netAssetValueAfterTrade.eq(ZERO)) {
3369
+ return ZERO;
3370
+ }
3371
+
3372
+ const newLeverage = totalLiabilityValueAfterTrade
3373
+ .mul(TEN_THOUSAND)
3374
+ .div(netAssetValueAfterTrade);
3375
+
3376
+ return newLeverage;
3377
+ }
3378
+
3379
+ const currentPosition =
3380
+ this.getPerpPositionWithLPSettle(targetMarketIndex)[0] ||
3381
+ this.getEmptyPosition(targetMarketIndex);
3382
+
3383
+ const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex);
3384
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
3385
+
3386
+ let {
3387
+ // eslint-disable-next-line prefer-const
3388
+ worstCaseBaseAssetAmount: worstCaseBase,
3389
+ worstCaseLiabilityValue: currentPositionQuoteAmount,
3390
+ } = calculateWorstCasePerpLiabilityValue(
3391
+ currentPosition,
3392
+ perpMarket,
3393
+ oracleData.price
3394
+ );
3395
+
3396
+ // current side is short if position base asset amount is negative OR there is no position open but open orders are short
3397
+ const currentSide =
3398
+ currentPosition.baseAssetAmount.isNeg() ||
3399
+ (currentPosition.baseAssetAmount.eq(ZERO) && worstCaseBase.isNeg())
3400
+ ? PositionDirection.SHORT
3401
+ : PositionDirection.LONG;
3402
+
3403
+ if (currentSide === PositionDirection.SHORT)
3404
+ currentPositionQuoteAmount = currentPositionQuoteAmount.neg();
3405
+
3406
+ if (tradeSide === PositionDirection.SHORT)
3407
+ tradeQuoteAmount = tradeQuoteAmount.neg();
3408
+
3409
+ const currentPerpPositionAfterTrade = currentPositionQuoteAmount
3410
+ .add(tradeQuoteAmount)
3411
+ .abs();
3412
+
3413
+ const totalPositionAfterTradeExcludingTargetMarket =
3414
+ this.getTotalPerpPositionValueExcludingMarket(
3415
+ targetMarketIndex,
3416
+ undefined,
3417
+ undefined,
3418
+ includeOpenOrders
3419
+ );
3420
+
3421
+ const totalAssetValue = this.getTotalAssetValue();
3422
+
3423
+ const totalPerpPositionLiability = currentPerpPositionAfterTrade
3424
+ .add(totalPositionAfterTradeExcludingTargetMarket)
3425
+ .abs();
3426
+
3427
+ const totalSpotLiability = this.getSpotMarketLiabilityValue(
3428
+ undefined,
3429
+ undefined,
3430
+ undefined,
3431
+ includeOpenOrders
3432
+ );
3433
+
3434
+ const totalLiabilitiesAfterTrade =
3435
+ totalPerpPositionLiability.add(totalSpotLiability);
3436
+
3437
+ const netAssetValue = totalAssetValue.sub(totalSpotLiability);
3438
+
3439
+ if (netAssetValue.eq(ZERO)) {
3440
+ return ZERO;
3441
+ }
3442
+
3443
+ const newLeverage = totalLiabilitiesAfterTrade
3444
+ .mul(TEN_THOUSAND)
3445
+ .div(netAssetValue);
3446
+
3447
+ return newLeverage;
3448
+ }
3449
+
3450
+ public getUserFeeTier(marketType: MarketType, now?: BN) {
3451
+ const state = this.driftClient.getStateAccount();
3452
+
3453
+ let feeTierIndex = 0;
3454
+ if (isVariant(marketType, 'perp')) {
3455
+ if (this.isHighLeverageMode()) {
3456
+ return state.perpFeeStructure.feeTiers[0];
3457
+ }
3458
+
3459
+ const userStatsAccount: UserStatsAccount = this.driftClient
3460
+ .getUserStats()
3461
+ .getAccount();
3462
+
3463
+ const total30dVolume = getUser30dRollingVolumeEstimate(
3464
+ userStatsAccount,
3465
+ now
3466
+ );
3467
+
3468
+ const stakedQuoteAssetAmount = userStatsAccount.ifStakedQuoteAssetAmount;
3469
+ const volumeTiers = [
3470
+ new BN(100_000_000).mul(QUOTE_PRECISION),
3471
+ new BN(50_000_000).mul(QUOTE_PRECISION),
3472
+ new BN(10_000_000).mul(QUOTE_PRECISION),
3473
+ new BN(5_000_000).mul(QUOTE_PRECISION),
3474
+ new BN(1_000_000).mul(QUOTE_PRECISION),
3475
+ ];
3476
+ const stakedTiers = [
3477
+ new BN(10000).mul(QUOTE_PRECISION),
3478
+ new BN(5000).mul(QUOTE_PRECISION),
3479
+ new BN(2000).mul(QUOTE_PRECISION),
3480
+ new BN(1000).mul(QUOTE_PRECISION),
3481
+ new BN(500).mul(QUOTE_PRECISION),
3482
+ ];
3483
+
3484
+ for (let i = 0; i < volumeTiers.length; i++) {
3485
+ if (
3486
+ total30dVolume.gte(volumeTiers[i]) ||
3487
+ stakedQuoteAssetAmount.gte(stakedTiers[i])
3488
+ ) {
3489
+ feeTierIndex = 5 - i;
3490
+ break;
3491
+ }
3492
+ }
3493
+
3494
+ return state.perpFeeStructure.feeTiers[feeTierIndex];
3495
+ }
3496
+
3497
+ return state.spotFeeStructure.feeTiers[feeTierIndex];
3498
+ }
3499
+
3500
+ /**
3501
+ * Calculates how much perp fee will be taken for a given sized trade
3502
+ * @param quoteAmount
3503
+ * @returns feeForQuote : Precision QUOTE_PRECISION
3504
+ */
3505
+ public calculateFeeForQuoteAmount(quoteAmount: BN, marketIndex?: number): BN {
3506
+ if (marketIndex !== undefined) {
3507
+ const takerFeeMultiplier = this.driftClient.getMarketFees(
3508
+ MarketType.PERP,
3509
+ marketIndex,
3510
+ this
3511
+ ).takerFee;
3512
+ const feeAmountNum =
3513
+ BigNum.from(quoteAmount, QUOTE_PRECISION_EXP).toNum() *
3514
+ takerFeeMultiplier;
3515
+ return BigNum.fromPrint(feeAmountNum.toString(), QUOTE_PRECISION_EXP).val;
3516
+ } else {
3517
+ const feeTier = this.getUserFeeTier(MarketType.PERP);
3518
+ return quoteAmount
3519
+ .mul(new BN(feeTier.feeNumerator))
3520
+ .div(new BN(feeTier.feeDenominator));
3521
+ }
3522
+ }
3523
+
3524
+ /**
3525
+ * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
3526
+ * it will return the max withdrawal amount without opening a liability for the user
3527
+ * @param marketIndex
3528
+ * @returns withdrawalLimit : Precision is the token precision for the chosen SpotMarket
3529
+ */
3530
+ public getWithdrawalLimit(marketIndex: number, reduceOnly?: boolean): BN {
3531
+ const nowTs = new BN(Math.floor(Date.now() / 1000));
3532
+ const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
3533
+
3534
+ // eslint-disable-next-line prefer-const
3535
+ let { borrowLimit, withdrawLimit } = calculateWithdrawLimit(
3536
+ spotMarket,
3537
+ nowTs
3538
+ );
3539
+
3540
+ const freeCollateral = this.getFreeCollateral();
3541
+ const initialMarginRequirement = this.getInitialMarginRequirement();
3542
+ const oracleData = this.getOracleDataForSpotMarket(marketIndex);
3543
+ const precisionIncrease = TEN.pow(new BN(spotMarket.decimals - 6));
3544
+
3545
+ const { canBypass, depositAmount: userDepositAmount } =
3546
+ this.canBypassWithdrawLimits(marketIndex);
3547
+ if (canBypass) {
3548
+ withdrawLimit = BN.max(withdrawLimit, userDepositAmount);
3549
+ }
3550
+
3551
+ const assetWeight = calculateAssetWeight(
3552
+ userDepositAmount,
3553
+ oracleData.price,
3554
+ spotMarket,
3555
+ 'Initial'
3556
+ );
3557
+
3558
+ let amountWithdrawable;
3559
+ if (assetWeight.eq(ZERO)) {
3560
+ amountWithdrawable = userDepositAmount;
3561
+ } else if (initialMarginRequirement.eq(ZERO)) {
3562
+ amountWithdrawable = userDepositAmount;
3563
+ } else {
3564
+ amountWithdrawable = divCeil(
3565
+ divCeil(freeCollateral.mul(MARGIN_PRECISION), assetWeight).mul(
3566
+ PRICE_PRECISION
3567
+ ),
3568
+ oracleData.price
3569
+ ).mul(precisionIncrease);
3570
+ }
3571
+
3572
+ const maxWithdrawValue = BN.min(
3573
+ BN.min(amountWithdrawable, userDepositAmount),
3574
+ withdrawLimit.abs()
3575
+ );
3576
+
3577
+ if (reduceOnly) {
3578
+ return BN.max(maxWithdrawValue, ZERO);
3579
+ } else {
3580
+ const weightedAssetValue = this.getSpotMarketAssetValue(
3581
+ marketIndex,
3582
+ 'Initial',
3583
+ false
3584
+ );
3585
+
3586
+ const freeCollatAfterWithdraw = userDepositAmount.gt(ZERO)
3587
+ ? freeCollateral.sub(weightedAssetValue)
3588
+ : freeCollateral;
3589
+
3590
+ const maxLiabilityAllowed = freeCollatAfterWithdraw
3591
+ .mul(MARGIN_PRECISION)
3592
+ .div(new BN(spotMarket.initialLiabilityWeight))
3593
+ .mul(PRICE_PRECISION)
3594
+ .div(oracleData.price)
3595
+ .mul(precisionIncrease);
3596
+
3597
+ const maxBorrowValue = BN.min(
3598
+ maxWithdrawValue.add(maxLiabilityAllowed),
3599
+ borrowLimit.abs()
3600
+ );
3601
+
3602
+ return BN.max(maxBorrowValue, ZERO);
3603
+ }
3604
+ }
3605
+
3606
+ public canBypassWithdrawLimits(marketIndex: number): {
3607
+ canBypass: boolean;
3608
+ netDeposits: BN;
3609
+ depositAmount: BN;
3610
+ maxDepositAmount: BN;
3611
+ } {
3612
+ const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
3613
+ const maxDepositAmount = spotMarket.withdrawGuardThreshold.div(new BN(10));
3614
+ const position = this.getSpotPosition(marketIndex);
3615
+
3616
+ const netDeposits = this.getUserAccount().totalDeposits.sub(
3617
+ this.getUserAccount().totalWithdraws
3618
+ );
3619
+
3620
+ if (!position) {
3621
+ return {
3622
+ canBypass: false,
3623
+ maxDepositAmount,
3624
+ depositAmount: ZERO,
3625
+ netDeposits,
3626
+ };
3627
+ }
3628
+
3629
+ if (isVariant(position.balanceType, 'borrow')) {
3630
+ return {
3631
+ canBypass: false,
3632
+ maxDepositAmount,
3633
+ netDeposits,
3634
+ depositAmount: ZERO,
3635
+ };
3636
+ }
3637
+
3638
+ const depositAmount = getTokenAmount(
3639
+ position.scaledBalance,
3640
+ spotMarket,
3641
+ SpotBalanceType.DEPOSIT
3642
+ );
3643
+
3644
+ if (netDeposits.lt(ZERO)) {
3645
+ return {
3646
+ canBypass: false,
3647
+ maxDepositAmount,
3648
+ depositAmount,
3649
+ netDeposits,
3650
+ };
3651
+ }
3652
+
3653
+ return {
3654
+ canBypass: depositAmount.lt(maxDepositAmount),
3655
+ maxDepositAmount,
3656
+ netDeposits,
3657
+ depositAmount,
3658
+ };
3659
+ }
3660
+
3661
+ public canMakeIdle(slot: BN): boolean {
3662
+ const userAccount = this.getUserAccount();
3663
+ if (userAccount.idle) {
3664
+ return false;
3665
+ }
3666
+
3667
+ const { totalAssetValue, totalLiabilityValue } =
3668
+ this.getSpotMarketAssetAndLiabilityValue();
3669
+ const equity = totalAssetValue.sub(totalLiabilityValue);
3670
+
3671
+ let slotsBeforeIdle: BN;
3672
+ if (equity.lt(QUOTE_PRECISION.muln(1000))) {
3673
+ slotsBeforeIdle = new BN(9000); // 1 hour
3674
+ } else {
3675
+ slotsBeforeIdle = new BN(1512000); // 1 week
3676
+ }
3677
+
3678
+ const userLastActiveSlot = userAccount.lastActiveSlot;
3679
+ const slotsSinceLastActive = slot.sub(userLastActiveSlot);
3680
+ if (slotsSinceLastActive.lt(slotsBeforeIdle)) {
3681
+ return false;
3682
+ }
3683
+
3684
+ if (this.isBeingLiquidated()) {
3685
+ return false;
3686
+ }
3687
+
3688
+ for (const perpPosition of userAccount.perpPositions) {
3689
+ if (!positionIsAvailable(perpPosition)) {
3690
+ return false;
3691
+ }
3692
+ }
3693
+
3694
+ for (const spotPosition of userAccount.spotPositions) {
3695
+ if (
3696
+ isVariant(spotPosition.balanceType, 'borrow') &&
3697
+ spotPosition.scaledBalance.gt(ZERO)
3698
+ ) {
3699
+ return false;
3700
+ }
3701
+
3702
+ if (spotPosition.openOrders !== 0) {
3703
+ return false;
3704
+ }
3705
+ }
3706
+
3707
+ for (const order of userAccount.orders) {
3708
+ if (!isVariant(order.status, 'init')) {
3709
+ return false;
3710
+ }
3711
+ }
3712
+
3713
+ return true;
3714
+ }
3715
+
3716
+ public getSafestTiers(): { perpTier: number; spotTier: number } {
3717
+ let safestPerpTier = 4;
3718
+ let safestSpotTier = 4;
3719
+
3720
+ for (const perpPosition of this.getActivePerpPositions()) {
3721
+ safestPerpTier = Math.min(
3722
+ safestPerpTier,
3723
+ getPerpMarketTierNumber(
3724
+ this.driftClient.getPerpMarketAccount(perpPosition.marketIndex)
3725
+ )
3726
+ );
3727
+ }
3728
+
3729
+ for (const spotPosition of this.getActiveSpotPositions()) {
3730
+ if (isVariant(spotPosition.balanceType, 'deposit')) {
3731
+ continue;
3732
+ }
3733
+
3734
+ safestSpotTier = Math.min(
3735
+ safestSpotTier,
3736
+ getSpotMarketTierNumber(
3737
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex)
3738
+ )
3739
+ );
3740
+ }
3741
+
3742
+ return {
3743
+ perpTier: safestPerpTier,
3744
+ spotTier: safestSpotTier,
3745
+ };
3746
+ }
3747
+
3748
+ public getPerpPositionHealth({
3749
+ marginCategory,
3750
+ perpPosition,
3751
+ oraclePriceData,
3752
+ quoteOraclePriceData,
3753
+ }: {
3754
+ marginCategory: MarginCategory;
3755
+ perpPosition: PerpPosition;
3756
+ oraclePriceData?: OraclePriceData;
3757
+ quoteOraclePriceData?: OraclePriceData;
3758
+ }): HealthComponent {
3759
+ const settledLpPosition = this.getPerpPositionWithLPSettle(
3760
+ perpPosition.marketIndex,
3761
+ perpPosition
3762
+ )[0];
3763
+ const perpMarket = this.driftClient.getPerpMarketAccount(
3764
+ perpPosition.marketIndex
3765
+ );
3766
+ const _oraclePriceData =
3767
+ oraclePriceData ||
3768
+ this.driftClient.getOracleDataForPerpMarket(perpMarket.marketIndex);
3769
+ const oraclePrice = _oraclePriceData.price;
3770
+ const {
3771
+ worstCaseBaseAssetAmount: worstCaseBaseAmount,
3772
+ worstCaseLiabilityValue,
3773
+ } = calculateWorstCasePerpLiabilityValue(
3774
+ settledLpPosition,
3775
+ perpMarket,
3776
+ oraclePrice
3777
+ );
3778
+
3779
+ const marginRatio = new BN(
3780
+ calculateMarketMarginRatio(
3781
+ perpMarket,
3782
+ worstCaseBaseAmount.abs(),
3783
+ marginCategory,
3784
+ this.getUserAccount().maxMarginRatio,
3785
+ this.isHighLeverageMode()
3786
+ )
3787
+ );
3788
+
3789
+ const _quoteOraclePriceData =
3790
+ quoteOraclePriceData ||
3791
+ this.driftClient.getOracleDataForSpotMarket(QUOTE_SPOT_MARKET_INDEX);
3792
+
3793
+ let marginRequirement = worstCaseLiabilityValue
3794
+ .mul(_quoteOraclePriceData.price)
3795
+ .div(PRICE_PRECISION)
3796
+ .mul(marginRatio)
3797
+ .div(MARGIN_PRECISION);
3798
+
3799
+ marginRequirement = marginRequirement.add(
3800
+ new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
3801
+ );
3802
+
3803
+ if (perpPosition.lpShares.gt(ZERO)) {
3804
+ marginRequirement = marginRequirement.add(
3805
+ BN.max(
3806
+ QUOTE_PRECISION,
3807
+ oraclePrice
3808
+ .mul(perpMarket.amm.orderStepSize)
3809
+ .mul(QUOTE_PRECISION)
3810
+ .div(AMM_RESERVE_PRECISION)
3811
+ .div(PRICE_PRECISION)
3812
+ )
3813
+ );
3814
+ }
3815
+
3816
+ return {
3817
+ marketIndex: perpMarket.marketIndex,
3818
+ size: worstCaseBaseAmount,
3819
+ value: worstCaseLiabilityValue,
3820
+ weight: marginRatio,
3821
+ weightedValue: marginRequirement,
3822
+ };
3823
+ }
3824
+
3825
+ public getHealthComponents({
3826
+ marginCategory,
3827
+ }: {
3828
+ marginCategory: MarginCategory;
3829
+ }): HealthComponents {
3830
+ const healthComponents: HealthComponents = {
3831
+ deposits: [],
3832
+ borrows: [],
3833
+ perpPositions: [],
3834
+ perpPnl: [],
3835
+ };
3836
+
3837
+ for (const perpPosition of this.getActivePerpPositions()) {
3838
+ const perpMarket = this.driftClient.getPerpMarketAccount(
3839
+ perpPosition.marketIndex
3840
+ );
3841
+
3842
+ const oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
3843
+ perpMarket.marketIndex
3844
+ );
3845
+
3846
+ const quoteOraclePriceData = this.driftClient.getOracleDataForSpotMarket(
3847
+ QUOTE_SPOT_MARKET_INDEX
3848
+ );
3849
+
3850
+ healthComponents.perpPositions.push(
3851
+ this.getPerpPositionHealth({
3852
+ marginCategory,
3853
+ perpPosition,
3854
+ oraclePriceData,
3855
+ quoteOraclePriceData,
3856
+ })
3857
+ );
3858
+
3859
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
3860
+ perpMarket.quoteSpotMarketIndex
3861
+ );
3862
+
3863
+ const settledPerpPosition = this.getPerpPositionWithLPSettle(
3864
+ perpPosition.marketIndex,
3865
+ perpPosition
3866
+ )[0];
3867
+
3868
+ const positionUnrealizedPnl = calculatePositionPNL(
3869
+ perpMarket,
3870
+ settledPerpPosition,
3871
+ true,
3872
+ oraclePriceData
3873
+ );
3874
+
3875
+ let pnlWeight;
3876
+ if (positionUnrealizedPnl.gt(ZERO)) {
3877
+ pnlWeight = calculateUnrealizedAssetWeight(
3878
+ perpMarket,
3879
+ quoteSpotMarket,
3880
+ positionUnrealizedPnl,
3881
+ marginCategory,
3882
+ oraclePriceData
3883
+ );
3884
+ } else {
3885
+ pnlWeight = SPOT_MARKET_WEIGHT_PRECISION;
3886
+ }
3887
+
3888
+ const pnlValue = positionUnrealizedPnl
3889
+ .mul(quoteOraclePriceData.price)
3890
+ .div(PRICE_PRECISION);
3891
+
3892
+ const wegithedPnlValue = pnlValue
3893
+ .mul(pnlWeight)
3894
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
3895
+
3896
+ healthComponents.perpPnl.push({
3897
+ marketIndex: perpMarket.marketIndex,
3898
+ size: positionUnrealizedPnl,
3899
+ value: pnlValue,
3900
+ weight: pnlWeight,
3901
+ weightedValue: wegithedPnlValue,
3902
+ });
3903
+ }
3904
+
3905
+ let netQuoteValue = ZERO;
3906
+ for (const spotPosition of this.getActiveSpotPositions()) {
3907
+ const spotMarketAccount: SpotMarketAccount =
3908
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
3909
+
3910
+ const oraclePriceData = this.getOracleDataForSpotMarket(
3911
+ spotPosition.marketIndex
3912
+ );
3913
+
3914
+ const strictOraclePrice = new StrictOraclePrice(oraclePriceData.price);
3915
+
3916
+ if (spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX) {
3917
+ const tokenAmount = getSignedTokenAmount(
3918
+ getTokenAmount(
3919
+ spotPosition.scaledBalance,
3920
+ spotMarketAccount,
3921
+ spotPosition.balanceType
3922
+ ),
3923
+ spotPosition.balanceType
3924
+ );
3925
+
3926
+ netQuoteValue = netQuoteValue.add(tokenAmount);
3927
+ continue;
3928
+ }
3929
+
3930
+ const {
3931
+ tokenAmount: worstCaseTokenAmount,
3932
+ tokenValue: tokenValue,
3933
+ weight,
3934
+ weightedTokenValue: weightedTokenValue,
3935
+ ordersValue: ordersValue,
3936
+ } = getWorstCaseTokenAmounts(
3937
+ spotPosition,
3938
+ spotMarketAccount,
3939
+ strictOraclePrice,
3940
+ marginCategory,
3941
+ this.getUserAccount().maxMarginRatio
3942
+ );
3943
+
3944
+ netQuoteValue = netQuoteValue.add(ordersValue);
3945
+
3946
+ const baseAssetValue = tokenValue.abs();
3947
+ const weightedValue = weightedTokenValue.abs();
3948
+
3949
+ if (weightedTokenValue.lt(ZERO)) {
3950
+ healthComponents.borrows.push({
3951
+ marketIndex: spotMarketAccount.marketIndex,
3952
+ size: worstCaseTokenAmount,
3953
+ value: baseAssetValue,
3954
+ weight: weight,
3955
+ weightedValue: weightedValue,
3956
+ });
3957
+ } else {
3958
+ healthComponents.deposits.push({
3959
+ marketIndex: spotMarketAccount.marketIndex,
3960
+ size: worstCaseTokenAmount,
3961
+ value: baseAssetValue,
3962
+ weight: weight,
3963
+ weightedValue: weightedValue,
3964
+ });
3965
+ }
3966
+ }
3967
+
3968
+ if (!netQuoteValue.eq(ZERO)) {
3969
+ const spotMarketAccount = this.driftClient.getQuoteSpotMarketAccount();
3970
+ const oraclePriceData = this.getOracleDataForSpotMarket(
3971
+ QUOTE_SPOT_MARKET_INDEX
3972
+ );
3973
+
3974
+ const baseAssetValue = getTokenValue(
3975
+ netQuoteValue,
3976
+ spotMarketAccount.decimals,
3977
+ oraclePriceData
3978
+ );
3979
+
3980
+ const { weight, weightedTokenValue } = calculateWeightedTokenValue(
3981
+ netQuoteValue,
3982
+ baseAssetValue,
3983
+ oraclePriceData.price,
3984
+ spotMarketAccount,
3985
+ marginCategory,
3986
+ this.getUserAccount().maxMarginRatio
3987
+ );
3988
+
3989
+ if (netQuoteValue.lt(ZERO)) {
3990
+ healthComponents.borrows.push({
3991
+ marketIndex: spotMarketAccount.marketIndex,
3992
+ size: netQuoteValue,
3993
+ value: baseAssetValue.abs(),
3994
+ weight: weight,
3995
+ weightedValue: weightedTokenValue.abs(),
3996
+ });
3997
+ } else {
3998
+ healthComponents.deposits.push({
3999
+ marketIndex: spotMarketAccount.marketIndex,
4000
+ size: netQuoteValue,
4001
+ value: baseAssetValue,
4002
+ weight: weight,
4003
+ weightedValue: weightedTokenValue,
4004
+ });
4005
+ }
4006
+ }
4007
+
4008
+ return healthComponents;
4009
+ }
4010
+
4011
+ /**
4012
+ * Get the total position value, excluding any position coming from the given target market
4013
+ * @param marketToIgnore
4014
+ * @returns positionValue : Precision QUOTE_PRECISION
4015
+ */
4016
+ private getTotalPerpPositionValueExcludingMarket(
4017
+ marketToIgnore: number,
4018
+ marginCategory?: MarginCategory,
4019
+ liquidationBuffer?: BN,
4020
+ includeOpenOrders?: boolean
4021
+ ): BN {
4022
+ const currentPerpPosition =
4023
+ this.getPerpPositionWithLPSettle(
4024
+ marketToIgnore,
4025
+ undefined,
4026
+ !!marginCategory
4027
+ )[0] || this.getEmptyPosition(marketToIgnore);
4028
+
4029
+ const oracleData = this.getOracleDataForPerpMarket(marketToIgnore);
4030
+
4031
+ let currentPerpPositionValueUSDC = ZERO;
4032
+ if (currentPerpPosition) {
4033
+ currentPerpPositionValueUSDC = this.getPerpLiabilityValue(
4034
+ marketToIgnore,
4035
+ oracleData,
4036
+ includeOpenOrders
4037
+ );
4038
+ }
4039
+
4040
+ return this.getTotalPerpPositionLiability(
4041
+ marginCategory,
4042
+ liquidationBuffer,
4043
+ includeOpenOrders
4044
+ ).sub(currentPerpPositionValueUSDC);
4045
+ }
4046
+
4047
+ private getOracleDataForPerpMarket(marketIndex: number): OraclePriceData {
4048
+ return this.driftClient.getOracleDataForPerpMarket(marketIndex);
4049
+ }
4050
+
4051
+ private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData {
4052
+ return this.driftClient.getOracleDataForSpotMarket(marketIndex);
4053
+ }
4054
+ }