@drift-labs/sdk 2.0.14 → 2.0.16
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/README.md +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +0 -3
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +0 -3
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +16 -0
- package/lib/constants/numericConstants.d.ts +3 -0
- package/lib/constants/numericConstants.js +4 -1
- package/lib/dlob/DLOB.d.ts +2 -7
- package/lib/dlob/DLOB.js +84 -166
- package/lib/idl/drift.json +65 -7
- package/lib/math/amm.d.ts +8 -7
- package/lib/math/amm.js +116 -61
- package/lib/math/oracles.d.ts +2 -0
- package/lib/math/oracles.js +23 -1
- package/lib/math/utils.d.ts +2 -1
- package/lib/math/utils.js +5 -1
- package/lib/types.d.ts +6 -2
- package/lib/user.js +10 -6
- package/package.json +1 -1
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +0 -4
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +0 -4
- package/src/adminClient.ts +28 -0
- package/src/constants/numericConstants.ts +7 -0
- package/src/dlob/DLOB.ts +152 -237
- package/src/idl/drift.json +65 -7
- package/src/math/amm.ts +220 -100
- package/src/math/oracles.ts +52 -0
- package/src/math/utils.ts +5 -1
- package/src/types.ts +6 -2
- package/src/user.ts +17 -6
- package/tests/dlob/helpers.ts +4 -2
- package/tests/dlob/test.ts +252 -0
- package/src/events/eventList.js +0 -77
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<img height="120x" src="https://uploads-ssl.webflow.com/611580035ad59b20437eb024/616f97a42f5637c4517d0193_Logo%20(1)%20(1).png" />
|
|
3
3
|
|
|
4
|
-
<h1 style="margin-top:20px;">Drift Protocol
|
|
4
|
+
<h1 style="margin-top:20px;">Drift Protocol v2</h1>
|
|
5
5
|
|
|
6
6
|
<p>
|
|
7
7
|
<a href="https://www.npmjs.com/package/@drift-labs/sdk"><img alt="SDK npm package" src="https://img.shields.io/npm/v/@drift-labs/sdk" /></a>
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
# Drift Protocol
|
|
14
|
+
# Drift Protocol v2
|
|
15
15
|
|
|
16
16
|
This repository provides open source access to Drift's Typescript SDK, Solana Programs, and more.
|
|
17
17
|
|
|
18
18
|
# SDK Guide
|
|
19
19
|
|
|
20
|
-
The technical documentation for the SDK can be found [here](https://drift-labs.github.io/protocol-
|
|
20
|
+
The technical documentation for the SDK can be found [here](https://drift-labs.github.io/protocol-v2/sdk/), and you can visit Drift's general purpose documentation [here](https://docs.drift.trade/sdk-documentation).
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
@@ -201,9 +201,6 @@ class PollingDriftClientAccountSubscriber {
|
|
|
201
201
|
return false;
|
|
202
202
|
}
|
|
203
203
|
async unsubscribe() {
|
|
204
|
-
if (!this.isSubscribed) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
204
|
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
208
205
|
this.accountLoader.removeAccount(accountToPoll.publicKey, accountToPoll.callbackId);
|
|
209
206
|
}
|
|
@@ -83,9 +83,6 @@ class PollingUserStatsAccountSubscriber {
|
|
|
83
83
|
return success;
|
|
84
84
|
}
|
|
85
85
|
async unsubscribe() {
|
|
86
|
-
if (!this.isSubscribed) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
86
|
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
90
87
|
this.accountLoader.removeAccount(accountToPoll.publicKey, accountToPoll.callbackId);
|
|
91
88
|
}
|
package/lib/adminClient.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare class AdminClient extends DriftClient {
|
|
|
27
27
|
updatePerpMarketMaxSpread(perpMarketIndex: number, maxSpread: number): Promise<TransactionSignature>;
|
|
28
28
|
updatePerpFeeStructure(feeStructure: FeeStructure): Promise<TransactionSignature>;
|
|
29
29
|
updateSpotFeeStructure(feeStructure: FeeStructure): Promise<TransactionSignature>;
|
|
30
|
+
updateInitialPctToLiquidate(initialPctToLiquidate: number): Promise<TransactionSignature>;
|
|
31
|
+
updateLiquidationDuration(liquidationDuration: number): Promise<TransactionSignature>;
|
|
30
32
|
updateOracleGuardRails(oracleGuardRails: OracleGuardRails): Promise<TransactionSignature>;
|
|
31
33
|
updateStateSettlementDuration(settlementDuration: number): Promise<TransactionSignature>;
|
|
32
34
|
updateWithdrawGuardThreshold(spotMarketIndex: number, withdrawGuardThreshold: BN): Promise<TransactionSignature>;
|
package/lib/adminClient.js
CHANGED
|
@@ -324,6 +324,22 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
324
324
|
},
|
|
325
325
|
});
|
|
326
326
|
}
|
|
327
|
+
async updateInitialPctToLiquidate(initialPctToLiquidate) {
|
|
328
|
+
return await this.program.rpc.updateInitialPctToLiquidate(initialPctToLiquidate, {
|
|
329
|
+
accounts: {
|
|
330
|
+
admin: this.wallet.publicKey,
|
|
331
|
+
state: await this.getStatePublicKey(),
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async updateLiquidationDuration(liquidationDuration) {
|
|
336
|
+
return await this.program.rpc.updateLiquidationDuration(liquidationDuration, {
|
|
337
|
+
accounts: {
|
|
338
|
+
admin: this.wallet.publicKey,
|
|
339
|
+
state: await this.getStatePublicKey(),
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
}
|
|
327
343
|
async updateOracleGuardRails(oracleGuardRails) {
|
|
328
344
|
return await this.program.rpc.updateOracleGuardRails(oracleGuardRails, {
|
|
329
345
|
accounts: {
|
|
@@ -50,7 +50,10 @@ export declare const PRICE_TO_QUOTE_PRECISION: BN;
|
|
|
50
50
|
export declare const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO: BN;
|
|
51
51
|
export declare const MARGIN_PRECISION: BN;
|
|
52
52
|
export declare const BID_ASK_SPREAD_PRECISION: BN;
|
|
53
|
+
export declare const LIQUIDATION_PCT_PRECISION: BN;
|
|
53
54
|
export declare const ONE_YEAR: BN;
|
|
54
55
|
export declare const QUOTE_SPOT_MARKET_INDEX = 0;
|
|
55
56
|
export declare const LAMPORTS_PRECISION: BN;
|
|
56
57
|
export declare const LAMPORTS_EXP: BN;
|
|
58
|
+
export declare const OPEN_ORDER_MARGIN_REQUIREMENT: BN;
|
|
59
|
+
export declare const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT: BN;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BID_ASK_SPREAD_PRECISION = exports.MARGIN_PRECISION = exports.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO = exports.PRICE_TO_QUOTE_PRECISION = exports.PRICE_DIV_PEG = exports.AMM_TO_QUOTE_PRECISION_RATIO = exports.BASE_PRECISION_EXP = exports.BASE_PRECISION = exports.AMM_RESERVE_PRECISION = exports.PEG_PRECISION = exports.FUNDING_RATE_BUFFER_PRECISION = exports.FUNDING_RATE_PRECISION = exports.PRICE_PRECISION = exports.QUOTE_PRECISION = exports.LIQUIDATION_FEE_PRECISION = exports.SPOT_MARKET_IMF_PRECISION = exports.SPOT_MARKET_IMF_PRECISION_EXP = exports.SPOT_MARKET_BALANCE_PRECISION = exports.SPOT_MARKET_BALANCE_PRECISION_EXP = exports.SPOT_MARKET_WEIGHT_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION_EXP = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION_EXP = exports.SPOT_MARKET_RATE_PRECISION = exports.SPOT_MARKET_RATE_PRECISION_EXP = exports.AMM_RESERVE_PRECISION_EXP = exports.PEG_PRECISION_EXP = exports.FUNDING_RATE_PRECISION_EXP = exports.PRICE_PRECISION_EXP = exports.FUNDING_RATE_BUFFER_PRECISION_EXP = exports.QUOTE_PRECISION_EXP = exports.CONCENTRATION_PRECISION = exports.PERCENTAGE_PRECISION = exports.PERCENTAGE_PRECISION_EXP = exports.MAX_LEVERAGE = exports.TEN_MILLION = exports.BN_MAX = exports.TEN_THOUSAND = exports.TEN = exports.NINE = exports.EIGHT = exports.SEVEN = exports.SIX = exports.FIVE = exports.FOUR = exports.THREE = exports.TWO = exports.ONE = exports.ZERO = void 0;
|
|
4
|
-
exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = void 0;
|
|
4
|
+
exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = exports.LIQUIDATION_PCT_PRECISION = void 0;
|
|
5
5
|
const web3_js_1 = require("@solana/web3.js");
|
|
6
6
|
const __1 = require("../");
|
|
7
7
|
exports.ZERO = new __1.BN(0);
|
|
@@ -54,7 +54,10 @@ exports.PRICE_TO_QUOTE_PRECISION = exports.PRICE_PRECISION.div(exports.QUOTE_PRE
|
|
|
54
54
|
exports.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO = exports.AMM_RESERVE_PRECISION.mul(exports.PEG_PRECISION).div(exports.QUOTE_PRECISION); // 10^9
|
|
55
55
|
exports.MARGIN_PRECISION = exports.TEN_THOUSAND;
|
|
56
56
|
exports.BID_ASK_SPREAD_PRECISION = new __1.BN(1000000); // 10^6
|
|
57
|
+
exports.LIQUIDATION_PCT_PRECISION = exports.TEN_THOUSAND;
|
|
57
58
|
exports.ONE_YEAR = new __1.BN(31536000);
|
|
58
59
|
exports.QUOTE_SPOT_MARKET_INDEX = 0;
|
|
59
60
|
exports.LAMPORTS_PRECISION = new __1.BN(web3_js_1.LAMPORTS_PER_SOL);
|
|
60
61
|
exports.LAMPORTS_EXP = new __1.BN(Math.log10(web3_js_1.LAMPORTS_PER_SOL));
|
|
62
|
+
exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.QUOTE_PRECISION.div(new __1.BN(100));
|
|
63
|
+
exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new __1.BN(-25).mul(exports.QUOTE_PRECISION);
|
package/lib/dlob/DLOB.d.ts
CHANGED
|
@@ -29,7 +29,6 @@ export declare type NodeToFill = {
|
|
|
29
29
|
export declare type NodeToTrigger = {
|
|
30
30
|
node: TriggerOrderNode;
|
|
31
31
|
};
|
|
32
|
-
declare type Side = 'ask' | 'bid' | 'both' | 'nocross';
|
|
33
32
|
export declare class DLOB {
|
|
34
33
|
openOrders: Map<MarketTypeStr, Set<string>>;
|
|
35
34
|
orderLists: Map<MarketTypeStr, Map<number, MarketNodeLists>>;
|
|
@@ -59,7 +58,7 @@ export declare class DLOB {
|
|
|
59
58
|
findNodesToFill(marketIndex: number, fallbackBid: BN | undefined, fallbackAsk: BN | undefined, slot: number, ts: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
60
59
|
findLimitOrderNodesToFill(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, isAmmPaused: boolean, fallbackAsk: BN | undefined, fallbackBid: BN | undefined): NodeToFill[];
|
|
61
60
|
findMarketNodesToFill(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, isAmmPaused: boolean, fallbackAsk: BN | undefined, fallbackBid?: BN | undefined): NodeToFill[];
|
|
62
|
-
findMarketNodesCrossingLimitNodes(slot: number, oraclePriceData: OraclePriceData, takerNodeGenerator: Generator<DLOBNode>,
|
|
61
|
+
findMarketNodesCrossingLimitNodes(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, takerNodeGenerator: Generator<DLOBNode>, makerNodeGeneratorFn: (marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData) => Generator<DLOBNode>, doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean): NodeToFill[];
|
|
63
62
|
findNodesCrossingFallbackLiquidity(marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, nodeGenerator: Generator<DLOBNode>, fallbackPrice: BN, doesCross: (nodePrice: BN | undefined, fallbackPrice: BN) => boolean): NodeToFill[];
|
|
64
63
|
findExpiredNodesToFill(marketIndex: number, ts: number, marketType: MarketType): NodeToFill[];
|
|
65
64
|
findJitAuctionNodesToFill(marketIndex: number, slot: number, marketType: MarketType): NodeToFill[];
|
|
@@ -70,14 +69,10 @@ export declare class DLOB {
|
|
|
70
69
|
getLimitBids(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
71
70
|
getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
72
71
|
getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
73
|
-
findCrossingLimitOrders(
|
|
74
|
-
crossingNodes: NodeToFill[];
|
|
75
|
-
exhaustedSide: Side;
|
|
76
|
-
};
|
|
72
|
+
findCrossingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
77
73
|
determineMakerAndTaker(askNode: DLOBNode, bidNode: DLOBNode): {
|
|
78
74
|
takerNode: DLOBNode;
|
|
79
75
|
makerNode: DLOBNode;
|
|
80
|
-
makerSide: Side;
|
|
81
76
|
};
|
|
82
77
|
getBestAsk(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
|
83
78
|
getBestBid(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -189,36 +189,12 @@ class DLOB {
|
|
|
189
189
|
}
|
|
190
190
|
findLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
|
|
191
191
|
const nodesToFill = new Array();
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
let nextBid = bidGenerator.next();
|
|
196
|
-
// First try to find orders that cross
|
|
197
|
-
while (!nextAsk.done && !nextBid.done) {
|
|
198
|
-
const { crossingNodes, exhaustedSide } = this.findCrossingLimitOrders(nextAsk.value, nextBid.value, oraclePriceData, slot);
|
|
199
|
-
if (exhaustedSide === 'bid') {
|
|
200
|
-
nextBid = bidGenerator.next();
|
|
201
|
-
}
|
|
202
|
-
else if (exhaustedSide === 'ask') {
|
|
203
|
-
nextAsk = askGenerator.next();
|
|
204
|
-
}
|
|
205
|
-
else if (exhaustedSide === 'both') {
|
|
206
|
-
nextBid = bidGenerator.next();
|
|
207
|
-
nextAsk = askGenerator.next();
|
|
208
|
-
}
|
|
209
|
-
else if (exhaustedSide === 'nocross') {
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
console.error(`invalid exhaustedSide: ${exhaustedSide}`);
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
for (const crossingNode of crossingNodes) {
|
|
217
|
-
nodesToFill.push(crossingNode);
|
|
218
|
-
}
|
|
192
|
+
const crossingNodes = this.findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData);
|
|
193
|
+
for (const crossingNode of crossingNodes) {
|
|
194
|
+
nodesToFill.push(crossingNode);
|
|
219
195
|
}
|
|
220
196
|
if (fallbackBid && !isAmmPaused) {
|
|
221
|
-
askGenerator = this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData);
|
|
197
|
+
const askGenerator = this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData);
|
|
222
198
|
const asksCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, askGenerator, fallbackBid, (askPrice, fallbackPrice) => {
|
|
223
199
|
return askPrice.lte(fallbackPrice);
|
|
224
200
|
});
|
|
@@ -227,7 +203,7 @@ class DLOB {
|
|
|
227
203
|
}
|
|
228
204
|
}
|
|
229
205
|
if (fallbackAsk && !isAmmPaused) {
|
|
230
|
-
bidGenerator = this.getLimitBids(marketIndex, slot, marketType, oraclePriceData);
|
|
206
|
+
const bidGenerator = this.getLimitBids(marketIndex, slot, marketType, oraclePriceData);
|
|
231
207
|
const bidsCrossingFallback = this.findNodesCrossingFallbackLiquidity(marketType, slot, oraclePriceData, bidGenerator, fallbackAsk, (bidPrice, fallbackPrice) => {
|
|
232
208
|
return bidPrice.gte(fallbackPrice);
|
|
233
209
|
});
|
|
@@ -240,8 +216,7 @@ class DLOB {
|
|
|
240
216
|
findMarketNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, fallbackAsk, fallbackBid) {
|
|
241
217
|
const nodesToFill = new Array();
|
|
242
218
|
let marketOrderGenerator = this.getMarketAsks(marketIndex, marketType);
|
|
243
|
-
|
|
244
|
-
const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(slot, oraclePriceData, marketOrderGenerator, limitOrderGenerator, (takerPrice, makerPrice) => {
|
|
219
|
+
const marketAsksCrossingBids = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getLimitBids.bind(this), (takerPrice, makerPrice) => {
|
|
245
220
|
return takerPrice === undefined || takerPrice.lte(makerPrice);
|
|
246
221
|
});
|
|
247
222
|
for (const marketAskCrossingBid of marketAsksCrossingBids) {
|
|
@@ -257,8 +232,7 @@ class DLOB {
|
|
|
257
232
|
}
|
|
258
233
|
}
|
|
259
234
|
marketOrderGenerator = this.getMarketBids(marketIndex, marketType);
|
|
260
|
-
|
|
261
|
-
const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(slot, oraclePriceData, marketOrderGenerator, limitOrderGenerator, (takerPrice, fallbackPrice) => {
|
|
235
|
+
const marketBidsToFill = this.findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, marketOrderGenerator, this.getLimitAsks.bind(this), (takerPrice, fallbackPrice) => {
|
|
262
236
|
return takerPrice === undefined || takerPrice.gte(fallbackPrice);
|
|
263
237
|
});
|
|
264
238
|
for (const marketBidToFill of marketBidsToFill) {
|
|
@@ -275,57 +249,46 @@ class DLOB {
|
|
|
275
249
|
}
|
|
276
250
|
return nodesToFill;
|
|
277
251
|
}
|
|
278
|
-
findMarketNodesCrossingLimitNodes(slot, oraclePriceData, takerNodeGenerator,
|
|
252
|
+
findMarketNodesCrossingLimitNodes(marketIndex, slot, marketType, oraclePriceData, takerNodeGenerator, makerNodeGeneratorFn, doesCross) {
|
|
279
253
|
const nodesToFill = new Array();
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (sameAuthority) {
|
|
290
|
-
if (Math.random() < 0.5) {
|
|
291
|
-
nextTakerNode = takerNodeGenerator.next();
|
|
254
|
+
for (const takerNode of takerNodeGenerator) {
|
|
255
|
+
const makerNodeGenerator = makerNodeGeneratorFn(marketIndex, slot, marketType, oraclePriceData);
|
|
256
|
+
for (const makerNode of makerNodeGenerator) {
|
|
257
|
+
const bidUserAuthority = this.userMap.getUserAuthority(makerNode.userAccount.toString());
|
|
258
|
+
const askUserAuthority = this.userMap.getUserAuthority(takerNode.userAccount.toString());
|
|
259
|
+
// Can't match orders from the same authority
|
|
260
|
+
const sameAuthority = bidUserAuthority.equals(askUserAuthority);
|
|
261
|
+
if (sameAuthority) {
|
|
262
|
+
continue;
|
|
292
263
|
}
|
|
293
|
-
|
|
294
|
-
|
|
264
|
+
const makerPrice = makerNode.getPrice(oraclePriceData, slot);
|
|
265
|
+
const takerPrice = takerNode.getPrice(oraclePriceData, slot);
|
|
266
|
+
const ordersCross = doesCross(takerPrice, makerPrice);
|
|
267
|
+
if (!ordersCross) {
|
|
268
|
+
// market orders aren't sorted by price, they are sorted by time, so we need to traverse
|
|
269
|
+
// through all of em
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
nodesToFill.push({
|
|
273
|
+
node: takerNode,
|
|
274
|
+
makerNode: makerNode,
|
|
275
|
+
});
|
|
276
|
+
const makerOrder = makerNode.order;
|
|
277
|
+
const takerOrder = takerNode.order;
|
|
278
|
+
const makerBaseRemaining = makerOrder.baseAssetAmount.sub(makerOrder.baseAssetAmountFilled);
|
|
279
|
+
const takerBaseRemaining = takerOrder.baseAssetAmount.sub(takerOrder.baseAssetAmountFilled);
|
|
280
|
+
const baseFilled = __1.BN.min(makerBaseRemaining, takerBaseRemaining);
|
|
281
|
+
const newMakerOrder = { ...makerOrder };
|
|
282
|
+
newMakerOrder.baseAssetAmountFilled =
|
|
283
|
+
makerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
284
|
+
this.getListForOrder(newMakerOrder).update(newMakerOrder, makerNode.userAccount);
|
|
285
|
+
const newTakerOrder = { ...takerOrder };
|
|
286
|
+
newTakerOrder.baseAssetAmountFilled =
|
|
287
|
+
takerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
288
|
+
this.getListForOrder(newTakerOrder).update(newTakerOrder, takerNode.userAccount);
|
|
289
|
+
if (newTakerOrder.baseAssetAmountFilled.eq(takerOrder.baseAssetAmount)) {
|
|
290
|
+
break;
|
|
295
291
|
}
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
const makerPrice = makerNode.getPrice(oraclePriceData, slot);
|
|
299
|
-
const takerPrice = takerNode.getPrice(oraclePriceData, slot);
|
|
300
|
-
const ordersCross = doesCross(takerPrice, makerPrice);
|
|
301
|
-
if (!ordersCross) {
|
|
302
|
-
// market orders aren't sorted by price, they are sorted by time, so we need to traverse
|
|
303
|
-
// through all of em
|
|
304
|
-
nextTakerNode = takerNodeGenerator.next();
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
nodesToFill.push({
|
|
308
|
-
node: takerNode,
|
|
309
|
-
makerNode: makerNode,
|
|
310
|
-
});
|
|
311
|
-
const makerOrder = makerNode.order;
|
|
312
|
-
const takerOrder = takerNode.order;
|
|
313
|
-
const makerBaseRemaining = makerOrder.baseAssetAmount.sub(makerOrder.baseAssetAmountFilled);
|
|
314
|
-
const takerBaseRemaining = takerOrder.baseAssetAmount.sub(takerOrder.baseAssetAmountFilled);
|
|
315
|
-
const baseFilled = __1.BN.min(makerBaseRemaining, takerBaseRemaining);
|
|
316
|
-
const newMakerOrder = { ...makerOrder };
|
|
317
|
-
newMakerOrder.baseAssetAmountFilled =
|
|
318
|
-
makerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
319
|
-
this.getListForOrder(newMakerOrder).update(newMakerOrder, makerNode.userAccount);
|
|
320
|
-
if (newMakerOrder.baseAssetAmountFilled.eq(makerOrder.baseAssetAmount)) {
|
|
321
|
-
nextMakerNode = makerNodeGenerator.next();
|
|
322
|
-
}
|
|
323
|
-
const newTakerOrder = { ...takerOrder };
|
|
324
|
-
newTakerOrder.baseAssetAmountFilled =
|
|
325
|
-
takerOrder.baseAssetAmountFilled.add(baseFilled);
|
|
326
|
-
this.getListForOrder(newTakerOrder).update(newTakerOrder, takerNode.userAccount);
|
|
327
|
-
if (newTakerOrder.baseAssetAmountFilled.eq(takerOrder.baseAssetAmount)) {
|
|
328
|
-
nextTakerNode = takerNodeGenerator.next();
|
|
329
292
|
}
|
|
330
293
|
}
|
|
331
294
|
return nodesToFill;
|
|
@@ -548,117 +511,72 @@ class DLOB {
|
|
|
548
511
|
return bestPrice.gt(currentPrice);
|
|
549
512
|
});
|
|
550
513
|
}
|
|
551
|
-
findCrossingLimitOrders(
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
exhaustedSide = 'ask';
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
exhaustedSide = 'bid';
|
|
588
|
-
}
|
|
589
|
-
// update the orders on DLOB as if they were fill - so we don't try to match them on the next iteration
|
|
590
|
-
// NOTE: if something goes wrong during the actual fill (transaction fails, i.e. due to orders already being filled)
|
|
591
|
-
// then we risk having a mismatch between this local DLOB and the actual DLOB state on the blockchain. This isn't
|
|
592
|
-
// a problem in the current implementation because we construct a new DLOB from the blockchain state every time, rather
|
|
593
|
-
// than updating the existing DLOB based on events.
|
|
594
|
-
if (exhaustedSide === 'ask') {
|
|
595
|
-
// bid partially filled
|
|
596
|
-
const newBidOrder = { ...bidOrder };
|
|
597
|
-
newBidOrder.baseAssetAmountFilled =
|
|
598
|
-
bidOrder.baseAssetAmountFilled.add(askBaseRemaining);
|
|
599
|
-
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
600
|
-
// ask completely filled
|
|
601
|
-
const newAskOrder = { ...askOrder };
|
|
602
|
-
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
603
|
-
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
604
|
-
}
|
|
605
|
-
else if (exhaustedSide === 'bid') {
|
|
606
|
-
// ask partially filled
|
|
607
|
-
const newAskOrder = { ...askOrder };
|
|
608
|
-
newAskOrder.baseAssetAmountFilled =
|
|
609
|
-
askOrder.baseAssetAmountFilled.add(bidBaseRemaining);
|
|
610
|
-
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
611
|
-
// bid completely filled
|
|
612
|
-
const newBidOrder = { ...bidOrder };
|
|
613
|
-
newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
614
|
-
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
615
|
-
}
|
|
616
|
-
else {
|
|
617
|
-
// both completely filled
|
|
618
|
-
const newBidOrder = { ...bidOrder };
|
|
619
|
-
newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
620
|
-
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
621
|
-
const newAskOrder = { ...askOrder };
|
|
622
|
-
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
623
|
-
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
624
|
-
}
|
|
625
|
-
return {
|
|
626
|
-
crossingNodes: [
|
|
627
|
-
{
|
|
514
|
+
findCrossingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
|
|
515
|
+
const nodesToFill = new Array();
|
|
516
|
+
for (const askNode of this.getLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
|
|
517
|
+
for (const bidNode of this.getLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
|
|
518
|
+
const bidPrice = bidNode.getPrice(oraclePriceData, slot);
|
|
519
|
+
const askPrice = askNode.getPrice(oraclePriceData, slot);
|
|
520
|
+
// orders don't cross - we're done walking the book
|
|
521
|
+
if (bidPrice.lt(askPrice)) {
|
|
522
|
+
return nodesToFill;
|
|
523
|
+
}
|
|
524
|
+
const bidOrder = bidNode.order;
|
|
525
|
+
const askOrder = askNode.order;
|
|
526
|
+
const bidUserAuthority = this.userMap.getUserAuthority(bidNode.userAccount.toString());
|
|
527
|
+
const askUserAuthority = this.userMap.getUserAuthority(askNode.userAccount.toString());
|
|
528
|
+
// Can't match orders from the same authority
|
|
529
|
+
const sameAuthority = bidUserAuthority.equals(askUserAuthority);
|
|
530
|
+
if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
|
|
534
|
+
const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
|
|
535
|
+
const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
|
|
536
|
+
const baseFilled = __1.BN.min(bidBaseRemaining, askBaseRemaining);
|
|
537
|
+
const newBidOrder = { ...bidOrder };
|
|
538
|
+
newBidOrder.baseAssetAmountFilled =
|
|
539
|
+
bidOrder.baseAssetAmountFilled.add(baseFilled);
|
|
540
|
+
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
541
|
+
// ask completely filled
|
|
542
|
+
const newAskOrder = { ...askOrder };
|
|
543
|
+
newAskOrder.baseAssetAmountFilled =
|
|
544
|
+
askOrder.baseAssetAmountFilled.add(baseFilled);
|
|
545
|
+
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
546
|
+
nodesToFill.push({
|
|
628
547
|
node: takerNode,
|
|
629
548
|
makerNode: makerNode,
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
549
|
+
});
|
|
550
|
+
if (newAskOrder.baseAssetAmount.eq(newAskOrder.baseAssetAmountFilled)) {
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return nodesToFill;
|
|
634
556
|
}
|
|
635
557
|
determineMakerAndTaker(askNode, bidNode) {
|
|
636
558
|
if (bidNode.order.postOnly) {
|
|
637
559
|
return {
|
|
638
560
|
takerNode: askNode,
|
|
639
561
|
makerNode: bidNode,
|
|
640
|
-
makerSide: 'bid',
|
|
641
562
|
};
|
|
642
563
|
}
|
|
643
564
|
else if (askNode.order.postOnly) {
|
|
644
565
|
return {
|
|
645
566
|
takerNode: bidNode,
|
|
646
567
|
makerNode: askNode,
|
|
647
|
-
makerSide: 'ask',
|
|
648
568
|
};
|
|
649
569
|
}
|
|
650
570
|
else if (askNode.order.slot.lt(bidNode.order.slot)) {
|
|
651
571
|
return {
|
|
652
572
|
takerNode: bidNode,
|
|
653
573
|
makerNode: askNode,
|
|
654
|
-
makerSide: 'ask',
|
|
655
574
|
};
|
|
656
575
|
}
|
|
657
576
|
else {
|
|
658
577
|
return {
|
|
659
578
|
takerNode: askNode,
|
|
660
579
|
makerNode: bidNode,
|
|
661
|
-
makerSide: 'bid',
|
|
662
580
|
};
|
|
663
581
|
}
|
|
664
582
|
}
|
package/lib/idl/drift.json
CHANGED
|
@@ -1432,7 +1432,7 @@
|
|
|
1432
1432
|
},
|
|
1433
1433
|
{
|
|
1434
1434
|
"name": "spotMarket",
|
|
1435
|
-
"isMut":
|
|
1435
|
+
"isMut": true,
|
|
1436
1436
|
"isSigner": false
|
|
1437
1437
|
},
|
|
1438
1438
|
{
|
|
@@ -3233,6 +3233,48 @@
|
|
|
3233
3233
|
}
|
|
3234
3234
|
]
|
|
3235
3235
|
},
|
|
3236
|
+
{
|
|
3237
|
+
"name": "updateInitialPctToLiquidate",
|
|
3238
|
+
"accounts": [
|
|
3239
|
+
{
|
|
3240
|
+
"name": "admin",
|
|
3241
|
+
"isMut": false,
|
|
3242
|
+
"isSigner": true
|
|
3243
|
+
},
|
|
3244
|
+
{
|
|
3245
|
+
"name": "state",
|
|
3246
|
+
"isMut": true,
|
|
3247
|
+
"isSigner": false
|
|
3248
|
+
}
|
|
3249
|
+
],
|
|
3250
|
+
"args": [
|
|
3251
|
+
{
|
|
3252
|
+
"name": "initialPctToLiquidate",
|
|
3253
|
+
"type": "u16"
|
|
3254
|
+
}
|
|
3255
|
+
]
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
"name": "updateLiquidationDuration",
|
|
3259
|
+
"accounts": [
|
|
3260
|
+
{
|
|
3261
|
+
"name": "admin",
|
|
3262
|
+
"isMut": false,
|
|
3263
|
+
"isSigner": true
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
"name": "state",
|
|
3267
|
+
"isMut": true,
|
|
3268
|
+
"isSigner": false
|
|
3269
|
+
}
|
|
3270
|
+
],
|
|
3271
|
+
"args": [
|
|
3272
|
+
{
|
|
3273
|
+
"name": "liquidationDuration",
|
|
3274
|
+
"type": "u8"
|
|
3275
|
+
}
|
|
3276
|
+
]
|
|
3277
|
+
},
|
|
3236
3278
|
{
|
|
3237
3279
|
"name": "updateOracleGuardRails",
|
|
3238
3280
|
"accounts": [
|
|
@@ -4320,12 +4362,20 @@
|
|
|
4320
4362
|
"defined": "ExchangeStatus"
|
|
4321
4363
|
}
|
|
4322
4364
|
},
|
|
4365
|
+
{
|
|
4366
|
+
"name": "liquidationDuration",
|
|
4367
|
+
"type": "u8"
|
|
4368
|
+
},
|
|
4369
|
+
{
|
|
4370
|
+
"name": "initialPctToLiquidate",
|
|
4371
|
+
"type": "u16"
|
|
4372
|
+
},
|
|
4323
4373
|
{
|
|
4324
4374
|
"name": "padding",
|
|
4325
4375
|
"type": {
|
|
4326
4376
|
"array": [
|
|
4327
4377
|
"u8",
|
|
4328
|
-
|
|
4378
|
+
14
|
|
4329
4379
|
]
|
|
4330
4380
|
}
|
|
4331
4381
|
}
|
|
@@ -4420,8 +4470,8 @@
|
|
|
4420
4470
|
"type": "u64"
|
|
4421
4471
|
},
|
|
4422
4472
|
{
|
|
4423
|
-
"name": "
|
|
4424
|
-
"type": "
|
|
4473
|
+
"name": "liquidationStartSlot",
|
|
4474
|
+
"type": "u64"
|
|
4425
4475
|
},
|
|
4426
4476
|
{
|
|
4427
4477
|
"name": "nextOrderId",
|
|
@@ -5596,11 +5646,11 @@
|
|
|
5596
5646
|
"type": "u64"
|
|
5597
5647
|
},
|
|
5598
5648
|
{
|
|
5599
|
-
"name": "
|
|
5649
|
+
"name": "lastBaseAssetAmountPerLp",
|
|
5600
5650
|
"type": "i64"
|
|
5601
5651
|
},
|
|
5602
5652
|
{
|
|
5603
|
-
"name": "
|
|
5653
|
+
"name": "lastQuoteAssetAmountPerLp",
|
|
5604
5654
|
"type": "i64"
|
|
5605
5655
|
},
|
|
5606
5656
|
{
|
|
@@ -6016,6 +6066,9 @@
|
|
|
6016
6066
|
},
|
|
6017
6067
|
{
|
|
6018
6068
|
"name": "OrderFillWithSerum"
|
|
6069
|
+
},
|
|
6070
|
+
{
|
|
6071
|
+
"name": "NoBorrowLiquidity"
|
|
6019
6072
|
}
|
|
6020
6073
|
]
|
|
6021
6074
|
}
|
|
@@ -8454,11 +8507,16 @@
|
|
|
8454
8507
|
},
|
|
8455
8508
|
{
|
|
8456
8509
|
"code": 6219,
|
|
8510
|
+
"name": "UnableToGetLimitPrice",
|
|
8511
|
+
"msg": "Unable To Get Limit Price"
|
|
8512
|
+
},
|
|
8513
|
+
{
|
|
8514
|
+
"code": 6220,
|
|
8457
8515
|
"name": "InvalidLiquidation",
|
|
8458
8516
|
"msg": "Invalid Liquidation"
|
|
8459
8517
|
},
|
|
8460
8518
|
{
|
|
8461
|
-
"code":
|
|
8519
|
+
"code": 6221,
|
|
8462
8520
|
"name": "SpotFulfillmentConfigDisabled",
|
|
8463
8521
|
"msg": "Spot Fulfullment Config Disabled"
|
|
8464
8522
|
}
|
package/lib/math/amm.d.ts
CHANGED
|
@@ -34,15 +34,16 @@ export declare type AssetType = 'quote' | 'base';
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function calculateAmmReservesAfterSwap(amm: Pick<AMM, 'pegMultiplier' | 'quoteAssetReserve' | 'sqrtK' | 'baseAssetReserve'>, inputAssetType: AssetType, swapAmount: BN, swapDirection: SwapDirection): [BN, BN];
|
|
36
36
|
export declare function calculateMarketOpenBidAsk(baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN): [BN, BN];
|
|
37
|
-
export declare function calculateInventoryScale(
|
|
37
|
+
export declare function calculateInventoryScale(baseAssetAmountWithAmm: BN, baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN, directionalSpread: number, maxSpread: number): number;
|
|
38
38
|
export declare function calculateEffectiveLeverage(baseSpread: number, quoteAssetReserve: BN, terminalQuoteAssetReserve: BN, pegMultiplier: BN, netBaseAssetAmount: BN, reservePrice: BN, totalFeeMinusDistributions: BN): number;
|
|
39
39
|
export declare function calculateMaxSpread(marginRatioInitial: number): number;
|
|
40
|
-
export declare function
|
|
41
|
-
export declare function
|
|
42
|
-
export declare function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
export declare function calculateVolSpreadBN(lastOracleConfPct: BN, reservePrice: BN, markStd: BN, oracleStd: BN, longIntensity: BN, shortIntensity: BN, volume24H: BN): [BN, BN];
|
|
41
|
+
export declare function calculateSpreadBN(baseSpread: number, lastOracleReservePriceSpreadPct: BN, lastOracleConfPct: BN, maxSpread: number, quoteAssetReserve: BN, terminalQuoteAssetReserve: BN, pegMultiplier: BN, baseAssetAmountWithAmm: BN, reservePrice: BN, totalFeeMinusDistributions: BN, netRevenueSinceLastFunding: BN, baseAssetReserve: BN, minBaseAssetReserve: BN, maxBaseAssetReserve: BN, markStd: BN, oracleStd: BN, longIntensity: BN, shortIntensity: BN, volume24H: BN): [number, number];
|
|
42
|
+
export declare function calculateSpread(amm: AMM, oraclePriceData: OraclePriceData): [number, number];
|
|
43
|
+
export declare function calculateSpreadReserves(amm: AMM, oraclePriceData: OraclePriceData): {
|
|
44
|
+
baseAssetReserve: any;
|
|
45
|
+
quoteAssetReserve: any;
|
|
46
|
+
}[];
|
|
46
47
|
/**
|
|
47
48
|
* Helper function calculating constant product curve output. Agnostic to whether input asset is quote or base
|
|
48
49
|
*
|