@drift-labs/sdk 0.2.0-master.25 → 0.2.0-master.27

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 (124) hide show
  1. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +14 -13
  2. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +30 -27
  3. package/lib/accounts/types.d.ts +9 -9
  4. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +15 -14
  5. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +38 -34
  6. package/lib/addresses/pda.d.ts +7 -6
  7. package/lib/addresses/pda.js +31 -27
  8. package/lib/admin.d.ts +13 -7
  9. package/lib/admin.js +111 -44
  10. package/lib/clearingHouse.d.ts +71 -42
  11. package/lib/clearingHouse.js +767 -278
  12. package/lib/clearingHouseConfig.d.ts +2 -2
  13. package/lib/clearingHouseUser.d.ts +24 -22
  14. package/lib/clearingHouseUser.js +273 -177
  15. package/lib/config.d.ts +7 -7
  16. package/lib/config.js +21 -21
  17. package/lib/constants/numericConstants.d.ts +12 -12
  18. package/lib/constants/numericConstants.js +13 -13
  19. package/lib/constants/{markets.d.ts → perpMarkets.d.ts} +5 -5
  20. package/lib/constants/{markets.js → perpMarkets.js} +4 -4
  21. package/lib/constants/{banks.d.ts → spotMarkets.d.ts} +6 -6
  22. package/lib/constants/{banks.js → spotMarkets.js} +16 -16
  23. package/lib/dlob/DLOB.d.ts +73 -0
  24. package/lib/dlob/DLOB.js +557 -0
  25. package/lib/dlob/DLOBNode.d.ts +52 -0
  26. package/lib/dlob/DLOBNode.js +82 -0
  27. package/lib/dlob/NodeList.d.ts +26 -0
  28. package/lib/dlob/NodeList.js +138 -0
  29. package/lib/events/types.d.ts +2 -1
  30. package/lib/events/types.js +1 -0
  31. package/lib/examples/makeTradeExample.js +7 -7
  32. package/lib/idl/clearing_house.json +1152 -503
  33. package/lib/index.d.ts +10 -3
  34. package/lib/index.js +10 -3
  35. package/lib/math/amm.d.ts +2 -2
  36. package/lib/math/amm.js +1 -1
  37. package/lib/math/funding.d.ts +6 -6
  38. package/lib/math/funding.js +2 -1
  39. package/lib/math/margin.d.ts +4 -4
  40. package/lib/math/margin.js +18 -11
  41. package/lib/math/market.d.ts +11 -10
  42. package/lib/math/market.js +30 -7
  43. package/lib/math/oracles.d.ts +2 -1
  44. package/lib/math/oracles.js +11 -1
  45. package/lib/math/orders.d.ts +6 -6
  46. package/lib/math/orders.js +31 -16
  47. package/lib/math/position.d.ts +13 -13
  48. package/lib/math/position.js +19 -19
  49. package/lib/math/spotBalance.d.ts +22 -0
  50. package/lib/math/spotBalance.js +193 -0
  51. package/lib/math/spotMarket.d.ts +4 -0
  52. package/lib/math/spotMarket.js +8 -0
  53. package/lib/math/spotPosition.d.ts +6 -0
  54. package/lib/math/spotPosition.js +23 -0
  55. package/lib/math/state.js +2 -2
  56. package/lib/math/trade.d.ts +4 -4
  57. package/lib/orderParams.d.ts +4 -4
  58. package/lib/orderParams.js +12 -4
  59. package/lib/serum/serumSubscriber.d.ts +23 -0
  60. package/lib/serum/serumSubscriber.js +41 -0
  61. package/lib/serum/types.d.ts +11 -0
  62. package/lib/serum/types.js +2 -0
  63. package/lib/tx/retryTxSender.d.ts +1 -1
  64. package/lib/tx/retryTxSender.js +4 -2
  65. package/lib/tx/types.d.ts +1 -1
  66. package/lib/types.d.ts +148 -57
  67. package/lib/types.js +39 -11
  68. package/lib/userMap/userMap.d.ts +25 -0
  69. package/lib/userMap/userMap.js +73 -0
  70. package/lib/userMap/userStatsMap.d.ts +19 -0
  71. package/lib/userMap/userStatsMap.js +68 -0
  72. package/package.json +6 -3
  73. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +42 -38
  74. package/src/accounts/types.ts +12 -9
  75. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +65 -52
  76. package/src/addresses/pda.ts +49 -44
  77. package/src/admin.ts +190 -55
  78. package/src/clearingHouse.ts +1092 -365
  79. package/src/clearingHouseConfig.ts +2 -2
  80. package/src/clearingHouseUser.ts +518 -255
  81. package/src/config.ts +30 -30
  82. package/src/constants/numericConstants.ts +17 -15
  83. package/src/constants/{markets.ts → perpMarkets.ts} +5 -5
  84. package/src/constants/{banks.ts → spotMarkets.ts} +19 -19
  85. package/src/dlob/DLOB.ts +884 -0
  86. package/src/dlob/DLOBNode.ts +163 -0
  87. package/src/dlob/NodeList.ts +185 -0
  88. package/src/events/types.ts +3 -0
  89. package/src/examples/makeTradeExample.js +152 -75
  90. package/src/examples/makeTradeExample.ts +10 -8
  91. package/src/idl/clearing_house.json +1152 -503
  92. package/src/index.ts +10 -3
  93. package/src/math/amm.ts +6 -3
  94. package/src/math/funding.ts +7 -7
  95. package/src/math/margin.ts +34 -23
  96. package/src/math/market.ts +72 -20
  97. package/src/math/oracles.ts +18 -1
  98. package/src/math/orders.ts +33 -25
  99. package/src/math/position.ts +31 -31
  100. package/src/math/spotBalance.ts +316 -0
  101. package/src/math/spotMarket.ts +9 -0
  102. package/src/math/spotPosition.ts +47 -0
  103. package/src/math/state.ts +2 -2
  104. package/src/math/trade.ts +4 -4
  105. package/src/orderParams.ts +16 -8
  106. package/src/serum/serumSubscriber.ts +80 -0
  107. package/src/serum/types.ts +13 -0
  108. package/src/tx/retryTxSender.ts +5 -2
  109. package/src/tx/types.ts +2 -1
  110. package/src/types.ts +135 -56
  111. package/src/userMap/userMap.ts +100 -0
  112. package/src/userMap/userStatsMap.ts +110 -0
  113. package/tests/bn/test.ts +2 -3
  114. package/tests/dlob/helpers.ts +322 -0
  115. package/tests/dlob/test.ts +2865 -0
  116. package/lib/math/bankBalance.d.ts +0 -15
  117. package/lib/math/bankBalance.js +0 -150
  118. package/src/constants/numericConstants.js +0 -41
  119. package/src/math/bankBalance.ts +0 -258
  120. package/src/math/oracles.js +0 -26
  121. package/src/math/state.js +0 -15
  122. package/src/orderParams.js +0 -20
  123. package/src/slot/SlotSubscriber.js +0 -39
  124. package/src/tokenFaucet.js +0 -189
@@ -10,7 +10,7 @@ import {
10
10
  ZERO,
11
11
  } from '../constants/numericConstants';
12
12
  import { OraclePriceData } from '../oracles/types';
13
- import { MarketAccount, PositionDirection, UserPosition } from '../types';
13
+ import { PerpMarketAccount, PositionDirection, PerpPosition } from '../types';
14
14
  import {
15
15
  calculateUpdatedAMM,
16
16
  calculateUpdatedAMMSpreadReserves,
@@ -29,8 +29,8 @@ import { calculateBaseAssetValueWithOracle } from './margin';
29
29
  * @returns Base Asset Value. : Precision QUOTE_PRECISION
30
30
  */
31
31
  export function calculateBaseAssetValue(
32
- market: MarketAccount,
33
- userPosition: UserPosition,
32
+ market: PerpMarketAccount,
33
+ userPosition: PerpPosition,
34
34
  oraclePriceData: OraclePriceData
35
35
  ): BN {
36
36
  if (userPosition.baseAssetAmount.eq(ZERO)) {
@@ -84,38 +84,38 @@ export function calculateBaseAssetValue(
84
84
  * calculatePositionPNL
85
85
  * = BaseAssetAmount * (Avg Exit Price - Avg Entry Price)
86
86
  * @param market
87
- * @param marketPosition
87
+ * @param PerpPosition
88
88
  * @param withFunding (adds unrealized funding payment pnl to result)
89
89
  * @param oraclePriceData
90
90
  * @returns BaseAssetAmount : Precision QUOTE_PRECISION
91
91
  */
92
92
  export function calculatePositionPNL(
93
- market: MarketAccount,
94
- marketPosition: UserPosition,
93
+ market: PerpMarketAccount,
94
+ perpPosition: PerpPosition,
95
95
  withFunding = false,
96
96
  oraclePriceData: OraclePriceData
97
97
  ): BN {
98
- if (marketPosition.baseAssetAmount.eq(ZERO)) {
99
- return marketPosition.quoteAssetAmount;
98
+ if (perpPosition.baseAssetAmount.eq(ZERO)) {
99
+ return perpPosition.quoteAssetAmount;
100
100
  }
101
101
 
102
102
  const baseAssetValue = calculateBaseAssetValueWithOracle(
103
103
  market,
104
- marketPosition,
104
+ perpPosition,
105
105
  oraclePriceData
106
106
  );
107
107
 
108
- const baseAssetValueSign = marketPosition.baseAssetAmount.isNeg()
108
+ const baseAssetValueSign = perpPosition.baseAssetAmount.isNeg()
109
109
  ? new BN(-1)
110
110
  : new BN(1);
111
111
  let pnl = baseAssetValue
112
112
  .mul(baseAssetValueSign)
113
- .add(marketPosition.quoteAssetAmount);
113
+ .add(perpPosition.quoteAssetAmount);
114
114
 
115
115
  if (withFunding) {
116
116
  const fundingRatePnL = calculatePositionFundingPNL(
117
117
  market,
118
- marketPosition
118
+ perpPosition
119
119
  ).div(PRICE_TO_QUOTE_PRECISION);
120
120
 
121
121
  pnl = pnl.add(fundingRatePnL);
@@ -125,26 +125,26 @@ export function calculatePositionPNL(
125
125
  }
126
126
 
127
127
  export function calculateUnsettledPnl(
128
- market: MarketAccount,
129
- marketPosition: UserPosition,
128
+ market: PerpMarketAccount,
129
+ perpPosition: PerpPosition,
130
130
  oraclePriceData: OraclePriceData
131
131
  ): BN {
132
132
  const unrealizedPnl = calculatePositionPNL(
133
133
  market,
134
- marketPosition,
134
+ perpPosition,
135
135
  true,
136
136
  oraclePriceData
137
137
  );
138
138
 
139
139
  let unsettledPnl = unrealizedPnl;
140
140
  if (unrealizedPnl.gt(ZERO)) {
141
- const fundingPnL = calculatePositionFundingPNL(market, marketPosition).div(
141
+ const fundingPnL = calculatePositionFundingPNL(market, perpPosition).div(
142
142
  PRICE_TO_QUOTE_PRECISION
143
143
  );
144
144
 
145
145
  const maxPositivePnl = BN.max(
146
- marketPosition.quoteAssetAmount
147
- .sub(marketPosition.quoteEntryAmount)
146
+ perpPosition.quoteAssetAmount
147
+ .sub(perpPosition.quoteEntryAmount)
148
148
  .add(fundingPnL),
149
149
  ZERO
150
150
  );
@@ -157,27 +157,27 @@ export function calculateUnsettledPnl(
157
157
  /**
158
158
  *
159
159
  * @param market
160
- * @param marketPosition
160
+ * @param PerpPosition
161
161
  * @returns // TODO-PRECISION
162
162
  */
163
163
  export function calculatePositionFundingPNL(
164
- market: MarketAccount,
165
- marketPosition: UserPosition
164
+ market: PerpMarketAccount,
165
+ perpPosition: PerpPosition
166
166
  ): BN {
167
- if (marketPosition.baseAssetAmount.eq(ZERO)) {
167
+ if (perpPosition.baseAssetAmount.eq(ZERO)) {
168
168
  return ZERO;
169
169
  }
170
170
 
171
171
  let ammCumulativeFundingRate: BN;
172
- if (marketPosition.baseAssetAmount.gt(ZERO)) {
172
+ if (perpPosition.baseAssetAmount.gt(ZERO)) {
173
173
  ammCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
174
174
  } else {
175
175
  ammCumulativeFundingRate = market.amm.cumulativeFundingRateShort;
176
176
  }
177
177
 
178
178
  const perPositionFundingRate = ammCumulativeFundingRate
179
- .sub(marketPosition.lastCumulativeFundingRate)
180
- .mul(marketPosition.baseAssetAmount)
179
+ .sub(perpPosition.lastCumulativeFundingRate)
180
+ .mul(perpPosition.baseAssetAmount)
181
181
  .div(AMM_RESERVE_PRECISION)
182
182
  .div(FUNDING_PAYMENT_PRECISION)
183
183
  .mul(new BN(-1));
@@ -185,7 +185,7 @@ export function calculatePositionFundingPNL(
185
185
  return perPositionFundingRate;
186
186
  }
187
187
 
188
- export function positionIsAvailable(position: UserPosition): boolean {
188
+ export function positionIsAvailable(position: PerpPosition): boolean {
189
189
  return (
190
190
  position.baseAssetAmount.eq(ZERO) &&
191
191
  position.openOrders.eq(ZERO) &&
@@ -199,7 +199,7 @@ export function positionIsAvailable(position: UserPosition): boolean {
199
199
  * @param userPosition
200
200
  * @returns Precision: MARK_PRICE_PRECISION (10^10)
201
201
  */
202
- export function calculateEntryPrice(userPosition: UserPosition): BN {
202
+ export function calculateEntryPrice(userPosition: PerpPosition): BN {
203
203
  if (userPosition.baseAssetAmount.eq(ZERO)) {
204
204
  return ZERO;
205
205
  }
@@ -216,7 +216,7 @@ export function calculateEntryPrice(userPosition: UserPosition): BN {
216
216
  * @param userPosition
217
217
  * @returns Precision: MARK_PRICE_PRECISION (10^10)
218
218
  */
219
- export function calculateCostBasis(userPosition: UserPosition): BN {
219
+ export function calculateCostBasis(userPosition: PerpPosition): BN {
220
220
  if (userPosition.baseAssetAmount.eq(ZERO)) {
221
221
  return ZERO;
222
222
  }
@@ -229,7 +229,7 @@ export function calculateCostBasis(userPosition: UserPosition): BN {
229
229
  }
230
230
 
231
231
  export function findDirectionToClose(
232
- userPosition: UserPosition
232
+ userPosition: PerpPosition
233
233
  ): PositionDirection {
234
234
  return userPosition.baseAssetAmount.gt(ZERO)
235
235
  ? PositionDirection.SHORT
@@ -237,14 +237,14 @@ export function findDirectionToClose(
237
237
  }
238
238
 
239
239
  export function positionCurrentDirection(
240
- userPosition: UserPosition
240
+ userPosition: PerpPosition
241
241
  ): PositionDirection {
242
242
  return userPosition.baseAssetAmount.gte(ZERO)
243
243
  ? PositionDirection.LONG
244
244
  : PositionDirection.SHORT;
245
245
  }
246
246
 
247
- export function isEmptyPosition(userPosition: UserPosition): boolean {
247
+ export function isEmptyPosition(userPosition: PerpPosition): boolean {
248
248
  return (
249
249
  userPosition.baseAssetAmount.eq(ZERO) && userPosition.openOrders.eq(ZERO)
250
250
  );
@@ -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_INTEREST_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(16 - 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(16 - 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(10 + spotDecimals - 6));
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_INTEREST_PRECISION)
257
+ .add(ONE);
258
+ const depositInterest = bank.cumulativeDepositInterest
259
+ .mul(modifiedDepositRate)
260
+ .div(ONE_YEAR)
261
+ .div(SPOT_MARKET_INTEREST_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
+ }
@@ -0,0 +1,47 @@
1
+ import { SpotMarketAccount, SpotPosition } from '../types';
2
+ import { ZERO } from '../constants/numericConstants';
3
+ import { BN } from '@project-serum/anchor';
4
+ import {
5
+ getSignedTokenAmount,
6
+ getTokenAmount,
7
+ getTokenValue,
8
+ } from './spotBalance';
9
+ import { OraclePriceData } from '../oracles/types';
10
+
11
+ export function isSpotPositionAvailable(position: SpotPosition): boolean {
12
+ return position.balance.eq(ZERO) && position.openOrders === 0;
13
+ }
14
+
15
+ export function getWorstCaseTokenAmounts(
16
+ spotPosition: SpotPosition,
17
+ spotMarketAccount: SpotMarketAccount,
18
+ oraclePriceData: OraclePriceData
19
+ ): [BN, BN] {
20
+ const tokenAmount = getSignedTokenAmount(
21
+ getTokenAmount(
22
+ spotPosition.balance,
23
+ spotMarketAccount,
24
+ spotPosition.balanceType
25
+ ),
26
+ spotPosition.balanceType
27
+ );
28
+
29
+ const tokenAmountAllBidsFill = tokenAmount.add(spotPosition.openBids);
30
+ const tokenAmountAllAsksFill = tokenAmount.add(spotPosition.openAsks);
31
+
32
+ if (tokenAmountAllAsksFill.abs().gt(tokenAmountAllBidsFill.abs())) {
33
+ const worstCaseQuoteTokenAmount = getTokenValue(
34
+ spotPosition.openBids.neg(),
35
+ spotMarketAccount.decimals,
36
+ oraclePriceData
37
+ );
38
+ return [tokenAmountAllBidsFill, worstCaseQuoteTokenAmount];
39
+ } else {
40
+ const worstCaseQuoteTokenAmount = getTokenValue(
41
+ spotPosition.openAsks.neg(),
42
+ spotMarketAccount.decimals,
43
+ oraclePriceData
44
+ );
45
+ return [tokenAmountAllAsksFill, worstCaseQuoteTokenAmount];
46
+ }
47
+ }
package/src/math/state.ts CHANGED
@@ -8,7 +8,7 @@ import { StateAccount } from '../types';
8
8
  */
9
9
  export function getExchangeFee(state: StateAccount): number {
10
10
  const exchangeFee =
11
- state.feeStructure.feeNumerator.toNumber() /
12
- state.feeStructure.feeDenominator.toNumber();
11
+ state.perpFeeStructure.feeNumerator.toNumber() /
12
+ state.perpFeeStructure.feeDenominator.toNumber();
13
13
  return exchangeFee;
14
14
  }
package/src/math/trade.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MarketAccount, PositionDirection } from '../types';
1
+ import { PerpMarketAccount, PositionDirection } from '../types';
2
2
  import { BN } from '@project-serum/anchor';
3
3
  import { assert } from '../assert/assert';
4
4
  import {
@@ -59,7 +59,7 @@ export type PriceImpactUnit =
59
59
  export function calculateTradeSlippage(
60
60
  direction: PositionDirection,
61
61
  amount: BN,
62
- market: MarketAccount,
62
+ market: PerpMarketAccount,
63
63
  inputAssetType: AssetType = 'quote',
64
64
  oraclePriceData?: OraclePriceData,
65
65
  useSpread = true
@@ -147,7 +147,7 @@ export function calculateTradeSlippage(
147
147
  export function calculateTradeAcquiredAmounts(
148
148
  direction: PositionDirection,
149
149
  amount: BN,
150
- market: MarketAccount,
150
+ market: PerpMarketAccount,
151
151
  inputAssetType: AssetType = 'quote',
152
152
  oraclePriceData: OraclePriceData,
153
153
  useSpread = true
@@ -204,7 +204,7 @@ export function calculateTradeAcquiredAmounts(
204
204
  * ]
205
205
  */
206
206
  export function calculateTargetPriceTrade(
207
- market: MarketAccount,
207
+ market: PerpMarketAccount,
208
208
  targetPrice: BN,
209
209
  pct: BN = MAXPCT,
210
210
  outputAssetType: AssetType = 'quote',
@@ -2,32 +2,40 @@ import { OptionalOrderParams, OrderTriggerCondition, OrderType } from './types';
2
2
  import { BN } from '@project-serum/anchor';
3
3
 
4
4
  export function getLimitOrderParams(
5
- params: Omit<OptionalOrderParams, 'orderType'> & { price: BN }
5
+ params: Omit<OptionalOrderParams, 'orderType' | 'marketType'> & { price: BN }
6
6
  ): OptionalOrderParams {
7
- return Object.assign({}, params, { orderType: OrderType.LIMIT });
7
+ return Object.assign({}, params, {
8
+ orderType: OrderType.LIMIT,
9
+ });
8
10
  }
9
11
 
10
12
  export function getTriggerMarketOrderParams(
11
- params: Omit<OptionalOrderParams, 'orderType'> & {
13
+ params: Omit<OptionalOrderParams, 'orderType' | 'marketType'> & {
12
14
  triggerCondition: OrderTriggerCondition;
13
15
  triggerPrice: BN;
14
16
  }
15
17
  ): OptionalOrderParams {
16
- return Object.assign({}, params, { orderType: OrderType.TRIGGER_MARKET });
18
+ return Object.assign({}, params, {
19
+ orderType: OrderType.TRIGGER_MARKET,
20
+ });
17
21
  }
18
22
 
19
23
  export function getTriggerLimitOrderParams(
20
- params: Omit<OptionalOrderParams, 'orderType'> & {
24
+ params: Omit<OptionalOrderParams, 'orderType' | 'marketType'> & {
21
25
  triggerCondition: OrderTriggerCondition;
22
26
  triggerPrice: BN;
23
27
  price: BN;
24
28
  }
25
29
  ): OptionalOrderParams {
26
- return Object.assign({}, params, { orderType: OrderType.TRIGGER_LIMIT });
30
+ return Object.assign({}, params, {
31
+ orderType: OrderType.TRIGGER_LIMIT,
32
+ });
27
33
  }
28
34
 
29
35
  export function getMarketOrderParams(
30
- params: Omit<OptionalOrderParams, 'orderType'>
36
+ params: Omit<OptionalOrderParams, 'orderType' | 'marketType'>
31
37
  ): OptionalOrderParams {
32
- return Object.assign({}, params, { orderType: OrderType.MARKET });
38
+ return Object.assign({}, params, {
39
+ orderType: OrderType.MARKET,
40
+ });
33
41
  }