@drift-labs/sdk 0.2.0-master.32 → 0.2.0-master.34
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 +18 -11
- package/lib/dlob/DLOB.js +179 -107
- package/lib/dlob/DLOBNode.js +2 -10
- package/lib/dlob/NodeList.js +1 -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 +1540 -1246
- 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 +60 -75
- package/lib/types.js +2 -1
- 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/assert/assert.js +9 -0
- 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 +257 -120
- package/src/dlob/DLOBNode.ts +2 -14
- package/src/dlob/NodeList.ts +1 -1
- package/src/events/eventList.js +77 -0
- 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/examples/makeTradeExample.js +157 -0
- package/src/factory/bigNum.ts +59 -6
- package/src/idl/clearing_house.json +1540 -1246
- 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/token/index.js +38 -0
- package/src/tx/types.js +2 -0
- package/src/tx/utils.js +17 -0
- package/src/types.ts +65 -51
- package/src/userMap/userMap.ts +25 -1
- package/src/userName.ts +2 -1
- package/src/util/computeUnits.js +27 -0
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/promiseTimeout.js +14 -0
- package/src/util/tps.js +27 -0
- package/tests/bn/test.ts +22 -1
- package/tests/dlob/helpers.ts +252 -81
- package/tests/dlob/test.ts +1040 -219
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());
|
|
@@ -94,32 +98,18 @@ class DLOB {
|
|
|
94
98
|
/**
|
|
95
99
|
* initializes a new DLOB instance
|
|
96
100
|
*
|
|
97
|
-
* @param clearingHouse The ClearingHouse instance to use for price data
|
|
98
101
|
* @returns a promise that resolves when the DLOB is initialized
|
|
99
102
|
*/
|
|
100
|
-
async init(
|
|
103
|
+
async init() {
|
|
101
104
|
if (this.initialized) {
|
|
102
105
|
return false;
|
|
103
106
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.insertOrder(order, userAccountPubkey);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
const programAccounts = await clearingHouse.program.account.user.all();
|
|
116
|
-
for (const programAccount of programAccounts) {
|
|
117
|
-
// @ts-ignore
|
|
118
|
-
const userAccount = programAccount.account;
|
|
119
|
-
const userAccountPublicKey = programAccount.publicKey;
|
|
120
|
-
for (const order of userAccount.orders) {
|
|
121
|
-
this.insertOrder(order, userAccountPublicKey);
|
|
122
|
-
}
|
|
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);
|
|
123
113
|
}
|
|
124
114
|
}
|
|
125
115
|
this.initialized = true;
|
|
@@ -165,7 +155,7 @@ class DLOB {
|
|
|
165
155
|
else if ((0, __1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket'])) {
|
|
166
156
|
type = 'market';
|
|
167
157
|
}
|
|
168
|
-
else if (
|
|
158
|
+
else if (order.oraclePriceOffset !== 0) {
|
|
169
159
|
type = 'floatingLimit';
|
|
170
160
|
}
|
|
171
161
|
else {
|
|
@@ -184,13 +174,22 @@ class DLOB {
|
|
|
184
174
|
}
|
|
185
175
|
return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
|
|
186
176
|
}
|
|
187
|
-
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
|
+
}
|
|
188
184
|
// Find all the crossing nodes
|
|
189
185
|
const crossingNodesToFill = this.findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData);
|
|
190
|
-
|
|
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
|
+
}
|
|
191
190
|
// get expired market nodes
|
|
192
|
-
const
|
|
193
|
-
return crossingNodesToFill.concat(
|
|
191
|
+
const expiredNodesToFill = this.findExpiredNodesToFill(marketIndex, ts, marketType);
|
|
192
|
+
return crossingNodesToFill.concat(fallbackCrossingNodesToFill, expiredNodesToFill);
|
|
194
193
|
}
|
|
195
194
|
findCrossingNodesToFill(marketIndex, slot, marketType, oraclePriceData) {
|
|
196
195
|
const nodesToFill = new Array();
|
|
@@ -226,7 +225,8 @@ class DLOB {
|
|
|
226
225
|
}
|
|
227
226
|
return nodesToFill;
|
|
228
227
|
}
|
|
229
|
-
|
|
228
|
+
findFallbackCrossingNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
229
|
+
var _a, _b;
|
|
230
230
|
const nodesToFill = new Array();
|
|
231
231
|
const askGenerator = this.getAsks(marketIndex, undefined, // dont include vask
|
|
232
232
|
slot, marketType, oraclePriceData);
|
|
@@ -234,53 +234,79 @@ class DLOB {
|
|
|
234
234
|
slot, marketType, oraclePriceData);
|
|
235
235
|
let nextAsk = askGenerator.next();
|
|
236
236
|
let nextBid = bidGenerator.next();
|
|
237
|
-
// check for asks that cross
|
|
238
|
-
while (!nextAsk.done) {
|
|
237
|
+
// check for asks that cross fallbackBid
|
|
238
|
+
while (!nextAsk.done && fallbackBid !== undefined) {
|
|
239
239
|
const askNode = nextAsk.value;
|
|
240
|
-
|
|
241
|
-
|
|
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) {
|
|
242
250
|
nodesToFill.push({
|
|
243
251
|
node: askNode,
|
|
244
|
-
makerNode: undefined, // filled by
|
|
252
|
+
makerNode: undefined, // filled by fallback
|
|
245
253
|
});
|
|
246
254
|
}
|
|
247
|
-
else {
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
255
|
nextAsk = askGenerator.next();
|
|
251
256
|
}
|
|
252
|
-
// check for bids that cross
|
|
253
|
-
while (!nextBid.done) {
|
|
257
|
+
// check for bids that cross fallbackAsk
|
|
258
|
+
while (!nextBid.done && fallbackAsk !== undefined) {
|
|
254
259
|
const bidNode = nextBid.value;
|
|
255
|
-
|
|
256
|
-
|
|
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) {
|
|
257
270
|
nodesToFill.push({
|
|
258
271
|
node: bidNode,
|
|
259
|
-
makerNode: undefined, // filled by
|
|
272
|
+
makerNode: undefined, // filled by fallback
|
|
260
273
|
});
|
|
261
274
|
}
|
|
262
|
-
else {
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
265
275
|
nextBid = bidGenerator.next();
|
|
266
276
|
}
|
|
267
277
|
return nodesToFill;
|
|
268
278
|
}
|
|
269
|
-
|
|
279
|
+
findExpiredNodesToFill(marketIndex, ts, marketType) {
|
|
270
280
|
const nodesToFill = new Array();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
+
}
|
|
277
301
|
}
|
|
278
302
|
}
|
|
279
|
-
for (const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
+
}
|
|
284
310
|
}
|
|
285
311
|
}
|
|
286
312
|
return nodesToFill;
|
|
@@ -318,7 +344,7 @@ class DLOB {
|
|
|
318
344
|
.get(marketIndex)
|
|
319
345
|
.market.ask.getGenerator();
|
|
320
346
|
}
|
|
321
|
-
*getAsks(marketIndex,
|
|
347
|
+
*getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
|
|
322
348
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
323
349
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
324
350
|
}
|
|
@@ -329,8 +355,8 @@ class DLOB {
|
|
|
329
355
|
nodeLists.floatingLimit.ask.getGenerator(),
|
|
330
356
|
nodeLists.market.ask.getGenerator(),
|
|
331
357
|
];
|
|
332
|
-
if (marketTypeStr === 'perp' &&
|
|
333
|
-
generatorList.push((0, NodeList_1.getVammNodeGenerator)(
|
|
358
|
+
if (marketTypeStr === 'perp' && fallbackAsk) {
|
|
359
|
+
generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackAsk));
|
|
334
360
|
}
|
|
335
361
|
if (generatorList.length === 0) {
|
|
336
362
|
throw new Error('No ask generators found');
|
|
@@ -362,6 +388,15 @@ class DLOB {
|
|
|
362
388
|
bestGenerator.next = bestGenerator.generator.next();
|
|
363
389
|
continue;
|
|
364
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
|
+
}
|
|
365
400
|
yield bestGenerator.next.value;
|
|
366
401
|
bestGenerator.next = bestGenerator.generator.next();
|
|
367
402
|
}
|
|
@@ -370,7 +405,7 @@ class DLOB {
|
|
|
370
405
|
}
|
|
371
406
|
}
|
|
372
407
|
}
|
|
373
|
-
*getBids(marketIndex,
|
|
408
|
+
*getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
|
|
374
409
|
if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
|
|
375
410
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
376
411
|
}
|
|
@@ -381,8 +416,8 @@ class DLOB {
|
|
|
381
416
|
nodeLists.floatingLimit.bid.getGenerator(),
|
|
382
417
|
nodeLists.market.bid.getGenerator(),
|
|
383
418
|
];
|
|
384
|
-
if (marketTypeStr === 'perp' &&
|
|
385
|
-
generatorList.push((0, NodeList_1.getVammNodeGenerator)(
|
|
419
|
+
if (marketTypeStr === 'perp' && fallbackBid) {
|
|
420
|
+
generatorList.push((0, NodeList_1.getVammNodeGenerator)(fallbackBid));
|
|
386
421
|
}
|
|
387
422
|
if (generatorList.length === 0) {
|
|
388
423
|
throw new Error('No bid generators found');
|
|
@@ -393,7 +428,7 @@ class DLOB {
|
|
|
393
428
|
generator,
|
|
394
429
|
};
|
|
395
430
|
});
|
|
396
|
-
let bidsExhausted = false; // there will always be the
|
|
431
|
+
let bidsExhausted = false; // there will always be the fallbackBid
|
|
397
432
|
while (!bidsExhausted) {
|
|
398
433
|
const bestGenerator = bidGenerators.reduce((bestGenerator, currentGenerator) => {
|
|
399
434
|
if (currentGenerator.next.done) {
|
|
@@ -414,6 +449,15 @@ class DLOB {
|
|
|
414
449
|
bestGenerator.next = bestGenerator.generator.next();
|
|
415
450
|
continue;
|
|
416
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
|
+
}
|
|
417
461
|
yield bestGenerator.next.value;
|
|
418
462
|
bestGenerator.next = bestGenerator.generator.next();
|
|
419
463
|
}
|
|
@@ -434,9 +478,11 @@ class DLOB {
|
|
|
434
478
|
}
|
|
435
479
|
const bidOrder = bidNode.order;
|
|
436
480
|
const askOrder = askNode.order;
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
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)) {
|
|
440
486
|
// don't have a principle way to pick which one to exhaust,
|
|
441
487
|
// exhaust each one 50% of the time so we can try each one against other orders
|
|
442
488
|
const exhaustedSide = Math.random() < 0.5 ? 'bid' : 'ask';
|
|
@@ -445,6 +491,14 @@ class DLOB {
|
|
|
445
491
|
exhaustedSide,
|
|
446
492
|
};
|
|
447
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
|
+
}
|
|
448
502
|
const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
|
|
449
503
|
const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
|
|
450
504
|
let exhaustedSide;
|
|
@@ -493,56 +547,74 @@ class DLOB {
|
|
|
493
547
|
newAskOrder.baseAssetAmountFilled = askOrder.baseAssetAmount;
|
|
494
548
|
this.getListForOrder(newAskOrder).update(newAskOrder, askNode.userAccount);
|
|
495
549
|
}
|
|
496
|
-
// Bid is maker
|
|
497
|
-
if (bidOrder.postOnly) {
|
|
498
|
-
return {
|
|
499
|
-
crossingNodes: [
|
|
500
|
-
{
|
|
501
|
-
node: askNode,
|
|
502
|
-
makerNode: bidNode,
|
|
503
|
-
},
|
|
504
|
-
],
|
|
505
|
-
exhaustedSide,
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
// Ask is maker
|
|
509
|
-
if (askOrder.postOnly) {
|
|
510
|
-
return {
|
|
511
|
-
crossingNodes: [
|
|
512
|
-
{
|
|
513
|
-
node: bidNode,
|
|
514
|
-
makerNode: askNode,
|
|
515
|
-
},
|
|
516
|
-
],
|
|
517
|
-
exhaustedSide,
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
// Both are takers
|
|
521
|
-
// older order is maker
|
|
522
|
-
const [olderNode, newerNode] = askOrder.ts.lt(bidOrder.ts)
|
|
523
|
-
? [askNode, bidNode]
|
|
524
|
-
: [bidNode, askNode];
|
|
525
550
|
return {
|
|
526
551
|
crossingNodes: [
|
|
527
552
|
{
|
|
528
|
-
node:
|
|
529
|
-
makerNode:
|
|
553
|
+
node: takerNode,
|
|
554
|
+
makerNode: makerNode,
|
|
530
555
|
},
|
|
531
556
|
],
|
|
532
557
|
exhaustedSide,
|
|
533
558
|
};
|
|
534
559
|
}
|
|
535
|
-
|
|
536
|
-
|
|
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)
|
|
537
606
|
.next()
|
|
538
607
|
.value.getPrice(oraclePriceData, slot);
|
|
539
608
|
}
|
|
540
|
-
getBestBid(marketIndex,
|
|
541
|
-
return this.getBids(marketIndex,
|
|
609
|
+
getBestBid(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
|
|
610
|
+
return this.getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData)
|
|
542
611
|
.next()
|
|
543
612
|
.value.getPrice(oraclePriceData, slot);
|
|
544
613
|
}
|
|
545
614
|
findNodesToTrigger(marketIndex, slot, oraclePrice, marketType) {
|
|
615
|
+
if ((0, exchangeStatus_1.exchangePaused)(this.stateAccount)) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
546
618
|
const nodesToTrigger = [];
|
|
547
619
|
const marketTypeStr = (0, __1.getVariant)(marketType);
|
|
548
620
|
for (const node of this.orderLists
|
|
@@ -581,11 +653,11 @@ class DLOB {
|
|
|
581
653
|
if ((0, __1.isVariant)(marketType, 'perp')) {
|
|
582
654
|
const market = clearingHouse.getPerpMarketAccount(marketIndex);
|
|
583
655
|
const slot = slotSubscriber.getSlot();
|
|
584
|
-
const oraclePriceData = clearingHouse.
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
const bestAsk = this.getBestAsk(marketIndex,
|
|
588
|
-
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);
|
|
589
661
|
const mid = bestAsk.add(bestBid).div(new __1.BN(2));
|
|
590
662
|
const bidSpread = ((0, __1.convertToNumber)(bestBid, __1.PRICE_PRECISION) /
|
|
591
663
|
(0, __1.convertToNumber)(oraclePriceData.price, __1.PRICE_PRECISION) -
|
|
@@ -602,7 +674,7 @@ class DLOB {
|
|
|
602
674
|
}
|
|
603
675
|
else if ((0, __1.isVariant)(marketType, 'spot')) {
|
|
604
676
|
const slot = slotSubscriber.getSlot();
|
|
605
|
-
const oraclePriceData = clearingHouse.
|
|
677
|
+
const oraclePriceData = clearingHouse.getOracleDataForPerpMarket(marketIndex);
|
|
606
678
|
const bestAsk = this.getBestAsk(marketIndex, undefined, slot, marketType, oraclePriceData);
|
|
607
679
|
const bestBid = this.getBestBid(marketIndex, undefined, slot, marketType, oraclePriceData);
|
|
608
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.js
CHANGED
|
@@ -64,7 +64,7 @@ class NodeList {
|
|
|
64
64
|
const currentOrderSortPrice = currentNode.sortValue;
|
|
65
65
|
const newOrderSortPrice = newNode.sortValue;
|
|
66
66
|
if (newOrderSortPrice.eq(currentOrderSortPrice)) {
|
|
67
|
-
return newOrder.
|
|
67
|
+
return newOrder.slot.lt(currentOrder.slot);
|
|
68
68
|
}
|
|
69
69
|
if (this.sortDirection === 'asc') {
|
|
70
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 {};
|
package/lib/events/fetchLogs.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LogParser = exports.fetchLogs = void 0;
|
|
4
|
+
function mapTransactionResponseToLog(transaction) {
|
|
5
|
+
return {
|
|
6
|
+
txSig: transaction.transaction.signatures[0],
|
|
7
|
+
slot: transaction.slot,
|
|
8
|
+
logs: transaction.meta.logMessages,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
4
11
|
async function fetchLogs(connection, programId, finality, beforeTx, untilTx, limit) {
|
|
5
12
|
const signatures = await connection.getSignaturesForAddress(programId, {
|
|
6
13
|
before: beforeTx,
|
|
@@ -16,11 +23,7 @@ async function fetchLogs(connection, programId, finality, beforeTx, untilTx, lim
|
|
|
16
23
|
const transactionLogs = (await Promise.all(chunkedSignatures.map(async (chunk) => {
|
|
17
24
|
const transactions = await connection.getTransactions(chunk.map((confirmedSignature) => confirmedSignature.signature), finality);
|
|
18
25
|
return transactions.map((transaction) => {
|
|
19
|
-
return
|
|
20
|
-
txSig: transaction.transaction.signatures[0],
|
|
21
|
-
slot: transaction.slot,
|
|
22
|
-
logs: transaction.meta.logMessages,
|
|
23
|
-
};
|
|
26
|
+
return mapTransactionResponseToLog(transaction);
|
|
24
27
|
});
|
|
25
28
|
}))).flat();
|
|
26
29
|
const earliest = filteredSignatures[0];
|
|
@@ -31,6 +34,7 @@ async function fetchLogs(connection, programId, finality, beforeTx, untilTx, lim
|
|
|
31
34
|
mostRecentTx: mostRecent.signature,
|
|
32
35
|
earliestSlot: earliest.slot,
|
|
33
36
|
mostRecentSlot: mostRecent.slot,
|
|
37
|
+
mostRecentBlockTime: mostRecent.blockTime,
|
|
34
38
|
};
|
|
35
39
|
}
|
|
36
40
|
exports.fetchLogs = fetchLogs;
|
|
@@ -44,6 +48,10 @@ class LogParser {
|
|
|
44
48
|
constructor(program) {
|
|
45
49
|
this.program = program;
|
|
46
50
|
}
|
|
51
|
+
parseEventsFromTransaction(transaction) {
|
|
52
|
+
const transactionLogObject = mapTransactionResponseToLog(transaction);
|
|
53
|
+
return this.parseEventsFromLogs(transactionLogObject);
|
|
54
|
+
}
|
|
47
55
|
parseEventsFromLogs(event) {
|
|
48
56
|
const records = [];
|
|
49
57
|
// @ts-ignore
|
|
@@ -29,7 +29,7 @@ class PollingLogProvider {
|
|
|
29
29
|
}
|
|
30
30
|
const { mostRecentTx, transactionLogs } = response;
|
|
31
31
|
for (const { txSig, slot, logs } of transactionLogs) {
|
|
32
|
-
callback(txSig, slot, logs);
|
|
32
|
+
callback(txSig, slot, logs, response.mostRecentBlockTime);
|
|
33
33
|
}
|
|
34
34
|
this.mostRecentSeenTx = mostRecentTx;
|
|
35
35
|
}
|
package/lib/events/types.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface EventSubscriberEvents {
|
|
|
39
39
|
newEvent: (event: WrappedEvent<EventType>) => void;
|
|
40
40
|
}
|
|
41
41
|
export declare type SortFn = (currentRecord: EventMap[EventType], newRecord: EventMap[EventType]) => 'less than' | 'greater than';
|
|
42
|
-
export declare type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[]) => void;
|
|
42
|
+
export declare type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[], mostRecentBlockTime: number | undefined) => void;
|
|
43
43
|
export interface LogProvider {
|
|
44
44
|
isSubscribed(): boolean;
|
|
45
45
|
subscribe(callback: logProviderCallback, skipHistory?: boolean): boolean;
|
|
@@ -12,7 +12,7 @@ class WebSocketLogProvider {
|
|
|
12
12
|
return true;
|
|
13
13
|
}
|
|
14
14
|
this.subscriptionId = this.connection.onLogs(this.programId, (logs, ctx) => {
|
|
15
|
-
callback(logs.signature, ctx.slot, logs.logs);
|
|
15
|
+
callback(logs.signature, ctx.slot, logs.logs, undefined);
|
|
16
16
|
}, this.commitment);
|
|
17
17
|
return true;
|
|
18
18
|
}
|