@drift-labs/sdk 2.18.0-beta.0 → 2.18.0-beta.1

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.
@@ -44,6 +44,16 @@ exports.DevnetPerpMarkets = [
44
44
  launchTs: 1675610186000,
45
45
  oracleSource: __1.OracleSource.PYTH,
46
46
  },
47
+ {
48
+ fullName: 'Bonk',
49
+ category: ['Meme'],
50
+ symbol: 'BONK-PERP',
51
+ baseAssetSymbol: 'BONK',
52
+ marketIndex: 4,
53
+ oracle: new web3_js_1.PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
54
+ launchTs: 1677068931000,
55
+ oracleSource: __1.OracleSource.PYTH_1000,
56
+ },
47
57
  ];
48
58
  exports.MainnetPerpMarkets = [
49
59
  {
@@ -92,7 +92,7 @@ export declare class DLOB {
92
92
  determineMakerAndTaker(askNode: DLOBNode, bidNode: DLOBNode): {
93
93
  takerNode: DLOBNode;
94
94
  makerNode: DLOBNode;
95
- };
95
+ } | undefined;
96
96
  getBestAsk(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
97
97
  getBestBid(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
98
98
  findNodesToTrigger(marketIndex: number, slot: number, oraclePrice: BN, marketType: MarketType, stateAccount: StateAccount): NodeToTrigger[];
package/lib/dlob/DLOB.js CHANGED
@@ -714,21 +714,27 @@ class DLOB {
714
714
  findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData, minAuctionDuration, fallbackAsk, fallbackBid) {
715
715
  const nodesToFill = new Array();
716
716
  for (const askNode of this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData)) {
717
- for (const bidNode of this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData)) {
717
+ const bidGenerator = this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData);
718
+ for (const bidNode of bidGenerator) {
718
719
  const bidPrice = bidNode.getPrice(oraclePriceData, slot);
719
720
  const askPrice = askNode.getPrice(oraclePriceData, slot);
720
- // orders don't cross - we're done walking the book
721
+ // orders don't cross
721
722
  if (bidPrice.lt(askPrice)) {
722
- return nodesToFill;
723
+ break;
723
724
  }
724
725
  const bidOrder = bidNode.order;
725
726
  const askOrder = askNode.order;
726
727
  // Can't match orders from the same user
727
728
  const sameUser = bidNode.userAccount.equals(askNode.userAccount);
728
- if (sameUser || (bidOrder.postOnly && askOrder.postOnly)) {
729
+ if (sameUser) {
730
+ continue;
731
+ }
732
+ const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
733
+ // unable to match maker and taker due to post only or slot
734
+ if (!makerAndTaker) {
729
735
  continue;
730
736
  }
731
- const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
737
+ const { takerNode, makerNode } = makerAndTaker;
732
738
  // extra guard against bad fills for limit orders where auction is incomplete
733
739
  if (!__1.isFallbackAvailableLiquiditySource(takerNode.order, minAuctionDuration, slot)) {
734
740
  let bidPrice;
@@ -769,30 +775,23 @@ class DLOB {
769
775
  return nodesToFill;
770
776
  }
771
777
  determineMakerAndTaker(askNode, bidNode) {
772
- if (bidNode.order.postOnly) {
773
- return {
774
- takerNode: askNode,
775
- makerNode: bidNode,
776
- };
777
- }
778
- else if (askNode.order.postOnly) {
778
+ const askSlot = askNode.order.slot.add(new __1.BN(askNode.order.auctionDuration));
779
+ const bidSlot = bidNode.order.slot.add(new __1.BN(bidNode.order.auctionDuration));
780
+ if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
779
781
  return {
780
782
  takerNode: bidNode,
781
783
  makerNode: askNode,
782
784
  };
783
785
  }
784
- else if (askNode.order.slot.lt(bidNode.order.slot)) {
785
- return {
786
- takerNode: bidNode,
787
- makerNode: askNode,
788
- };
789
- }
790
- else {
786
+ else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
791
787
  return {
792
788
  takerNode: askNode,
793
789
  makerNode: bidNode,
794
790
  };
795
791
  }
792
+ else {
793
+ return undefined;
794
+ }
796
795
  }
797
796
  getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
798
797
  return this.getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData)
@@ -5,10 +5,14 @@ const types_1 = require("../types");
5
5
  const pythClient_1 = require("../oracles/pythClient");
6
6
  // import { SwitchboardClient } from '../oracles/switchboardClient';
7
7
  const quoteAssetOracleClient_1 = require("../oracles/quoteAssetOracleClient");
8
+ const anchor_1 = require("@project-serum/anchor");
8
9
  function getOracleClient(oracleSource, connection) {
9
10
  if (types_1.isVariant(oracleSource, 'pyth')) {
10
11
  return new pythClient_1.PythClient(connection);
11
12
  }
13
+ if (types_1.isVariant(oracleSource, 'pyth1000')) {
14
+ return new pythClient_1.PythClient(connection, new anchor_1.BN(1000));
15
+ }
12
16
  // if (isVariant(oracleSource, 'switchboard')) {
13
17
  // return new SwitchboardClient(connection);
14
18
  // }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.18.0-beta.0",
2
+ "version": "2.18.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -6237,6 +6237,9 @@
6237
6237
  {
6238
6238
  "name": "Pyth"
6239
6239
  },
6240
+ {
6241
+ "name": "Pyth1000"
6242
+ },
6240
6243
  {
6241
6244
  "name": "Switchboard"
6242
6245
  },
@@ -9,6 +9,9 @@ import { Orderbook } from '@project-serum/serum';
9
9
  export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' | 'priceDeltaAsNumber' | 'pctAvg' | 'pctMax' | 'quoteAssetAmount' | 'quoteAssetAmountPeg' | 'acquiredBaseAssetAmount' | 'acquiredQuoteAssetAmount' | 'all';
10
10
  /**
11
11
  * Calculates avg/max slippage (price impact) for candidate trade
12
+ *
13
+ * @deprecated use calculateEstimatedPerpEntryPrice instead
14
+ *
12
15
  * @param direction
13
16
  * @param amount
14
17
  * @param market
@@ -40,6 +43,9 @@ export declare function calculateTradeAcquiredAmounts(direction: PositionDirecti
40
43
  /**
41
44
  * calculateTargetPriceTrade
42
45
  * simple function for finding arbitraging trades
46
+ *
47
+ * @deprecated
48
+ *
43
49
  * @param market
44
50
  * @param targetPrice
45
51
  * @param pct optional default is 100% gap filling, can set smaller.
package/lib/math/trade.js CHANGED
@@ -12,6 +12,9 @@ const types_2 = require("../types");
12
12
  const MAXPCT = new anchor_1.BN(1000); //percentage units are [0,1000] => [0,1]
13
13
  /**
14
14
  * Calculates avg/max slippage (price impact) for candidate trade
15
+ *
16
+ * @deprecated use calculateEstimatedPerpEntryPrice instead
17
+ *
15
18
  * @param direction
16
19
  * @param amount
17
20
  * @param market
@@ -120,6 +123,9 @@ exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
120
123
  /**
121
124
  * calculateTargetPriceTrade
122
125
  * simple function for finding arbitraging trades
126
+ *
127
+ * @deprecated
128
+ *
123
129
  * @param market
124
130
  * @param targetPrice
125
131
  * @param pct optional default is 100% gap filling, can set smaller.
@@ -1,12 +1,13 @@
1
- /// <reference types="node" />
2
1
  /// <reference types="bn.js" />
2
+ /// <reference types="node" />
3
3
  import { Connection, PublicKey } from '@solana/web3.js';
4
4
  import { OracleClient, OraclePriceData } from './types';
5
5
  import { BN } from '@project-serum/anchor';
6
6
  export declare class PythClient implements OracleClient {
7
7
  private connection;
8
- constructor(connection: Connection);
8
+ private multiple;
9
+ constructor(connection: Connection, multiple?: BN);
9
10
  getOraclePriceData(pricePublicKey: PublicKey): Promise<OraclePriceData>;
10
11
  getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData;
11
12
  }
12
- export declare function convertPythPrice(price: number, exponent: number): BN;
13
+ export declare function convertPythPrice(price: number, exponent: number, multiple: BN): BN;
@@ -5,8 +5,9 @@ const client_1 = require("@pythnetwork/client");
5
5
  const anchor_1 = require("@project-serum/anchor");
6
6
  const numericConstants_1 = require("../constants/numericConstants");
7
7
  class PythClient {
8
- constructor(connection) {
8
+ constructor(connection, multiple = numericConstants_1.ONE) {
9
9
  this.connection = connection;
10
+ this.multiple = multiple;
10
11
  }
11
12
  async getOraclePriceData(pricePublicKey) {
12
13
  const accountInfo = await this.connection.getAccountInfo(pricePublicKey);
@@ -15,19 +16,19 @@ class PythClient {
15
16
  getOraclePriceDataFromBuffer(buffer) {
16
17
  const priceData = client_1.parsePriceData(buffer);
17
18
  return {
18
- price: convertPythPrice(priceData.aggregate.price, priceData.exponent),
19
+ price: convertPythPrice(priceData.aggregate.price, priceData.exponent, this.multiple),
19
20
  slot: new anchor_1.BN(priceData.lastSlot.toString()),
20
- confidence: convertPythPrice(priceData.confidence, priceData.exponent),
21
- twap: convertPythPrice(priceData.twap.value, priceData.exponent),
22
- twapConfidence: convertPythPrice(priceData.twac.value, priceData.exponent),
21
+ confidence: convertPythPrice(priceData.confidence, priceData.exponent, this.multiple),
22
+ twap: convertPythPrice(priceData.twap.value, priceData.exponent, this.multiple),
23
+ twapConfidence: convertPythPrice(priceData.twac.value, priceData.exponent, this.multiple),
23
24
  hasSufficientNumberOfDataPoints: true,
24
25
  };
25
26
  }
26
27
  }
27
28
  exports.PythClient = PythClient;
28
- function convertPythPrice(price, exponent) {
29
+ function convertPythPrice(price, exponent, multiple) {
29
30
  exponent = Math.abs(exponent);
30
- const pythPrecision = numericConstants_1.TEN.pow(new anchor_1.BN(exponent).abs());
31
+ const pythPrecision = numericConstants_1.TEN.pow(new anchor_1.BN(exponent).abs()).div(multiple);
31
32
  return new anchor_1.BN(price * Math.pow(10, exponent))
32
33
  .mul(numericConstants_1.PRICE_PRECISION)
33
34
  .div(pythPrecision);
package/lib/types.d.ts CHANGED
@@ -130,6 +130,9 @@ export declare class OracleSource {
130
130
  static readonly PYTH: {
131
131
  pyth: {};
132
132
  };
133
+ static readonly PYTH_1000: {
134
+ pyth1000: {};
135
+ };
133
136
  static readonly QUOTE_ASSET: {
134
137
  quoteAsset: {};
135
138
  };
package/lib/types.js CHANGED
@@ -78,6 +78,7 @@ class OracleSource {
78
78
  }
79
79
  exports.OracleSource = OracleSource;
80
80
  OracleSource.PYTH = { pyth: {} };
81
+ OracleSource.PYTH_1000 = { pyth1000: {} };
81
82
  // static readonly SWITCHBOARD = { switchboard: {} };
82
83
  OracleSource.QUOTE_ASSET = { quoteAsset: {} };
83
84
  class OrderType {
package/lib/user.js CHANGED
@@ -400,11 +400,13 @@ class User {
400
400
  }
401
401
  totalLiabilityValue = totalLiabilityValue.add(new _1.BN(spotPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
402
402
  }
403
- if (netQuoteValue.gt(numericConstants_1.ZERO)) {
404
- totalAssetValue = totalAssetValue.add(netQuoteValue);
405
- }
406
- else {
407
- totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
403
+ if (marketIndex === undefined || marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
404
+ if (netQuoteValue.gt(numericConstants_1.ZERO)) {
405
+ totalAssetValue = totalAssetValue.add(netQuoteValue);
406
+ }
407
+ else {
408
+ totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
409
+ }
408
410
  }
409
411
  return { totalAssetValue, totalLiabilityValue };
410
412
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.18.0-beta.0",
3
+ "version": "2.18.0-beta.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -54,6 +54,16 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
54
54
  launchTs: 1675610186000,
55
55
  oracleSource: OracleSource.PYTH,
56
56
  },
57
+ {
58
+ fullName: 'Bonk',
59
+ category: ['Meme'],
60
+ symbol: 'BONK-PERP',
61
+ baseAssetSymbol: 'BONK',
62
+ marketIndex: 4,
63
+ oracle: new PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
64
+ launchTs: 1677068931000,
65
+ oracleSource: OracleSource.PYTH_1000,
66
+ },
57
67
  ];
58
68
 
59
69
  export const MainnetPerpMarkets: PerpMarketConfig[] = [
package/src/dlob/DLOB.ts CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  calculateBidPrice,
7
7
  DriftClient,
8
8
  convertToNumber,
9
- isAuctionComplete,
10
9
  isOrderExpired,
11
10
  isOneOfVariant,
12
11
  isVariant,
@@ -1308,18 +1307,20 @@ export class DLOB {
1308
1307
  marketType,
1309
1308
  oraclePriceData
1310
1309
  )) {
1311
- for (const bidNode of this.getRestingLimitBids(
1310
+ const bidGenerator = this.getRestingLimitBids(
1312
1311
  marketIndex,
1313
1312
  slot,
1314
1313
  marketType,
1315
1314
  oraclePriceData
1316
- )) {
1315
+ );
1316
+
1317
+ for (const bidNode of bidGenerator) {
1317
1318
  const bidPrice = bidNode.getPrice(oraclePriceData, slot);
1318
1319
  const askPrice = askNode.getPrice(oraclePriceData, slot);
1319
1320
 
1320
- // orders don't cross - we're done walking the book
1321
+ // orders don't cross
1321
1322
  if (bidPrice.lt(askPrice)) {
1322
- return nodesToFill;
1323
+ break;
1323
1324
  }
1324
1325
 
1325
1326
  const bidOrder = bidNode.order;
@@ -1327,14 +1328,18 @@ export class DLOB {
1327
1328
 
1328
1329
  // Can't match orders from the same user
1329
1330
  const sameUser = bidNode.userAccount.equals(askNode.userAccount);
1330
- if (sameUser || (bidOrder.postOnly && askOrder.postOnly)) {
1331
+ if (sameUser) {
1331
1332
  continue;
1332
1333
  }
1333
1334
 
1334
- const { takerNode, makerNode } = this.determineMakerAndTaker(
1335
- askNode,
1336
- bidNode
1337
- );
1335
+ const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
1336
+
1337
+ // unable to match maker and taker due to post only or slot
1338
+ if (!makerAndTaker) {
1339
+ continue;
1340
+ }
1341
+
1342
+ const { takerNode, makerNode } = makerAndTaker;
1338
1343
 
1339
1344
  // extra guard against bad fills for limit orders where auction is incomplete
1340
1345
  if (
@@ -1408,27 +1413,25 @@ export class DLOB {
1408
1413
  determineMakerAndTaker(
1409
1414
  askNode: DLOBNode,
1410
1415
  bidNode: DLOBNode
1411
- ): { takerNode: DLOBNode; makerNode: DLOBNode } {
1412
- if (bidNode.order.postOnly) {
1413
- return {
1414
- takerNode: askNode,
1415
- makerNode: bidNode,
1416
- };
1417
- } else if (askNode.order.postOnly) {
1418
- return {
1419
- takerNode: bidNode,
1420
- makerNode: askNode,
1421
- };
1422
- } else if (askNode.order.slot.lt(bidNode.order.slot)) {
1416
+ ): { takerNode: DLOBNode; makerNode: DLOBNode } | undefined {
1417
+ const askSlot = askNode.order.slot.add(
1418
+ new BN(askNode.order.auctionDuration)
1419
+ );
1420
+ const bidSlot = bidNode.order.slot.add(
1421
+ new BN(bidNode.order.auctionDuration)
1422
+ );
1423
+ if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
1423
1424
  return {
1424
1425
  takerNode: bidNode,
1425
1426
  makerNode: askNode,
1426
1427
  };
1427
- } else {
1428
+ } else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
1428
1429
  return {
1429
1430
  takerNode: askNode,
1430
1431
  makerNode: bidNode,
1431
1432
  };
1433
+ } else {
1434
+ return undefined;
1432
1435
  }
1433
1436
  }
1434
1437
 
@@ -4,6 +4,7 @@ import { OracleClient } from '../oracles/types';
4
4
  import { PythClient } from '../oracles/pythClient';
5
5
  // import { SwitchboardClient } from '../oracles/switchboardClient';
6
6
  import { QuoteAssetOracleClient } from '../oracles/quoteAssetOracleClient';
7
+ import { BN } from '@project-serum/anchor';
7
8
 
8
9
  export function getOracleClient(
9
10
  oracleSource: OracleSource,
@@ -13,6 +14,10 @@ export function getOracleClient(
13
14
  return new PythClient(connection);
14
15
  }
15
16
 
17
+ if (isVariant(oracleSource, 'pyth1000')) {
18
+ return new PythClient(connection, new BN(1000));
19
+ }
20
+
16
21
  // if (isVariant(oracleSource, 'switchboard')) {
17
22
  // return new SwitchboardClient(connection);
18
23
  // }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.18.0-beta.0",
2
+ "version": "2.18.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -6237,6 +6237,9 @@
6237
6237
  {
6238
6238
  "name": "Pyth"
6239
6239
  },
6240
+ {
6241
+ "name": "Pyth1000"
6242
+ },
6240
6243
  {
6241
6244
  "name": "Switchboard"
6242
6245
  },
package/src/math/trade.ts CHANGED
@@ -52,6 +52,9 @@ export type PriceImpactUnit =
52
52
 
53
53
  /**
54
54
  * Calculates avg/max slippage (price impact) for candidate trade
55
+ *
56
+ * @deprecated use calculateEstimatedPerpEntryPrice instead
57
+ *
55
58
  * @param direction
56
59
  * @param amount
57
60
  * @param market
@@ -200,6 +203,9 @@ export function calculateTradeAcquiredAmounts(
200
203
  /**
201
204
  * calculateTargetPriceTrade
202
205
  * simple function for finding arbitraging trades
206
+ *
207
+ * @deprecated
208
+ *
203
209
  * @param market
204
210
  * @param targetPrice
205
211
  * @param pct optional default is 100% gap filling, can set smaller.
@@ -2,13 +2,15 @@ import { parsePriceData } from '@pythnetwork/client';
2
2
  import { Connection, PublicKey } from '@solana/web3.js';
3
3
  import { OracleClient, OraclePriceData } from './types';
4
4
  import { BN } from '@project-serum/anchor';
5
- import { PRICE_PRECISION, TEN } from '../constants/numericConstants';
5
+ import { ONE, PRICE_PRECISION, TEN } from '../constants/numericConstants';
6
6
 
7
7
  export class PythClient implements OracleClient {
8
8
  private connection: Connection;
9
+ private multiple: BN;
9
10
 
10
- public constructor(connection: Connection) {
11
+ public constructor(connection: Connection, multiple = ONE) {
11
12
  this.connection = connection;
13
+ this.multiple = multiple;
12
14
  }
13
15
 
14
16
  public async getOraclePriceData(
@@ -21,22 +23,39 @@ export class PythClient implements OracleClient {
21
23
  public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
22
24
  const priceData = parsePriceData(buffer);
23
25
  return {
24
- price: convertPythPrice(priceData.aggregate.price, priceData.exponent),
26
+ price: convertPythPrice(
27
+ priceData.aggregate.price,
28
+ priceData.exponent,
29
+ this.multiple
30
+ ),
25
31
  slot: new BN(priceData.lastSlot.toString()),
26
- confidence: convertPythPrice(priceData.confidence, priceData.exponent),
27
- twap: convertPythPrice(priceData.twap.value, priceData.exponent),
32
+ confidence: convertPythPrice(
33
+ priceData.confidence,
34
+ priceData.exponent,
35
+ this.multiple
36
+ ),
37
+ twap: convertPythPrice(
38
+ priceData.twap.value,
39
+ priceData.exponent,
40
+ this.multiple
41
+ ),
28
42
  twapConfidence: convertPythPrice(
29
43
  priceData.twac.value,
30
- priceData.exponent
44
+ priceData.exponent,
45
+ this.multiple
31
46
  ),
32
47
  hasSufficientNumberOfDataPoints: true,
33
48
  };
34
49
  }
35
50
  }
36
51
 
37
- export function convertPythPrice(price: number, exponent: number): BN {
52
+ export function convertPythPrice(
53
+ price: number,
54
+ exponent: number,
55
+ multiple: BN
56
+ ): BN {
38
57
  exponent = Math.abs(exponent);
39
- const pythPrecision = TEN.pow(new BN(exponent).abs());
58
+ const pythPrecision = TEN.pow(new BN(exponent).abs()).div(multiple);
40
59
  return new BN(price * Math.pow(10, exponent))
41
60
  .mul(PRICE_PRECISION)
42
61
  .div(pythPrecision);
package/src/types.ts CHANGED
@@ -76,6 +76,7 @@ export class DepositDirection {
76
76
 
77
77
  export class OracleSource {
78
78
  static readonly PYTH = { pyth: {} };
79
+ static readonly PYTH_1000 = { pyth1000: {} };
79
80
  // static readonly SWITCHBOARD = { switchboard: {} };
80
81
  static readonly QUOTE_ASSET = { quoteAsset: {} };
81
82
  }
package/src/user.ts CHANGED
@@ -697,10 +697,12 @@ export class User {
697
697
  );
698
698
  }
699
699
 
700
- if (netQuoteValue.gt(ZERO)) {
701
- totalAssetValue = totalAssetValue.add(netQuoteValue);
702
- } else {
703
- totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
700
+ if (marketIndex === undefined || marketIndex === QUOTE_SPOT_MARKET_INDEX) {
701
+ if (netQuoteValue.gt(ZERO)) {
702
+ totalAssetValue = totalAssetValue.add(netQuoteValue);
703
+ } else {
704
+ totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
705
+ }
704
706
  }
705
707
 
706
708
  return { totalAssetValue, totalLiabilityValue };
@@ -3435,7 +3435,7 @@ describe('DLOB Perp Tests', () => {
3435
3435
  PositionDirection.LONG,
3436
3436
  vBid,
3437
3437
  vAsk,
3438
- new BN(slot - 1), // later order becomes taker
3438
+ new BN(slot + 1), // later order becomes taker
3439
3439
  new BN(200),
3440
3440
  undefined,
3441
3441
  undefined,
@@ -5109,7 +5109,8 @@ describe('DLOB Spot Tests', () => {
5109
5109
  BASE_PRECISION, // quantity
5110
5110
  PositionDirection.SHORT,
5111
5111
  vBid,
5112
- vAsk
5112
+ vAsk,
5113
+ new BN(1)
5113
5114
  );
5114
5115
  insertOrderToDLOB(
5115
5116
  dlob,
@@ -5122,7 +5123,8 @@ describe('DLOB Spot Tests', () => {
5122
5123
  BASE_PRECISION, // quantity
5123
5124
  PositionDirection.SHORT,
5124
5125
  vBid,
5125
- vAsk
5126
+ vAsk,
5127
+ new BN(1)
5126
5128
  );
5127
5129
 
5128
5130
  // should have no crossing orders
@@ -5152,7 +5154,7 @@ describe('DLOB Spot Tests', () => {
5152
5154
  PositionDirection.LONG,
5153
5155
  vBid,
5154
5156
  vAsk,
5155
- undefined,
5157
+ new BN(0),
5156
5158
  undefined,
5157
5159
  undefined,
5158
5160
  true
@@ -5169,7 +5171,7 @@ describe('DLOB Spot Tests', () => {
5169
5171
  PositionDirection.LONG,
5170
5172
  vBid,
5171
5173
  vAsk,
5172
- undefined,
5174
+ new BN(0),
5173
5175
  undefined,
5174
5176
  undefined,
5175
5177
  true
@@ -5205,6 +5207,186 @@ describe('DLOB Spot Tests', () => {
5205
5207
  expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(3);
5206
5208
  });
5207
5209
 
5210
+ it('Test limit orders skipping more recent post onlys', () => {
5211
+ const vAsk = new BN(15);
5212
+ const vBid = new BN(8);
5213
+
5214
+ const user0 = Keypair.generate();
5215
+ const user1 = Keypair.generate();
5216
+
5217
+ const dlob = new DLOB();
5218
+ const marketIndex = 0;
5219
+
5220
+ const slot = 12;
5221
+ const oracle = {
5222
+ price: vBid.add(vAsk).div(new BN(2)),
5223
+ slot: new BN(slot),
5224
+ confidence: new BN(1),
5225
+ hasSufficientNumberOfDataPoints: true,
5226
+ };
5227
+
5228
+ // insert some limit sells below vAMM ask, above bid
5229
+ insertOrderToDLOB(
5230
+ dlob,
5231
+ user0.publicKey,
5232
+ OrderType.LIMIT,
5233
+ MarketType.PERP,
5234
+ 1, // orderId
5235
+ marketIndex,
5236
+ new BN(14), // price
5237
+ BASE_PRECISION, // quantity
5238
+ PositionDirection.SHORT,
5239
+ vBid,
5240
+ vAsk,
5241
+ new BN(1)
5242
+ );
5243
+ insertOrderToDLOB(
5244
+ dlob,
5245
+ user1.publicKey,
5246
+ OrderType.LIMIT,
5247
+ MarketType.PERP,
5248
+ 2, // orderId
5249
+ marketIndex,
5250
+ new BN(13), // price
5251
+ BASE_PRECISION, // quantity
5252
+ PositionDirection.SHORT,
5253
+ vBid,
5254
+ vAsk,
5255
+ new BN(1)
5256
+ );
5257
+
5258
+ // should have no crossing orders
5259
+ const nodesToFillBefore = dlob.findNodesToFill(
5260
+ marketIndex,
5261
+ undefined,
5262
+ undefined,
5263
+ 12, // auction over
5264
+ Date.now(),
5265
+ MarketType.PERP,
5266
+ oracle,
5267
+ mockStateAccount,
5268
+ mockPerpMarkets[marketIndex]
5269
+ );
5270
+ expect(nodesToFillBefore.length).to.equal(0);
5271
+
5272
+ // add post only orders that are newer than resting limit orders and thus cant match
5273
+ insertOrderToDLOB(
5274
+ dlob,
5275
+ user1.publicKey,
5276
+ OrderType.LIMIT,
5277
+ MarketType.PERP,
5278
+ 3, // orderId
5279
+ marketIndex,
5280
+ new BN(15), // price
5281
+ BASE_PRECISION, // quantity
5282
+ PositionDirection.LONG,
5283
+ vBid,
5284
+ vAsk,
5285
+ new BN(2),
5286
+ undefined,
5287
+ undefined,
5288
+ true
5289
+ );
5290
+ insertOrderToDLOB(
5291
+ dlob,
5292
+ user0.publicKey,
5293
+ OrderType.LIMIT,
5294
+ MarketType.PERP,
5295
+ 4, // orderId
5296
+ marketIndex,
5297
+ new BN(14), // price
5298
+ BASE_PRECISION, // quantity
5299
+ PositionDirection.LONG,
5300
+ vBid,
5301
+ vAsk,
5302
+ new BN(2),
5303
+ undefined,
5304
+ undefined,
5305
+ true
5306
+ );
5307
+
5308
+ let nodesToFillAfter = dlob.findNodesToFill(
5309
+ marketIndex,
5310
+ undefined,
5311
+ undefined,
5312
+ slot, // auction over
5313
+ Date.now(),
5314
+ MarketType.PERP,
5315
+ oracle,
5316
+ mockStateAccount,
5317
+ mockPerpMarkets[marketIndex]
5318
+ );
5319
+
5320
+ expect(nodesToFillAfter.length).to.equal(0);
5321
+
5322
+ // add post only orders that are older than resting limit orders
5323
+ insertOrderToDLOB(
5324
+ dlob,
5325
+ user1.publicKey,
5326
+ OrderType.LIMIT,
5327
+ MarketType.PERP,
5328
+ 5, // orderId
5329
+ marketIndex,
5330
+ new BN(15), // price
5331
+ BASE_PRECISION, // quantity
5332
+ PositionDirection.LONG,
5333
+ vBid,
5334
+ vAsk,
5335
+ new BN(0),
5336
+ undefined,
5337
+ undefined,
5338
+ true
5339
+ );
5340
+ insertOrderToDLOB(
5341
+ dlob,
5342
+ user0.publicKey,
5343
+ OrderType.LIMIT,
5344
+ MarketType.PERP,
5345
+ 6, // orderId
5346
+ marketIndex,
5347
+ new BN(14), // price
5348
+ BASE_PRECISION, // quantity
5349
+ PositionDirection.LONG,
5350
+ vBid,
5351
+ vAsk,
5352
+ new BN(0),
5353
+ undefined,
5354
+ undefined,
5355
+ true
5356
+ );
5357
+
5358
+ nodesToFillAfter = dlob.findNodesToFill(
5359
+ marketIndex,
5360
+ undefined,
5361
+ undefined,
5362
+ slot, // auction over
5363
+ Date.now(),
5364
+ MarketType.PERP,
5365
+ oracle,
5366
+ mockStateAccount,
5367
+ mockPerpMarkets[marketIndex]
5368
+ );
5369
+
5370
+ expect(nodesToFillAfter.length).to.equal(2);
5371
+
5372
+ printBookState(dlob, marketIndex, vBid, vAsk, slot, oracle);
5373
+
5374
+ for (const n of nodesToFillAfter) {
5375
+ console.log(
5376
+ `cross found: taker orderId: ${n.node.order?.orderId.toString()}: BAA: ${n.node.order?.baseAssetAmountFilled.toString()}/${n.node.order?.baseAssetAmount.toString()}, maker orderId: ${n.makerNode?.order?.orderId.toString()}: BAA: ${n.makerNode?.order?.baseAssetAmountFilled.toString()}/${n.makerNode?.order?.baseAssetAmount.toString()}`
5377
+ );
5378
+ }
5379
+ expect(nodesToFillAfter.length).to.equal(2);
5380
+
5381
+ // taker should fill completely with best maker
5382
+ expect(nodesToFillAfter[0].node.order?.orderId).to.equal(2);
5383
+ expect(nodesToFillAfter[0].makerNode?.order?.orderId).to.equal(6);
5384
+
5385
+ // taker should fill completely with second best maker
5386
+ expect(nodesToFillAfter[1].node.order?.orderId).to.equal(1);
5387
+ expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(5);
5388
+ });
5389
+
5208
5390
  it('Test trigger orders', () => {
5209
5391
  const vAsk = new BN(15);
5210
5392
  const vBid = new BN(8);