@drift-labs/sdk 2.60.0-beta.0 → 2.60.0-beta.10

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.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.60.0-beta.0
1
+ 2.60.0-beta.10
@@ -486,6 +486,16 @@ exports.MainnetPerpMarkets = [
486
486
  launchTs: 1706219971000,
487
487
  oracleSource: __1.OracleSource.PYTH,
488
488
  },
489
+ {
490
+ fullName: 'JUP',
491
+ category: ['Exchange', 'Infra'],
492
+ symbol: 'JUP-PERP',
493
+ baseAssetSymbol: 'JUP',
494
+ marketIndex: 24,
495
+ oracle: new web3_js_1.PublicKey('g6eRCbboSwK4tSWngn773RCMexr1APQr4uA9bGZBYfo'),
496
+ launchTs: 1706713201000,
497
+ oracleSource: __1.OracleSource.PYTH,
498
+ },
489
499
  ];
490
500
  exports.PerpMarkets = {
491
501
  devnet: exports.DevnetPerpMarkets,
@@ -11,6 +11,7 @@ export type SpotMarketConfig = {
11
11
  precisionExp: BN;
12
12
  serumMarket?: PublicKey;
13
13
  phoenixMarket?: PublicKey;
14
+ launchTs?: number;
14
15
  };
15
16
  export declare const WRAPPED_SOL_MINT: PublicKey;
16
17
  export declare const DevnetSpotMarkets: SpotMarketConfig[];
@@ -139,6 +139,7 @@ exports.MainnetSpotMarkets = [
139
139
  precision: new __1.BN(10).pow(numericConstants_1.NINE),
140
140
  precisionExp: numericConstants_1.NINE,
141
141
  serumMarket: new web3_js_1.PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
142
+ phoenixMarket: new web3_js_1.PublicKey('BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'),
142
143
  },
143
144
  {
144
145
  symbol: 'WIF',
@@ -149,6 +150,18 @@ exports.MainnetSpotMarkets = [
149
150
  precision: new __1.BN(10).pow(numericConstants_1.SIX),
150
151
  precisionExp: numericConstants_1.SIX,
151
152
  serumMarket: new web3_js_1.PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
153
+ phoenixMarket: new web3_js_1.PublicKey('6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'),
154
+ },
155
+ {
156
+ symbol: 'JUP',
157
+ marketIndex: 11,
158
+ oracle: new web3_js_1.PublicKey('g6eRCbboSwK4tSWngn773RCMexr1APQr4uA9bGZBYfo'),
159
+ oracleSource: __1.OracleSource.PYTH,
160
+ mint: new web3_js_1.PublicKey('JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN'),
161
+ precision: new __1.BN(10).pow(numericConstants_1.SIX),
162
+ precisionExp: numericConstants_1.SIX,
163
+ phoenixMarket: new web3_js_1.PublicKey('2pspvjWWaf3dNgt3jsgSzFCNvMGPb7t8FrEYvLGjvcCe'),
164
+ launchTs: 1706731200000,
152
165
  },
153
166
  ];
154
167
  exports.SpotMarkets = {
@@ -55,6 +55,7 @@ const spotMarket_1 = require("./math/spotMarket");
55
55
  const memcmp_1 = require("./memcmp");
56
56
  const marinade_1 = require("./marinade");
57
57
  const orderParams_1 = require("./orderParams");
58
+ const utils_2 = require("./math/utils");
58
59
  /**
59
60
  * # DriftClient
60
61
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -794,16 +795,24 @@ class DriftClient {
794
795
  * @param amount
795
796
  */
796
797
  convertToPerpPrecision(amount) {
797
- amount = typeof amount === 'number' ? new anchor_1.BN(amount) : amount;
798
- return amount.mul(numericConstants_1.BASE_PRECISION);
798
+ if (typeof amount === 'number') {
799
+ return (0, utils_2.numberToSafeBN)(amount, numericConstants_1.BASE_PRECISION);
800
+ }
801
+ else {
802
+ return amount.mul(numericConstants_1.BASE_PRECISION);
803
+ }
799
804
  }
800
805
  /**
801
806
  * Converts an amount to the price precision. The perp market precision is {@link PRICE_PRECISION} (1e6).
802
807
  * @param amount
803
808
  */
804
809
  convertToPricePrecision(amount) {
805
- amount = typeof amount === 'number' ? new anchor_1.BN(amount) : amount;
806
- return amount.mul(numericConstants_1.PRICE_PRECISION);
810
+ if (typeof amount === 'number') {
811
+ return (0, utils_2.numberToSafeBN)(amount, numericConstants_1.PRICE_PRECISION);
812
+ }
813
+ else {
814
+ return amount.mul(numericConstants_1.BASE_PRECISION);
815
+ }
807
816
  }
808
817
  /**
809
818
  * Each drift instruction must include perp and sport market accounts in the ix remaining accounts.
package/lib/index.d.ts CHANGED
@@ -96,4 +96,5 @@ export * from './orderSubscriber/types';
96
96
  export * from './auctionSubscriber';
97
97
  export * from './auctionSubscriber/types';
98
98
  export * from './memcmp';
99
+ export * from './decode/user';
99
100
  export { BN, PublicKey, pyth };
package/lib/index.js CHANGED
@@ -119,3 +119,4 @@ __exportStar(require("./orderSubscriber/types"), exports);
119
119
  __exportStar(require("./auctionSubscriber"), exports);
120
120
  __exportStar(require("./auctionSubscriber/types"), exports);
121
121
  __exportStar(require("./memcmp"), exports);
122
+ __exportStar(require("./decode/user"), exports);
package/lib/math/amm.js CHANGED
@@ -425,16 +425,13 @@ function calculateSpread(amm, oraclePriceData, now, reservePrice) {
425
425
  reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
426
426
  }
427
427
  const targetPrice = (oraclePriceData === null || oraclePriceData === void 0 ? void 0 : oraclePriceData.price) || reservePrice;
428
- const confInterval = oraclePriceData.confidence || numericConstants_1.ZERO;
429
428
  const targetMarkSpreadPct = reservePrice
430
429
  .sub(targetPrice)
431
430
  .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
432
431
  .div(reservePrice);
433
- const confIntervalPct = confInterval
434
- .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
435
- .div(reservePrice);
436
432
  now = now || new anchor_1.BN(new Date().getTime() / 1000); //todo
437
433
  const liveOracleStd = (0, oracles_1.calculateLiveOracleStd)(amm, oraclePriceData, now);
434
+ const confIntervalPct = (0, oracles_1.getNewOracleConfPct)(amm, oraclePriceData, reservePrice, now);
438
435
  const spreads = calculateSpreadBN(amm.baseSpread, targetMarkSpreadPct, confIntervalPct, amm.maxSpread, amm.quoteAssetReserve, amm.terminalQuoteAssetReserve, amm.pegMultiplier, amm.baseAssetAmountWithAmm, reservePrice, amm.totalFeeMinusDistributions, amm.netRevenueSinceLastFunding, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve, amm.markStd, liveOracleStd, amm.longIntensityVolume, amm.shortIntensityVolume, amm.volume24H);
439
436
  const longSpread = spreads[0];
440
437
  const shortSpread = spreads[1];
@@ -7,3 +7,4 @@ export declare function isOracleValid(amm: AMM, oraclePriceData: OraclePriceData
7
7
  export declare function isOracleTooDivergent(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, now: BN): boolean;
8
8
  export declare function calculateLiveOracleTwap(histOracleData: HistoricalOracleData, oraclePriceData: OraclePriceData, now: BN, period: BN): BN;
9
9
  export declare function calculateLiveOracleStd(amm: AMM, oraclePriceData: OraclePriceData, now: BN): BN;
10
+ export declare function getNewOracleConfPct(amm: AMM, oraclePriceData: OraclePriceData, reservePrice: BN, now: BN): BN;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateLiveOracleStd = exports.calculateLiveOracleTwap = exports.isOracleTooDivergent = exports.isOracleValid = exports.oraclePriceBands = void 0;
3
+ exports.getNewOracleConfPct = exports.calculateLiveOracleStd = exports.calculateLiveOracleTwap = exports.isOracleTooDivergent = exports.isOracleValid = exports.oraclePriceBands = void 0;
4
4
  const numericConstants_1 = require("../constants/numericConstants");
5
5
  const index_1 = require("../index");
6
6
  const assert_1 = require("../assert/assert");
@@ -80,3 +80,18 @@ function calculateLiveOracleStd(amm, oraclePriceData, now) {
80
80
  return oracleStd;
81
81
  }
82
82
  exports.calculateLiveOracleStd = calculateLiveOracleStd;
83
+ function getNewOracleConfPct(amm, oraclePriceData, reservePrice, now) {
84
+ const confInterval = oraclePriceData.confidence || numericConstants_1.ZERO;
85
+ const sinceLastUpdate = index_1.BN.max(numericConstants_1.ZERO, now.sub(amm.historicalOracleData.lastOraclePriceTwapTs));
86
+ let lowerBoundConfPct = amm.lastOracleConfPct;
87
+ if (sinceLastUpdate.gt(numericConstants_1.ZERO)) {
88
+ const lowerBoundConfDivisor = index_1.BN.max(new index_1.BN(21).sub(sinceLastUpdate), new index_1.BN(5));
89
+ lowerBoundConfPct = amm.lastOracleConfPct.sub(amm.lastOracleConfPct.div(lowerBoundConfDivisor));
90
+ }
91
+ const confIntervalPct = confInterval
92
+ .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
93
+ .div(reservePrice);
94
+ const confIntervalPctResult = index_1.BN.max(confIntervalPct, lowerBoundConfPct);
95
+ return confIntervalPctResult;
96
+ }
97
+ exports.getNewOracleConfPct = getNewOracleConfPct;
@@ -5,9 +5,10 @@ const anchor_1 = require("@coral-xyz/anchor");
5
5
  const types_1 = require("../types");
6
6
  const spotBalance_1 = require("./spotBalance");
7
7
  const numericConstants_1 = require("../constants/numericConstants");
8
+ const utils_1 = require("./utils");
8
9
  function castNumberToSpotPrecision(value, spotMarket) {
9
10
  if (typeof value === 'number') {
10
- return new anchor_1.BN(value * Math.pow(10, spotMarket.decimals));
11
+ return (0, utils_1.numberToSafeBN)(value, new anchor_1.BN(Math.pow(10, spotMarket.decimals)));
11
12
  }
12
13
  else {
13
14
  return value.mul(new anchor_1.BN(Math.pow(10, spotMarket.decimals)));
@@ -93,22 +93,18 @@ export declare function findBestLstSuperStakeIxs({ amount, jupiterClient, driftC
93
93
  method: 'jupiter' | 'marinade';
94
94
  }>;
95
95
  export type JITO_SOL_METRICS_ENDPOINT_RESPONSE = {
96
- data: {
97
- getStakePoolStats: {
98
- tvl: {
99
- data: number;
100
- date: string;
101
- }[];
102
- supply: {
103
- data: number;
104
- date: string;
105
- }[];
106
- apy: {
107
- data: number;
108
- date: string;
109
- }[];
110
- };
111
- };
96
+ tvl: {
97
+ data: number;
98
+ date: string;
99
+ }[];
100
+ supply: {
101
+ data: number;
102
+ date: string;
103
+ }[];
104
+ apy: {
105
+ data: number;
106
+ date: string;
107
+ }[];
112
108
  };
113
109
  export declare function fetchJitoSolMetrics(): Promise<JITO_SOL_METRICS_ENDPOINT_RESPONSE>;
114
110
  export type MSOL_METRICS_ENDPOINT_RESPONSE = {
@@ -151,42 +151,35 @@ async function findBestLstSuperStakeIxs({ amount, jupiterClient, driftClient, us
151
151
  };
152
152
  }
153
153
  exports.findBestLstSuperStakeIxs = findBestLstSuperStakeIxs;
154
- const JITO_SOL_START_DATE = '2022-10-31T00:00:00Z';
154
+ /**
155
+ * Removes hours, minutes, seconds from a date, and returns the ISO string value (with milliseconds trimmed from the output (required by Jito API))
156
+ * @param inDate
157
+ * @returns
158
+ */
159
+ const getNormalizedDateString = (inDate) => {
160
+ const date = new Date(inDate.getTime());
161
+ date.setUTCHours(0, 0, 0, 0);
162
+ return date.toISOString().slice(0, 19) + 'Z';
163
+ };
164
+ const get30DAgo = () => {
165
+ const date = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
166
+ return date;
167
+ };
155
168
  async function fetchJitoSolMetrics() {
156
- const res = await (0, node_fetch_1.default)('https://kobe.mainnet.jito.network/', {
169
+ const res = await (0, node_fetch_1.default)('https://kobe.mainnet.jito.network/api/v1/stake_pool_stats', {
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ },
157
173
  body: JSON.stringify({
158
- operationName: 'QueryRoot',
159
- variables: {
160
- request: {
161
- bucketType: 'DAILY',
162
- rangeFilter: {
163
- start: JITO_SOL_START_DATE,
164
- end: new Date().toISOString(),
165
- },
166
- sortBy: {
167
- order: 'ASC',
168
- field: 'BLOCK_TIME',
169
- },
170
- },
174
+ bucket_type: 'Daily',
175
+ range_filter: {
176
+ start: getNormalizedDateString(get30DAgo()),
177
+ end: getNormalizedDateString(new Date()),
178
+ },
179
+ sort_by: {
180
+ order: 'Asc',
181
+ field: 'BlockTime',
171
182
  },
172
- query: `
173
- query QueryRoot($request: GetStakePoolStatsRequest!) {
174
- getStakePoolStats(req: $request) {
175
- tvl {
176
- data
177
- date
178
- }
179
- apy {
180
- data
181
- date
182
- }
183
- supply {
184
- data
185
- date
186
- }
187
- }
188
- }
189
- `,
190
183
  }),
191
184
  method: 'POST',
192
185
  });
@@ -205,13 +198,11 @@ const getJitoSolHistoricalPriceMap = async (timestamps) => {
205
198
  const data = await fetchJitoSolMetrics();
206
199
  const jitoSolHistoricalPriceMap = new Map();
207
200
  const jitoSolHistoricalPriceInSol = [];
208
- for (let i = 0; i < data.data.getStakePoolStats.supply.length; i++) {
209
- const priceInSol = data.data.getStakePoolStats.tvl[i].data /
210
- 10 ** 9 /
211
- data.data.getStakePoolStats.supply[i].data;
201
+ for (let i = 0; i < data.supply.length; i++) {
202
+ const priceInSol = data.tvl[i].data / 10 ** 9 / data.supply[i].data;
212
203
  jitoSolHistoricalPriceInSol.push({
213
204
  price: priceInSol,
214
- ts: data.data.getStakePoolStats.tvl[i].date,
205
+ ts: data.tvl[i].date,
215
206
  });
216
207
  }
217
208
  for (const timestamp of timestamps) {
@@ -14,3 +14,10 @@ export declare const sigNum: (x: BN) => BN;
14
14
  */
15
15
  export declare function timeRemainingUntilUpdate(now: BN, lastUpdateTs: BN, updatePeriod: BN): BN;
16
16
  export declare const checkSameDate: (dateString1: string, dateString2: string) => boolean;
17
+ export declare function isBNSafe(number: number): boolean;
18
+ /**
19
+ * Converts a number to BN makes sure the number is safe to convert to BN (that it does not overflow number after multiplying by precision)
20
+ * @param number the number to convert to BN
21
+ * @param precision the BN precision to use (i.e. QUOTE_PRECISION and BASE_PRECISION from drift sdk)
22
+ */
23
+ export declare function numberToSafeBN(number: number, precision: BN): BN;
package/lib/math/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkSameDate = exports.timeRemainingUntilUpdate = exports.sigNum = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
3
+ exports.numberToSafeBN = exports.isBNSafe = exports.checkSameDate = exports.timeRemainingUntilUpdate = exports.sigNum = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
4
4
  const __1 = require("../");
5
5
  function clampBN(x, min, max) {
6
6
  return __1.BN.max(min, __1.BN.min(x, max));
@@ -85,3 +85,28 @@ const checkSameDate = (dateString1, dateString2) => {
85
85
  return isSameDate;
86
86
  };
87
87
  exports.checkSameDate = checkSameDate;
88
+ function isBNSafe(number) {
89
+ return number <= 0x1fffffffffffff;
90
+ }
91
+ exports.isBNSafe = isBNSafe;
92
+ /**
93
+ * Converts a number to BN makes sure the number is safe to convert to BN (that it does not overflow number after multiplying by precision)
94
+ * @param number the number to convert to BN
95
+ * @param precision the BN precision to use (i.e. QUOTE_PRECISION and BASE_PRECISION from drift sdk)
96
+ */
97
+ function numberToSafeBN(number, precision) {
98
+ // check if number has decimals
99
+ const candidate = number * precision.toNumber();
100
+ if (isBNSafe(candidate)) {
101
+ return new __1.BN(candidate);
102
+ }
103
+ else {
104
+ if (number % 1 === 0) {
105
+ return new __1.BN(number.toString()).mul(precision);
106
+ }
107
+ else {
108
+ return new __1.BN(number).mul(precision);
109
+ }
110
+ }
111
+ }
112
+ exports.numberToSafeBN = numberToSafeBN;
package/lib/user.d.ts CHANGED
@@ -248,7 +248,8 @@ export declare class User {
248
248
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
249
249
  * @returns Precision : PRICE_PRECISION
250
250
  */
251
- liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN): BN;
251
+ liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN): BN;
252
+ calculateEntriesEffectOnFreeCollateral(market: PerpMarketAccount, oraclePrice: BN, perpPosition: PerpPosition, positionBaseSizeChange: BN, estimatedEntryPrice: BN): BN;
252
253
  calculateFreeCollateralDeltaForPerp(market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN): BN | undefined;
253
254
  calculateFreeCollateralDeltaForSpot(market: SpotMarketAccount, signedTokenAmount: BN): BN;
254
255
  /**
@@ -257,7 +258,7 @@ export declare class User {
257
258
  * @param closeQuoteAmount
258
259
  * @returns : Precision PRICE_PRECISION
259
260
  */
260
- liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN): BN;
261
+ liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN, estimatedEntryPrice?: BN): BN;
261
262
  /**
262
263
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
263
264
  *
package/lib/user.js CHANGED
@@ -1082,18 +1082,22 @@ class User {
1082
1082
  * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
1083
1083
  * @returns Precision : PRICE_PRECISION
1084
1084
  */
1085
- liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO) {
1085
+ liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO) {
1086
1086
  const totalCollateral = this.getTotalCollateral('Maintenance');
1087
1087
  const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
1088
- const freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(maintenanceMarginRequirement));
1088
+ let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(maintenanceMarginRequirement));
1089
+ const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
1090
+ const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1089
1091
  const market = this.driftClient.getPerpMarketAccount(marketIndex);
1090
1092
  const currentPerpPosition = this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] ||
1091
1093
  this.getEmptyPosition(marketIndex);
1094
+ positionBaseSizeChange = (0, _1.standardizeBaseAssetAmount)(positionBaseSizeChange, market.amm.orderStepSize);
1095
+ const freeCollateralChangeFromNewPosition = this.calculateEntriesEffectOnFreeCollateral(market, oraclePrice, currentPerpPosition, positionBaseSizeChange, estimatedEntryPrice);
1096
+ freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
1092
1097
  let freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(market, currentPerpPosition, positionBaseSizeChange);
1093
1098
  if (!freeCollateralDelta) {
1094
1099
  return new _1.BN(-1);
1095
1100
  }
1096
- const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
1097
1101
  const spotMarketWithSameOracle = this.driftClient
1098
1102
  .getSpotMarketAccounts()
1099
1103
  .find((market) => market.oracle.equals(oracle));
@@ -1108,7 +1112,6 @@ class User {
1108
1112
  if (freeCollateralDelta.eq(numericConstants_1.ZERO)) {
1109
1113
  return new _1.BN(-1);
1110
1114
  }
1111
- const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1112
1115
  const liqPriceDelta = freeCollateral
1113
1116
  .mul(numericConstants_1.QUOTE_PRECISION)
1114
1117
  .div(freeCollateralDelta);
@@ -1118,6 +1121,47 @@ class User {
1118
1121
  }
1119
1122
  return liqPrice;
1120
1123
  }
1124
+ calculateEntriesEffectOnFreeCollateral(market, oraclePrice, perpPosition, positionBaseSizeChange, estimatedEntryPrice) {
1125
+ let freeCollateralChange = numericConstants_1.ZERO;
1126
+ // update free collateral to account for change in pnl from new position
1127
+ if (!estimatedEntryPrice.eq(numericConstants_1.ZERO) && !positionBaseSizeChange.eq(numericConstants_1.ZERO)) {
1128
+ const costBasis = oraclePrice
1129
+ .mul(positionBaseSizeChange.abs())
1130
+ .div(numericConstants_1.BASE_PRECISION);
1131
+ const newPositionValue = estimatedEntryPrice
1132
+ .mul(positionBaseSizeChange.abs())
1133
+ .div(numericConstants_1.BASE_PRECISION);
1134
+ if (positionBaseSizeChange.gt(numericConstants_1.ZERO)) {
1135
+ freeCollateralChange = costBasis.sub(newPositionValue);
1136
+ }
1137
+ else {
1138
+ freeCollateralChange = newPositionValue.sub(costBasis);
1139
+ }
1140
+ // assume worst fee tier
1141
+ const takerFeeTier = this.driftClient.getStateAccount().perpFeeStructure.feeTiers[0];
1142
+ const takerFee = newPositionValue
1143
+ .muln(takerFeeTier.feeNumerator)
1144
+ .divn(takerFeeTier.feeDenominator);
1145
+ freeCollateralChange = freeCollateralChange.sub(takerFee);
1146
+ }
1147
+ const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
1148
+ const marginRatioBefore = (0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), 'Maintenance');
1149
+ const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(positionBaseSizeChange);
1150
+ const newMarginRatio = (0, _1.calculateMarketMarginRatio)(market, newWorstCaseBaseAssetAmount.abs(), 'Maintenance');
1151
+ // update free collateral to account for change in margin ratio from position change
1152
+ freeCollateralChange = freeCollateralChange.sub(worstCaseBaseAssetAmount
1153
+ .mul(oraclePrice)
1154
+ .div(numericConstants_1.BASE_PRECISION)
1155
+ .mul(new _1.BN(newMarginRatio - marginRatioBefore))
1156
+ .div(numericConstants_1.MARGIN_PRECISION));
1157
+ // update free collateral to account for new margin requirement from position change
1158
+ freeCollateralChange = freeCollateralChange.sub(positionBaseSizeChange
1159
+ .mul(oraclePrice)
1160
+ .div(numericConstants_1.BASE_PRECISION)
1161
+ .mul(new _1.BN(newMarginRatio))
1162
+ .div(numericConstants_1.MARGIN_PRECISION));
1163
+ return freeCollateralChange;
1164
+ }
1121
1165
  calculateFreeCollateralDeltaForPerp(market, perpPosition, positionBaseSizeChange) {
1122
1166
  const currentBaseAssetAmount = perpPosition.baseAssetAmount;
1123
1167
  const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
@@ -1174,7 +1218,7 @@ class User {
1174
1218
  * @param closeQuoteAmount
1175
1219
  * @returns : Precision PRICE_PRECISION
1176
1220
  */
1177
- liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount) {
1221
+ liquidationPriceAfterClose(positionMarketIndex, closeQuoteAmount, estimatedEntryPrice = numericConstants_1.ZERO) {
1178
1222
  const currentPosition = this.getPerpPositionWithLPSettle(positionMarketIndex, undefined, true)[0] || this.getEmptyPosition(positionMarketIndex);
1179
1223
  const closeBaseAmount = currentPosition.baseAssetAmount
1180
1224
  .mul(closeQuoteAmount)
@@ -1183,7 +1227,7 @@ class User {
1183
1227
  .mul(closeQuoteAmount)
1184
1228
  .mod(currentPosition.quoteAssetAmount.abs()))
1185
1229
  .neg();
1186
- return this.liquidationPrice(positionMarketIndex, closeBaseAmount);
1230
+ return this.liquidationPrice(positionMarketIndex, closeBaseAmount, estimatedEntryPrice);
1187
1231
  }
1188
1232
  /**
1189
1233
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.60.0-beta.0",
3
+ "version": "2.60.0-beta.10",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -497,6 +497,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
497
497
  launchTs: 1706219971000,
498
498
  oracleSource: OracleSource.PYTH,
499
499
  },
500
+ {
501
+ fullName: 'JUP',
502
+ category: ['Exchange', 'Infra'],
503
+ symbol: 'JUP-PERP',
504
+ baseAssetSymbol: 'JUP',
505
+ marketIndex: 24,
506
+ oracle: new PublicKey('g6eRCbboSwK4tSWngn773RCMexr1APQr4uA9bGZBYfo'),
507
+ launchTs: 1706713201000,
508
+ oracleSource: OracleSource.PYTH,
509
+ },
500
510
  ];
501
511
 
502
512
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
@@ -20,6 +20,7 @@ export type SpotMarketConfig = {
20
20
  precisionExp: BN;
21
21
  serumMarket?: PublicKey;
22
22
  phoenixMarket?: PublicKey;
23
+ launchTs?: number;
23
24
  };
24
25
 
25
26
  export const WRAPPED_SOL_MINT = new PublicKey(
@@ -169,6 +170,9 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
169
170
  precision: new BN(10).pow(NINE),
170
171
  precisionExp: NINE,
171
172
  serumMarket: new PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
173
+ phoenixMarket: new PublicKey(
174
+ 'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ'
175
+ ),
172
176
  },
173
177
  {
174
178
  symbol: 'WIF',
@@ -179,6 +183,22 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
179
183
  precision: new BN(10).pow(SIX),
180
184
  precisionExp: SIX,
181
185
  serumMarket: new PublicKey('2BtDHBTCTUxvdur498ZEcMgimasaFrY5GzLv8wS8XgCb'),
186
+ phoenixMarket: new PublicKey(
187
+ '6ojSigXF7nDPyhFRgmn3V9ywhYseKF9J32ZrranMGVSX'
188
+ ),
189
+ },
190
+ {
191
+ symbol: 'JUP',
192
+ marketIndex: 11,
193
+ oracle: new PublicKey('g6eRCbboSwK4tSWngn773RCMexr1APQr4uA9bGZBYfo'),
194
+ oracleSource: OracleSource.PYTH,
195
+ mint: new PublicKey('JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN'),
196
+ precision: new BN(10).pow(SIX),
197
+ precisionExp: SIX,
198
+ phoenixMarket: new PublicKey(
199
+ '2pspvjWWaf3dNgt3jsgSzFCNvMGPb7t8FrEYvLGjvcCe'
200
+ ),
201
+ launchTs: 1706731200000,
182
202
  },
183
203
  ];
184
204
 
@@ -124,6 +124,7 @@ import { getNonIdleUserFilter } from './memcmp';
124
124
  import { UserStatsSubscriptionConfig } from './userStatsConfig';
125
125
  import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
126
126
  import { getOrderParams } from './orderParams';
127
+ import { numberToSafeBN } from './math/utils';
127
128
 
128
129
  type RemainingAccountParams = {
129
130
  userAccounts: UserAccount[];
@@ -1339,8 +1340,11 @@ export class DriftClient {
1339
1340
  * @param amount
1340
1341
  */
1341
1342
  public convertToPerpPrecision(amount: BN | number): BN {
1342
- amount = typeof amount === 'number' ? new BN(amount) : amount;
1343
- return amount.mul(BASE_PRECISION);
1343
+ if (typeof amount === 'number') {
1344
+ return numberToSafeBN(amount, BASE_PRECISION);
1345
+ } else {
1346
+ return amount.mul(BASE_PRECISION);
1347
+ }
1344
1348
  }
1345
1349
 
1346
1350
  /**
@@ -1348,8 +1352,11 @@ export class DriftClient {
1348
1352
  * @param amount
1349
1353
  */
1350
1354
  public convertToPricePrecision(amount: BN | number): BN {
1351
- amount = typeof amount === 'number' ? new BN(amount) : amount;
1352
- return amount.mul(PRICE_PRECISION);
1355
+ if (typeof amount === 'number') {
1356
+ return numberToSafeBN(amount, PRICE_PRECISION);
1357
+ } else {
1358
+ return amount.mul(BASE_PRECISION);
1359
+ }
1353
1360
  }
1354
1361
 
1355
1362
  /**
package/src/index.ts CHANGED
@@ -97,5 +97,6 @@ export * from './orderSubscriber/types';
97
97
  export * from './auctionSubscriber';
98
98
  export * from './auctionSubscriber/types';
99
99
  export * from './memcmp';
100
+ export * from './decode/user';
100
101
 
101
102
  export { BN, PublicKey, pyth };
package/src/math/amm.ts CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  calculateBudgetedPeg,
33
33
  } from './repeg';
34
34
 
35
- import { calculateLiveOracleStd } from './oracles';
35
+ import { calculateLiveOracleStd, getNewOracleConfPct } from './oracles';
36
36
 
37
37
  export function calculatePegFromTargetPrice(
38
38
  targetPrice: BN,
@@ -792,18 +792,19 @@ export function calculateSpread(
792
792
  }
793
793
 
794
794
  const targetPrice = oraclePriceData?.price || reservePrice;
795
- const confInterval = oraclePriceData.confidence || ZERO;
796
795
  const targetMarkSpreadPct = reservePrice
797
796
  .sub(targetPrice)
798
797
  .mul(BID_ASK_SPREAD_PRECISION)
799
798
  .div(reservePrice);
800
799
 
801
- const confIntervalPct = confInterval
802
- .mul(BID_ASK_SPREAD_PRECISION)
803
- .div(reservePrice);
804
-
805
800
  now = now || new BN(new Date().getTime() / 1000); //todo
806
801
  const liveOracleStd = calculateLiveOracleStd(amm, oraclePriceData, now);
802
+ const confIntervalPct = getNewOracleConfPct(
803
+ amm,
804
+ oraclePriceData,
805
+ reservePrice,
806
+ now
807
+ );
807
808
 
808
809
  const spreads = calculateSpreadBN(
809
810
  amm.baseSpread,
@@ -151,3 +151,34 @@ export function calculateLiveOracleStd(
151
151
 
152
152
  return oracleStd;
153
153
  }
154
+
155
+ export function getNewOracleConfPct(
156
+ amm: AMM,
157
+ oraclePriceData: OraclePriceData,
158
+ reservePrice: BN,
159
+ now: BN
160
+ ): BN {
161
+ const confInterval = oraclePriceData.confidence || ZERO;
162
+
163
+ const sinceLastUpdate = BN.max(
164
+ ZERO,
165
+ now.sub(amm.historicalOracleData.lastOraclePriceTwapTs)
166
+ );
167
+ let lowerBoundConfPct = amm.lastOracleConfPct;
168
+ if (sinceLastUpdate.gt(ZERO)) {
169
+ const lowerBoundConfDivisor = BN.max(
170
+ new BN(21).sub(sinceLastUpdate),
171
+ new BN(5)
172
+ );
173
+ lowerBoundConfPct = amm.lastOracleConfPct.sub(
174
+ amm.lastOracleConfPct.div(lowerBoundConfDivisor)
175
+ );
176
+ }
177
+ const confIntervalPct = confInterval
178
+ .mul(BID_ASK_SPREAD_PRECISION)
179
+ .div(reservePrice);
180
+
181
+ const confIntervalPctResult = BN.max(confIntervalPct, lowerBoundConfPct);
182
+
183
+ return confIntervalPctResult;
184
+ }