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

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
  {
@@ -88,11 +88,11 @@ export declare class DLOB {
88
88
  getMakerLimitBids(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, fallbackAsk?: BN): Generator<DLOBNode>;
89
89
  getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
90
90
  getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
91
- findCrossingRestingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, minAuctionDuration: number, fallbackAsk: BN | undefined, fallbackBid: BN | undefined): NodeToFill[];
91
+ findCrossingRestingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
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
@@ -310,7 +310,7 @@ class DLOB {
310
310
  }
311
311
  findRestingLimitOrderNodesToFill(marketIndex, slot, marketType, oraclePriceData, isAmmPaused, minAuctionDuration, fallbackAsk, fallbackBid) {
312
312
  const nodesToFill = new Array();
313
- const crossingNodes = this.findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData, minAuctionDuration, fallbackAsk, fallbackBid);
313
+ const crossingNodes = this.findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData);
314
314
  for (const crossingNode of crossingNodes) {
315
315
  nodesToFill.push(crossingNode);
316
316
  }
@@ -711,40 +711,30 @@ class DLOB {
711
711
  .gt(currentNode.getPrice(oraclePriceData, slot));
712
712
  });
713
713
  }
714
- findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData, minAuctionDuration, fallbackAsk, fallbackBid) {
714
+ findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
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) {
729
730
  continue;
730
731
  }
731
- const { takerNode, makerNode } = this.determineMakerAndTaker(askNode, bidNode);
732
- // extra guard against bad fills for limit orders where auction is incomplete
733
- if (!__1.isFallbackAvailableLiquiditySource(takerNode.order, minAuctionDuration, slot)) {
734
- let bidPrice;
735
- let askPrice;
736
- if (__1.isVariant(takerNode.order.direction, 'long')) {
737
- bidPrice = __1.BN.min(takerNode.getPrice(oraclePriceData, slot), fallbackAsk || __1.BN_MAX);
738
- askPrice = makerNode.getPrice(oraclePriceData, slot);
739
- }
740
- else {
741
- bidPrice = makerNode.getPrice(oraclePriceData, slot);
742
- askPrice = __1.BN.max(takerNode.getPrice(oraclePriceData, slot), fallbackBid || __1.ZERO);
743
- }
744
- if (bidPrice.lt(askPrice)) {
745
- continue;
746
- }
732
+ const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
733
+ // unable to match maker and taker due to post only or slot
734
+ if (!makerAndTaker) {
735
+ continue;
747
736
  }
737
+ const { takerNode, makerNode } = makerAndTaker;
748
738
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(bidOrder.baseAssetAmountFilled);
749
739
  const askBaseRemaining = askOrder.baseAssetAmount.sub(askOrder.baseAssetAmountFilled);
750
740
  const baseFilled = __1.BN.min(bidBaseRemaining, askBaseRemaining);
@@ -769,30 +759,23 @@ class DLOB {
769
759
  return nodesToFill;
770
760
  }
771
761
  determineMakerAndTaker(askNode, bidNode) {
772
- if (bidNode.order.postOnly) {
773
- return {
774
- takerNode: askNode,
775
- makerNode: bidNode,
776
- };
777
- }
778
- else if (askNode.order.postOnly) {
779
- return {
780
- takerNode: bidNode,
781
- makerNode: askNode,
782
- };
783
- }
784
- else if (askNode.order.slot.lt(bidNode.order.slot)) {
762
+ const askSlot = askNode.order.slot.add(new __1.BN(askNode.order.auctionDuration));
763
+ const bidSlot = bidNode.order.slot.add(new __1.BN(bidNode.order.auctionDuration));
764
+ if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
785
765
  return {
786
766
  takerNode: bidNode,
787
767
  makerNode: askNode,
788
768
  };
789
769
  }
790
- else {
770
+ else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
791
771
  return {
792
772
  takerNode: askNode,
793
773
  makerNode: bidNode,
794
774
  };
795
775
  }
776
+ else {
777
+ return undefined;
778
+ }
796
779
  }
797
780
  getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
798
781
  return this.getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData)
@@ -2094,7 +2094,7 @@ class DriftClient {
2094
2094
  writablePerpMarketIndexes: [marketIndex],
2095
2095
  writableSpotMarketIndexes: [numericConstants_1.QUOTE_SPOT_MARKET_INDEX],
2096
2096
  });
2097
- const spotMarket = this.getSpotMarketAccount(marketIndex);
2097
+ const spotMarket = this.getQuoteSpotMarketAccount();
2098
2098
  return await this.program.instruction.resolvePerpBankruptcy(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, marketIndex, {
2099
2099
  accounts: {
2100
2100
  state: await this.getStatePublicKey(),
@@ -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.2",
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.2",
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,
@@ -25,8 +24,6 @@ import {
25
24
  UserMap,
26
25
  OrderRecord,
27
26
  OrderActionRecord,
28
- ZERO,
29
- BN_MAX,
30
27
  isRestingLimitOrder,
31
28
  isTakingOrder,
32
29
  isFallbackAvailableLiquiditySource,
@@ -536,10 +533,7 @@ export class DLOB {
536
533
  marketIndex,
537
534
  slot,
538
535
  marketType,
539
- oraclePriceData,
540
- minAuctionDuration,
541
- fallbackAsk,
542
- fallbackBid
536
+ oraclePriceData
543
537
  );
544
538
 
545
539
  for (const crossingNode of crossingNodes) {
@@ -1295,10 +1289,7 @@ export class DLOB {
1295
1289
  marketIndex: number,
1296
1290
  slot: number,
1297
1291
  marketType: MarketType,
1298
- oraclePriceData: OraclePriceData,
1299
- minAuctionDuration: number,
1300
- fallbackAsk: BN | undefined,
1301
- fallbackBid: BN | undefined
1292
+ oraclePriceData: OraclePriceData
1302
1293
  ): NodeToFill[] {
1303
1294
  const nodesToFill = new Array<NodeToFill>();
1304
1295
 
@@ -1308,18 +1299,20 @@ export class DLOB {
1308
1299
  marketType,
1309
1300
  oraclePriceData
1310
1301
  )) {
1311
- for (const bidNode of this.getRestingLimitBids(
1302
+ const bidGenerator = this.getRestingLimitBids(
1312
1303
  marketIndex,
1313
1304
  slot,
1314
1305
  marketType,
1315
1306
  oraclePriceData
1316
- )) {
1307
+ );
1308
+
1309
+ for (const bidNode of bidGenerator) {
1317
1310
  const bidPrice = bidNode.getPrice(oraclePriceData, slot);
1318
1311
  const askPrice = askNode.getPrice(oraclePriceData, slot);
1319
1312
 
1320
- // orders don't cross - we're done walking the book
1313
+ // orders don't cross
1321
1314
  if (bidPrice.lt(askPrice)) {
1322
- return nodesToFill;
1315
+ break;
1323
1316
  }
1324
1317
 
1325
1318
  const bidOrder = bidNode.order;
@@ -1327,44 +1320,19 @@ export class DLOB {
1327
1320
 
1328
1321
  // Can't match orders from the same user
1329
1322
  const sameUser = bidNode.userAccount.equals(askNode.userAccount);
1330
- if (sameUser || (bidOrder.postOnly && askOrder.postOnly)) {
1323
+ if (sameUser) {
1331
1324
  continue;
1332
1325
  }
1333
1326
 
1334
- const { takerNode, makerNode } = this.determineMakerAndTaker(
1335
- askNode,
1336
- bidNode
1337
- );
1327
+ const makerAndTaker = this.determineMakerAndTaker(askNode, bidNode);
1338
1328
 
1339
- // extra guard against bad fills for limit orders where auction is incomplete
1340
- if (
1341
- !isFallbackAvailableLiquiditySource(
1342
- takerNode.order,
1343
- minAuctionDuration,
1344
- slot
1345
- )
1346
- ) {
1347
- let bidPrice: BN;
1348
- let askPrice: BN;
1349
- if (isVariant(takerNode.order.direction, 'long')) {
1350
- bidPrice = BN.min(
1351
- takerNode.getPrice(oraclePriceData, slot),
1352
- fallbackAsk || BN_MAX
1353
- );
1354
- askPrice = makerNode.getPrice(oraclePriceData, slot);
1355
- } else {
1356
- bidPrice = makerNode.getPrice(oraclePriceData, slot);
1357
- askPrice = BN.max(
1358
- takerNode.getPrice(oraclePriceData, slot),
1359
- fallbackBid || ZERO
1360
- );
1361
- }
1362
-
1363
- if (bidPrice.lt(askPrice)) {
1364
- continue;
1365
- }
1329
+ // unable to match maker and taker due to post only or slot
1330
+ if (!makerAndTaker) {
1331
+ continue;
1366
1332
  }
1367
1333
 
1334
+ const { takerNode, makerNode } = makerAndTaker;
1335
+
1368
1336
  const bidBaseRemaining = bidOrder.baseAssetAmount.sub(
1369
1337
  bidOrder.baseAssetAmountFilled
1370
1338
  );
@@ -1408,27 +1376,25 @@ export class DLOB {
1408
1376
  determineMakerAndTaker(
1409
1377
  askNode: DLOBNode,
1410
1378
  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)) {
1379
+ ): { takerNode: DLOBNode; makerNode: DLOBNode } | undefined {
1380
+ const askSlot = askNode.order.slot.add(
1381
+ new BN(askNode.order.auctionDuration)
1382
+ );
1383
+ const bidSlot = bidNode.order.slot.add(
1384
+ new BN(bidNode.order.auctionDuration)
1385
+ );
1386
+ if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
1423
1387
  return {
1424
1388
  takerNode: bidNode,
1425
1389
  makerNode: askNode,
1426
1390
  };
1427
- } else {
1391
+ } else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
1428
1392
  return {
1429
1393
  takerNode: askNode,
1430
1394
  makerNode: bidNode,
1431
1395
  };
1396
+ } else {
1397
+ return undefined;
1432
1398
  }
1433
1399
  }
1434
1400
 
@@ -3695,7 +3695,7 @@ export class DriftClient {
3695
3695
  writableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
3696
3696
  });
3697
3697
 
3698
- const spotMarket = this.getSpotMarketAccount(marketIndex);
3698
+ const spotMarket = this.getQuoteSpotMarketAccount();
3699
3699
 
3700
3700
  return await this.program.instruction.resolvePerpBankruptcy(
3701
3701
  QUOTE_SPOT_MARKET_INDEX,
@@ -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.2",
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);