@drift-labs/sdk 0.2.0-master.31 → 0.2.0-master.33
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/bulkAccountLoader.js +2 -1
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +2 -2
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +8 -8
- package/lib/accounts/types.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +2 -2
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +7 -7
- package/lib/addresses/marketAddresses.js +1 -1
- package/lib/addresses/pda.d.ts +4 -4
- package/lib/addresses/pda.js +23 -22
- package/lib/admin.d.ts +30 -32
- package/lib/admin.js +111 -119
- package/lib/clearingHouse.d.ts +43 -34
- package/lib/clearingHouse.js +353 -193
- package/lib/clearingHouseConfig.d.ts +2 -2
- package/lib/clearingHouseUser.d.ts +26 -5
- package/lib/clearingHouseUser.js +151 -51
- package/lib/config.d.ts +2 -0
- package/lib/config.js +5 -1
- package/lib/constants/numericConstants.d.ts +1 -0
- package/lib/constants/numericConstants.js +3 -2
- package/lib/dlob/DLOB.d.ts +19 -11
- package/lib/dlob/DLOB.js +208 -107
- package/lib/dlob/DLOBNode.js +2 -10
- package/lib/dlob/NodeList.d.ts +1 -0
- package/lib/dlob/NodeList.js +6 -1
- package/lib/events/eventSubscriber.d.ts +1 -0
- package/lib/events/eventSubscriber.js +11 -4
- package/lib/events/fetchLogs.d.ts +3 -1
- package/lib/events/fetchLogs.js +13 -5
- package/lib/events/pollingLogProvider.js +1 -1
- package/lib/events/types.d.ts +1 -1
- package/lib/events/webSocketLogProvider.js +1 -1
- package/lib/factory/bigNum.d.ts +5 -4
- package/lib/factory/bigNum.js +36 -6
- package/lib/idl/clearing_house.json +1527 -1242
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/math/amm.js +9 -9
- package/lib/math/exchangeStatus.d.ts +4 -0
- package/lib/math/exchangeStatus.js +18 -0
- package/lib/math/funding.js +10 -10
- package/lib/math/margin.js +6 -1
- package/lib/math/market.js +9 -9
- package/lib/math/orders.d.ts +7 -3
- package/lib/math/orders.js +39 -31
- package/lib/math/repeg.js +3 -3
- package/lib/math/spotBalance.js +3 -3
- package/lib/math/spotPosition.js +2 -2
- package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
- package/lib/serum/serumFulfillmentConfigMap.js +17 -0
- package/lib/serum/serumSubscriber.d.ts +4 -0
- package/lib/serum/serumSubscriber.js +16 -1
- package/lib/types.d.ts +70 -85
- package/lib/types.js +12 -11
- package/lib/userMap/userMap.d.ts +17 -1
- package/lib/userMap/userMap.js +12 -0
- package/lib/userName.d.ts +1 -0
- package/lib/userName.js +3 -2
- package/package.json +1 -1
- package/src/accounts/bulkAccountLoader.ts +5 -1
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +9 -9
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +8 -8
- package/src/addresses/marketAddresses.ts +2 -2
- package/src/addresses/pda.ts +20 -20
- package/src/admin.ts +246 -221
- package/src/clearingHouse.ts +556 -236
- package/src/clearingHouseConfig.ts +2 -2
- package/src/clearingHouseUser.ts +237 -87
- package/src/config.ts +8 -1
- package/src/constants/numericConstants.ts +5 -1
- package/src/dlob/DLOB.ts +290 -120
- package/src/dlob/DLOBNode.ts +2 -14
- package/src/dlob/NodeList.ts +7 -1
- package/src/events/eventSubscriber.ts +18 -4
- package/src/events/fetchLogs.ts +20 -5
- package/src/events/pollingLogProvider.ts +1 -1
- package/src/events/types.ts +2 -1
- package/src/events/webSocketLogProvider.ts +1 -1
- package/src/factory/bigNum.ts +59 -6
- package/src/idl/clearing_house.json +1527 -1242
- package/src/idl/pyth.json +98 -2
- package/src/index.ts +3 -0
- package/src/math/amm.ts +9 -9
- package/src/math/exchangeStatus.ts +31 -0
- package/src/math/funding.ts +20 -10
- package/src/math/margin.ts +7 -1
- package/src/math/market.ts +9 -9
- package/src/math/orders.ts +44 -29
- package/src/math/repeg.ts +3 -3
- package/src/math/spotBalance.ts +4 -4
- package/src/math/spotPosition.ts +2 -2
- package/src/serum/serumFulfillmentConfigMap.ts +26 -0
- package/src/serum/serumSubscriber.ts +20 -1
- package/src/types.ts +75 -61
- package/src/userMap/userMap.ts +25 -1
- package/src/userName.ts +2 -1
- package/tests/bn/test.ts +22 -1
- package/tests/dlob/helpers.ts +252 -81
- package/tests/dlob/test.ts +1115 -215
package/lib/dlob/DLOB.js
CHANGED
|
@@ -3,19 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DLOB = void 0;
|
|
4
4
|
const NodeList_1 = require("./NodeList");
|
|
5
5
|
const __1 = require("..");
|
|
6
|
+
const exchangeStatus_1 = require("../math/exchangeStatus");
|
|
6
7
|
class DLOB {
|
|
7
8
|
/**
|
|
8
9
|
*
|
|
9
10
|
* @param perpMarkets The perp markets to maintain a DLOB for
|
|
10
11
|
* @param spotMarkets The spot markets to maintain a DLOB for
|
|
12
|
+
* @param userMap map of all users
|
|
11
13
|
* @param silent set to true to prevent logging on inserts and removals
|
|
12
14
|
*/
|
|
13
|
-
constructor(perpMarkets, spotMarkets, silent) {
|
|
15
|
+
constructor(perpMarkets, spotMarkets, stateAccount, userMap, silent) {
|
|
14
16
|
this.openOrders = new Map();
|
|
15
17
|
this.orderLists = new Map();
|
|
16
18
|
this.marketIndexToAccount = new Map();
|
|
17
19
|
this.silent = false;
|
|
18
20
|
this.initialized = false;
|
|
21
|
+
this.stateAccount = stateAccount;
|
|
22
|
+
this.userMap = userMap;
|
|
19
23
|
this.silent = silent;
|
|
20
24
|
this.openOrders.set('perp', new Set());
|
|
21
25
|
this.openOrders.set('spot', new Set());
|
|
@@ -68,35 +72,44 @@ class DLOB {
|
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
74
|
}
|
|
75
|
+
clear() {
|
|
76
|
+
for (const marketType of this.openOrders.keys()) {
|
|
77
|
+
this.openOrders.get(marketType).clear();
|
|
78
|
+
}
|
|
79
|
+
this.openOrders.clear();
|
|
80
|
+
for (const marketType of this.orderLists.keys()) {
|
|
81
|
+
for (const marketIndex of this.orderLists.get(marketType).keys()) {
|
|
82
|
+
const marketNodeLists = this.orderLists
|
|
83
|
+
.get(marketType)
|
|
84
|
+
.get(marketIndex);
|
|
85
|
+
for (const side of Object.keys(marketNodeLists)) {
|
|
86
|
+
for (const orderType of Object.keys(marketNodeLists[side])) {
|
|
87
|
+
marketNodeLists[side][orderType].clear();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
this.orderLists.clear();
|
|
93
|
+
for (const marketType of this.marketIndexToAccount.keys()) {
|
|
94
|
+
this.marketIndexToAccount.get(marketType).clear();
|
|
95
|
+
}
|
|
96
|
+
this.marketIndexToAccount.clear();
|
|
97
|
+
}
|
|
71
98
|
/**
|
|
72
99
|
* initializes a new DLOB instance
|
|
73
100
|
*
|
|
74
|
-
* @param clearingHouse The ClearingHouse instance to use for price data
|
|
75
101
|
* @returns a promise that resolves when the DLOB is initialized
|
|
76
102
|
*/
|
|
77
|
-
async init(
|
|
103
|
+
async init() {
|
|
78
104
|
if (this.initialized) {
|
|
79
105
|
return false;
|
|
80
106
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.insertOrder(order, userAccountPubkey);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
const programAccounts = await clearingHouse.program.account.user.all();
|
|
93
|
-
for (const programAccount of programAccounts) {
|
|
94
|
-
// @ts-ignore
|
|
95
|
-
const userAccount = programAccount.account;
|
|
96
|
-
const userAccountPublicKey = programAccount.publicKey;
|
|
97
|
-
for (const order of userAccount.orders) {
|
|
98
|
-
this.insertOrder(order, userAccountPublicKey);
|
|
99
|
-
}
|
|
107
|
+
// initialize the dlob with the user map (prevents hitting getProgramAccounts)
|
|
108
|
+
for (const user of this.userMap.values()) {
|
|
109
|
+
const userAccount = user.getUserAccount();
|
|
110
|
+
const userAccountPubkey = user.getUserAccountPublicKey();
|
|
111
|
+
for (const order of userAccount.orders) {
|
|
112
|
+
this.insertOrder(order, userAccountPubkey);
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
this.initialized = true;
|
|
@@ -142,7 +155,7 @@ class DLOB {
|
|
|
142
155
|
else if ((0, __1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket'])) {
|
|
143
156
|
type = 'market';
|
|
144
157
|
}
|
|
145
|
-
else if (
|
|
158
|
+
else if (order.oraclePriceOffset !== 0) {
|
|
146
159
|
type = 'floatingLimit';
|
|
147
160
|
}
|
|
148
161
|
else {
|
|
@@ -161,13 +174,22 @@ class DLOB {
|
|
|
161
174
|
}
|
|
162
175
|
return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
|
|
163
176
|
}
|
|
164
|
-
findNodesToFill(marketIndex,
|
|
177
|
+
findNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, ts, marketType, oraclePriceData) {
|
|
178
|
+
const marketAccount = this.marketIndexToAccount
|
|
179
|
+
.get((0, __1.getVariant)(marketType))
|
|
180
|
+
.get(marketIndex);
|
|
181
|
+
if ((0, exchangeStatus_1.fillPaused)(this.stateAccount, marketAccount)) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
165
184
|
// Find all the crossing nodes
|
|
166
185
|
const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
|
|
167
|
-
|
|
186
|
+
let fallbackCrossingNodesToFill = new Array();
|
|
187
|
+
if (!(0, exchangeStatus_1.ammPaused)(this.stateAccount, marketAccount)) {
|
|
188
|
+
fallbackCrossingNodesToFill = this.findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData);
|
|
189
|
+
}
|
|
168
190
|
// get expired market nodes
|
|
169
|
-
const
|
|
170
|
-
return crossingNodesToFill.concat(
|
|
191
|
+
const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
|
|
192
|
+
return crossingNodesToFill.concat(fallbackCrossingNodesToFill, expiredNodesToFill);
|
|
171
193
|
}
|
|
172
194
|
findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
|
|
173
195
|
const nodesToFill = new Array();
|
|
@@ -203,7 +225,8 @@ class DLOB {
|
|
|
203
225
|
}
|
|
204
226
|
return nodesToFill;
|
|
205
227
|
}
|
|
206
|
-
|
|
228
|
+
findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
229
|
+
var _a, _b;
|
|
207
230
|
const nodesToFill = new Array();
|
|
208
231
|
const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
|
|
209
232
|
slot, marketType, oraclePriceData);
|
|
@@ -211,53 +234,79 @@ class DLOB {
|
|
|
211
234
|
slot, marketType, oraclePriceData);
|
|
212
235
|
let nextAsk = askGenerator.next();
|
|
213
236
|
let nextBid = bidGenerator.next();
|
|
214
|
-
// check for asks that cross
|
|
215
|
-
while (!nextAsk.done) {
|
|
237
|
+
// check for asks that cross fallbackBid
|
|
238
|
+
while (!nextAsk.done && fallbackBid !== undefined) {
|
|
216
239
|
const askNode = nextAsk.value;
|
|
217
|
-
|
|
218
|
-
|
|
240
|
+
if ((0, __1.isVariant)(marketType, 'spot') && ((_a = askNode.order) === null || _a === void 0 ? void 0 : _a.postOnly)) {
|
|
241
|
+
nextAsk = askGenerator.next();
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const askLimitPrice = (0, __1.getOptionalLimitPrice)(askNode.order, oraclePriceData, slot);
|
|
245
|
+
// order crosses if there is no limit price or it crosses fallback price
|
|
246
|
+
const crosses = askLimitPrice === undefined || askLimitPrice.lte(fallbackBid);
|
|
247
|
+
// fallback is available if auction is complete or it's a spot order
|
|
248
|
+
const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(askNode.order, slot);
|
|
249
|
+
if (crosses && fallbackAvailable) {
|
|
219
250
|
nodesToFill.push({
|
|
220
251
|
node: askNode,
|
|
221
|
-
makerNode: undefined, // filled by
|
|
252
|
+
makerNode: undefined, // filled by fallback
|
|
222
253
|
});
|
|
223
254
|
}
|
|
224
|
-
else {
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
255
|
nextAsk = askGenerator.next();
|
|
228
256
|
}
|
|
229
|
-
// check for bids that cross
|
|
230
|
-
while (!nextBid.done) {
|
|
257
|
+
// check for bids that cross fallbackAsk
|
|
258
|
+
while (!nextBid.done && fallbackAsk !== undefined) {
|
|
231
259
|
const bidNode = nextBid.value;
|
|
232
|
-
|
|
233
|
-
|
|
260
|
+
if ((0, __1.isVariant)(marketType, 'spot') && ((_b = bidNode.order) === null || _b === void 0 ? void 0 : _b.postOnly)) {
|
|
261
|
+
nextBid = bidGenerator.next();
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const bidLimitPrice = (0, __1.getOptionalLimitPrice)(bidNode.order, oraclePriceData, slot);
|
|
265
|
+
// order crosses if there is no limit price or it crosses fallback price
|
|
266
|
+
const crosses = bidLimitPrice === undefined || bidLimitPrice.gte(fallbackAsk);
|
|
267
|
+
// fallback is available if auction is complete or it's a spot order
|
|
268
|
+
const fallbackAvailable = (0, __1.isVariant)(marketType, 'spot') || (0, __1.isAuctionComplete)(bidNode.order, slot);
|
|
269
|
+
if (crosses && fallbackAvailable) {
|
|
234
270
|
nodesToFill.push({
|
|
235
271
|
node: bidNode,
|
|
236
|
-
makerNode: undefined, // filled by
|
|
272
|
+
makerNode: undefined, // filled by fallback
|
|
237
273
|
});
|
|
238
274
|
}
|
|
239
|
-
else {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
275
|
nextBid = bidGenerator.next();
|
|
243
276
|
}
|
|
244
277
|
return nodesToFill;
|
|
245
278
|
}
|
|
246
|
-
|
|
279
|
+
findExpiredNodesToFill(marketIndex, ts, marketType) {
|
|
247
280
|
const nodesToFill = new Array();
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
281
|
+
const marketTypeStr = (0, __1.getVariant)(marketType);
|
|
282
|
+
const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex);
|
|
283
|
+
// All bids/asks that can expire
|
|
284
|
+
const bidGenerators = [
|
|
285
|
+
nodeLists.limit.bid.getGenerator(),
|
|
286
|
+
nodeLists.floatingLimit.bid.getGenerator(),
|
|
287
|
+
nodeLists.market.bid.getGenerator(),
|
|
288
|
+
];
|
|
289
|
+
const askGenerators = [
|
|
290
|
+
nodeLists.limit.ask.getGenerator(),
|
|
291
|
+
nodeLists.floatingLimit.ask.getGenerator(),
|
|
292
|
+
nodeLists.market.ask.getGenerator(),
|
|
293
|
+
];
|
|
294
|
+
for (const bidGenerator of bidGenerators) {
|
|
295
|
+
for (const bid of bidGenerator) {
|
|
296
|
+
if ((0, __1.isOrderExpired)(bid.order, ts)) {
|
|
297
|
+
nodesToFill.push({
|
|
298
|
+
node: bid,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
254
301
|
}
|
|
255
302
|
}
|
|
256
|
-
for (const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
303
|
+
for (const askGenerator of askGenerators) {
|
|
304
|
+
for (const ask of askGenerator) {
|
|
305
|
+
if ((0, __1.isOrderExpired)(ask.order, ts)) {
|
|
306
|
+
nodesToFill.push({
|
|
307
|
+
node: ask,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
261
310
|
}
|
|
262
311
|
}
|
|
263
312
|
return nodesToFill;
|
|
@@ -295,7 +344,7 @@ class DLOB {
|
|
|
295
344
|
.get(marketIndex)
|
|
296
345
|
.market.ask.getGenerator();
|
|
297
346
|
}
|
|
298
|
-
*getAsks(marketIndex,
|
|
347
|
+
*getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
299
348
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
300
349
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
301
350
|
}
|
|
@@ -306,8 +355,11 @@ class DLOB {
|
|
|
306
355
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
307
356
|
nodeLists.market.ask.getGenerator(),
|
|
308
357
|
];
|
|
309
|
-
if (marketTypeStr === 'perp' &&
|
|
310
|
-
generatorList.push((0, NodeList_1.getVammNodeGenerator)(
|
|
358
|
+
if (marketTypeStr === 'perp' && fallbackAsk) {
|
|
359
|
+
generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
|
|
360
|
+
}
|
|
361
|
+
if (generatorList.length === 0) {
|
|
362
|
+
throw new Error('No ask generators found');
|
|
311
363
|
}
|
|
312
364
|
const askGenerators = generatorList.map((generator) => {
|
|
313
365
|
return {
|
|
@@ -336,6 +388,15 @@ class DLOB {
|
|
|
336
388
|
bestGenerator.next = bestGenerator.generator.next();
|
|
337
389
|
continue;
|
|
338
390
|
}
|
|
391
|
+
// skip order if user is being liquidated/bankrupt
|
|
392
|
+
if (bestGenerator.next.value.userAccount !== undefined) {
|
|
393
|
+
const user = this.userMap.get(bestGenerator.next.value.userAccount.toString());
|
|
394
|
+
if ((user === null || user === void 0 ? void 0 : user.getUserAccount().isBeingLiquidated) ||
|
|
395
|
+
(user === null || user === void 0 ? void 0 : user.getUserAccount().isBankrupt)) {
|
|
396
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
339
400
|
yield bestGenerator.next.value;
|
|
340
401
|
bestGenerator.next = bestGenerator.generator.next();
|
|
341
402
|
}
|
|
@@ -344,7 +405,7 @@ class DLOB {
|
|
|
344
405
|
}
|
|
345
406
|
}
|
|
346
407
|
}
|
|
347
|
-
*getBids(marketIndex,
|
|
408
|
+
*getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
|
|
348
409
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
349
410
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
350
411
|
}
|
|
@@ -355,8 +416,11 @@ class DLOB {
|
|
|
355
416
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
356
417
|
nodeLists.market.bid.getGenerator(),
|
|
357
418
|
];
|
|
358
|
-
if (marketTypeStr === 'perp' &&
|
|
359
|
-
generatorList.push((0, NodeList_1.getVammNodeGenerator)(
|
|
419
|
+
if (marketTypeStr === 'perp' && fallbackBid) {
|
|
420
|
+
generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackBid));
|
|
421
|
+
}
|
|
422
|
+
if (generatorList.length === 0) {
|
|
423
|
+
throw new Error('No bid generators found');
|
|
360
424
|
}
|
|
361
425
|
const bidGenerators = generatorList.map((generator) => {
|
|
362
426
|
return {
|
|
@@ -364,7 +428,7 @@ class DLOB {
|
|
|
364
428
|
generator,
|
|
365
429
|
};
|
|
366
430
|
});
|
|
367
|
-
let bidsExhausted = false; // there will always be the
|
|
431
|
+
let bidsExhausted = false; // there will always be the fallbackBid
|
|
368
432
|
while (!bidsExhausted) {
|
|
369
433
|
const bestGenerator = bidGenerators.reduce((bestGenerator, currentGenerator) => {
|
|
370
434
|
if (currentGenerator.next.done) {
|
|
@@ -385,6 +449,15 @@ class DLOB {
|
|
|
385
449
|
bestGenerator.next = bestGenerator.generator.next();
|
|
386
450
|
continue;
|
|
387
451
|
}
|
|
452
|
+
// skip order if user is being liquidated/bankrupt
|
|
453
|
+
if (bestGenerator.next.value.userAccount !== undefined) {
|
|
454
|
+
const user = this.userMap.get(bestGenerator.next.value.userAccount.toString());
|
|
455
|
+
if ((user === null || user === void 0 ? void 0 : user.getUserAccount().isBeingLiquidated) ||
|
|
456
|
+
(user === null || user === void 0 ? void 0 : user.getUserAccount().isBankrupt)) {
|
|
457
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
388
461
|
yield bestGenerator.next.value;
|
|
389
462
|
bestGenerator.next = bestGenerator.generator.next();
|
|
390
463
|
}
|
|
@@ -405,9 +478,11 @@ class DLOB {
|
|
|
405
478
|
}
|
|
406
479
|
const bidOrder = bidNode.order;
|
|
407
480
|
const askOrder = askNode.order;
|
|
408
|
-
|
|
409
|
-
const
|
|
410
|
-
|
|
481
|
+
const bidUserAuthority = this.userMap.getUserAuthority(bidNode.userAccount.toString());
|
|
482
|
+
const askUserAuthority = this.userMap.getUserAuthority(askNode.userAccount.toString());
|
|
483
|
+
// Can't match orders from the same authority
|
|
484
|
+
const sameAuthority = bidUserAuthority.equals(askUserAuthority);
|
|
485
|
+
if (sameAuthority || (bidOrder.postOnly && askOrder.postOnly)) {
|
|
411
486
|
// don't have a principle way to pick which one to exhaust,
|
|
412
487
|
// exhaust each one 50% of the time so we can try each one against other orders
|
|
413
488
|
const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
|
|
@@ -416,6 +491,14 @@ class DLOB {
|
|
|
416
491
|
exhaustedSide,
|
|
417
492
|
};
|
|
418
493
|
}
|
|
494
|
+
const { takerNode, makerNode, makerSide } = this.determineMakerAndTaker(askNode, bidNode);
|
|
495
|
+
// If order doesn't have limit price, cant be maker
|
|
496
|
+
if (!(0, __1.hasLimitPrice)(makerNode.order, slot)) {
|
|
497
|
+
return {
|
|
498
|
+
crossingNodes: [],
|
|
499
|
+
exhaustedSide: makerSide,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
419
502
|
const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
|
|
420
503
|
const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
|
|
421
504
|
let exhaustedSide;
|
|
@@ -464,56 +547,74 @@ class DLOB {
|
|
|
464
547
|
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
465
548
|
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
466
549
|
}
|
|
467
|
-
// Bid is maker
|
|
468
|
-
if (bidOrder.postOnly) {
|
|
469
|
-
return {
|
|
470
|
-
crossingNodes: [
|
|
471
|
-
{
|
|
472
|
-
node: askNode,
|
|
473
|
-
makerNode: bidNode,
|
|
474
|
-
},
|
|
475
|
-
],
|
|
476
|
-
exhaustedSide,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
// Ask is maker
|
|
480
|
-
if (askOrder.postOnly) {
|
|
481
|
-
return {
|
|
482
|
-
crossingNodes: [
|
|
483
|
-
{
|
|
484
|
-
node: bidNode,
|
|
485
|
-
makerNode: askNode,
|
|
486
|
-
},
|
|
487
|
-
],
|
|
488
|
-
exhaustedSide,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
// Both are takers
|
|
492
|
-
// older order is maker
|
|
493
|
-
const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
|
|
494
|
-
? [askNode, bidNode]
|
|
495
|
-
: [bidNode, askNode];
|
|
496
550
|
return {
|
|
497
551
|
crossingNodes: [
|
|
498
552
|
{
|
|
499
|
-
node:
|
|
500
|
-
makerNode:
|
|
553
|
+
node: takerNode,
|
|
554
|
+
makerNode: makerNode,
|
|
501
555
|
},
|
|
502
556
|
],
|
|
503
557
|
exhaustedSide,
|
|
504
558
|
};
|
|
505
559
|
}
|
|
506
|
-
|
|
507
|
-
|
|
560
|
+
determineMakerAndTaker(askNode, bidNode) {
|
|
561
|
+
if (bidNode.order.postOnly) {
|
|
562
|
+
return {
|
|
563
|
+
takerNode: askNode,
|
|
564
|
+
makerNode: bidNode,
|
|
565
|
+
makerSide: 'bid',
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
else if (askNode.order.postOnly) {
|
|
569
|
+
return {
|
|
570
|
+
takerNode: bidNode,
|
|
571
|
+
makerNode: askNode,
|
|
572
|
+
makerSide: 'ask',
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
else if ((0, __1.isMarketOrder)(bidNode.order) && (0, __1.isLimitOrder)(askNode.order)) {
|
|
576
|
+
return {
|
|
577
|
+
takerNode: bidNode,
|
|
578
|
+
makerNode: askNode,
|
|
579
|
+
makerSide: 'ask',
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
else if ((0, __1.isMarketOrder)(askNode.order) && (0, __1.isLimitOrder)(bidNode.order)) {
|
|
583
|
+
return {
|
|
584
|
+
takerNode: askNode,
|
|
585
|
+
makerNode: bidNode,
|
|
586
|
+
makerSide: 'bid',
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
else if (askNode.order.slot.lt(bidNode.order.slot)) {
|
|
590
|
+
return {
|
|
591
|
+
takerNode: bidNode,
|
|
592
|
+
makerNode: askNode,
|
|
593
|
+
makerSide: 'ask',
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
return {
|
|
598
|
+
takerNode: askNode,
|
|
599
|
+
makerNode: bidNode,
|
|
600
|
+
makerSide: 'bid',
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
605
|
+
return this.getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData)
|
|
508
606
|
.next()
|
|
509
607
|
.value.getPrice(oraclePriceData, slot);
|
|
510
608
|
}
|
|
511
|
-
getBestBid(marketIndex,
|
|
512
|
-
return this.getBids(marketIndex,
|
|
609
|
+
getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
|
|
610
|
+
return this.getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData)
|
|
513
611
|
.next()
|
|
514
612
|
.value.getPrice(oraclePriceData, slot);
|
|
515
613
|
}
|
|
516
614
|
findNodesToTrigger(marketIndex, slot, oraclePrice, marketType) {
|
|
615
|
+
if ((0, exchangeStatus_1.exchangePaused)(this.stateAccount)) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
517
618
|
const nodesToTrigger = [];
|
|
518
619
|
const marketTypeStr = (0, __1.getVariant)(marketType);
|
|
519
620
|
for (const node of this.orderLists
|
|
@@ -552,11 +653,11 @@ class DLOB {
|
|
|
552
653
|
if ((0, __1.isVariant)(marketType, 'perp')) {
|
|
553
654
|
const market = clearingHouse.getPerpMarketAccount(marketIndex);
|
|
554
655
|
const slot = slotSubscriber.getSlot();
|
|
555
|
-
const oraclePriceData = clearingHouse.
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const bestAsk = this.getBestAsk(marketIndex,
|
|
559
|
-
const bestBid = this.getBestBid(marketIndex,
|
|
656
|
+
const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
|
|
657
|
+
const fallbackAsk = (0, __1.calculateAskPrice)(market, oraclePriceData);
|
|
658
|
+
const fallbackBid = (0, __1.calculateBidPrice)(market, oraclePriceData);
|
|
659
|
+
const bestAsk = this.getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData);
|
|
660
|
+
const bestBid = this.getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData);
|
|
560
661
|
const mid = bestAsk.add(bestBid).div(new __1.BN(2));
|
|
561
662
|
const bidSpread = ((0, __1.convertToNumber)(bestBid, __1.PRICE_PRECISION) /
|
|
562
663
|
(0, __1.convertToNumber)(oraclePriceData.price, __1.PRICE_PRECISION) -
|
|
@@ -573,7 +674,7 @@ class DLOB {
|
|
|
573
674
|
}
|
|
574
675
|
else if ((0, __1.isVariant)(marketType, 'spot')) {
|
|
575
676
|
const slot = slotSubscriber.getSlot();
|
|
576
|
-
const oraclePriceData = clearingHouse.
|
|
677
|
+
const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
|
|
577
678
|
const bestAsk = this.getBestAsk(marketIndex, undefined, slot, marketType, oraclePriceData);
|
|
578
679
|
const bestBid = this.getBestBid(marketIndex, undefined, slot, marketType, oraclePriceData);
|
|
579
680
|
const mid = bestAsk.add(bestBid).div(new __1.BN(2));
|
package/lib/dlob/DLOBNode.js
CHANGED
|
@@ -26,15 +26,7 @@ class OrderNode {
|
|
|
26
26
|
return msg;
|
|
27
27
|
}
|
|
28
28
|
getPrice(oraclePriceData, slot) {
|
|
29
|
-
|
|
30
|
-
return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot);
|
|
31
|
-
}
|
|
32
|
-
else if ((0, __1.isOneOfVariant)(this.order.marketType, ['perp'])) {
|
|
33
|
-
return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot, this.market);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
console.error(`Unknown market type: ${this.order.marketType}`);
|
|
37
|
-
}
|
|
29
|
+
return (0, __1.getLimitPrice)(this.order, oraclePriceData, slot);
|
|
38
30
|
}
|
|
39
31
|
isBaseFilled() {
|
|
40
32
|
return this.order.baseAssetAmountFilled.eq(this.order.baseAssetAmount);
|
|
@@ -52,7 +44,7 @@ class LimitOrderNode extends OrderNode {
|
|
|
52
44
|
exports.LimitOrderNode = LimitOrderNode;
|
|
53
45
|
class FloatingLimitOrderNode extends OrderNode {
|
|
54
46
|
getSortValue(order) {
|
|
55
|
-
return order.oraclePriceOffset;
|
|
47
|
+
return new __1.BN(order.oraclePriceOffset);
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
50
|
exports.FloatingLimitOrderNode = FloatingLimitOrderNode;
|
package/lib/dlob/NodeList.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class NodeList<NodeType extends keyof DLOBNodeMap> implements DLO
|
|
|
14
14
|
length: number;
|
|
15
15
|
nodeMap: Map<string, DLOBNodeMap[NodeType]>;
|
|
16
16
|
constructor(nodeType: NodeType, sortDirection: SortDirection);
|
|
17
|
+
clear(): void;
|
|
17
18
|
insert(order: Order, marketType: MarketTypeStr, market: PerpMarketAccount | SpotMarketAccount, userAccount: PublicKey): void;
|
|
18
19
|
prependNode(currentNode: DLOBNodeMap[NodeType], newNode: DLOBNodeMap[NodeType]): boolean;
|
|
19
20
|
update(order: Order, userAccount: PublicKey): void;
|
package/lib/dlob/NodeList.js
CHANGED
|
@@ -14,6 +14,11 @@ class NodeList {
|
|
|
14
14
|
this.length = 0;
|
|
15
15
|
this.nodeMap = new Map();
|
|
16
16
|
}
|
|
17
|
+
clear() {
|
|
18
|
+
this.head = undefined;
|
|
19
|
+
this.length = 0;
|
|
20
|
+
this.nodeMap.clear();
|
|
21
|
+
}
|
|
17
22
|
insert(order, marketType, market, userAccount) {
|
|
18
23
|
if ((0, __1.isVariant)(order.status, 'init')) {
|
|
19
24
|
return;
|
|
@@ -59,7 +64,7 @@ class NodeList {
|
|
|
59
64
|
const currentOrderSortPrice = currentNode.sortValue;
|
|
60
65
|
const newOrderSortPrice = newNode.sortValue;
|
|
61
66
|
if (newOrderSortPrice.eq(currentOrderSortPrice)) {
|
|
62
|
-
return newOrder.
|
|
67
|
+
return newOrder.slot.lt(currentOrder.slot);
|
|
63
68
|
}
|
|
64
69
|
if (this.sortDirection === 'asc') {
|
|
65
70
|
return newOrderSortPrice.lt(currentOrderSortPrice);
|
|
@@ -16,6 +16,7 @@ export declare class EventSubscriber {
|
|
|
16
16
|
private logProvider;
|
|
17
17
|
eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
|
|
18
18
|
private lastSeenSlot;
|
|
19
|
+
private lastSeenBlockTime;
|
|
19
20
|
lastSeenTxSig: string;
|
|
20
21
|
constructor(connection: Connection, program: Program, options?: EventSubscriptionOptions);
|
|
21
22
|
subscribe(): Promise<boolean>;
|
|
@@ -35,8 +35,8 @@ class EventSubscriber {
|
|
|
35
35
|
if (this.logProvider.isSubscribed()) {
|
|
36
36
|
return true;
|
|
37
37
|
}
|
|
38
|
-
this.logProvider.subscribe((txSig, slot, logs) => {
|
|
39
|
-
this.handleTxLogs(txSig, slot, logs);
|
|
38
|
+
this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
|
|
39
|
+
this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
|
|
40
40
|
}, true);
|
|
41
41
|
return true;
|
|
42
42
|
}
|
|
@@ -46,13 +46,16 @@ class EventSubscriber {
|
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
handleTxLogs(txSig, slot, logs) {
|
|
49
|
+
handleTxLogs(txSig, slot, logs, mostRecentBlockTime) {
|
|
50
50
|
if (this.txEventCache.has(txSig)) {
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
|
|
54
54
|
for (const wrappedEvent of wrappedEvents) {
|
|
55
55
|
this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
|
|
56
|
+
}
|
|
57
|
+
// dont emit event till we've added all the events to the eventListMap
|
|
58
|
+
for (const wrappedEvent of wrappedEvents) {
|
|
56
59
|
this.eventEmitter.emit('newEvent', wrappedEvent);
|
|
57
60
|
}
|
|
58
61
|
if (this.awaitTxPromises.has(txSig)) {
|
|
@@ -63,6 +66,10 @@ class EventSubscriber {
|
|
|
63
66
|
if (slot > this.lastSeenSlot) {
|
|
64
67
|
this.lastSeenTxSig = txSig;
|
|
65
68
|
}
|
|
69
|
+
if (this.lastSeenBlockTime === undefined ||
|
|
70
|
+
mostRecentBlockTime > this.lastSeenBlockTime) {
|
|
71
|
+
this.lastSeenBlockTime = mostRecentBlockTime;
|
|
72
|
+
}
|
|
66
73
|
this.txEventCache.add(txSig, wrappedEvents);
|
|
67
74
|
}
|
|
68
75
|
async fetchPreviousTx(fetchMax) {
|
|
@@ -80,7 +87,7 @@ class EventSubscriber {
|
|
|
80
87
|
txFetched += response.transactionLogs.length;
|
|
81
88
|
beforeTx = response.earliestTx;
|
|
82
89
|
for (const { txSig, slot, logs } of response.transactionLogs) {
|
|
83
|
-
this.handleTxLogs(txSig, slot, logs);
|
|
90
|
+
this.handleTxLogs(txSig, slot, logs, response.mostRecentBlockTime);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Program } from '@project-serum/anchor';
|
|
2
|
-
import { Connection, Finality, PublicKey, TransactionSignature } from '@solana/web3.js';
|
|
2
|
+
import { Connection, Finality, PublicKey, TransactionResponse, TransactionSignature } from '@solana/web3.js';
|
|
3
3
|
import { WrappedEvents } from './types';
|
|
4
4
|
declare type Log = {
|
|
5
5
|
txSig: TransactionSignature;
|
|
@@ -12,11 +12,13 @@ declare type FetchLogsResponse = {
|
|
|
12
12
|
earliestSlot: number;
|
|
13
13
|
mostRecentSlot: number;
|
|
14
14
|
transactionLogs: Log[];
|
|
15
|
+
mostRecentBlockTime: number | undefined;
|
|
15
16
|
};
|
|
16
17
|
export declare function fetchLogs(connection: Connection, programId: PublicKey, finality: Finality, beforeTx?: TransactionSignature, untilTx?: TransactionSignature, limit?: number): Promise<FetchLogsResponse>;
|
|
17
18
|
export declare class LogParser {
|
|
18
19
|
private program;
|
|
19
20
|
constructor(program: Program);
|
|
21
|
+
parseEventsFromTransaction(transaction: TransactionResponse): WrappedEvents;
|
|
20
22
|
parseEventsFromLogs(event: Log): WrappedEvents;
|
|
21
23
|
}
|
|
22
24
|
export {};
|