@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30

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 (208) hide show
  1. package/README.md +13 -13
  2. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  3. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  4. package/lib/accounts/bulkUserSubscription.js +0 -1
  5. package/lib/accounts/fetch.d.ts +2 -1
  6. package/lib/accounts/fetch.js +9 -1
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
  8. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
  9. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  10. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  11. package/lib/accounts/types.d.ts +22 -11
  12. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
  13. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  15. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  16. package/lib/addresses/marketAddresses.d.ts +1 -3
  17. package/lib/addresses/pda.d.ts +10 -4
  18. package/lib/addresses/pda.js +51 -14
  19. package/lib/admin.d.ts +34 -22
  20. package/lib/admin.js +182 -73
  21. package/lib/clearingHouse.d.ts +120 -42
  22. package/lib/clearingHouse.js +1505 -254
  23. package/lib/clearingHouseConfig.d.ts +4 -4
  24. package/lib/clearingHouseUser.d.ts +50 -38
  25. package/lib/clearingHouseUser.js +410 -190
  26. package/lib/clearingHouseUserStats.d.ts +18 -0
  27. package/lib/clearingHouseUserStats.js +49 -0
  28. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  29. package/lib/clearingHouseUserStatsConfig.js +2 -0
  30. package/lib/config.d.ts +7 -9
  31. package/lib/config.js +21 -21
  32. package/lib/constants/numericConstants.d.ts +18 -12
  33. package/lib/constants/numericConstants.js +28 -21
  34. package/lib/constants/perpMarkets.d.ts +18 -0
  35. package/lib/constants/{markets.js → perpMarkets.js} +7 -7
  36. package/lib/constants/spotMarkets.d.ts +19 -0
  37. package/lib/constants/spotMarkets.js +53 -0
  38. package/lib/dlob/DLOB.d.ts +73 -0
  39. package/lib/dlob/DLOB.js +553 -0
  40. package/lib/dlob/DLOBNode.d.ts +52 -0
  41. package/lib/dlob/DLOBNode.js +82 -0
  42. package/lib/dlob/NodeList.d.ts +26 -0
  43. package/lib/dlob/NodeList.js +138 -0
  44. package/lib/events/eventList.js +3 -0
  45. package/lib/events/eventSubscriber.d.ts +4 -2
  46. package/lib/events/eventSubscriber.js +16 -9
  47. package/lib/events/fetchLogs.d.ts +10 -1
  48. package/lib/events/fetchLogs.js +27 -7
  49. package/lib/events/pollingLogProvider.d.ts +2 -1
  50. package/lib/events/pollingLogProvider.js +6 -2
  51. package/lib/events/sort.js +8 -11
  52. package/lib/events/types.d.ts +7 -2
  53. package/lib/events/types.js +5 -0
  54. package/lib/examples/makeTradeExample.js +20 -8
  55. package/lib/factory/bigNum.d.ts +1 -0
  56. package/lib/factory/bigNum.js +34 -10
  57. package/lib/idl/clearing_house.json +4313 -1433
  58. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  59. package/lib/index.d.ts +21 -6
  60. package/lib/index.js +25 -6
  61. package/lib/math/amm.d.ts +8 -5
  62. package/lib/math/amm.js +68 -46
  63. package/lib/math/auction.js +4 -1
  64. package/lib/math/conversion.js +1 -1
  65. package/lib/math/funding.d.ts +6 -6
  66. package/lib/math/funding.js +11 -10
  67. package/lib/math/insurance.d.ts +4 -0
  68. package/lib/math/insurance.js +27 -0
  69. package/lib/math/margin.d.ts +11 -0
  70. package/lib/math/margin.js +77 -0
  71. package/lib/math/market.d.ts +14 -9
  72. package/lib/math/market.js +69 -7
  73. package/lib/math/oracles.d.ts +4 -0
  74. package/lib/math/oracles.js +36 -8
  75. package/lib/math/orders.d.ts +6 -2
  76. package/lib/math/orders.js +78 -3
  77. package/lib/math/position.d.ts +21 -13
  78. package/lib/math/position.js +76 -36
  79. package/lib/math/repeg.js +14 -5
  80. package/lib/math/spotBalance.d.ts +22 -0
  81. package/lib/math/spotBalance.js +193 -0
  82. package/lib/math/spotMarket.d.ts +4 -0
  83. package/lib/math/spotMarket.js +8 -0
  84. package/lib/math/spotPosition.d.ts +6 -0
  85. package/lib/math/spotPosition.js +23 -0
  86. package/lib/math/trade.d.ts +10 -10
  87. package/lib/math/trade.js +22 -29
  88. package/lib/oracles/pythClient.js +1 -1
  89. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  90. package/lib/oracles/switchboardClient.js +1 -1
  91. package/lib/orderParams.d.ts +14 -5
  92. package/lib/orderParams.js +12 -92
  93. package/lib/serum/serumSubscriber.d.ts +23 -0
  94. package/lib/serum/serumSubscriber.js +41 -0
  95. package/lib/serum/types.d.ts +11 -0
  96. package/lib/serum/types.js +2 -0
  97. package/lib/slot/SlotSubscriber.d.ts +7 -0
  98. package/lib/slot/SlotSubscriber.js +3 -0
  99. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  100. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  101. package/lib/tx/retryTxSender.d.ts +1 -1
  102. package/lib/tx/retryTxSender.js +13 -4
  103. package/lib/tx/types.d.ts +1 -1
  104. package/lib/tx/utils.js +1 -1
  105. package/lib/types.d.ts +474 -123
  106. package/lib/types.js +99 -5
  107. package/lib/userMap/userMap.d.ts +25 -0
  108. package/lib/userMap/userMap.js +73 -0
  109. package/lib/userMap/userStatsMap.d.ts +19 -0
  110. package/lib/userMap/userStatsMap.js +68 -0
  111. package/lib/util/computeUnits.js +1 -1
  112. package/lib/util/getTokenAddress.d.ts +2 -0
  113. package/lib/util/getTokenAddress.js +9 -0
  114. package/package.json +9 -5
  115. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  116. package/src/accounts/bulkUserSubscription.ts +0 -1
  117. package/src/accounts/fetch.ts +27 -2
  118. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
  119. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  120. package/src/accounts/types.ts +31 -11
  121. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
  122. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  123. package/src/addresses/marketAddresses.ts +1 -2
  124. package/src/addresses/pda.ts +88 -14
  125. package/src/admin.ts +333 -128
  126. package/src/assert/assert.js +9 -0
  127. package/src/clearingHouse.ts +2464 -458
  128. package/src/clearingHouseConfig.ts +4 -3
  129. package/src/clearingHouseUser.ts +747 -291
  130. package/src/clearingHouseUserStats.ts +75 -0
  131. package/src/clearingHouseUserStatsConfig.ts +18 -0
  132. package/src/config.ts +30 -31
  133. package/src/constants/numericConstants.ts +41 -25
  134. package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
  135. package/src/constants/spotMarkets.ts +72 -0
  136. package/src/dlob/DLOB.ts +868 -0
  137. package/src/dlob/DLOBNode.ts +162 -0
  138. package/src/dlob/NodeList.ts +185 -0
  139. package/src/events/eventList.js +77 -0
  140. package/src/events/eventList.ts +3 -0
  141. package/src/events/eventSubscriber.ts +20 -12
  142. package/src/events/fetchLogs.ts +35 -8
  143. package/src/events/pollingLogProvider.ts +10 -2
  144. package/src/events/sort.ts +11 -15
  145. package/src/events/types.ts +16 -1
  146. package/src/examples/makeTradeExample.js +157 -0
  147. package/src/examples/makeTradeExample.ts +32 -14
  148. package/src/factory/bigNum.ts +42 -13
  149. package/src/idl/clearing_house.json +4313 -1433
  150. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  151. package/src/index.ts +21 -6
  152. package/src/math/amm.ts +136 -66
  153. package/src/math/auction.ts +5 -1
  154. package/src/math/conversion.ts +2 -2
  155. package/src/math/funding.ts +20 -18
  156. package/src/math/insurance.ts +35 -0
  157. package/src/math/margin.ts +127 -0
  158. package/src/math/market.ts +138 -12
  159. package/src/math/oracles.ts +63 -9
  160. package/src/math/orders.ts +138 -4
  161. package/src/math/position.ts +119 -58
  162. package/src/math/repeg.ts +16 -6
  163. package/src/math/spotBalance.ts +316 -0
  164. package/src/math/spotMarket.ts +9 -0
  165. package/src/math/spotPosition.ts +47 -0
  166. package/src/math/trade.ts +43 -49
  167. package/src/oracles/pythClient.ts +2 -2
  168. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  169. package/src/oracles/switchboardClient.ts +2 -2
  170. package/src/orderParams.ts +24 -137
  171. package/src/serum/serumSubscriber.ts +80 -0
  172. package/src/serum/types.ts +13 -0
  173. package/src/slot/SlotSubscriber.ts +11 -1
  174. package/src/token/index.js +38 -0
  175. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  176. package/src/tx/retryTxSender.ts +16 -5
  177. package/src/tx/types.js +2 -0
  178. package/src/tx/types.ts +2 -1
  179. package/src/tx/utils.js +17 -0
  180. package/src/tx/utils.ts +1 -1
  181. package/src/types.ts +477 -125
  182. package/src/userMap/userMap.ts +100 -0
  183. package/src/userMap/userStatsMap.ts +110 -0
  184. package/src/util/computeUnits.js +21 -11
  185. package/src/util/computeUnits.ts +1 -1
  186. package/src/util/getTokenAddress.js +9 -0
  187. package/src/util/getTokenAddress.ts +18 -0
  188. package/src/util/promiseTimeout.js +14 -0
  189. package/src/util/tps.js +27 -0
  190. package/tests/bn/test.ts +12 -3
  191. package/tests/dlob/helpers.ts +374 -0
  192. package/tests/dlob/test.ts +2865 -0
  193. package/lib/constants/banks.d.ts +0 -16
  194. package/lib/constants/banks.js +0 -34
  195. package/lib/constants/markets.d.ts +0 -19
  196. package/lib/math/bankBalance.d.ts +0 -9
  197. package/lib/math/bankBalance.js +0 -75
  198. package/lib/math/state.d.ts +0 -8
  199. package/lib/math/state.js +0 -15
  200. package/lib/orders.d.ts +0 -8
  201. package/lib/orders.js +0 -134
  202. package/src/constants/banks.ts +0 -43
  203. package/src/math/bankBalance.ts +0 -112
  204. package/src/math/state.ts +0 -14
  205. package/src/math/utils.js +0 -27
  206. package/src/math/utils.js.map +0 -1
  207. package/src/orders.ts +0 -244
  208. package/src/util/computeUnits.js.map +0 -1
@@ -1,22 +1,23 @@
1
- import { BN } from '../';
1
+ import { BN, SpotMarketAccount } from '../';
2
2
  import {
3
3
  AMM_RESERVE_PRECISION,
4
4
  AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO,
5
5
  AMM_TO_QUOTE_PRECISION_RATIO,
6
- FUNDING_PAYMENT_PRECISION,
7
- MARK_PRICE_PRECISION,
6
+ FUNDING_RATE_BUFFER_PRECISION,
7
+ PRICE_PRECISION,
8
8
  ONE,
9
- PRICE_TO_QUOTE_PRECISION,
10
9
  ZERO,
11
10
  } from '../constants/numericConstants';
12
11
  import { OraclePriceData } from '../oracles/types';
13
- import { MarketAccount, PositionDirection, UserPosition } from '../types';
12
+ import { PerpMarketAccount, PositionDirection, PerpPosition } from '../types';
14
13
  import {
15
14
  calculateUpdatedAMM,
16
15
  calculateUpdatedAMMSpreadReserves,
17
16
  calculateAmmReservesAfterSwap,
18
17
  getSwapDirection,
19
18
  } from './amm';
19
+ import { calculateBaseAssetValueWithOracle } from './margin';
20
+ import { calculateNetUserPnlImbalance } from './market';
20
21
 
21
22
  /**
22
23
  * calculateBaseAssetValue
@@ -27,9 +28,11 @@ import {
27
28
  * @returns Base Asset Value. : Precision QUOTE_PRECISION
28
29
  */
29
30
  export function calculateBaseAssetValue(
30
- market: MarketAccount,
31
- userPosition: UserPosition,
32
- oraclePriceData: OraclePriceData
31
+ market: PerpMarketAccount,
32
+ userPosition: PerpPosition,
33
+ oraclePriceData: OraclePriceData,
34
+ useSpread = true,
35
+ skipUpdate = false
33
36
  ): BN {
34
37
  if (userPosition.baseAssetAmount.eq(ZERO)) {
35
38
  return ZERO;
@@ -38,21 +41,25 @@ export function calculateBaseAssetValue(
38
41
  const directionToClose = findDirectionToClose(userPosition);
39
42
  let prepegAmm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
40
43
 
41
- if (market.amm.baseSpread > 0) {
42
- const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
43
- calculateUpdatedAMMSpreadReserves(
44
- market.amm,
45
- directionToClose,
46
- oraclePriceData
47
- );
48
- prepegAmm = {
49
- baseAssetReserve,
50
- quoteAssetReserve,
51
- sqrtK: sqrtK,
52
- pegMultiplier: newPeg,
53
- };
44
+ if (!skipUpdate) {
45
+ if (market.amm.baseSpread > 0 && useSpread) {
46
+ const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
47
+ calculateUpdatedAMMSpreadReserves(
48
+ market.amm,
49
+ directionToClose,
50
+ oraclePriceData
51
+ );
52
+ prepegAmm = {
53
+ baseAssetReserve,
54
+ quoteAssetReserve,
55
+ sqrtK: sqrtK,
56
+ pegMultiplier: newPeg,
57
+ };
58
+ } else {
59
+ prepegAmm = calculateUpdatedAMM(market.amm, oraclePriceData);
60
+ }
54
61
  } else {
55
- prepegAmm = calculateUpdatedAMM(market.amm, oraclePriceData);
62
+ prepegAmm = market.amm;
56
63
  }
57
64
 
58
65
  const [newQuoteAssetReserve, _] = calculateAmmReservesAfterSwap(
@@ -82,38 +89,36 @@ export function calculateBaseAssetValue(
82
89
  * calculatePositionPNL
83
90
  * = BaseAssetAmount * (Avg Exit Price - Avg Entry Price)
84
91
  * @param market
85
- * @param marketPosition
92
+ * @param PerpPosition
86
93
  * @param withFunding (adds unrealized funding payment pnl to result)
94
+ * @param oraclePriceData
87
95
  * @returns BaseAssetAmount : Precision QUOTE_PRECISION
88
96
  */
89
97
  export function calculatePositionPNL(
90
- market: MarketAccount,
91
- marketPosition: UserPosition,
98
+ market: PerpMarketAccount,
99
+ perpPosition: PerpPosition,
92
100
  withFunding = false,
93
101
  oraclePriceData: OraclePriceData
94
102
  ): BN {
95
- if (marketPosition.baseAssetAmount.eq(ZERO)) {
96
- return ZERO;
103
+ if (perpPosition.baseAssetAmount.eq(ZERO)) {
104
+ return perpPosition.quoteAssetAmount;
97
105
  }
98
106
 
99
- const baseAssetValue = calculateBaseAssetValue(
107
+ const baseAssetValue = calculateBaseAssetValueWithOracle(
100
108
  market,
101
- marketPosition,
109
+ perpPosition,
102
110
  oraclePriceData
103
111
  );
104
112
 
105
- let pnl;
106
- if (marketPosition.baseAssetAmount.gt(ZERO)) {
107
- pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount);
108
- } else {
109
- pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue);
110
- }
113
+ const baseAssetValueSign = perpPosition.baseAssetAmount.isNeg()
114
+ ? new BN(-1)
115
+ : new BN(1);
116
+ let pnl = baseAssetValue
117
+ .mul(baseAssetValueSign)
118
+ .add(perpPosition.quoteAssetAmount);
111
119
 
112
120
  if (withFunding) {
113
- const fundingRatePnL = calculatePositionFundingPNL(
114
- market,
115
- marketPosition
116
- ).div(PRICE_TO_QUOTE_PRECISION);
121
+ const fundingRatePnL = calculatePositionFundingPNL(market, perpPosition);
117
122
 
118
123
  pnl = pnl.add(fundingRatePnL);
119
124
  }
@@ -121,60 +126,118 @@ export function calculatePositionPNL(
121
126
  return pnl;
122
127
  }
123
128
 
129
+ export function calculateClaimablePnl(
130
+ market: PerpMarketAccount,
131
+ spotMarket: SpotMarketAccount,
132
+ perpPosition: PerpPosition,
133
+ oraclePriceData: OraclePriceData
134
+ ): BN {
135
+ const unrealizedPnl = calculatePositionPNL(
136
+ market,
137
+ perpPosition,
138
+ true,
139
+ oraclePriceData
140
+ );
141
+
142
+ const fundingPnL = calculatePositionFundingPNL(market, perpPosition);
143
+
144
+ let unsettledPnl = unrealizedPnl.add(fundingPnL);
145
+ if (unrealizedPnl.gt(ZERO)) {
146
+ const excessPnlPool = BN.max(
147
+ ZERO,
148
+ calculateNetUserPnlImbalance(market, spotMarket, oraclePriceData).mul(
149
+ new BN(-1)
150
+ )
151
+ );
152
+
153
+ const maxPositivePnl = BN.max(
154
+ perpPosition.quoteAssetAmount
155
+ .sub(perpPosition.quoteEntryAmount)
156
+ .add(excessPnlPool),
157
+ ZERO
158
+ );
159
+
160
+ unsettledPnl = BN.min(maxPositivePnl, unrealizedPnl);
161
+ }
162
+ return unsettledPnl;
163
+ }
164
+
124
165
  /**
125
166
  *
126
167
  * @param market
127
- * @param marketPosition
168
+ * @param PerpPosition
128
169
  * @returns // TODO-PRECISION
129
170
  */
130
171
  export function calculatePositionFundingPNL(
131
- market: MarketAccount,
132
- marketPosition: UserPosition
172
+ market: PerpMarketAccount,
173
+ perpPosition: PerpPosition
133
174
  ): BN {
134
- if (marketPosition.baseAssetAmount.eq(ZERO)) {
175
+ if (perpPosition.baseAssetAmount.eq(ZERO)) {
135
176
  return ZERO;
136
177
  }
137
178
 
138
179
  let ammCumulativeFundingRate: BN;
139
- if (marketPosition.baseAssetAmount.gt(ZERO)) {
180
+ if (perpPosition.baseAssetAmount.gt(ZERO)) {
140
181
  ammCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
141
182
  } else {
142
183
  ammCumulativeFundingRate = market.amm.cumulativeFundingRateShort;
143
184
  }
144
185
 
145
186
  const perPositionFundingRate = ammCumulativeFundingRate
146
- .sub(marketPosition.lastCumulativeFundingRate)
147
- .mul(marketPosition.baseAssetAmount)
187
+ .sub(perpPosition.lastCumulativeFundingRate)
188
+ .mul(perpPosition.baseAssetAmount)
148
189
  .div(AMM_RESERVE_PRECISION)
149
- .div(FUNDING_PAYMENT_PRECISION)
190
+ .div(FUNDING_RATE_BUFFER_PRECISION)
150
191
  .mul(new BN(-1));
151
192
 
152
193
  return perPositionFundingRate;
153
194
  }
154
195
 
155
- export function positionIsAvailable(position: UserPosition): boolean {
156
- return position.baseAssetAmount.eq(ZERO) && position.openOrders.eq(ZERO);
196
+ export function positionIsAvailable(position: PerpPosition): boolean {
197
+ return (
198
+ position.baseAssetAmount.eq(ZERO) &&
199
+ position.openOrders === 0 &&
200
+ position.quoteAssetAmount.eq(ZERO) &&
201
+ position.lpShares.eq(ZERO)
202
+ );
157
203
  }
158
204
 
159
205
  /**
160
206
  *
161
207
  * @param userPosition
162
- * @returns Precision: MARK_PRICE_PRECISION (10^10)
208
+ * @returns Precision: PRICE_PRECISION (10^10)
163
209
  */
164
- export function calculateEntryPrice(userPosition: UserPosition): BN {
210
+ export function calculateEntryPrice(userPosition: PerpPosition): BN {
211
+ if (userPosition.baseAssetAmount.eq(ZERO)) {
212
+ return ZERO;
213
+ }
214
+
215
+ return userPosition.quoteEntryAmount
216
+ .mul(PRICE_PRECISION)
217
+ .mul(AMM_TO_QUOTE_PRECISION_RATIO)
218
+ .div(userPosition.baseAssetAmount)
219
+ .abs();
220
+ }
221
+
222
+ /**
223
+ *
224
+ * @param userPosition
225
+ * @returns Precision: PRICE_PRECISION (10^10)
226
+ */
227
+ export function calculateCostBasis(userPosition: PerpPosition): BN {
165
228
  if (userPosition.baseAssetAmount.eq(ZERO)) {
166
229
  return ZERO;
167
230
  }
168
231
 
169
232
  return userPosition.quoteAssetAmount
170
- .mul(MARK_PRICE_PRECISION)
233
+ .mul(PRICE_PRECISION)
171
234
  .mul(AMM_TO_QUOTE_PRECISION_RATIO)
172
235
  .div(userPosition.baseAssetAmount)
173
236
  .abs();
174
237
  }
175
238
 
176
239
  export function findDirectionToClose(
177
- userPosition: UserPosition
240
+ userPosition: PerpPosition
178
241
  ): PositionDirection {
179
242
  return userPosition.baseAssetAmount.gt(ZERO)
180
243
  ? PositionDirection.SHORT
@@ -182,15 +245,13 @@ export function findDirectionToClose(
182
245
  }
183
246
 
184
247
  export function positionCurrentDirection(
185
- userPosition: UserPosition
248
+ userPosition: PerpPosition
186
249
  ): PositionDirection {
187
250
  return userPosition.baseAssetAmount.gte(ZERO)
188
251
  ? PositionDirection.LONG
189
252
  : PositionDirection.SHORT;
190
253
  }
191
254
 
192
- export function isEmptyPosition(userPosition: UserPosition): boolean {
193
- return (
194
- userPosition.baseAssetAmount.eq(ZERO) && userPosition.openOrders.eq(ZERO)
195
- );
255
+ export function isEmptyPosition(userPosition: PerpPosition): boolean {
256
+ return userPosition.baseAssetAmount.eq(ZERO) && userPosition.openOrders === 0;
196
257
  }
package/src/math/repeg.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BN } from '@project-serum/anchor';
2
2
  import { assert } from '../assert/assert';
3
3
  import {
4
- MARK_PRICE_PRECISION,
4
+ PRICE_PRECISION,
5
5
  AMM_RESERVE_PRECISION,
6
6
  PEG_PRECISION,
7
7
  AMM_TO_QUOTE_PRECISION_RATIO,
@@ -31,15 +31,15 @@ export function calculateAdjustKCost(
31
31
 
32
32
  const quoteScale = y.mul(d).mul(Q); //.div(AMM_RESERVE_PRECISION);
33
33
 
34
- const p = numerator.mul(MARK_PRICE_PRECISION).div(denomenator);
34
+ const p = numerator.mul(PRICE_PRECISION).div(denomenator);
35
35
 
36
36
  const cost = quoteScale
37
37
  .div(x.add(d))
38
38
  .sub(
39
39
  quoteScale
40
40
  .mul(p)
41
- .div(MARK_PRICE_PRECISION)
42
- .div(x.mul(p).div(MARK_PRICE_PRECISION).add(d))
41
+ .div(PRICE_PRECISION)
42
+ .div(x.mul(p).div(PRICE_PRECISION).add(d))
43
43
  )
44
44
  .div(AMM_TO_QUOTE_PRECISION_RATIO)
45
45
  .div(PEG_PRECISION);
@@ -98,6 +98,16 @@ export function calculateBudgetedKBN(
98
98
  .div(QUOTE_PRECISION);
99
99
  const denom2 = pegged_y_d_d;
100
100
 
101
+ // protocol is spending to increase k
102
+ if (C.lt(ZERO)) {
103
+ // thus denom1 is negative and solution is unstable
104
+ if (denom1.abs().gt(denom2.abs())) {
105
+ console.log('denom1 > denom2', denom1.toString(), denom2.toString());
106
+ console.log('budget cost exceeds stable K solution');
107
+ return [new BN(10000), new BN(1)];
108
+ }
109
+ }
110
+
101
111
  const numerator = numer1.sub(numer2).div(AMM_TO_QUOTE_PRECISION_RATIO);
102
112
  const denominator = denom1.add(denom2).div(AMM_TO_QUOTE_PRECISION_RATIO);
103
113
 
@@ -165,11 +175,11 @@ export function calculateBudgetedPeg(amm: AMM, cost: BN, targetPrice: BN): BN {
165
175
  return targetPeg;
166
176
  }
167
177
 
168
- const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION).div(
178
+ const deltaPegMultiplier = C.mul(PRICE_PRECISION).div(
169
179
  deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO)
170
180
  );
171
181
  const newPeg = Q.sub(
172
- deltaPegMultiplier.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION)
182
+ deltaPegMultiplier.mul(PEG_PRECISION).div(PRICE_PRECISION)
173
183
  );
174
184
 
175
185
  return newPeg;
@@ -0,0 +1,316 @@
1
+ import {
2
+ SpotMarketAccount,
3
+ SpotBalanceType,
4
+ isVariant,
5
+ MarginCategory,
6
+ } from '../types';
7
+ import { BN } from '@project-serum/anchor';
8
+ import {
9
+ SPOT_MARKET_UTILIZATION_PRECISION,
10
+ ONE,
11
+ TEN,
12
+ ZERO,
13
+ SPOT_MARKET_RATE_PRECISION,
14
+ SPOT_MARKET_WEIGHT_PRECISION,
15
+ ONE_YEAR,
16
+ AMM_RESERVE_PRECISION,
17
+ } from '../constants/numericConstants';
18
+ import {
19
+ calculateSizeDiscountAssetWeight,
20
+ calculateSizePremiumLiabilityWeight,
21
+ } from './margin';
22
+ import { OraclePriceData } from '../oracles/types';
23
+
24
+ export function getBalance(
25
+ tokenAmount: BN,
26
+ spotMarket: SpotMarketAccount,
27
+ balanceType: SpotBalanceType
28
+ ): BN {
29
+ const precisionIncrease = TEN.pow(new BN(19 - spotMarket.decimals));
30
+
31
+ const cumulativeInterest = isVariant(balanceType, 'deposit')
32
+ ? spotMarket.cumulativeDepositInterest
33
+ : spotMarket.cumulativeBorrowInterest;
34
+
35
+ let balance = tokenAmount.mul(precisionIncrease).div(cumulativeInterest);
36
+
37
+ if (!balance.eq(ZERO) && isVariant(balanceType, 'borrow')) {
38
+ balance = balance.add(ONE);
39
+ }
40
+
41
+ return balance;
42
+ }
43
+
44
+ export function getTokenAmount(
45
+ balanceAmount: BN,
46
+ spotMarket: SpotMarketAccount,
47
+ balanceType: SpotBalanceType
48
+ ): BN {
49
+ const precisionDecrease = TEN.pow(new BN(19 - spotMarket.decimals));
50
+
51
+ const cumulativeInterest = isVariant(balanceType, 'deposit')
52
+ ? spotMarket.cumulativeDepositInterest
53
+ : spotMarket.cumulativeBorrowInterest;
54
+
55
+ return balanceAmount.mul(cumulativeInterest).div(precisionDecrease);
56
+ }
57
+
58
+ export function getSignedTokenAmount(
59
+ tokenAmount: BN,
60
+ balanceType: SpotBalanceType
61
+ ): BN {
62
+ if (isVariant(balanceType, 'deposit')) {
63
+ return tokenAmount;
64
+ } else {
65
+ return tokenAmount.abs().neg();
66
+ }
67
+ }
68
+
69
+ export function getTokenValue(
70
+ tokenAmount: BN,
71
+ spotDecimals: number,
72
+ oraclePriceData: OraclePriceData
73
+ ): BN {
74
+ if (tokenAmount.eq(ZERO)) {
75
+ return ZERO;
76
+ }
77
+
78
+ const precisionDecrease = TEN.pow(new BN(spotDecimals));
79
+
80
+ return tokenAmount.mul(oraclePriceData.price).div(precisionDecrease);
81
+ }
82
+
83
+ export function calculateAssetWeight(
84
+ balanceAmount: BN,
85
+ spotMarket: SpotMarketAccount,
86
+ marginCategory: MarginCategory
87
+ ): BN {
88
+ const sizePrecision = TEN.pow(new BN(spotMarket.decimals));
89
+ let sizeInAmmReservePrecision;
90
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
91
+ sizeInAmmReservePrecision = balanceAmount.div(
92
+ sizePrecision.div(AMM_RESERVE_PRECISION)
93
+ );
94
+ } else {
95
+ sizeInAmmReservePrecision = balanceAmount
96
+ .mul(AMM_RESERVE_PRECISION)
97
+ .div(sizePrecision);
98
+ }
99
+
100
+ let assetWeight;
101
+
102
+ switch (marginCategory) {
103
+ case 'Initial':
104
+ assetWeight = calculateSizeDiscountAssetWeight(
105
+ sizeInAmmReservePrecision,
106
+ spotMarket.imfFactor,
107
+ spotMarket.initialAssetWeight
108
+ );
109
+ break;
110
+ case 'Maintenance':
111
+ assetWeight = calculateSizeDiscountAssetWeight(
112
+ sizeInAmmReservePrecision,
113
+ spotMarket.imfFactor,
114
+ spotMarket.maintenanceAssetWeight
115
+ );
116
+ break;
117
+ default:
118
+ assetWeight = spotMarket.initialAssetWeight;
119
+ break;
120
+ }
121
+
122
+ return assetWeight;
123
+ }
124
+
125
+ export function calculateLiabilityWeight(
126
+ balanceAmount: BN,
127
+ spotMarket: SpotMarketAccount,
128
+ marginCategory: MarginCategory
129
+ ): BN {
130
+ const sizePrecision = TEN.pow(new BN(spotMarket.decimals));
131
+ let sizeInAmmReservePrecision;
132
+ if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
133
+ sizeInAmmReservePrecision = balanceAmount.div(
134
+ sizePrecision.div(AMM_RESERVE_PRECISION)
135
+ );
136
+ } else {
137
+ sizeInAmmReservePrecision = balanceAmount
138
+ .mul(AMM_RESERVE_PRECISION)
139
+ .div(sizePrecision);
140
+ }
141
+
142
+ let assetWeight;
143
+
144
+ switch (marginCategory) {
145
+ case 'Initial':
146
+ assetWeight = calculateSizePremiumLiabilityWeight(
147
+ sizeInAmmReservePrecision,
148
+ spotMarket.imfFactor,
149
+ spotMarket.initialLiabilityWeight,
150
+ SPOT_MARKET_WEIGHT_PRECISION
151
+ );
152
+ break;
153
+ case 'Maintenance':
154
+ assetWeight = calculateSizePremiumLiabilityWeight(
155
+ sizeInAmmReservePrecision,
156
+ spotMarket.imfFactor,
157
+ spotMarket.maintenanceLiabilityWeight,
158
+ SPOT_MARKET_WEIGHT_PRECISION
159
+ );
160
+ break;
161
+ default:
162
+ assetWeight = spotMarket.initialLiabilityWeight;
163
+ break;
164
+ }
165
+
166
+ return assetWeight;
167
+ }
168
+
169
+ export function calculateUtilization(bank: SpotMarketAccount): BN {
170
+ const tokenDepositAmount = getTokenAmount(
171
+ bank.depositBalance,
172
+ bank,
173
+ SpotBalanceType.DEPOSIT
174
+ );
175
+ const tokenBorrowAmount = getTokenAmount(
176
+ bank.borrowBalance,
177
+ bank,
178
+ SpotBalanceType.BORROW
179
+ );
180
+
181
+ let utilization: BN;
182
+ if (tokenBorrowAmount.eq(ZERO) && tokenDepositAmount.eq(ZERO)) {
183
+ utilization = ZERO;
184
+ } else if (tokenDepositAmount.eq(ZERO)) {
185
+ utilization = SPOT_MARKET_UTILIZATION_PRECISION;
186
+ } else {
187
+ utilization = tokenBorrowAmount
188
+ .mul(SPOT_MARKET_UTILIZATION_PRECISION)
189
+ .div(tokenDepositAmount);
190
+ }
191
+
192
+ return utilization;
193
+ }
194
+
195
+ export function calculateInterestRate(bank: SpotMarketAccount): BN {
196
+ const utilization = calculateUtilization(bank);
197
+
198
+ let interestRate: BN;
199
+ if (utilization.gt(bank.optimalUtilization)) {
200
+ const surplusUtilization = utilization.sub(bank.optimalUtilization);
201
+ const borrowRateSlope = bank.maxBorrowRate
202
+ .sub(bank.optimalBorrowRate)
203
+ .mul(SPOT_MARKET_UTILIZATION_PRECISION)
204
+ .div(SPOT_MARKET_UTILIZATION_PRECISION.sub(bank.optimalUtilization));
205
+
206
+ interestRate = bank.optimalBorrowRate.add(
207
+ surplusUtilization
208
+ .mul(borrowRateSlope)
209
+ .div(SPOT_MARKET_UTILIZATION_PRECISION)
210
+ );
211
+ } else {
212
+ const borrowRateSlope = bank.optimalBorrowRate
213
+ .mul(SPOT_MARKET_UTILIZATION_PRECISION)
214
+ .div(SPOT_MARKET_UTILIZATION_PRECISION.sub(bank.optimalUtilization));
215
+
216
+ interestRate = utilization
217
+ .mul(borrowRateSlope)
218
+ .div(SPOT_MARKET_UTILIZATION_PRECISION);
219
+ }
220
+
221
+ return interestRate;
222
+ }
223
+
224
+ export function calculateDepositRate(bank: SpotMarketAccount): BN {
225
+ const utilization = calculateUtilization(bank);
226
+ const borrowRate = calculateBorrowRate(bank);
227
+ const depositRate = borrowRate
228
+ .mul(utilization)
229
+ .div(SPOT_MARKET_UTILIZATION_PRECISION);
230
+ return depositRate;
231
+ }
232
+
233
+ export function calculateBorrowRate(bank: SpotMarketAccount): BN {
234
+ return calculateInterestRate(bank);
235
+ }
236
+
237
+ export function calculateInterestAccumulated(
238
+ bank: SpotMarketAccount,
239
+ now: BN
240
+ ): { borrowInterest: BN; depositInterest: BN } {
241
+ const interestRate = calculateInterestRate(bank);
242
+
243
+ const timeSinceLastUpdate = now.sub(bank.lastInterestTs);
244
+
245
+ const modifiedBorrowRate = interestRate.mul(timeSinceLastUpdate);
246
+
247
+ const utilization = calculateUtilization(bank);
248
+
249
+ const modifiedDepositRate = modifiedBorrowRate
250
+ .mul(utilization)
251
+ .div(SPOT_MARKET_UTILIZATION_PRECISION);
252
+
253
+ const borrowInterest = bank.cumulativeBorrowInterest
254
+ .mul(modifiedBorrowRate)
255
+ .div(ONE_YEAR)
256
+ .div(SPOT_MARKET_RATE_PRECISION)
257
+ .add(ONE);
258
+ const depositInterest = bank.cumulativeDepositInterest
259
+ .mul(modifiedDepositRate)
260
+ .div(ONE_YEAR)
261
+ .div(SPOT_MARKET_RATE_PRECISION);
262
+
263
+ return { borrowInterest, depositInterest };
264
+ }
265
+
266
+ export function calculateWithdrawLimit(
267
+ spotMarket: SpotMarketAccount,
268
+ now: BN
269
+ ): { borrowLimit: BN; withdrawLimit: BN } {
270
+ const marketDepositTokenAmount = getTokenAmount(
271
+ spotMarket.depositBalance,
272
+ spotMarket,
273
+ SpotBalanceType.DEPOSIT
274
+ );
275
+ const marketBorrowTokenAmount = getTokenAmount(
276
+ spotMarket.borrowBalance,
277
+ spotMarket,
278
+ SpotBalanceType.BORROW
279
+ );
280
+
281
+ const twentyFourHours = new BN(60 * 60 * 24);
282
+ const sinceLast = now.sub(spotMarket.lastTwapTs);
283
+ const sinceStart = BN.max(ZERO, twentyFourHours.sub(sinceLast));
284
+ const borrowTokenTwapLive = spotMarket.borrowTokenTwap
285
+ .mul(sinceStart)
286
+ .add(marketBorrowTokenAmount.mul(sinceLast))
287
+ .div(sinceLast.add(sinceLast));
288
+
289
+ const depositTokenTwapLive = spotMarket.depositTokenTwap
290
+ .mul(sinceStart)
291
+ .add(marketDepositTokenAmount.mul(sinceLast))
292
+ .div(sinceLast.add(sinceLast));
293
+
294
+ const maxBorrowTokens = BN.min(
295
+ BN.max(
296
+ marketDepositTokenAmount.div(new BN(6)),
297
+ borrowTokenTwapLive.add(borrowTokenTwapLive.div(new BN(5)))
298
+ ),
299
+ marketDepositTokenAmount.sub(marketDepositTokenAmount.div(new BN(10)))
300
+ ); // between ~15-90% utilization with friction on twap
301
+
302
+ const minDepositTokens = depositTokenTwapLive.sub(
303
+ BN.min(
304
+ BN.max(
305
+ depositTokenTwapLive.div(new BN(5)),
306
+ spotMarket.withdrawGuardThreshold
307
+ ),
308
+ depositTokenTwapLive
309
+ )
310
+ );
311
+
312
+ return {
313
+ borrowLimit: maxBorrowTokens.sub(marketBorrowTokenAmount),
314
+ withdrawLimit: marketDepositTokenAmount.sub(minDepositTokens),
315
+ };
316
+ }
@@ -0,0 +1,9 @@
1
+ import { BN } from '@project-serum/anchor';
2
+ import { SpotMarketAccount } from '../types';
3
+
4
+ export function castNumberToSpotPrecision(
5
+ value: number,
6
+ spotMarket: SpotMarketAccount
7
+ ): BN {
8
+ return new BN(value * Math.pow(10, spotMarket.decimals));
9
+ }