@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 +1 -1
- package/lib/constants/perpMarkets.js +10 -0
- package/lib/constants/spotMarkets.d.ts +1 -0
- package/lib/constants/spotMarkets.js +13 -0
- package/lib/driftClient.js +13 -4
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/math/amm.js +1 -4
- package/lib/math/oracles.d.ts +1 -0
- package/lib/math/oracles.js +16 -1
- package/lib/math/spotMarket.js +2 -1
- package/lib/math/superStake.d.ts +12 -16
- package/lib/math/superStake.js +29 -38
- package/lib/math/utils.d.ts +7 -0
- package/lib/math/utils.js +26 -1
- package/lib/user.d.ts +3 -2
- package/lib/user.js +50 -6
- package/package.json +1 -1
- package/src/constants/perpMarkets.ts +10 -0
- package/src/constants/spotMarkets.ts +20 -0
- package/src/driftClient.ts +11 -4
- package/src/index.ts +1 -0
- package/src/math/amm.ts +7 -6
- package/src/math/oracles.ts +31 -0
- package/src/math/spotMarket.ts +2 -1
- package/src/math/superStake.ts +51 -61
- package/src/math/utils.ts +23 -0
- package/src/user.ts +105 -8
- package/tests/amm/test.ts +19 -3
- package/tests/bn/test.ts +15 -1
- package/examples/phoenix.ts +0 -66
- package/lib/examples/loadDlob.d.ts +0 -1
- package/lib/examples/loadDlob.js +0 -59
- package/lib/examples/makeTradeExample.d.ts +0 -2
- package/lib/examples/makeTradeExample.js +0 -83
- package/src/examples/loadDlob.ts +0 -86
- package/src/examples/makeTradeExample.ts +0 -162
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.60.0-beta.
|
|
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,
|
|
@@ -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 = {
|
package/lib/driftClient.js
CHANGED
|
@@ -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
|
-
|
|
798
|
-
|
|
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
|
-
|
|
806
|
-
|
|
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
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];
|
package/lib/math/oracles.d.ts
CHANGED
|
@@ -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;
|
package/lib/math/oracles.js
CHANGED
|
@@ -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;
|
package/lib/math/spotMarket.js
CHANGED
|
@@ -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(
|
|
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)));
|
package/lib/math/superStake.d.ts
CHANGED
|
@@ -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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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 = {
|
package/lib/math/superStake.js
CHANGED
|
@@ -151,42 +151,35 @@ async function findBestLstSuperStakeIxs({ amount, jupiterClient, driftClient, us
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
exports.findBestLstSuperStakeIxs = findBestLstSuperStakeIxs;
|
|
154
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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.
|
|
209
|
-
const priceInSol = 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.
|
|
205
|
+
ts: data.tvl[i].date,
|
|
215
206
|
});
|
|
216
207
|
}
|
|
217
208
|
for (const timestamp of timestamps) {
|
package/lib/math/utils.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
@@ -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
|
|
package/src/driftClient.ts
CHANGED
|
@@ -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
|
-
|
|
1343
|
-
|
|
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
|
-
|
|
1352
|
-
|
|
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
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,
|
package/src/math/oracles.ts
CHANGED
|
@@ -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
|
+
}
|