@drift-labs/sdk 2.10.0-beta.0 → 2.10.0-beta.2
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/lib/accounts/pollingDriftClientAccountSubscriber.js +12 -3
- package/lib/accounts/types.d.ts +0 -1
- package/lib/adminClient.js +96 -34
- package/lib/driftClient.d.ts +3 -2
- package/lib/driftClient.js +99 -58
- package/lib/idl/drift.json +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/math/amm.js +15 -6
- package/lib/math/oracles.js +2 -2
- package/lib/math/repeg.d.ts +1 -1
- package/lib/math/repeg.js +46 -19
- package/lib/math/spotBalance.js +1 -1
- package/lib/testClient.d.ts +8 -0
- package/lib/testClient.js +22 -0
- package/lib/user.d.ts +1 -0
- package/lib/user.js +4 -0
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +26 -3
- package/src/adminClient.ts +301 -169
- package/src/driftClient.ts +199 -118
- package/src/idl/drift.json +1 -1
- package/src/index.ts +1 -0
- package/src/math/amm.ts +27 -12
- package/src/math/oracles.ts +6 -4
- package/src/math/repeg.ts +54 -26
- package/src/math/spotBalance.ts +1 -5
- package/src/testClient.ts +40 -0
- package/src/user.ts +5 -0
package/lib/math/amm.js
CHANGED
|
@@ -23,6 +23,7 @@ function calculateOptimalPegAndBudget(amm, oraclePriceData) {
|
|
|
23
23
|
const prePegCost = (0, repeg_1.calculateRepegCost)(amm, newPeg);
|
|
24
24
|
const totalFeeLB = amm.totalExchangeFee.div(new anchor_1.BN(2));
|
|
25
25
|
const budget = anchor_1.BN.max(numericConstants_1.ZERO, amm.totalFeeMinusDistributions.sub(totalFeeLB));
|
|
26
|
+
let checkLowerBound = true;
|
|
26
27
|
if (budget.lt(prePegCost)) {
|
|
27
28
|
const halfMaxPriceSpread = new anchor_1.BN(amm.maxSpread)
|
|
28
29
|
.div(new anchor_1.BN(2))
|
|
@@ -42,19 +43,23 @@ function calculateOptimalPegAndBudget(amm, oraclePriceData) {
|
|
|
42
43
|
}
|
|
43
44
|
newOptimalPeg = calculatePegFromTargetPrice(newTargetPrice, amm.baseAssetReserve, amm.quoteAssetReserve);
|
|
44
45
|
newBudget = (0, repeg_1.calculateRepegCost)(amm, newOptimalPeg);
|
|
46
|
+
checkLowerBound = false;
|
|
45
47
|
return [newTargetPrice, newOptimalPeg, newBudget, false];
|
|
46
48
|
}
|
|
49
|
+
else if (amm.totalFeeMinusDistributions.lt(amm.totalExchangeFee.div(new anchor_1.BN(2)))) {
|
|
50
|
+
checkLowerBound = false;
|
|
51
|
+
}
|
|
47
52
|
}
|
|
48
|
-
return [targetPrice, newPeg, budget,
|
|
53
|
+
return [targetPrice, newPeg, budget, checkLowerBound];
|
|
49
54
|
}
|
|
50
55
|
exports.calculateOptimalPegAndBudget = calculateOptimalPegAndBudget;
|
|
51
56
|
function calculateNewAmm(amm, oraclePriceData) {
|
|
52
57
|
let pKNumer = new anchor_1.BN(1);
|
|
53
58
|
let pKDenom = new anchor_1.BN(1);
|
|
54
|
-
const [targetPrice, _newPeg, budget,
|
|
59
|
+
const [targetPrice, _newPeg, budget, _checkLowerBound] = calculateOptimalPegAndBudget(amm, oraclePriceData);
|
|
55
60
|
let prePegCost = (0, repeg_1.calculateRepegCost)(amm, _newPeg);
|
|
56
61
|
let newPeg = _newPeg;
|
|
57
|
-
if (prePegCost.
|
|
62
|
+
if (prePegCost.gte(budget) && prePegCost.gt(numericConstants_1.ZERO)) {
|
|
58
63
|
[pKNumer, pKDenom] = [new anchor_1.BN(999), new anchor_1.BN(1000)];
|
|
59
64
|
const deficitMadeup = (0, repeg_1.calculateAdjustKCost)(amm, pKNumer, pKDenom);
|
|
60
65
|
(0, assert_1.assert)(deficitMadeup.lte(new anchor_1.BN(0)));
|
|
@@ -76,7 +81,7 @@ function calculateNewAmm(amm, oraclePriceData) {
|
|
|
76
81
|
}
|
|
77
82
|
exports.calculateNewAmm = calculateNewAmm;
|
|
78
83
|
function calculateUpdatedAMM(amm, oraclePriceData) {
|
|
79
|
-
if (amm.curveUpdateIntensity == 0) {
|
|
84
|
+
if (amm.curveUpdateIntensity == 0 || oraclePriceData === undefined) {
|
|
80
85
|
return amm;
|
|
81
86
|
}
|
|
82
87
|
const newAmm = Object.assign({}, amm);
|
|
@@ -325,8 +330,12 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
|
|
|
325
330
|
spreadTerms.longSpreadwEL = longSpread;
|
|
326
331
|
spreadTerms.shortSpreadwEL = shortSpread;
|
|
327
332
|
if (netRevenueSinceLastFunding.lt(numericConstants_1.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT)) {
|
|
328
|
-
const
|
|
329
|
-
|
|
333
|
+
const maxRetreat = maxTargetSpread / 10;
|
|
334
|
+
let revenueRetreatAmount = maxRetreat;
|
|
335
|
+
if (netRevenueSinceLastFunding.gte(numericConstants_1.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT.mul(new anchor_1.BN(1000)))) {
|
|
336
|
+
revenueRetreatAmount = Math.min(maxRetreat, Math.floor((baseSpread * netRevenueSinceLastFunding.abs().toNumber()) /
|
|
337
|
+
numericConstants_1.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT.abs().toNumber()));
|
|
338
|
+
}
|
|
330
339
|
const halfRevenueRetreatAmount = Math.floor(revenueRetreatAmount / 2);
|
|
331
340
|
spreadTerms.revenueRetreatAmount = revenueRetreatAmount;
|
|
332
341
|
spreadTerms.halfRevenueRetreatAmount = halfRevenueRetreatAmount;
|
package/lib/math/oracles.js
CHANGED
|
@@ -53,7 +53,7 @@ function isOracleTooDivergent(amm, oraclePriceData, oracleGuardRails, now) {
|
|
|
53
53
|
}
|
|
54
54
|
exports.isOracleTooDivergent = isOracleTooDivergent;
|
|
55
55
|
function calculateLiveOracleTwap(amm, oraclePriceData, now) {
|
|
56
|
-
const sinceLastUpdate = now.sub(amm.historicalOracleData.lastOraclePriceTwapTs);
|
|
56
|
+
const sinceLastUpdate = index_1.BN.max(numericConstants_1.ONE, now.sub(amm.historicalOracleData.lastOraclePriceTwapTs));
|
|
57
57
|
const sinceStart = index_1.BN.max(numericConstants_1.ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
58
58
|
const clampRange = amm.historicalOracleData.lastOraclePriceTwap.div(new index_1.BN(3));
|
|
59
59
|
const clampedOraclePrice = index_1.BN.min(amm.historicalOracleData.lastOraclePriceTwap.add(clampRange), index_1.BN.max(oraclePriceData.price, amm.historicalOracleData.lastOraclePriceTwap.sub(clampRange)));
|
|
@@ -65,7 +65,7 @@ function calculateLiveOracleTwap(amm, oraclePriceData, now) {
|
|
|
65
65
|
}
|
|
66
66
|
exports.calculateLiveOracleTwap = calculateLiveOracleTwap;
|
|
67
67
|
function calculateLiveOracleStd(amm, oraclePriceData, now) {
|
|
68
|
-
const sinceLastUpdate = now.sub(amm.historicalOracleData.lastOraclePriceTwapTs);
|
|
68
|
+
const sinceLastUpdate = index_1.BN.max(numericConstants_1.ONE, now.sub(amm.historicalOracleData.lastOraclePriceTwapTs));
|
|
69
69
|
const sinceStart = index_1.BN.max(numericConstants_1.ZERO, amm.fundingPeriod.sub(sinceLastUpdate));
|
|
70
70
|
const liveOracleTwap = calculateLiveOracleTwap(amm, oraclePriceData, now);
|
|
71
71
|
const priceDeltaVsTwap = oraclePriceData.price.sub(liveOracleTwap).abs();
|
package/lib/math/repeg.d.ts
CHANGED
|
@@ -19,4 +19,4 @@ export declare function calculateAdjustKCost(amm: AMM, numerator: BN, denomenato
|
|
|
19
19
|
export declare function calculateRepegCost(amm: AMM, newPeg: BN): BN;
|
|
20
20
|
export declare function calculateBudgetedKBN(x: BN, y: BN, budget: BN, Q: BN, d: BN): [BN, BN];
|
|
21
21
|
export declare function calculateBudgetedK(amm: AMM, cost: BN): [BN, BN];
|
|
22
|
-
export declare function calculateBudgetedPeg(amm: AMM,
|
|
22
|
+
export declare function calculateBudgetedPeg(amm: AMM, budget: BN, targetPrice: BN): BN;
|
package/lib/math/repeg.js
CHANGED
|
@@ -20,16 +20,46 @@ function calculateAdjustKCost(amm, numerator, denomenator) {
|
|
|
20
20
|
const quoteScale = y.mul(d).mul(Q); //.div(AMM_RESERVE_PRECISION);
|
|
21
21
|
const p = numerator.mul(numericConstants_1.PRICE_PRECISION).div(denomenator);
|
|
22
22
|
const cost = quoteScale
|
|
23
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
24
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
23
25
|
.div(x.add(d))
|
|
24
26
|
.sub(quoteScale
|
|
25
27
|
.mul(p)
|
|
28
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
29
|
+
.mul(numericConstants_1.PERCENTAGE_PRECISION)
|
|
26
30
|
.div(numericConstants_1.PRICE_PRECISION)
|
|
27
31
|
.div(x.mul(p).div(numericConstants_1.PRICE_PRECISION).add(d)))
|
|
32
|
+
.div(numericConstants_1.PERCENTAGE_PRECISION)
|
|
33
|
+
.div(numericConstants_1.PERCENTAGE_PRECISION)
|
|
28
34
|
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO)
|
|
29
35
|
.div(numericConstants_1.PEG_PRECISION);
|
|
30
36
|
return cost.mul(new anchor_1.BN(-1));
|
|
31
37
|
}
|
|
32
38
|
exports.calculateAdjustKCost = calculateAdjustKCost;
|
|
39
|
+
// /**
|
|
40
|
+
// * Helper function calculating adjust k cost
|
|
41
|
+
// * @param amm
|
|
42
|
+
// * @param numerator
|
|
43
|
+
// * @param denomenator
|
|
44
|
+
// * @returns cost : Precision QUOTE_ASSET_PRECISION
|
|
45
|
+
// */
|
|
46
|
+
// export function calculateAdjustKCost2(
|
|
47
|
+
// amm: AMM,
|
|
48
|
+
// numerator: BN,
|
|
49
|
+
// denomenator: BN
|
|
50
|
+
// ): BN {
|
|
51
|
+
// // const k = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
52
|
+
// const directionToClose = amm.baseAssetAmountWithAmm.gt(ZERO)
|
|
53
|
+
// ? PositionDirection.SHORT
|
|
54
|
+
// : PositionDirection.LONG;
|
|
55
|
+
// const [newQuoteAssetReserve, _newBaseAssetReserve] =
|
|
56
|
+
// calculateAmmReservesAfterSwap(
|
|
57
|
+
// amm,
|
|
58
|
+
// 'base',
|
|
59
|
+
// amm.baseAssetAmountWithAmm.abs(),
|
|
60
|
+
// getSwapDirection('base', directionToClose)
|
|
61
|
+
// );
|
|
62
|
+
// }
|
|
33
63
|
/**
|
|
34
64
|
* Helper function calculating adjust pegMultiplier (repeg) cost
|
|
35
65
|
*
|
|
@@ -107,31 +137,28 @@ function calculateBudgetedK(amm, cost) {
|
|
|
107
137
|
return [numerator, denominator];
|
|
108
138
|
}
|
|
109
139
|
exports.calculateBudgetedK = calculateBudgetedK;
|
|
110
|
-
function calculateBudgetedPeg(amm,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
140
|
+
function calculateBudgetedPeg(amm, budget, targetPrice) {
|
|
141
|
+
let perPegCost = amm.quoteAssetReserve
|
|
142
|
+
.sub(amm.terminalQuoteAssetReserve)
|
|
143
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION.div(numericConstants_1.PRICE_PRECISION));
|
|
144
|
+
if (perPegCost.gt(numericConstants_1.ZERO)) {
|
|
145
|
+
perPegCost = perPegCost.add(numericConstants_1.ONE);
|
|
146
|
+
}
|
|
147
|
+
else if (perPegCost.lt(numericConstants_1.ZERO)) {
|
|
148
|
+
perPegCost = perPegCost.sub(numericConstants_1.ONE);
|
|
149
|
+
}
|
|
116
150
|
const targetPeg = targetPrice
|
|
117
151
|
.mul(amm.baseAssetReserve)
|
|
118
152
|
.div(amm.quoteAssetReserve)
|
|
119
153
|
.div(numericConstants_1.PRICE_DIV_PEG);
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const Q = amm.pegMultiplier;
|
|
125
|
-
const C = cost.mul(new anchor_1.BN(-1));
|
|
126
|
-
const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
|
|
127
|
-
const pegChangeDirection = targetPeg.sub(Q);
|
|
128
|
-
const useTargetPeg = (deltaQuoteAssetReserves.lt(numericConstants_1.ZERO) && pegChangeDirection.gt(numericConstants_1.ZERO)) ||
|
|
129
|
-
(deltaQuoteAssetReserves.gt(numericConstants_1.ZERO) && pegChangeDirection.lt(numericConstants_1.ZERO));
|
|
130
|
-
if (deltaQuoteAssetReserves.eq(numericConstants_1.ZERO) || useTargetPeg) {
|
|
154
|
+
const pegChangeDirection = targetPeg.sub(amm.pegMultiplier);
|
|
155
|
+
const useTargetPeg = (perPegCost.lt(numericConstants_1.ZERO) && pegChangeDirection.gt(numericConstants_1.ZERO)) ||
|
|
156
|
+
(perPegCost.gt(numericConstants_1.ZERO) && pegChangeDirection.lt(numericConstants_1.ZERO));
|
|
157
|
+
if (perPegCost.eq(numericConstants_1.ZERO) || useTargetPeg) {
|
|
131
158
|
return targetPeg;
|
|
132
159
|
}
|
|
133
|
-
const
|
|
134
|
-
const newPeg =
|
|
160
|
+
const budgetDeltaPeg = budget.mul(numericConstants_1.PEG_PRECISION).div(perPegCost);
|
|
161
|
+
const newPeg = anchor_1.BN.max(numericConstants_1.ONE, amm.pegMultiplier.add(budgetDeltaPeg));
|
|
135
162
|
return newPeg;
|
|
136
163
|
}
|
|
137
164
|
exports.calculateBudgetedPeg = calculateBudgetedPeg;
|
package/lib/math/spotBalance.js
CHANGED
|
@@ -60,7 +60,7 @@ function calculateAssetWeight(balanceAmount, spotMarket, marginCategory) {
|
|
|
60
60
|
assetWeight = (0, margin_1.calculateSizeDiscountAssetWeight)(sizeInAmmReservePrecision, new anchor_1.BN(spotMarket.imfFactor), new anchor_1.BN(spotMarket.initialAssetWeight));
|
|
61
61
|
break;
|
|
62
62
|
case 'Maintenance':
|
|
63
|
-
assetWeight =
|
|
63
|
+
assetWeight = new anchor_1.BN(spotMarket.maintenanceAssetWeight);
|
|
64
64
|
break;
|
|
65
65
|
default:
|
|
66
66
|
assetWeight = new anchor_1.BN(spotMarket.initialAssetWeight);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AdminClient } from './adminClient';
|
|
2
|
+
import { ConfirmOptions, Signer, Transaction } from '@solana/web3.js';
|
|
3
|
+
import { TxSigAndSlot } from './tx/types';
|
|
4
|
+
import { DriftClientConfig } from './driftClientConfig';
|
|
5
|
+
export declare class TestClient extends AdminClient {
|
|
6
|
+
constructor(config: DriftClientConfig);
|
|
7
|
+
sendTransaction(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestClient = void 0;
|
|
4
|
+
const adminClient_1 = require("./adminClient");
|
|
5
|
+
class TestClient extends adminClient_1.AdminClient {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
if (config.accountSubscription.type !== 'polling') {
|
|
8
|
+
throw new Error('Test client must be polling');
|
|
9
|
+
}
|
|
10
|
+
super(config);
|
|
11
|
+
}
|
|
12
|
+
async sendTransaction(tx, additionalSigners, opts, preSigned) {
|
|
13
|
+
const { txSig, slot } = await super.sendTransaction(tx, additionalSigners, opts, preSigned);
|
|
14
|
+
let lastFetchedSlot = this.accountSubscriber.accountLoader.mostRecentSlot;
|
|
15
|
+
while (lastFetchedSlot < slot) {
|
|
16
|
+
await this.fetchAccounts();
|
|
17
|
+
lastFetchedSlot = this.accountSubscriber.accountLoader.mostRecentSlot;
|
|
18
|
+
}
|
|
19
|
+
return { txSig, slot };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.TestClient = TestClient;
|
package/lib/user.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class User {
|
|
|
29
29
|
fetchAccounts(): Promise<void>;
|
|
30
30
|
unsubscribe(): Promise<void>;
|
|
31
31
|
getUserAccount(): UserAccount;
|
|
32
|
+
forceGetUserAccount(): Promise<UserAccount>;
|
|
32
33
|
getUserAccountAndSlot(): DataAndSlot<UserAccount> | undefined;
|
|
33
34
|
/**
|
|
34
35
|
* Gets the user's current position for a given perp market. If the user has no position returns undefined
|
package/lib/user.js
CHANGED
|
@@ -52,6 +52,10 @@ class User {
|
|
|
52
52
|
getUserAccount() {
|
|
53
53
|
return this.accountSubscriber.getUserAccountAndSlot().data;
|
|
54
54
|
}
|
|
55
|
+
async forceGetUserAccount() {
|
|
56
|
+
await this.fetchAccounts();
|
|
57
|
+
return this.accountSubscriber.getUserAccountAndSlot().data;
|
|
58
|
+
}
|
|
55
59
|
getUserAccountAndSlot() {
|
|
56
60
|
return this.accountSubscriber.getUserAccountAndSlot();
|
|
57
61
|
}
|
package/package.json
CHANGED
|
@@ -340,21 +340,44 @@ export class PollingDriftClientAccountSubscriber
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
async addSpotMarket(marketIndex: number): Promise<boolean> {
|
|
343
|
+
const marketPublicKey = await getSpotMarketPublicKey(
|
|
344
|
+
this.program.programId,
|
|
345
|
+
marketIndex
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (this.accountsToPoll.has(marketPublicKey.toString())) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
343
352
|
await this.addSpotMarketAccountToPoll(marketIndex);
|
|
344
|
-
|
|
353
|
+
|
|
354
|
+
const accountToPoll = this.accountsToPoll.get(marketPublicKey.toString());
|
|
355
|
+
|
|
345
356
|
await this.addAccountToAccountLoader(accountToPoll);
|
|
346
357
|
return true;
|
|
347
358
|
}
|
|
348
359
|
|
|
349
360
|
async addPerpMarket(marketIndex: number): Promise<boolean> {
|
|
361
|
+
const marketPublicKey = await getPerpMarketPublicKey(
|
|
362
|
+
this.program.programId,
|
|
363
|
+
marketIndex
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
if (this.accountsToPoll.has(marketPublicKey.toString())) {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
350
370
|
await this.addPerpMarketAccountToPoll(marketIndex);
|
|
351
|
-
const accountToPoll = this.accountsToPoll.get(
|
|
371
|
+
const accountToPoll = this.accountsToPoll.get(marketPublicKey.toString());
|
|
352
372
|
await this.addAccountToAccountLoader(accountToPoll);
|
|
353
373
|
return true;
|
|
354
374
|
}
|
|
355
375
|
|
|
356
376
|
async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
|
|
357
|
-
if (
|
|
377
|
+
if (
|
|
378
|
+
oracleInfo.publicKey.equals(PublicKey.default) ||
|
|
379
|
+
this.oraclesToPoll.has(oracleInfo.publicKey.toString())
|
|
380
|
+
) {
|
|
358
381
|
return true;
|
|
359
382
|
}
|
|
360
383
|
|