@drift-labs/sdk 0.1.8 → 0.1.12

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 (73) hide show
  1. package/.eslintrc.json +36 -0
  2. package/.prettierignore +1 -0
  3. package/.prettierrc.js +9 -0
  4. package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts +1 -0
  5. package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +1 -0
  6. package/lib/accounts/defaultHistoryAccountSubscriber.d.ts +1 -0
  7. package/lib/accounts/defaultHistoryAccountSubscriber.d.ts.map +1 -0
  8. package/lib/accounts/defaultUserAccountSubscriber.d.ts +1 -0
  9. package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +1 -0
  10. package/lib/accounts/types.d.ts +1 -0
  11. package/lib/accounts/types.d.ts.map +1 -0
  12. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -0
  13. package/lib/accounts/webSocketAccountSubscriber.d.ts.map +1 -0
  14. package/lib/addresses.d.ts +1 -0
  15. package/lib/addresses.d.ts.map +1 -0
  16. package/lib/admin.d.ts +1 -0
  17. package/lib/admin.d.ts.map +1 -0
  18. package/lib/assert/assert.d.ts +1 -0
  19. package/lib/assert/assert.d.ts.map +1 -0
  20. package/lib/clearingHouse.d.ts +1 -0
  21. package/lib/clearingHouse.d.ts.map +1 -0
  22. package/lib/clearingHouseUser.d.ts +3 -2
  23. package/lib/clearingHouseUser.d.ts.map +1 -0
  24. package/lib/clearingHouseUser.js +12 -32
  25. package/lib/config.d.ts +1 -0
  26. package/lib/config.d.ts.map +1 -0
  27. package/lib/constants/markets.d.ts +1 -0
  28. package/lib/constants/markets.d.ts.map +1 -0
  29. package/lib/constants/markets.js +21 -7
  30. package/lib/constants/numericConstants.d.ts +1 -0
  31. package/lib/constants/numericConstants.d.ts.map +1 -0
  32. package/lib/examples/makeTradeExample.d.ts +1 -0
  33. package/lib/examples/makeTradeExample.d.ts.map +1 -0
  34. package/lib/idl/clearing_house.json +6 -2
  35. package/lib/index.d.ts +1 -0
  36. package/lib/index.d.ts.map +1 -0
  37. package/lib/math/amm.d.ts +1 -0
  38. package/lib/math/amm.d.ts.map +1 -0
  39. package/lib/math/conversion.d.ts +1 -0
  40. package/lib/math/conversion.d.ts.map +1 -0
  41. package/lib/math/funding.d.ts +11 -1
  42. package/lib/math/funding.d.ts.map +1 -0
  43. package/lib/math/funding.js +85 -19
  44. package/lib/math/market.d.ts +1 -0
  45. package/lib/math/market.d.ts.map +1 -0
  46. package/lib/math/position.d.ts +7 -0
  47. package/lib/math/position.d.ts.map +1 -0
  48. package/lib/math/position.js +17 -1
  49. package/lib/math/trade.d.ts +2 -1
  50. package/lib/math/trade.d.ts.map +1 -0
  51. package/lib/math/utils.d.ts +1 -0
  52. package/lib/math/utils.d.ts.map +1 -0
  53. package/lib/mockUSDCFaucet.d.ts +1 -0
  54. package/lib/mockUSDCFaucet.d.ts.map +1 -0
  55. package/lib/pythClient.d.ts +1 -0
  56. package/lib/pythClient.d.ts.map +1 -0
  57. package/lib/tx/defaultTxSender.d.ts +1 -0
  58. package/lib/tx/defaultTxSender.d.ts.map +1 -0
  59. package/lib/tx/types.d.ts +1 -0
  60. package/lib/tx/types.d.ts.map +1 -0
  61. package/lib/tx/utils.d.ts +1 -0
  62. package/lib/tx/utils.d.ts.map +1 -0
  63. package/lib/types.d.ts +13 -41
  64. package/lib/types.d.ts.map +1 -0
  65. package/package.json +12 -2
  66. package/src/clearingHouseUser.ts +26 -46
  67. package/src/constants/markets.ts +21 -7
  68. package/src/idl/clearing_house.json +6 -2
  69. package/src/math/funding.ts +143 -60
  70. package/src/math/position.ts +19 -0
  71. package/src/math/trade.ts +4 -3
  72. package/src/types.ts +12 -44
  73. package/tsconfig.json +2 -1
@@ -1,23 +1,26 @@
1
1
  import { BN } from '@project-serum/anchor';
2
2
  import {
3
- AMM_RESERVE_PRECISION, MARK_PRICE_PRECISION, QUOTE_PRECISION, ZERO
3
+ AMM_RESERVE_PRECISION,
4
+ MARK_PRICE_PRECISION,
5
+ QUOTE_PRECISION,
6
+ ZERO,
4
7
  } from '../constants/numericConstants';
5
8
  import { PythClient } from '../pythClient';
6
9
  import { Market } from '../types';
7
10
  import { calculateMarkPrice } from './market';
8
11
 
9
12
  /**
10
- *
11
- * @param market
12
- * @param pythClient
13
- * @param periodAdjustment
13
+ *
14
+ * @param market
15
+ * @param pythClient
16
+ * @param periodAdjustment
14
17
  * @returns Estimated funding rate. : Precision //TODO-PRECISION
15
18
  */
16
- export async function calculateAllEstimatedFundingRate(
19
+ export async function calculateAllEstimatedFundingRate(
17
20
  market: Market,
18
21
  pythClient: PythClient,
19
- periodAdjustment: BN = new BN(1),
20
- ): Promise<[BN, BN, BN]> {
22
+ periodAdjustment: BN = new BN(1)
23
+ ): Promise<[BN, BN, BN, BN, BN]> {
21
24
  // periodAdjustment
22
25
  // 1: hourly
23
26
  // 24: daily
@@ -26,27 +29,24 @@ import { calculateMarkPrice } from './market';
26
29
  const hoursInDay = new BN(24);
27
30
 
28
31
  if (!market.initialized) {
29
- return [new BN(0), new BN(0), new BN(0)];
32
+ return [ZERO, ZERO, ZERO, ZERO, ZERO];
30
33
  }
31
34
 
32
35
  const payFreq = new BN(market.amm.fundingPeriod);
33
36
 
34
- const oraclePriceData = await pythClient.getPriceData(market.amm.oracle);
35
- const oracleTwapWithMantissa = new BN(
36
- oraclePriceData.twap.value * MARK_PRICE_PRECISION.toNumber()
37
- );
38
-
37
+ // todo: sufficiently differs from blockchain timestamp?
39
38
  const now = new BN((Date.now() / 1000).toFixed(0));
40
39
  const timeSinceLastUpdate = now.sub(market.amm.lastFundingRateTs);
41
40
 
41
+ // calculate real-time mark twap
42
42
  const lastMarkTwapWithMantissa = market.amm.lastMarkPriceTwap;
43
43
  const lastMarkPriceTwapTs = market.amm.lastMarkPriceTwapTs;
44
44
 
45
45
  const timeSinceLastMarkChange = now.sub(lastMarkPriceTwapTs);
46
- const markTwapTimeSinceLastUpdate = lastMarkPriceTwapTs.sub(
47
- market.amm.lastFundingRateTs
46
+ const markTwapTimeSinceLastUpdate = BN.max(
47
+ secondsInHour,
48
+ secondsInHour.sub(timeSinceLastMarkChange)
48
49
  );
49
-
50
50
  const baseAssetPriceWithMantissa = calculateMarkPrice(market);
51
51
 
52
52
  const markTwapWithMantissa = markTwapTimeSinceLastUpdate
@@ -54,6 +54,27 @@ import { calculateMarkPrice } from './market';
54
54
  .add(timeSinceLastMarkChange.mul(baseAssetPriceWithMantissa))
55
55
  .div(timeSinceLastMarkChange.add(markTwapTimeSinceLastUpdate));
56
56
 
57
+ // calculate real-time (predicted) oracle twap
58
+ // note: oracle twap depends on `when the chord is struck` (market is trade)
59
+ const lastOracleTwapWithMantissa = market.amm.lastOraclePriceTwap;
60
+ const lastOraclePriceTwapTs = market.amm.lastOraclePriceTwapTs;
61
+
62
+ const timeSinceLastOracleTwapUpdate = now.sub(lastOraclePriceTwapTs);
63
+ const oracleTwapTimeSinceLastUpdate = BN.max(
64
+ secondsInHour,
65
+ secondsInHour.sub(timeSinceLastOracleTwapUpdate)
66
+ );
67
+ const oraclePriceData = await pythClient.getPriceData(market.amm.oracle);
68
+ const oraclePriceStableWithMantissa = new BN(
69
+ ((oraclePriceData.price + oraclePriceData.previousPrice) / 2) *
70
+ MARK_PRICE_PRECISION.toNumber()
71
+ );
72
+
73
+ const oracleTwapWithMantissa = oracleTwapTimeSinceLastUpdate
74
+ .mul(lastOracleTwapWithMantissa)
75
+ .add(timeSinceLastMarkChange.mul(oraclePriceStableWithMantissa))
76
+ .div(timeSinceLastOracleTwapUpdate.add(oracleTwapTimeSinceLastUpdate));
77
+
57
78
  const twapSpread = markTwapWithMantissa.sub(oracleTwapWithMantissa);
58
79
 
59
80
  const twapSpreadPct = twapSpread
@@ -61,7 +82,6 @@ import { calculateMarkPrice } from './market';
61
82
  .mul(new BN(100))
62
83
  .div(oracleTwapWithMantissa);
63
84
 
64
-
65
85
  const lowerboundEst = twapSpreadPct
66
86
  .mul(payFreq)
67
87
  .mul(BN.min(secondsInHour, timeSinceLastUpdate))
@@ -72,54 +92,82 @@ import { calculateMarkPrice } from './market';
72
92
 
73
93
  const interpEst = twapSpreadPct.mul(periodAdjustment).div(hoursInDay);
74
94
 
75
-
76
- const interpRateQuote = twapSpreadPct.mul(periodAdjustment).div(hoursInDay)
77
- .div(MARK_PRICE_PRECISION.div(QUOTE_PRECISION));
95
+ const interpRateQuote = twapSpreadPct
96
+ .mul(periodAdjustment)
97
+ .div(hoursInDay)
98
+ .div(MARK_PRICE_PRECISION.div(QUOTE_PRECISION));
78
99
  let feePoolSize = calculateFundingPool(market);
79
- if(interpRateQuote.lt(new BN(0))){
100
+ if (interpRateQuote.lt(new BN(0))) {
80
101
  feePoolSize = feePoolSize.mul(new BN(-1));
81
102
  }
82
103
 
83
104
  let cappedAltEst: BN;
84
105
  let largerSide: BN;
85
106
  let smallerSide: BN;
86
-
87
- if(market.baseAssetAmountLong.gt(market.baseAssetAmountShort)){
107
+ if (market.baseAssetAmountLong.gt(market.baseAssetAmountShort.abs())) {
88
108
  largerSide = market.baseAssetAmountLong.abs();
89
109
  smallerSide = market.baseAssetAmountShort.abs();
90
- if(twapSpread.gt(new BN(0))){
91
- return [lowerboundEst, interpEst, interpEst];
110
+ if (twapSpread.gt(new BN(0))) {
111
+ return [
112
+ markTwapWithMantissa,
113
+ oracleTwapWithMantissa,
114
+ lowerboundEst,
115
+ interpEst,
116
+ interpEst,
117
+ ];
92
118
  }
93
- } else if(market.baseAssetAmountLong.lt(market.baseAssetAmountShort)){
119
+ } else if (market.baseAssetAmountLong.lt(market.baseAssetAmountShort.abs())) {
94
120
  largerSide = market.baseAssetAmountShort.abs();
95
121
  smallerSide = market.baseAssetAmountLong.abs();
96
- if(twapSpread.lt(new BN(0))){
97
- return [lowerboundEst, interpEst, interpEst];
122
+ if (twapSpread.lt(new BN(0))) {
123
+ return [
124
+ markTwapWithMantissa,
125
+ oracleTwapWithMantissa,
126
+ lowerboundEst,
127
+ interpEst,
128
+ interpEst,
129
+ ];
98
130
  }
99
- } else{
100
- return [lowerboundEst, interpEst, interpEst];
131
+ } else {
132
+ return [
133
+ markTwapWithMantissa,
134
+ oracleTwapWithMantissa,
135
+ lowerboundEst,
136
+ interpEst,
137
+ interpEst,
138
+ ];
101
139
  }
102
140
 
103
- if(largerSide.gt(ZERO)){
141
+ if (largerSide.gt(ZERO)) {
104
142
  cappedAltEst = smallerSide.mul(twapSpread).div(largerSide);
105
- const feePoolTopOff = feePoolSize.mul(MARK_PRICE_PRECISION.div(QUOTE_PRECISION))
106
- .mul(AMM_RESERVE_PRECISION).div(largerSide);
143
+
144
+ const feePoolTopOff = feePoolSize
145
+ .mul(MARK_PRICE_PRECISION.div(QUOTE_PRECISION))
146
+ .mul(AMM_RESERVE_PRECISION)
147
+ .div(largerSide);
148
+
107
149
  cappedAltEst = cappedAltEst.add(feePoolTopOff);
108
-
109
- cappedAltEst = cappedAltEst.mul(MARK_PRICE_PRECISION)
110
- .mul(new BN(100))
111
- .div(oracleTwapWithMantissa)
112
- .mul(periodAdjustment).div(hoursInDay);
113
-
114
- if(cappedAltEst.abs().gt(interpEst.abs())){
150
+
151
+ cappedAltEst = cappedAltEst
152
+ .mul(MARK_PRICE_PRECISION)
153
+ .mul(new BN(100))
154
+ .div(oracleTwapWithMantissa)
155
+ .mul(periodAdjustment)
156
+ .div(hoursInDay);
157
+ if (cappedAltEst.abs().gt(interpEst.abs())) {
115
158
  cappedAltEst = interpEst;
116
159
  }
117
- } else{
160
+ } else {
118
161
  cappedAltEst = interpEst;
119
162
  }
120
163
 
121
-
122
- return [lowerboundEst, cappedAltEst, interpEst];
164
+ return [
165
+ markTwapWithMantissa,
166
+ oracleTwapWithMantissa,
167
+ lowerboundEst,
168
+ cappedAltEst,
169
+ interpEst,
170
+ ];
123
171
  }
124
172
 
125
173
  /**
@@ -136,8 +184,12 @@ export async function calculateEstimatedFundingRate(
136
184
  periodAdjustment: BN = new BN(1),
137
185
  estimationMethod: 'interpolated' | 'lowerbound' | 'capped'
138
186
  ): Promise<BN> {
139
- const [lowerboundEst, cappedAltEst, interpEst] =
140
- await calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
187
+ const [_1, _2, lowerboundEst, cappedAltEst, interpEst] =
188
+ await calculateAllEstimatedFundingRate(
189
+ market,
190
+ pythClient,
191
+ periodAdjustment
192
+ );
141
193
 
142
194
  if (estimationMethod == 'lowerbound') {
143
195
  //assuming remaining funding period has no gap
@@ -149,31 +201,62 @@ export async function calculateEstimatedFundingRate(
149
201
  }
150
202
  }
151
203
 
152
-
153
204
  /**
154
- *
155
- * @param market
156
- * @param pythClient
157
- * @param periodAdjustment
158
- * @param estimationMethod
205
+ *
206
+ * @param market
207
+ * @param pythClient
208
+ * @param periodAdjustment
209
+ * @param estimationMethod
159
210
  * @returns Estimated funding rate. : Precision //TODO-PRECISION
160
211
  */
161
- export async function calculateLongShortFundingRate(
212
+ export async function calculateLongShortFundingRate(
162
213
  market: Market,
163
214
  pythClient: PythClient,
164
- periodAdjustment: BN = new BN(1),
215
+ periodAdjustment: BN = new BN(1)
165
216
  ): Promise<[BN, BN]> {
166
- const [_, cappedAltEst, interpEst] =
167
- await calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
217
+ const [_1, _2, _, cappedAltEst, interpEst] =
218
+ await calculateAllEstimatedFundingRate(
219
+ market,
220
+ pythClient,
221
+ periodAdjustment
222
+ );
168
223
 
169
- if(market.baseAssetAmountLong.gt(market.baseAssetAmountShort)){
224
+ if (market.baseAssetAmountLong.gt(market.baseAssetAmountShort)) {
170
225
  return [cappedAltEst, interpEst];
171
- } else if(market.baseAssetAmountLong.lt(market.baseAssetAmountShort)){
226
+ } else if (market.baseAssetAmountLong.lt(market.baseAssetAmountShort)) {
172
227
  return [interpEst, cappedAltEst];
173
- } else{
228
+ } else {
174
229
  return [interpEst, interpEst];
175
230
  }
231
+ }
176
232
 
233
+ /**
234
+ *
235
+ * @param market
236
+ * @param pythClient
237
+ * @param periodAdjustment
238
+ * @param estimationMethod
239
+ * @returns Estimated funding rate. : Precision //TODO-PRECISION
240
+ */
241
+ export async function calculateLongShortFundingRateAndLiveTwaps(
242
+ market: Market,
243
+ pythClient: PythClient,
244
+ periodAdjustment: BN = new BN(1)
245
+ ): Promise<[BN, BN, BN, BN]> {
246
+ const [markTwapLive, oracleTwapLive, _2, cappedAltEst, interpEst] =
247
+ await calculateAllEstimatedFundingRate(
248
+ market,
249
+ pythClient,
250
+ periodAdjustment
251
+ );
252
+
253
+ if (market.baseAssetAmountLong.gt(market.baseAssetAmountShort.abs())) {
254
+ return [markTwapLive, oracleTwapLive, cappedAltEst, interpEst];
255
+ } else if (market.baseAssetAmountLong.lt(market.baseAssetAmountShort.abs())) {
256
+ return [markTwapLive, oracleTwapLive, interpEst, cappedAltEst];
257
+ } else {
258
+ return [markTwapLive, oracleTwapLive, interpEst, interpEst];
259
+ }
177
260
  }
178
261
 
179
262
  /**
@@ -182,8 +265,8 @@ export async function calculateEstimatedFundingRate(
182
265
  * @returns Estimated fee pool size
183
266
  */
184
267
  export function calculateFundingPool(market: Market): BN {
268
+ // todo
185
269
  const totalFeeLB = market.amm.totalFee.div(new BN(2));
186
270
  const feePool = market.amm.totalFeeMinusDistributions.sub(totalFeeLB);
187
- // return new BN(QUOTE_PRECISION.mul(new BN(2400)));
188
271
  return feePool;
189
272
  }
@@ -1,7 +1,9 @@
1
1
  import { Market, PositionDirection, UserPosition } from '../types';
2
2
  import {
3
3
  AMM_TO_QUOTE_PRECISION_RATIO,
4
+ MARK_PRICE_PRECISION,
4
5
  PEG_PRECISION,
6
+ QUOTE_PRECISION,
5
7
  ZERO,
6
8
  } from '../constants/numericConstants';
7
9
  import BN from 'bn.js';
@@ -131,3 +133,20 @@ export function calculatePositionFundingPNL(
131
133
 
132
134
  return perPositionFundingRate;
133
135
  }
136
+
137
+ /**
138
+ *
139
+ * @param userPosition
140
+ * @returns Precision: MARK_PRICE_PRECISION (10^10)
141
+ */
142
+ export function calculateEntryPrice(userPosition: UserPosition): BN {
143
+ if (userPosition.baseAssetAmount.eq(ZERO)) {
144
+ return ZERO;
145
+ }
146
+
147
+ return userPosition.quoteAssetAmount
148
+ .mul(MARK_PRICE_PRECISION)
149
+ .mul(AMM_TO_QUOTE_PRECISION_RATIO)
150
+ .div(userPosition.baseAssetAmount)
151
+ .abs();
152
+ }
package/src/math/trade.ts CHANGED
@@ -28,7 +28,8 @@ export type PriceImpactUnit =
28
28
  | 'quoteAssetAmount'
29
29
  | 'quoteAssetAmountPeg'
30
30
  | 'acquiredBaseAssetAmount'
31
- | 'acquiredQuoteAssetAmount';
31
+ | 'acquiredQuoteAssetAmount'
32
+ | 'all';
32
33
 
33
34
  /**
34
35
  * Calculates avg/max slippage (price impact) for candidate trade
@@ -49,7 +50,7 @@ export function calculateTradeSlippage(
49
50
  direction: PositionDirection,
50
51
  amount: BN,
51
52
  market: Market,
52
- inputAssetType: AssetType = 'quote',
53
+ inputAssetType: AssetType = 'quote'
53
54
  ): [BN, BN, BN, BN] {
54
55
  const oldPrice = calculateMarkPrice(market);
55
56
  if (amount.eq(ZERO)) {
@@ -107,7 +108,7 @@ export function calculateTradeAcquiredAmounts(
107
108
  direction: PositionDirection,
108
109
  amount: BN,
109
110
  market: Market,
110
- inputAssetType: AssetType = 'quote',
111
+ inputAssetType: AssetType = 'quote'
111
112
  ): [BN, BN] {
112
113
  if (amount.eq(ZERO)) {
113
114
  return [ZERO, ZERO];
package/src/types.ts CHANGED
@@ -221,6 +221,8 @@ export type AMM = {
221
221
  lastFundingRateTs: BN;
222
222
  lastMarkPriceTwap: BN;
223
223
  lastMarkPriceTwapTs: BN;
224
+ lastOraclePriceTwap: BN;
225
+ lastOraclePriceTwapTs: BN;
224
226
  oracle: PublicKey;
225
227
  oracleSource: OracleSource;
226
228
  fundingPeriod: BN;
@@ -242,6 +244,9 @@ export type UserPosition = {
242
244
  lastCumulativeFundingRate: BN;
243
245
  marketIndex: BN;
244
246
  quoteAssetAmount: BN;
247
+ unrealizedPnl?: BN;
248
+ unrealizedFundingPnl?: BN;
249
+ baseAssetValue?: BN;
245
250
  };
246
251
 
247
252
  export type UserPositionsAccount = {
@@ -257,53 +262,16 @@ export type UserAccount = {
257
262
  totalFeePaid: BN;
258
263
  };
259
264
 
260
- // # UI History Server Data Types
261
- export interface Trade {
262
- price: number;
263
- beforePrice: number;
264
- afterPrice: number;
265
- side: TradeSide;
266
- size: number;
267
- ts: number;
268
- fee: number;
269
- marketIndex: number;
270
- chainTs: number;
271
- }
272
-
273
- export type Liquidation = {
274
- ts: number;
275
- chainTs: number;
276
- recordId: number;
265
+ export type UserSnapshotRecord = {
266
+ ts: BN;
277
267
  userAuthority: PublicKey;
278
268
  user: PublicKey;
279
- partial: boolean;
280
- baseAssetValue: number;
281
- baseAssetValueClosed: number;
282
- liquidationFee: number;
283
- feeToLiquidator: number;
284
- feeToInsuranceFund: number;
285
- liquidator: PublicKey;
286
- totalCollateral: number;
287
- collateral: number;
288
- unrealizedPnl: number;
289
- marginRatio: number;
290
- };
291
-
292
- export type Candle = {
293
- open: number;
294
- close: number;
295
- high: number;
296
- low: number;
297
- volume: number;
298
- start: number;
299
- end: number;
269
+ userPositions: UserPosition[];
270
+ userTotalRealizedPnl: BN;
271
+ userTotalUnrealizedPnl: BN;
272
+ userTotalUnrealizedFundingPnl: BN;
273
+ userCollateral: BN;
300
274
  };
301
- export interface FundingPayment {
302
- userPublicKey: string;
303
- ts: number;
304
- marketIndex: number;
305
- amount: string;
306
- }
307
275
 
308
276
  // # Misc Types
309
277
  export interface IWallet {
package/tsconfig.json CHANGED
@@ -6,7 +6,8 @@
6
6
  "declaration": true,
7
7
  "outDir": "./lib",
8
8
  "resolveJsonModule": true,
9
- "skipLibCheck": true
9
+ "skipLibCheck": true,
10
+ "declarationMap": true
10
11
  },
11
12
  "include": ["src"],
12
13
  "exclude": ["node_modules"]