@drift-labs/sdk 0.1.36-master.4 → 0.1.36-master.7

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 (41) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +1 -0
  2. package/lib/accounts/bulkAccountLoader.js +4 -0
  3. package/lib/addresses.d.ts +1 -0
  4. package/lib/addresses.js +7 -1
  5. package/lib/admin.d.ts +5 -0
  6. package/lib/admin.js +68 -0
  7. package/lib/clearingHouse.d.ts +11 -3
  8. package/lib/clearingHouse.js +130 -4
  9. package/lib/clearingHouseUser.d.ts +5 -1
  10. package/lib/clearingHouseUser.js +28 -0
  11. package/lib/idl/clearing_house.json +988 -727
  12. package/lib/index.d.ts +1 -0
  13. package/lib/index.js +1 -0
  14. package/lib/math/oracles.d.ts +3 -0
  15. package/lib/math/oracles.js +26 -0
  16. package/lib/math/position.d.ts +1 -0
  17. package/lib/math/position.js +19 -1
  18. package/lib/math/trade.d.ts +10 -6
  19. package/lib/math/trade.js +68 -13
  20. package/lib/oracles/pythClient.js +1 -0
  21. package/lib/oracles/switchboardClient.js +3 -0
  22. package/lib/oracles/types.d.ts +1 -0
  23. package/lib/settlement.d.ts +4 -0
  24. package/lib/settlement.js +10 -0
  25. package/lib/types.d.ts +11 -0
  26. package/package.json +1 -1
  27. package/src/accounts/bulkAccountLoader.ts +5 -0
  28. package/src/addresses.ts +11 -0
  29. package/src/admin.ts +83 -0
  30. package/src/clearingHouse.ts +204 -4
  31. package/src/clearingHouseUser.ts +64 -1
  32. package/src/idl/clearing_house.json +988 -727
  33. package/src/index.ts +1 -0
  34. package/src/math/oracles.ts +36 -0
  35. package/src/math/position.ts +22 -0
  36. package/src/math/trade.ts +84 -16
  37. package/src/oracles/pythClient.ts +1 -0
  38. package/src/oracles/switchboardClient.ts +5 -0
  39. package/src/oracles/types.ts +1 -0
  40. package/src/settlement.ts +9 -0
  41. package/src/types.ts +12 -0
package/src/index.ts CHANGED
@@ -26,6 +26,7 @@ export * from './math/funding';
26
26
  export * from './math/insuranceFund';
27
27
  export * from './math/market';
28
28
  export * from './math/position';
29
+ export * from './math/oracles';
29
30
  export * from './math/amm';
30
31
  export * from './math/trade';
31
32
  export * from './math/orders';
@@ -0,0 +1,36 @@
1
+ import { AMM, OracleGuardRails } from '../types';
2
+ import { OraclePriceData } from '../oracles/types';
3
+ import { ONE, ZERO } from '../constants/numericConstants';
4
+ import { BN } from '../index';
5
+
6
+ export function isOracleValid(
7
+ amm: AMM,
8
+ oraclePriceData: OraclePriceData,
9
+ oracleGuardRails: OracleGuardRails,
10
+ slot: number
11
+ ): boolean {
12
+ const isOraclePriceNonPositive = oraclePriceData.price.lt(ZERO);
13
+ const isOraclePriceTooVolatile =
14
+ oraclePriceData.price
15
+ .div(BN.max(ONE, amm.lastOraclePriceTwap))
16
+ .gt(oracleGuardRails.validity.tooVolatileRatio) ||
17
+ amm.lastOraclePriceTwap
18
+ .div(BN.max(ONE, oraclePriceData.price))
19
+ .gt(oracleGuardRails.validity.tooVolatileRatio);
20
+
21
+ const isConfidenceTooLarge = oraclePriceData.price
22
+ .div(BN.max(ONE, oraclePriceData.confidence))
23
+ .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
24
+
25
+ const oracleIsStale = oraclePriceData.slot
26
+ .sub(new BN(slot))
27
+ .gt(oracleGuardRails.validity.slotsBeforeStale);
28
+
29
+ return !(
30
+ !oraclePriceData.hasSufficientNumberOfDataPoints ||
31
+ oracleIsStale ||
32
+ isOraclePriceNonPositive ||
33
+ isOraclePriceTooVolatile ||
34
+ isConfidenceTooLarge
35
+ );
36
+ }
@@ -11,6 +11,7 @@ import {
11
11
  } from '../constants/numericConstants';
12
12
  import { Market, PositionDirection, UserPosition } from '../types';
13
13
  import { calculateAmmReservesAfterSwap, getSwapDirection } from './amm';
14
+ import { SETTLEMENT_RATIO_PRECISION, SETTLEMENT_RATIOS } from '../settlement';
14
15
 
15
16
  /**
16
17
  * calculateBaseAssetValue
@@ -90,6 +91,27 @@ export function calculatePositionPNL(
90
91
  return pnl;
91
92
  }
92
93
 
94
+ export function calculateSettledPositionPNL(
95
+ market: Market,
96
+ marketPosition: UserPosition
97
+ ): BN {
98
+ let pnl = calculatePositionPNL(market, marketPosition);
99
+
100
+ if (pnl.gt(ZERO)) {
101
+ try {
102
+ pnl = pnl
103
+ .mul(new BN(SETTLEMENT_RATIOS[marketPosition.marketIndex.toNumber()]))
104
+ .div(SETTLEMENT_RATIO_PRECISION);
105
+ } catch (e) {
106
+ console.log(pnl.toString());
107
+ console.log(marketPosition.marketIndex.toNumber());
108
+ throw e;
109
+ }
110
+ }
111
+
112
+ return pnl;
113
+ }
114
+
93
115
  /**
94
116
  *
95
117
  * @param market
package/src/math/trade.ts CHANGED
@@ -7,7 +7,11 @@ import {
7
7
  AMM_TO_QUOTE_PRECISION_RATIO,
8
8
  ZERO,
9
9
  } from '../constants/numericConstants';
10
- import { calculateMarkPrice } from './market';
10
+ import {
11
+ calculateBidPrice,
12
+ calculateAskPrice,
13
+ calculateMarkPrice,
14
+ } from './market';
11
15
  import {
12
16
  calculateAmmReservesAfterSwap,
13
17
  calculatePrice,
@@ -16,6 +20,7 @@ import {
16
20
  calculateSpreadReserves,
17
21
  } from './amm';
18
22
  import { squareRootBN } from './utils';
23
+ import { isVariant } from '../types';
19
24
 
20
25
  const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
21
26
 
@@ -37,6 +42,8 @@ export type PriceImpactUnit =
37
42
  * @param direction
38
43
  * @param amount
39
44
  * @param market
45
+ * @param inputAssetType which asset is being traded
46
+ * @param useSpread whether to consider spread with calculating slippage
40
47
  * @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
41
48
  *
42
49
  * 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
@@ -51,9 +58,20 @@ export function calculateTradeSlippage(
51
58
  direction: PositionDirection,
52
59
  amount: BN,
53
60
  market: Market,
54
- inputAssetType: AssetType = 'quote'
61
+ inputAssetType: AssetType = 'quote',
62
+ useSpread = true
55
63
  ): [BN, BN, BN, BN] {
56
- const oldPrice = calculateMarkPrice(market);
64
+ let oldPrice: BN;
65
+
66
+ if (useSpread && market.amm.baseSpread > 0) {
67
+ if (isVariant(direction, 'long')) {
68
+ oldPrice = calculateAskPrice(market);
69
+ } else {
70
+ oldPrice = calculateBidPrice(market);
71
+ }
72
+ } else {
73
+ oldPrice = calculateMarkPrice(market);
74
+ }
57
75
  if (amount.eq(ZERO)) {
58
76
  return [ZERO, ZERO, oldPrice, oldPrice];
59
77
  }
@@ -61,7 +79,8 @@ export function calculateTradeSlippage(
61
79
  direction,
62
80
  amount,
63
81
  market,
64
- inputAssetType
82
+ inputAssetType,
83
+ useSpread
65
84
  );
66
85
 
67
86
  const entryPrice = calculatePrice(
@@ -70,10 +89,26 @@ export function calculateTradeSlippage(
70
89
  market.amm.pegMultiplier
71
90
  ).mul(new BN(-1));
72
91
 
92
+ let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
93
+ if (useSpread && market.amm.baseSpread > 0) {
94
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
95
+ market.amm,
96
+ direction
97
+ );
98
+ amm = {
99
+ baseAssetReserve,
100
+ quoteAssetReserve,
101
+ sqrtK: market.amm.sqrtK,
102
+ pegMultiplier: market.amm.pegMultiplier,
103
+ };
104
+ } else {
105
+ amm = market.amm;
106
+ }
107
+
73
108
  const newPrice = calculatePrice(
74
- market.amm.baseAssetReserve.sub(acquiredBase),
75
- market.amm.quoteAssetReserve.sub(acquiredQuote),
76
- market.amm.pegMultiplier
109
+ amm.baseAssetReserve.sub(acquiredBase),
110
+ amm.quoteAssetReserve.sub(acquiredQuote),
111
+ amm.pegMultiplier
77
112
  );
78
113
 
79
114
  if (direction == PositionDirection.SHORT) {
@@ -104,7 +139,7 @@ export function calculateTradeSlippage(
104
139
  * @param inputAssetType
105
140
  * @param useSpread
106
141
  * @return
107
- * | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
142
+ * | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
108
143
  * | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
109
144
  */
110
145
  export function calculateTradeAcquiredAmounts(
@@ -150,43 +185,63 @@ export function calculateTradeAcquiredAmounts(
150
185
  * @param market
151
186
  * @param targetPrice
152
187
  * @param pct optional default is 100% gap filling, can set smaller.
188
+ * @param outputAssetType which asset to trade.
189
+ * @param useSpread whether or not to consider the spread when calculating the trade size
153
190
  * @returns trade direction/size in order to push price to a targetPrice,
154
191
  *
155
192
  * [
156
- * direction => direction of trade required, TODO-PRECISION
193
+ * direction => direction of trade required, PositionDirection
157
194
  * tradeSize => size of trade required, TODO-PRECISION
158
- * entryPrice => the entry price for the trade, TODO-PRECISION
159
- * targetPrice => the target price TODO-PRECISION
195
+ * entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
196
+ * targetPrice => the target price MARK_PRICE_PRECISION
160
197
  * ]
161
198
  */
162
199
  export function calculateTargetPriceTrade(
163
200
  market: Market,
164
201
  targetPrice: BN,
165
202
  pct: BN = MAXPCT,
166
- outputAssetType: AssetType = 'quote'
203
+ outputAssetType: AssetType = 'quote',
204
+ useSpread = true
167
205
  ): [PositionDirection, BN, BN, BN] {
168
206
  assert(market.amm.baseAssetReserve.gt(ZERO));
169
207
  assert(targetPrice.gt(ZERO));
170
208
  assert(pct.lte(MAXPCT) && pct.gt(ZERO));
171
209
 
172
210
  const markPriceBefore = calculateMarkPrice(market);
211
+ const bidPriceBefore = calculateBidPrice(market);
212
+ const askPriceBefore = calculateAskPrice(market);
173
213
 
214
+ let direction;
174
215
  if (targetPrice.gt(markPriceBefore)) {
175
216
  const priceGap = targetPrice.sub(markPriceBefore);
176
217
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
177
218
  targetPrice = markPriceBefore.add(priceGapScaled);
219
+ direction = PositionDirection.LONG;
178
220
  } else {
179
221
  const priceGap = markPriceBefore.sub(targetPrice);
180
222
  const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
181
223
  targetPrice = markPriceBefore.sub(priceGapScaled);
224
+ direction = PositionDirection.SHORT;
182
225
  }
183
226
 
184
- let direction;
185
227
  let tradeSize;
186
228
  let baseSize;
187
229
 
188
- const baseAssetReserveBefore = market.amm.baseAssetReserve;
189
- const quoteAssetReserveBefore = market.amm.quoteAssetReserve;
230
+ let baseAssetReserveBefore: BN;
231
+ let quoteAssetReserveBefore: BN;
232
+
233
+ if (useSpread && market.amm.baseSpread > 0) {
234
+ const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
235
+ market.amm,
236
+ direction
237
+ );
238
+ baseAssetReserveBefore = baseAssetReserve;
239
+ quoteAssetReserveBefore = quoteAssetReserve;
240
+ } else {
241
+ baseAssetReserveBefore = market.amm.baseAssetReserve;
242
+ quoteAssetReserveBefore = market.amm.quoteAssetReserve;
243
+ }
244
+
190
245
  const peg = market.amm.pegMultiplier;
191
246
  const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
192
247
  const k = invariant.mul(MARK_PRICE_PRECISION);
@@ -196,7 +251,20 @@ export function calculateTargetPriceTrade(
196
251
  const biasModifier = new BN(1);
197
252
  let markPriceAfter;
198
253
 
199
- if (markPriceBefore.gt(targetPrice)) {
254
+ if (
255
+ useSpread &&
256
+ targetPrice.lt(askPriceBefore) &&
257
+ targetPrice.gt(bidPriceBefore)
258
+ ) {
259
+ // no trade, market is at target
260
+ if (markPriceBefore.gt(targetPrice)) {
261
+ direction = PositionDirection.SHORT;
262
+ } else {
263
+ direction = PositionDirection.LONG;
264
+ }
265
+ tradeSize = ZERO;
266
+ return [direction, tradeSize, targetPrice, targetPrice];
267
+ } else if (markPriceBefore.gt(targetPrice)) {
200
268
  // overestimate y2
201
269
  baseAssetReserveAfter = squareRootBN(
202
270
  k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
@@ -36,6 +36,7 @@ export class PythClient {
36
36
  priceData.twac.value,
37
37
  priceData.exponent
38
38
  ),
39
+ hasSufficientNumberOfDataPoints: true,
39
40
  };
40
41
  }
41
42
  }
@@ -48,11 +48,16 @@ export class SwitchboardClient implements OracleClient {
48
48
  .stdDeviation as SwitchboardDecimal
49
49
  );
50
50
 
51
+ const hasSufficientNumberOfDataPoints =
52
+ aggregatorAccountData.latestConfirmedRound.numSuccess >=
53
+ aggregatorAccountData.minOracleResults;
54
+
51
55
  const slot: BN = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
52
56
  return {
53
57
  price,
54
58
  slot,
55
59
  confidence,
60
+ hasSufficientNumberOfDataPoints,
56
61
  };
57
62
  }
58
63
 
@@ -5,6 +5,7 @@ export type OraclePriceData = {
5
5
  price: BN;
6
6
  slot: BN;
7
7
  confidence: BN;
8
+ hasSufficientNumberOfDataPoints: boolean;
8
9
  twap?: BN;
9
10
  twapConfidence?: BN;
10
11
  };
@@ -0,0 +1,9 @@
1
+ import { BN } from '@project-serum/anchor';
2
+
3
+ export const SETTLEMENT_RATIO_PRECISION = new BN(1000000);
4
+
5
+ export const SETTLEMENT_RATIOS = [
6
+ 291786, 243613, 712271, 293771, 555181, 604938, 241133, 218489, 251741,
7
+ 883058, 51614, 531737, 956092, 106424, 727107, 477277, 217325, 127477, 248561,
8
+ 565938, 59349,
9
+ ];
package/src/types.ts CHANGED
@@ -268,6 +268,13 @@ export type OrderStateAccount = {
268
268
  minOrderQuoteAssetAmount: BN;
269
269
  };
270
270
 
271
+ export type SettlementStateAccount = {
272
+ totalSettlementValue: BN;
273
+ collateralAvailableToClaim: BN;
274
+ collateralClaimed: BN;
275
+ enabled: boolean;
276
+ };
277
+
271
278
  export type MarketsAccount = {
272
279
  markets: Market[];
273
280
  };
@@ -336,6 +343,11 @@ export type UserAccount = {
336
343
  totalTokenDiscount: BN;
337
344
  totalReferralReward: BN;
338
345
  totalRefereeDiscount: BN;
346
+ settledPositionValue: BN;
347
+ collateralClaimed: BN;
348
+ lastCollateralAvailableToClaim: BN;
349
+ forgoPositionSettlement: number;
350
+ hasSettledPosition: number;
339
351
  };
340
352
 
341
353
  export type UserOrdersAccount = {