@drift-labs/sdk 0.2.0-master.30 → 0.2.0-master.31
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/admin.d.ts +11 -7
- package/lib/admin.js +50 -12
- package/lib/clearingHouse.d.ts +26 -23
- package/lib/clearingHouse.js +290 -652
- package/lib/clearingHouseUser.d.ts +1 -1
- package/lib/clearingHouseUser.js +9 -12
- package/lib/config.js +1 -1
- package/lib/constants/numericConstants.d.ts +7 -0
- package/lib/constants/numericConstants.js +8 -1
- package/lib/constants/spotMarkets.js +4 -4
- package/lib/dlob/DLOB.d.ts +6 -5
- package/lib/dlob/DLOB.js +130 -88
- package/lib/dlob/DLOBNode.d.ts +2 -0
- package/lib/dlob/DLOBNode.js +3 -0
- package/lib/dlob/NodeList.d.ts +1 -1
- package/lib/dlob/NodeList.js +5 -4
- package/lib/events/types.d.ts +2 -1
- package/lib/events/types.js +1 -0
- package/lib/factory/bigNum.d.ts +1 -0
- package/lib/factory/bigNum.js +14 -0
- package/lib/idl/clearing_house.json +1091 -611
- package/lib/math/amm.d.ts +2 -2
- package/lib/math/amm.js +31 -28
- package/lib/math/market.d.ts +1 -1
- package/lib/math/market.js +6 -6
- package/lib/math/spotBalance.js +7 -8
- package/lib/math/trade.js +11 -11
- package/lib/types.d.ts +117 -40
- package/lib/types.js +33 -3
- package/package.json +4 -2
- package/src/admin.ts +129 -29
- package/src/clearingHouse.ts +380 -787
- package/src/clearingHouseUser.ts +11 -14
- package/src/config.ts +1 -1
- package/src/constants/numericConstants.ts +7 -0
- package/src/constants/spotMarkets.ts +5 -4
- package/src/dlob/DLOB.ts +188 -103
- package/src/dlob/DLOBNode.ts +5 -0
- package/src/dlob/NodeList.ts +9 -5
- package/src/events/types.ts +3 -0
- package/src/factory/bigNum.ts +23 -0
- package/src/idl/clearing_house.json +1091 -611
- package/src/math/amm.ts +42 -29
- package/src/math/market.ts +9 -4
- package/src/math/spotBalance.ts +11 -8
- package/src/math/trade.ts +11 -11
- package/src/types.ts +81 -40
- package/tests/bn/test.ts +12 -7
- package/tests/dlob/helpers.ts +56 -33
- package/tests/dlob/test.ts +1227 -404
package/lib/clearingHouseUser.js
CHANGED
|
@@ -65,7 +65,7 @@ class ClearingHouseUser {
|
|
|
65
65
|
getEmptyPosition(marketIndex) {
|
|
66
66
|
return {
|
|
67
67
|
baseAssetAmount: numericConstants_1.ZERO,
|
|
68
|
-
remainderBaseAssetAmount:
|
|
68
|
+
remainderBaseAssetAmount: 0,
|
|
69
69
|
lastCumulativeFundingRate: numericConstants_1.ZERO,
|
|
70
70
|
marketIndex,
|
|
71
71
|
quoteAssetAmount: numericConstants_1.ZERO,
|
|
@@ -75,7 +75,6 @@ class ClearingHouseUser {
|
|
|
75
75
|
openAsks: numericConstants_1.ZERO,
|
|
76
76
|
settledPnl: numericConstants_1.ZERO,
|
|
77
77
|
lpShares: numericConstants_1.ZERO,
|
|
78
|
-
lastFeePerLp: numericConstants_1.ZERO,
|
|
79
78
|
lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
|
|
80
79
|
lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
|
|
81
80
|
};
|
|
@@ -89,7 +88,7 @@ class ClearingHouseUser {
|
|
|
89
88
|
* @returns Order
|
|
90
89
|
*/
|
|
91
90
|
getOrder(orderId) {
|
|
92
|
-
return this.getUserAccount().orders.find((order) => order.orderId
|
|
91
|
+
return this.getUserAccount().orders.find((order) => order.orderId === orderId);
|
|
93
92
|
}
|
|
94
93
|
/**
|
|
95
94
|
* @param userOrderId
|
|
@@ -134,15 +133,13 @@ class ClearingHouseUser {
|
|
|
134
133
|
return [standardizedAmount, remainder];
|
|
135
134
|
}
|
|
136
135
|
const [standardizedBaa, remainderBaa] = standardize(deltaBaa, market.amm.baseAssetAmountStepSize);
|
|
137
|
-
position.remainderBaseAssetAmount
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.abs()
|
|
141
|
-
.gte(market.amm.baseAssetAmountStepSize)) {
|
|
136
|
+
position.remainderBaseAssetAmount += remainderBaa.toNumber();
|
|
137
|
+
if (Math.abs(position.remainderBaseAssetAmount) >
|
|
138
|
+
market.amm.baseAssetAmountStepSize.toNumber()) {
|
|
142
139
|
const [newStandardizedBaa, newRemainderBaa] = standardize(position.remainderBaseAssetAmount, market.amm.baseAssetAmountStepSize);
|
|
143
140
|
position.baseAssetAmount =
|
|
144
141
|
position.baseAssetAmount.add(newStandardizedBaa);
|
|
145
|
-
position.remainderBaseAssetAmount = newRemainderBaa;
|
|
142
|
+
position.remainderBaseAssetAmount = newRemainderBaa.toNumber();
|
|
146
143
|
}
|
|
147
144
|
let updateType;
|
|
148
145
|
if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
|
|
@@ -300,7 +297,7 @@ class ClearingHouseUser {
|
|
|
300
297
|
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
|
|
301
298
|
let newTotalLiabilityValue = totalLiabilityValue;
|
|
302
299
|
if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
|
|
303
|
-
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
|
|
300
|
+
const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount.abs(), oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
|
|
304
301
|
newTotalLiabilityValue =
|
|
305
302
|
newTotalLiabilityValue.add(baseLiabilityValue);
|
|
306
303
|
}
|
|
@@ -310,6 +307,7 @@ class ClearingHouseUser {
|
|
|
310
307
|
weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
|
|
311
308
|
}
|
|
312
309
|
const weightedTokenValue = worstCaseQuoteTokenAmount
|
|
310
|
+
.abs()
|
|
313
311
|
.mul(weight)
|
|
314
312
|
.div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
|
|
315
313
|
newTotalLiabilityValue =
|
|
@@ -615,7 +613,7 @@ class ClearingHouseUser {
|
|
|
615
613
|
const proposedPerpPosition = {
|
|
616
614
|
marketIndex: perpPosition.marketIndex,
|
|
617
615
|
baseAssetAmount: proposedBaseAssetAmount,
|
|
618
|
-
remainderBaseAssetAmount:
|
|
616
|
+
remainderBaseAssetAmount: 0,
|
|
619
617
|
quoteAssetAmount: new _1.BN(0),
|
|
620
618
|
lastCumulativeFundingRate: numericConstants_1.ZERO,
|
|
621
619
|
quoteEntryAmount: new _1.BN(0),
|
|
@@ -624,7 +622,6 @@ class ClearingHouseUser {
|
|
|
624
622
|
openAsks: new _1.BN(0),
|
|
625
623
|
settledPnl: numericConstants_1.ZERO,
|
|
626
624
|
lpShares: numericConstants_1.ZERO,
|
|
627
|
-
lastFeePerLp: numericConstants_1.ZERO,
|
|
628
625
|
lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
|
|
629
626
|
lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
|
|
630
627
|
};
|
package/lib/config.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.configs = {
|
|
|
7
7
|
devnet: {
|
|
8
8
|
ENV: 'devnet',
|
|
9
9
|
PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
|
|
10
|
-
CLEARING_HOUSE_PROGRAM_ID: '
|
|
10
|
+
CLEARING_HOUSE_PROGRAM_ID: 'DUZwKJKAk2C9S88BYvQzck1M1i5hySQjxB4zW6tJ29Nw',
|
|
11
11
|
USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
|
|
12
12
|
PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
|
|
13
13
|
SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
|
|
@@ -3,6 +3,13 @@ import { BN } from '../';
|
|
|
3
3
|
export declare const ZERO: BN;
|
|
4
4
|
export declare const ONE: BN;
|
|
5
5
|
export declare const TWO: BN;
|
|
6
|
+
export declare const THREE: BN;
|
|
7
|
+
export declare const FOUR: BN;
|
|
8
|
+
export declare const FIVE: BN;
|
|
9
|
+
export declare const SIX: BN;
|
|
10
|
+
export declare const SEVEN: BN;
|
|
11
|
+
export declare const EIGHT: BN;
|
|
12
|
+
export declare const NINE: BN;
|
|
6
13
|
export declare const TEN: BN;
|
|
7
14
|
export declare const TEN_THOUSAND: BN;
|
|
8
15
|
export declare const BN_MAX: BN;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = 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.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_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.MAX_LEVERAGE = exports.TEN_MILLION = exports.BN_MAX = exports.TEN_THOUSAND = exports.TEN = exports.TWO = exports.ONE = exports.ZERO = void 0;
|
|
3
|
+
exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = 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.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_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.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
4
|
const web3_js_1 = require("@solana/web3.js");
|
|
5
5
|
const __1 = require("../");
|
|
6
6
|
exports.ZERO = new __1.BN(0);
|
|
7
7
|
exports.ONE = new __1.BN(1);
|
|
8
8
|
exports.TWO = new __1.BN(2);
|
|
9
|
+
exports.THREE = new __1.BN(3);
|
|
10
|
+
exports.FOUR = new __1.BN(4);
|
|
11
|
+
exports.FIVE = new __1.BN(5);
|
|
12
|
+
exports.SIX = new __1.BN(6);
|
|
13
|
+
exports.SEVEN = new __1.BN(7);
|
|
14
|
+
exports.EIGHT = new __1.BN(8);
|
|
15
|
+
exports.NINE = new __1.BN(9);
|
|
9
16
|
exports.TEN = new __1.BN(10);
|
|
10
17
|
exports.TEN_THOUSAND = new __1.BN(10000);
|
|
11
18
|
exports.BN_MAX = new __1.BN(Number.MAX_SAFE_INTEGER);
|
|
@@ -12,8 +12,8 @@ exports.DevnetSpotMarkets = [
|
|
|
12
12
|
oracle: web3_js_1.PublicKey.default,
|
|
13
13
|
oracleSource: __1.OracleSource.QUOTE_ASSET,
|
|
14
14
|
mint: new web3_js_1.PublicKey('8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2'),
|
|
15
|
-
precision: numericConstants_1.
|
|
16
|
-
precisionExp: numericConstants_1.
|
|
15
|
+
precision: new __1.BN(10).pow(numericConstants_1.SIX),
|
|
16
|
+
precisionExp: numericConstants_1.SIX,
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
symbol: 'SOL',
|
|
@@ -31,8 +31,8 @@ exports.DevnetSpotMarkets = [
|
|
|
31
31
|
oracle: new web3_js_1.PublicKey('HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J'),
|
|
32
32
|
oracleSource: __1.OracleSource.PYTH,
|
|
33
33
|
mint: new web3_js_1.PublicKey('3BZPwbcqB5kKScF3TEXxwNfx5ipV13kbRVDvfVp5c6fv'),
|
|
34
|
-
precision: numericConstants_1.
|
|
35
|
-
precisionExp: numericConstants_1.
|
|
34
|
+
precision: new __1.BN(10).pow(numericConstants_1.SIX),
|
|
35
|
+
precisionExp: numericConstants_1.SIX,
|
|
36
36
|
serumMarket: new web3_js_1.PublicKey('AGsmbVu3MS9u68GEYABWosQQCZwmLcBHu4pWEuBYH7Za'),
|
|
37
37
|
},
|
|
38
38
|
];
|
package/lib/dlob/DLOB.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare type NodeToFill = {
|
|
|
29
29
|
export declare type NodeToTrigger = {
|
|
30
30
|
node: TriggerOrderNode;
|
|
31
31
|
};
|
|
32
|
-
declare type Side = 'ask' | 'bid' | 'both';
|
|
32
|
+
declare type Side = 'ask' | 'bid' | 'both' | 'nocross';
|
|
33
33
|
export declare class DLOB {
|
|
34
34
|
openOrders: Map<MarketTypeStr, Set<string>>;
|
|
35
35
|
orderLists: Map<MarketTypeStr, Map<number, MarketNodeLists>>;
|
|
@@ -54,16 +54,17 @@ export declare class DLOB {
|
|
|
54
54
|
trigger(order: Order, userAccount: PublicKey, onTrigger?: OrderBookCallback): void;
|
|
55
55
|
getListForOrder(order: Order): NodeList<any> | undefined;
|
|
56
56
|
findNodesToFill(marketIndex: number, vBid: BN | undefined, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
57
|
-
findCrossingNodesToFill(marketIndex: number,
|
|
58
|
-
|
|
57
|
+
findCrossingNodesToFill(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
58
|
+
findvAMMCrossingNodesToFill(marketIndex: number, vBid: BN, vAsk: BN, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
|
|
59
|
+
findExpiredMarketNodesToFill(marketIndex: number, slot: number, marketType: MarketType): NodeToFill[];
|
|
59
60
|
findJitAuctionNodesToFill(marketIndex: number, slot: number, marketType: MarketType): NodeToFill[];
|
|
60
61
|
getMarketBids(marketIndex: number, marketType: MarketType): Generator<DLOBNode>;
|
|
61
62
|
getMarketAsks(marketIndex: number, marketType: MarketType): Generator<DLOBNode>;
|
|
62
63
|
getAsks(marketIndex: number, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
63
64
|
getBids(marketIndex: number, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
|
|
64
65
|
findCrossingOrders(askNode: DLOBNode, bidNode: DLOBNode, oraclePriceData: OraclePriceData, slot: number): {
|
|
65
|
-
crossingNodes
|
|
66
|
-
exhaustedSide
|
|
66
|
+
crossingNodes: NodeToFill[];
|
|
67
|
+
exhaustedSide: Side;
|
|
67
68
|
};
|
|
68
69
|
getBestAsk(marketIndex: number, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
|
69
70
|
getBestBid(marketIndex: number, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -142,7 +142,7 @@ class DLOB {
|
|
|
142
142
|
else if ((0, __1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket'])) {
|
|
143
143
|
type = 'market';
|
|
144
144
|
}
|
|
145
|
-
else if (order.oraclePriceOffset.
|
|
145
|
+
else if (!order.oraclePriceOffset.eq(__1.ZERO)) {
|
|
146
146
|
type = 'floatingLimit';
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
@@ -163,15 +163,18 @@ class DLOB {
|
|
|
163
163
|
}
|
|
164
164
|
findNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData) {
|
|
165
165
|
// Find all the crossing nodes
|
|
166
|
-
const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex,
|
|
166
|
+
const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
|
|
167
|
+
const vAMMCrossingNodesToFill = this.findvAMMCrossingNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData);
|
|
167
168
|
// get expired market nodes
|
|
168
|
-
const marketNodesToFill = this.
|
|
169
|
-
return crossingNodesToFill.concat(marketNodesToFill);
|
|
169
|
+
const marketNodesToFill = this.findExpiredMarketNodesToFill(marketIndex, slot, marketType);
|
|
170
|
+
return crossingNodesToFill.concat(vAMMCrossingNodesToFill, marketNodesToFill);
|
|
170
171
|
}
|
|
171
|
-
findCrossingNodesToFill(marketIndex,
|
|
172
|
+
findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
|
|
172
173
|
const nodesToFill = new Array();
|
|
173
|
-
const askGenerator = this.getAsks(marketIndex,
|
|
174
|
-
|
|
174
|
+
const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
|
|
175
|
+
slot, marketType, oraclePriceData);
|
|
176
|
+
const bidGenerator = this.getBids(marketIndex, undefined, // dont include vbid
|
|
177
|
+
slot, marketType, oraclePriceData);
|
|
175
178
|
let nextAsk = askGenerator.next();
|
|
176
179
|
let nextBid = bidGenerator.next();
|
|
177
180
|
// First try to find orders that cross
|
|
@@ -187,33 +190,71 @@ class DLOB {
|
|
|
187
190
|
nextBid = bidGenerator.next();
|
|
188
191
|
nextAsk = askGenerator.next();
|
|
189
192
|
}
|
|
193
|
+
else if (exhaustedSide === 'nocross') {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
190
196
|
else {
|
|
191
197
|
console.error(`invalid exhaustedSide: ${exhaustedSide}`);
|
|
192
198
|
break;
|
|
193
199
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// Verify that each side is different user
|
|
197
|
-
if (crossingNodes && !takerIsMaker) {
|
|
198
|
-
nodesToFill.push(crossingNodes);
|
|
200
|
+
for (const crossingNode of crossingNodes) {
|
|
201
|
+
nodesToFill.push(crossingNode);
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
return nodesToFill;
|
|
202
205
|
}
|
|
203
|
-
|
|
206
|
+
findvAMMCrossingNodesToFill(marketIndex, vBid, vAsk, slot, marketType, oraclePriceData) {
|
|
207
|
+
const nodesToFill = new Array();
|
|
208
|
+
const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
|
|
209
|
+
slot, marketType, oraclePriceData);
|
|
210
|
+
const bidGenerator = this.getBids(marketIndex, undefined, // dont include vbid
|
|
211
|
+
slot, marketType, oraclePriceData);
|
|
212
|
+
let nextAsk = askGenerator.next();
|
|
213
|
+
let nextBid = bidGenerator.next();
|
|
214
|
+
// check for asks that cross vBid
|
|
215
|
+
while (!nextAsk.done) {
|
|
216
|
+
const askNode = nextAsk.value;
|
|
217
|
+
const askPrice = askNode.getPrice(oraclePriceData, slot);
|
|
218
|
+
if (askPrice.lte(vBid) && (0, __1.isAuctionComplete)(askNode.order, slot)) {
|
|
219
|
+
nodesToFill.push({
|
|
220
|
+
node: askNode,
|
|
221
|
+
makerNode: undefined, // filled by vAMM
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
nextAsk = askGenerator.next();
|
|
228
|
+
}
|
|
229
|
+
// check for bids that cross vAsk
|
|
230
|
+
while (!nextBid.done) {
|
|
231
|
+
const bidNode = nextBid.value;
|
|
232
|
+
const bidPrice = bidNode.getPrice(oraclePriceData, slot);
|
|
233
|
+
if (bidPrice.gte(vAsk) && (0, __1.isAuctionComplete)(bidNode.order, slot)) {
|
|
234
|
+
nodesToFill.push({
|
|
235
|
+
node: bidNode,
|
|
236
|
+
makerNode: undefined, // filled by vAMM
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
nextBid = bidGenerator.next();
|
|
243
|
+
}
|
|
244
|
+
return nodesToFill;
|
|
245
|
+
}
|
|
246
|
+
findExpiredMarketNodesToFill(marketIndex, slot, marketType) {
|
|
204
247
|
const nodesToFill = new Array();
|
|
205
248
|
// Then see if there are orders to fill against vamm
|
|
206
249
|
for (const marketBid of this.getMarketBids(marketIndex, marketType)) {
|
|
207
|
-
if ((0, __1.
|
|
208
|
-
(0, __1.isOrderExpired)(marketBid.order, slot)) {
|
|
250
|
+
if ((0, __1.isOrderExpired)(marketBid.order, slot)) {
|
|
209
251
|
nodesToFill.push({
|
|
210
252
|
node: marketBid,
|
|
211
253
|
});
|
|
212
254
|
}
|
|
213
255
|
}
|
|
214
256
|
for (const marketAsk of this.getMarketAsks(marketIndex, marketType)) {
|
|
215
|
-
if ((0, __1.
|
|
216
|
-
(0, __1.isOrderExpired)(marketAsk.order, slot)) {
|
|
257
|
+
if ((0, __1.isOrderExpired)(marketAsk.order, slot)) {
|
|
217
258
|
nodesToFill.push({
|
|
218
259
|
node: marketAsk,
|
|
219
260
|
});
|
|
@@ -265,7 +306,7 @@ class DLOB {
|
|
|
265
306
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
266
307
|
nodeLists.market.ask.getGenerator(),
|
|
267
308
|
];
|
|
268
|
-
if (marketTypeStr === 'perp') {
|
|
309
|
+
if (marketTypeStr === 'perp' && vAsk) {
|
|
269
310
|
generatorList.push((0, NodeList_1.getVammNodeGenerator)(vAsk));
|
|
270
311
|
}
|
|
271
312
|
const askGenerators = generatorList.map((generator) => {
|
|
@@ -290,6 +331,11 @@ class DLOB {
|
|
|
290
331
|
: currentGenerator;
|
|
291
332
|
});
|
|
292
333
|
if (!bestGenerator.next.done) {
|
|
334
|
+
// skip this node if it's already completely filled
|
|
335
|
+
if (bestGenerator.next.value.isBaseFilled()) {
|
|
336
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
293
339
|
yield bestGenerator.next.value;
|
|
294
340
|
bestGenerator.next = bestGenerator.generator.next();
|
|
295
341
|
}
|
|
@@ -309,7 +355,7 @@ class DLOB {
|
|
|
309
355
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
310
356
|
nodeLists.market.bid.getGenerator(),
|
|
311
357
|
];
|
|
312
|
-
if (marketTypeStr === 'perp') {
|
|
358
|
+
if (marketTypeStr === 'perp' && vBid) {
|
|
313
359
|
generatorList.push((0, NodeList_1.getVammNodeGenerator)(vBid));
|
|
314
360
|
}
|
|
315
361
|
const bidGenerators = generatorList.map((generator) => {
|
|
@@ -334,6 +380,11 @@ class DLOB {
|
|
|
334
380
|
: currentGenerator;
|
|
335
381
|
});
|
|
336
382
|
if (!bestGenerator.next.done) {
|
|
383
|
+
// skip this node if it's already completely filled
|
|
384
|
+
if (bestGenerator.next.value.isBaseFilled()) {
|
|
385
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
337
388
|
yield bestGenerator.next.value;
|
|
338
389
|
bestGenerator.next = bestGenerator.generator.next();
|
|
339
390
|
}
|
|
@@ -345,54 +396,24 @@ class DLOB {
|
|
|
345
396
|
findCrossingOrders(askNode, bidNode, oraclePriceData, slot) {
|
|
346
397
|
const bidPrice = bidNode.getPrice(oraclePriceData, slot);
|
|
347
398
|
const askPrice = askNode.getPrice(oraclePriceData, slot);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const containsMarketOrder = (askOrder && (0, __1.isVariant)(askOrder.orderType, 'market')) ||
|
|
351
|
-
(bidOrder && (0, __1.isVariant)(bidOrder.orderType, 'market'));
|
|
352
|
-
if (!containsMarketOrder || bidPrice.lt(askPrice)) {
|
|
353
|
-
// no market orders, and no crossing orders - return
|
|
354
|
-
if (askNode.isVammNode() && !bidNode.isVammNode()) {
|
|
355
|
-
return {
|
|
356
|
-
exhaustedSide: 'bid',
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
else if (!askNode.isVammNode() && bidNode.isVammNode()) {
|
|
360
|
-
return {
|
|
361
|
-
exhaustedSide: 'ask',
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
return {
|
|
366
|
-
exhaustedSide: 'both',
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// User bid crosses the vamm ask
|
|
371
|
-
if (askNode.isVammNode()) {
|
|
372
|
-
if (!(0, __1.isAuctionComplete)(bidOrder, slot)) {
|
|
373
|
-
return {
|
|
374
|
-
exhaustedSide: 'bid',
|
|
375
|
-
};
|
|
376
|
-
}
|
|
399
|
+
// orders don't cross - we're done walkin gup the book
|
|
400
|
+
if (bidPrice.lt(askPrice)) {
|
|
377
401
|
return {
|
|
378
|
-
crossingNodes:
|
|
379
|
-
|
|
380
|
-
},
|
|
381
|
-
exhaustedSide: 'bid',
|
|
402
|
+
crossingNodes: [],
|
|
403
|
+
exhaustedSide: 'nocross',
|
|
382
404
|
};
|
|
383
405
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
406
|
+
const bidOrder = bidNode.order;
|
|
407
|
+
const askOrder = askNode.order;
|
|
408
|
+
// Can't match two maker orders or if maker and taker are the same
|
|
409
|
+
const makerIsTaker = bidNode.userAccount.equals(askNode.userAccount);
|
|
410
|
+
if (makerIsTaker || (bidOrder.postOnly && askOrder.postOnly)) {
|
|
411
|
+
// don't have a principle way to pick which one to exhaust,
|
|
412
|
+
// exhaust each one 50% of the time so we can try each one against other orders
|
|
413
|
+
const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
|
|
391
414
|
return {
|
|
392
|
-
crossingNodes:
|
|
393
|
-
|
|
394
|
-
},
|
|
395
|
-
exhaustedSide: 'ask',
|
|
415
|
+
crossingNodes: [],
|
|
416
|
+
exhaustedSide,
|
|
396
417
|
};
|
|
397
418
|
}
|
|
398
419
|
const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
|
|
@@ -407,44 +428,63 @@ class DLOB {
|
|
|
407
428
|
else {
|
|
408
429
|
exhaustedSide = 'bid';
|
|
409
430
|
}
|
|
410
|
-
//
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
// update the orders as if they fill - so we don't try to match them on the next iteration
|
|
431
|
+
// update the orders on DLOB as if they were fill - so we don't try to match them on the next iteration
|
|
432
|
+
// NOTE: if something goes wrong during the actual fill (transaction fails, i.e. due to orders already being filled)
|
|
433
|
+
// then we risk having a mismatch between this local DLOB and the actual DLOB state on the blockchain. This isn't
|
|
434
|
+
// a problem in the current implementation because we construct a new DLOB from the blockchain state every time, rather
|
|
435
|
+
// than updating the existing DLOB based on events.
|
|
417
436
|
if (exhaustedSide === 'ask') {
|
|
418
|
-
|
|
437
|
+
// bid partially filled
|
|
438
|
+
const newBidOrder = { ...bidOrder };
|
|
439
|
+
newBidOrder.baseAssetAmountFilled =
|
|
419
440
|
bidOrder.baseAssetAmountFilled.add(askBaseRemaining);
|
|
420
|
-
|
|
441
|
+
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
442
|
+
// ask completely filled
|
|
443
|
+
const newAskOrder = { ...askOrder };
|
|
444
|
+
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
445
|
+
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
421
446
|
}
|
|
422
447
|
else if (exhaustedSide === 'bid') {
|
|
423
|
-
|
|
448
|
+
// ask partially filled
|
|
449
|
+
const newAskOrder = { ...askOrder };
|
|
450
|
+
newAskOrder.baseAssetAmountFilled =
|
|
424
451
|
askOrder.baseAssetAmountFilled.add(bidBaseRemaining);
|
|
425
|
-
|
|
452
|
+
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
453
|
+
// bid completely filled
|
|
454
|
+
const newBidOrder = { ...bidOrder };
|
|
455
|
+
newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
456
|
+
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
426
457
|
}
|
|
427
458
|
else {
|
|
428
|
-
|
|
429
|
-
|
|
459
|
+
// both completely filled
|
|
460
|
+
const newBidOrder = { ...bidOrder };
|
|
461
|
+
newBidOrder.baseAssetAmountFilled = bidOrder.baseAssetAmount;
|
|
462
|
+
this.getListForOrder(newBidOrder).update(newBidOrder, bidNode.userAccount);
|
|
463
|
+
const newAskOrder = { ...askOrder };
|
|
464
|
+
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
465
|
+
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
430
466
|
}
|
|
431
467
|
// Bid is maker
|
|
432
468
|
if (bidOrder.postOnly) {
|
|
433
469
|
return {
|
|
434
|
-
crossingNodes:
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
470
|
+
crossingNodes: [
|
|
471
|
+
{
|
|
472
|
+
node: askNode,
|
|
473
|
+
makerNode: bidNode,
|
|
474
|
+
},
|
|
475
|
+
],
|
|
438
476
|
exhaustedSide,
|
|
439
477
|
};
|
|
440
478
|
}
|
|
441
479
|
// Ask is maker
|
|
442
480
|
if (askOrder.postOnly) {
|
|
443
481
|
return {
|
|
444
|
-
crossingNodes:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
482
|
+
crossingNodes: [
|
|
483
|
+
{
|
|
484
|
+
node: bidNode,
|
|
485
|
+
makerNode: askNode,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
448
488
|
exhaustedSide,
|
|
449
489
|
};
|
|
450
490
|
}
|
|
@@ -454,10 +494,12 @@ class DLOB {
|
|
|
454
494
|
? [askNode, bidNode]
|
|
455
495
|
: [bidNode, askNode];
|
|
456
496
|
return {
|
|
457
|
-
crossingNodes:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
497
|
+
crossingNodes: [
|
|
498
|
+
{
|
|
499
|
+
node: newerNode,
|
|
500
|
+
makerNode: olderNode,
|
|
501
|
+
},
|
|
502
|
+
],
|
|
461
503
|
exhaustedSide,
|
|
462
504
|
};
|
|
463
505
|
}
|
package/lib/dlob/DLOBNode.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface DLOBNode {
|
|
|
5
5
|
getPrice(oraclePriceData: OraclePriceData, slot: number): BN;
|
|
6
6
|
isVammNode(): boolean;
|
|
7
7
|
order: Order | undefined;
|
|
8
|
+
isBaseFilled(): boolean;
|
|
8
9
|
haveFilled: boolean;
|
|
9
10
|
userAccount: PublicKey | undefined;
|
|
10
11
|
market: SpotMarketAccount | PerpMarketAccount;
|
|
@@ -20,6 +21,7 @@ export declare abstract class OrderNode implements DLOBNode {
|
|
|
20
21
|
abstract getSortValue(order: Order): BN;
|
|
21
22
|
getLabel(): string;
|
|
22
23
|
getPrice(oraclePriceData: OraclePriceData, slot: number): BN;
|
|
24
|
+
isBaseFilled(): boolean;
|
|
23
25
|
isVammNode(): boolean;
|
|
24
26
|
}
|
|
25
27
|
export declare class LimitOrderNode extends OrderNode {
|
package/lib/dlob/DLOBNode.js
CHANGED
package/lib/dlob/NodeList.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { BN, MarketTypeStr, Order, PerpMarketAccount, SpotMarketAccount } from '
|
|
|
3
3
|
import { PublicKey } from '@solana/web3.js';
|
|
4
4
|
import { DLOBNode, DLOBNodeMap } from './DLOBNode';
|
|
5
5
|
export declare type SortDirection = 'asc' | 'desc';
|
|
6
|
-
export declare function getOrderSignature(orderId:
|
|
6
|
+
export declare function getOrderSignature(orderId: number, userAccount: PublicKey): string;
|
|
7
7
|
export interface DLOBNodeGenerator {
|
|
8
8
|
getGenerator(): Generator<DLOBNode>;
|
|
9
9
|
}
|
package/lib/dlob/NodeList.js
CHANGED
|
@@ -25,11 +25,11 @@ class NodeList {
|
|
|
25
25
|
market = market;
|
|
26
26
|
}
|
|
27
27
|
const newNode = (0, DLOBNode_1.createNode)(this.nodeType, order, market, userAccount);
|
|
28
|
-
const
|
|
29
|
-
if (this.nodeMap.has(
|
|
28
|
+
const orderSignature = getOrderSignature(order.orderId, userAccount);
|
|
29
|
+
if (this.nodeMap.has(orderSignature)) {
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
this.nodeMap.set(
|
|
32
|
+
this.nodeMap.set(orderSignature, newNode);
|
|
33
33
|
this.length += 1;
|
|
34
34
|
if (this.head === undefined) {
|
|
35
35
|
this.head = newNode;
|
|
@@ -86,7 +86,7 @@ class NodeList {
|
|
|
86
86
|
if (node.previous) {
|
|
87
87
|
node.previous.next = node.next;
|
|
88
88
|
}
|
|
89
|
-
if (this.head && node.order.orderId
|
|
89
|
+
if (this.head && node.order.orderId === this.head.order.orderId) {
|
|
90
90
|
this.head = node.next;
|
|
91
91
|
}
|
|
92
92
|
node.previous = undefined;
|
|
@@ -132,6 +132,7 @@ function* getVammNodeGenerator(price) {
|
|
|
132
132
|
order: undefined,
|
|
133
133
|
market: undefined,
|
|
134
134
|
userAccount: undefined,
|
|
135
|
+
isBaseFilled: () => false,
|
|
135
136
|
haveFilled: false,
|
|
136
137
|
};
|
|
137
138
|
}
|
package/lib/events/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Commitment, TransactionSignature } from '@solana/web3.js';
|
|
2
|
-
import { DepositRecord, FundingPaymentRecord, FundingRateRecord, LiquidationRecord, NewUserRecord, OrderActionRecord, OrderRecord, SettlePnlRecord, LPRecord, InsuranceFundRecord } from '../index';
|
|
2
|
+
import { DepositRecord, FundingPaymentRecord, FundingRateRecord, LiquidationRecord, NewUserRecord, OrderActionRecord, OrderRecord, SettlePnlRecord, LPRecord, InsuranceFundRecord, SpotInterestRecord } from '../index';
|
|
3
3
|
export declare type EventSubscriptionOptions = {
|
|
4
4
|
eventTypes?: EventType[];
|
|
5
5
|
maxEventsPerType?: number;
|
|
@@ -32,6 +32,7 @@ export declare type EventMap = {
|
|
|
32
32
|
NewUserRecord: Event<NewUserRecord>;
|
|
33
33
|
LPRecord: Event<LPRecord>;
|
|
34
34
|
InsuranceFundRecord: Event<InsuranceFundRecord>;
|
|
35
|
+
SpotInterestRecord: Event<SpotInterestRecord>;
|
|
35
36
|
};
|
|
36
37
|
export declare type EventType = keyof EventMap;
|
|
37
38
|
export interface EventSubscriberEvents {
|
package/lib/events/types.js
CHANGED