@drift-labs/sdk 2.38.1-beta.1 → 2.38.1-beta.11

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 (54) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/fetch.js +2 -2
  3. package/lib/accounts/pollingDriftClientAccountSubscriber.js +2 -14
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +2 -2
  5. package/lib/adminClient.d.ts +1 -0
  6. package/lib/adminClient.js +11 -0
  7. package/lib/config.d.ts +1 -0
  8. package/lib/config.js +2 -0
  9. package/lib/driftClient.d.ts +22 -2
  10. package/lib/driftClient.js +101 -15
  11. package/lib/examples/loadDlob.js +3 -1
  12. package/lib/examples/makeTradeExample.js +3 -1
  13. package/lib/idl/drift.json +36 -1
  14. package/lib/index.d.ts +3 -0
  15. package/lib/index.js +3 -0
  16. package/lib/jupiter/jupiterClient.d.ts +197 -0
  17. package/lib/jupiter/jupiterClient.js +48 -3
  18. package/lib/math/spotBalance.d.ts +6 -5
  19. package/lib/math/spotBalance.js +29 -12
  20. package/lib/math/spotMarket.d.ts +1 -1
  21. package/lib/math/spotMarket.js +2 -2
  22. package/lib/math/spotPosition.d.ts +16 -3
  23. package/lib/math/spotPosition.js +53 -9
  24. package/lib/oracles/strictOraclePrice.d.ts +8 -0
  25. package/lib/oracles/strictOraclePrice.js +17 -0
  26. package/lib/priorityFee/priorityFeeSubscriber.d.ts +22 -0
  27. package/lib/priorityFee/priorityFeeSubscriber.js +46 -0
  28. package/lib/tokenFaucet.js +1 -0
  29. package/lib/types.d.ts +4 -0
  30. package/lib/types.js +3 -0
  31. package/lib/user.d.ts +5 -4
  32. package/lib/user.js +71 -105
  33. package/package.json +2 -2
  34. package/src/accounts/fetch.ts +2 -2
  35. package/src/accounts/pollingDriftClientAccountSubscriber.ts +5 -23
  36. package/src/accounts/pollingUserStatsAccountSubscriber.ts +10 -8
  37. package/src/adminClient.ts +23 -0
  38. package/src/config.ts +3 -0
  39. package/src/driftClient.ts +173 -13
  40. package/src/examples/loadDlob.ts +1 -0
  41. package/src/examples/makeTradeExample.ts +1 -0
  42. package/src/idl/drift.json +36 -1
  43. package/src/index.ts +3 -0
  44. package/src/jupiter/jupiterClient.ts +246 -3
  45. package/src/math/spotBalance.ts +38 -12
  46. package/src/math/spotMarket.ts +7 -1
  47. package/src/math/spotPosition.ts +133 -18
  48. package/src/oracles/strictOraclePrice.ts +19 -0
  49. package/src/priorityFee/priorityFeeSubscriber.ts +75 -0
  50. package/src/tokenFaucet.ts +1 -0
  51. package/src/types.ts +4 -0
  52. package/src/user.ts +171 -228
  53. package/tests/dlob/helpers.ts +10 -7
  54. package/tests/user/test.ts +77 -4
@@ -22,6 +22,7 @@ import {
22
22
  import { OraclePriceData } from '../oracles/types';
23
23
  import { PERCENTAGE_PRECISION } from '../constants/numericConstants';
24
24
  import { divCeil } from './utils';
25
+ import { StrictOraclePrice } from '../oracles/strictOraclePrice';
25
26
 
26
27
  /**
27
28
  * Calculates the balance of a given token amount including any accumulated interest. This
@@ -102,25 +103,23 @@ export function getSignedTokenAmount(
102
103
  *
103
104
  * @param {BN} tokenAmount - The amount of tokens to calculate the value for (from `getTokenAmount`)
104
105
  * @param {number} spotDecimals - The number of decimals in the token.
105
- * @param {OraclePriceData} oraclePriceData - The oracle price data (typically a token/USD oracle).
106
- * @param {BN} oraclePriceTwap - The Time-Weighted Average Price of the oracle.
106
+ * @param {StrictOraclePrice} strictOraclePrice - Contains oracle price and 5min twap.
107
107
  * @return {BN} The calculated value of the given token amount, scaled by `PRICE_PRECISION`
108
108
  */
109
109
  export function getStrictTokenValue(
110
110
  tokenAmount: BN,
111
111
  spotDecimals: number,
112
- oraclePriceData: OraclePriceData,
113
- oraclePriceTwap: BN
112
+ strictOraclePrice: StrictOraclePrice
114
113
  ): BN {
115
114
  if (tokenAmount.eq(ZERO)) {
116
115
  return ZERO;
117
116
  }
118
117
 
119
- let price = oraclePriceData.price;
120
- if (tokenAmount.gt(ZERO)) {
121
- price = BN.min(oraclePriceData.price, oraclePriceTwap);
118
+ let price;
119
+ if (tokenAmount.gte(ZERO)) {
120
+ price = strictOraclePrice.min();
122
121
  } else {
123
- price = BN.max(oraclePriceData.price, oraclePriceTwap);
122
+ price = strictOraclePrice.max();
124
123
  }
125
124
 
126
125
  const precisionDecrease = TEN.pow(new BN(spotDecimals));
@@ -139,7 +138,7 @@ export function getStrictTokenValue(
139
138
  export function getTokenValue(
140
139
  tokenAmount: BN,
141
140
  spotDecimals: number,
142
- oraclePriceData: OraclePriceData
141
+ oraclePriceData: Pick<OraclePriceData, 'price'>
143
142
  ): BN {
144
143
  if (tokenAmount.eq(ZERO)) {
145
144
  return ZERO;
@@ -152,6 +151,7 @@ export function getTokenValue(
152
151
 
153
152
  export function calculateAssetWeight(
154
153
  balanceAmount: BN,
154
+ oraclePrice: BN,
155
155
  spotMarket: SpotMarketAccount,
156
156
  marginCategory: MarginCategory
157
157
  ): BN {
@@ -174,7 +174,7 @@ export function calculateAssetWeight(
174
174
  assetWeight = calculateSizeDiscountAssetWeight(
175
175
  sizeInAmmReservePrecision,
176
176
  new BN(spotMarket.imfFactor),
177
- new BN(spotMarket.initialAssetWeight)
177
+ calculateScaledInitialAssetWeight(spotMarket, oraclePrice)
178
178
  );
179
179
  break;
180
180
  case 'Maintenance':
@@ -185,13 +185,39 @@ export function calculateAssetWeight(
185
185
  );
186
186
  break;
187
187
  default:
188
- assetWeight = new BN(spotMarket.initialAssetWeight);
188
+ assetWeight = calculateScaledInitialAssetWeight(spotMarket, oraclePrice);
189
189
  break;
190
190
  }
191
191
 
192
192
  return assetWeight;
193
193
  }
194
194
 
195
+ export function calculateScaledInitialAssetWeight(
196
+ spotMarket: SpotMarketAccount,
197
+ oraclePrice: BN
198
+ ): BN {
199
+ if (spotMarket.scaleInitialAssetWeightStart.eq(ZERO)) {
200
+ return new BN(spotMarket.initialAssetWeight);
201
+ }
202
+
203
+ const deposits = getTokenAmount(
204
+ spotMarket.depositBalance,
205
+ spotMarket,
206
+ SpotBalanceType.DEPOSIT
207
+ );
208
+ const depositsValue = getTokenValue(deposits, spotMarket.decimals, {
209
+ price: oraclePrice,
210
+ });
211
+
212
+ if (depositsValue.lt(spotMarket.scaleInitialAssetWeightStart)) {
213
+ return new BN(spotMarket.initialAssetWeight);
214
+ } else {
215
+ return new BN(spotMarket.initialAssetWeight)
216
+ .mul(spotMarket.scaleInitialAssetWeightStart)
217
+ .div(depositsValue);
218
+ }
219
+ }
220
+
195
221
  export function calculateLiabilityWeight(
196
222
  size: BN,
197
223
  spotMarket: SpotMarketAccount,
@@ -229,7 +255,7 @@ export function calculateLiabilityWeight(
229
255
  );
230
256
  break;
231
257
  default:
232
- liabilityWeight = spotMarket.initialLiabilityWeight;
258
+ liabilityWeight = new BN(spotMarket.initialLiabilityWeight);
233
259
  break;
234
260
  }
235
261
 
@@ -21,12 +21,18 @@ export function castNumberToSpotPrecision(
21
21
 
22
22
  export function calculateSpotMarketMarginRatio(
23
23
  market: SpotMarketAccount,
24
+ oraclePrice: BN,
24
25
  marginCategory: MarginCategory,
25
26
  size: BN,
26
27
  balanceType: SpotBalanceType
27
28
  ): number {
28
29
  if (isVariant(balanceType, 'deposit')) {
29
- const assetWeight = calculateAssetWeight(size, market, marginCategory);
30
+ const assetWeight = calculateAssetWeight(
31
+ size,
32
+ oraclePrice,
33
+ market,
34
+ marginCategory
35
+ );
30
36
  return MARGIN_PRECISION.sub(assetWeight).toNumber();
31
37
  } else {
32
38
  const liabilityWeight = calculateLiabilityWeight(
@@ -1,22 +1,38 @@
1
- import { SpotMarketAccount, SpotPosition } from '../types';
2
- import { ZERO } from '../constants/numericConstants';
1
+ import { MarginCategory, SpotMarketAccount, SpotPosition } from '../types';
2
+ import {
3
+ SPOT_MARKET_WEIGHT_PRECISION,
4
+ ZERO,
5
+ } from '../constants/numericConstants';
3
6
  import { BN } from '@coral-xyz/anchor';
4
7
  import {
8
+ calculateAssetWeight,
9
+ calculateLiabilityWeight,
5
10
  getSignedTokenAmount,
11
+ getStrictTokenValue,
6
12
  getTokenAmount,
7
13
  getTokenValue,
8
14
  } from './spotBalance';
9
- import { OraclePriceData } from '../oracles/types';
15
+ import { StrictOraclePrice } from '../oracles/strictOraclePrice';
10
16
 
11
17
  export function isSpotPositionAvailable(position: SpotPosition): boolean {
12
18
  return position.scaledBalance.eq(ZERO) && position.openOrders === 0;
13
19
  }
14
20
 
21
+ export type OrderFillSimulation = {
22
+ tokenAmount: BN;
23
+ ordersValue: BN;
24
+ tokenValue: BN;
25
+ weight: BN;
26
+ weightedTokenValue: BN;
27
+ freeCollateralContribution;
28
+ };
29
+
15
30
  export function getWorstCaseTokenAmounts(
16
31
  spotPosition: SpotPosition,
17
32
  spotMarketAccount: SpotMarketAccount,
18
- oraclePriceData: OraclePriceData
19
- ): [BN, BN] {
33
+ strictOraclePrice: StrictOraclePrice,
34
+ marginCategory: MarginCategory
35
+ ): OrderFillSimulation {
20
36
  const tokenAmount = getSignedTokenAmount(
21
37
  getTokenAmount(
22
38
  spotPosition.scaledBalance,
@@ -26,22 +42,121 @@ export function getWorstCaseTokenAmounts(
26
42
  spotPosition.balanceType
27
43
  );
28
44
 
29
- const tokenAmountAllBidsFill = tokenAmount.add(spotPosition.openBids);
30
- const tokenAmountAllAsksFill = tokenAmount.add(spotPosition.openAsks);
45
+ const tokenValue = getStrictTokenValue(
46
+ tokenAmount,
47
+ spotMarketAccount.decimals,
48
+ strictOraclePrice
49
+ );
31
50
 
32
- if (tokenAmountAllBidsFill.abs().gt(tokenAmountAllAsksFill.abs())) {
33
- const worstCaseQuoteTokenAmount = getTokenValue(
34
- spotPosition.openBids.neg(),
35
- spotMarketAccount.decimals,
36
- oraclePriceData
51
+ if (spotPosition.openBids.eq(ZERO) && spotPosition.openAsks.eq(ZERO)) {
52
+ const { weight, weightedTokenValue } = calculateWeightedTokenValue(
53
+ tokenAmount,
54
+ tokenValue,
55
+ strictOraclePrice.current,
56
+ spotMarketAccount,
57
+ marginCategory
37
58
  );
38
- return [tokenAmountAllBidsFill, worstCaseQuoteTokenAmount];
59
+ return {
60
+ tokenAmount,
61
+ ordersValue: ZERO,
62
+ tokenValue,
63
+ weight,
64
+ weightedTokenValue,
65
+ freeCollateralContribution: weightedTokenValue,
66
+ };
67
+ }
68
+
69
+ const bidsSimulation = simulateOrderFill(
70
+ tokenAmount,
71
+ tokenValue,
72
+ spotPosition.openBids,
73
+ strictOraclePrice,
74
+ spotMarketAccount,
75
+ marginCategory
76
+ );
77
+ const asksSimulation = simulateOrderFill(
78
+ tokenAmount,
79
+ tokenValue,
80
+ spotPosition.openAsks,
81
+ strictOraclePrice,
82
+ spotMarketAccount,
83
+ marginCategory
84
+ );
85
+
86
+ if (
87
+ asksSimulation.freeCollateralContribution.lt(
88
+ bidsSimulation.freeCollateralContribution
89
+ )
90
+ ) {
91
+ return asksSimulation;
39
92
  } else {
40
- const worstCaseQuoteTokenAmount = getTokenValue(
41
- spotPosition.openAsks.neg(),
42
- spotMarketAccount.decimals,
43
- oraclePriceData
93
+ return bidsSimulation;
94
+ }
95
+ }
96
+
97
+ export function calculateWeightedTokenValue(
98
+ tokenAmount: BN,
99
+ tokenValue: BN,
100
+ oraclePrice: BN,
101
+ spotMarket: SpotMarketAccount,
102
+ marginCategory: MarginCategory
103
+ ): { weight: BN; weightedTokenValue: BN } {
104
+ let weight: BN;
105
+ if (tokenValue.gte(ZERO)) {
106
+ weight = calculateAssetWeight(
107
+ tokenAmount,
108
+ oraclePrice,
109
+ spotMarket,
110
+ marginCategory
111
+ );
112
+ } else {
113
+ weight = calculateLiabilityWeight(
114
+ tokenAmount.abs(),
115
+ spotMarket,
116
+ marginCategory
44
117
  );
45
- return [tokenAmountAllAsksFill, worstCaseQuoteTokenAmount];
46
118
  }
119
+
120
+ return {
121
+ weight: weight,
122
+ weightedTokenValue: tokenValue
123
+ .mul(weight)
124
+ .div(SPOT_MARKET_WEIGHT_PRECISION),
125
+ };
126
+ }
127
+
128
+ export function simulateOrderFill(
129
+ tokenAmount: BN,
130
+ tokenValue: BN,
131
+ openOrders: BN,
132
+ strictOraclePrice: StrictOraclePrice,
133
+ spotMarket: SpotMarketAccount,
134
+ marginCategory: MarginCategory
135
+ ): OrderFillSimulation {
136
+ const ordersValue = getTokenValue(openOrders.neg(), spotMarket.decimals, {
137
+ price: strictOraclePrice.max(),
138
+ });
139
+ const tokenAmountAfterFill = tokenAmount.add(openOrders);
140
+ const tokenValueAfterFill = tokenValue.add(ordersValue.neg());
141
+
142
+ const { weight, weightedTokenValue: weightedTokenValueAfterFill } =
143
+ calculateWeightedTokenValue(
144
+ tokenAmountAfterFill,
145
+ tokenValueAfterFill,
146
+ strictOraclePrice.current,
147
+ spotMarket,
148
+ marginCategory
149
+ );
150
+
151
+ const freeCollateralContribution =
152
+ weightedTokenValueAfterFill.add(ordersValue);
153
+
154
+ return {
155
+ tokenAmount: tokenAmountAfterFill,
156
+ ordersValue: ordersValue,
157
+ tokenValue: tokenValueAfterFill,
158
+ weight,
159
+ weightedTokenValue: weightedTokenValueAfterFill,
160
+ freeCollateralContribution,
161
+ };
47
162
  }
@@ -0,0 +1,19 @@
1
+ import { BN } from '@coral-xyz/anchor';
2
+
3
+ export class StrictOraclePrice {
4
+ current: BN;
5
+ twap?: BN;
6
+
7
+ constructor(current: BN, twap?: BN) {
8
+ this.current = current;
9
+ this.twap = twap;
10
+ }
11
+
12
+ public max(): BN {
13
+ return this.twap ? BN.max(this.twap, this.current) : this.current;
14
+ }
15
+
16
+ public min(): BN {
17
+ return this.twap ? BN.min(this.twap, this.current) : this.current;
18
+ }
19
+ }
@@ -0,0 +1,75 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js';
2
+
3
+ export class PriorityFeeSubscriber {
4
+ connection: Connection;
5
+ frequencyMs: number;
6
+ addresses: PublicKey[];
7
+ slotsToCheck: number;
8
+
9
+ intervalId?: NodeJS.Timer;
10
+
11
+ latestPriorityFee = 0;
12
+ // avg of last {slotsToCheck} slots
13
+ avgPriorityFee = 0;
14
+ // max of last {slotsToCheck} slots
15
+ maxPriorityFee = 0;
16
+ lastSlotSeen = 0;
17
+
18
+ public constructor({
19
+ connection,
20
+ frequencyMs,
21
+ addresses,
22
+ slotsToCheck = 10,
23
+ }: {
24
+ connection: Connection;
25
+ frequencyMs: number;
26
+ addresses: PublicKey[];
27
+ slotsToCheck?: number;
28
+ }) {
29
+ this.connection = connection;
30
+ this.frequencyMs = frequencyMs;
31
+ this.addresses = addresses;
32
+ this.slotsToCheck = slotsToCheck;
33
+ }
34
+
35
+ public async subscribe(): Promise<void> {
36
+ if (this.intervalId) {
37
+ return;
38
+ }
39
+
40
+ this.intervalId = setInterval(this.load.bind(this), this.frequencyMs);
41
+ }
42
+
43
+ public async load(): Promise<void> {
44
+ // @ts-ignore
45
+ const rpcJSONResponse: any = await this.connection._rpcRequest(
46
+ 'getRecentPrioritizationFees',
47
+ [this.addresses]
48
+ );
49
+
50
+ const descResults: { slot: number; prioritizationFee: number }[] =
51
+ rpcJSONResponse?.result
52
+ ?.sort((a, b) => b.slot - a.slot)
53
+ ?.slice(0, this.slotsToCheck) ?? [];
54
+
55
+ if (!descResults?.length) return;
56
+
57
+ const mostRecentResult = descResults[0];
58
+ this.latestPriorityFee = mostRecentResult.prioritizationFee;
59
+ this.lastSlotSeen = mostRecentResult.slot;
60
+ this.avgPriorityFee =
61
+ descResults.reduce((a, b) => {
62
+ return a + b.prioritizationFee;
63
+ }, 0) / descResults.length;
64
+ this.maxPriorityFee = Math.max(
65
+ ...descResults.map((result) => result.prioritizationFee)
66
+ );
67
+ }
68
+
69
+ public async unsubscribe(): Promise<void> {
70
+ if (this.intervalId) {
71
+ clearInterval(this.intervalId);
72
+ this.intervalId = undefined;
73
+ }
74
+ }
75
+ }
@@ -38,6 +38,7 @@ export class TokenFaucet {
38
38
  this.connection = connection;
39
39
  this.wallet = wallet;
40
40
  this.opts = opts || AnchorProvider.defaultOptions();
41
+ // @ts-ignore
41
42
  const provider = new AnchorProvider(connection, wallet, this.opts);
42
43
  this.provider = provider;
43
44
  this.program = new Program(tokenFaucet as Idl, programId, provider);
package/src/types.ts CHANGED
@@ -155,6 +155,9 @@ export class OrderActionExplanation {
155
155
  static readonly ORDER_FILLED_WITH_SERUM = {
156
156
  orderFillWithSerum: {},
157
157
  };
158
+ static readonly ORDER_FILLED_WITH_PHOENIX = {
159
+ orderFillWithPhoenix: {},
160
+ };
158
161
  static readonly REDUCE_ONLY_ORDER_INCREASED_POSITION = {
159
162
  reduceOnlyOrderIncreasedPosition: {},
160
163
  };
@@ -652,6 +655,7 @@ export type SpotMarketAccount = {
652
655
  maintenanceLiabilityWeight: number;
653
656
  liquidatorFee: number;
654
657
  imfFactor: number;
658
+ scaleInitialAssetWeightStart: BN;
655
659
 
656
660
  withdrawGuardThreshold: BN;
657
661
  depositTokenTwap: BN;