@drift-labs/sdk 0.2.0-master.36 → 0.2.0-master.38

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.
@@ -86,7 +86,7 @@ type RemainingAccountParams = {
86
86
  writablePerpMarketIndexes?: number[];
87
87
  writableSpotMarketIndexes?: number[];
88
88
  readablePerpMarketIndex?: number;
89
- readableSpotMarketIndex?: number;
89
+ readableSpotMarketIndexes?: number[];
90
90
  useMarketLastSlotCache?: boolean;
91
91
  };
92
92
 
@@ -108,7 +108,8 @@ export class ClearingHouse {
108
108
  eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
109
109
  _isSubscribed = false;
110
110
  txSender: TxSender;
111
- marketLastSlotCache = new Map<number, number>();
111
+ perpMarketLastSlotCache = new Map<number, number>();
112
+ spotMarketLastSlotCache = new Map<number, number>();
112
113
  authority: PublicKey;
113
114
 
114
115
  public get isSubscribed() {
@@ -664,11 +665,14 @@ export class ClearingHouse {
664
665
  this.getRemainingAccountMapsForUsers(params.userAccounts);
665
666
 
666
667
  if (params.useMarketLastSlotCache) {
667
- const lastUserPositionsSlot = this.getUserAccountAndSlot()?.slot;
668
- for (const [marketIndex, slot] of this.marketLastSlotCache.entries()) {
668
+ const lastUserSlot = this.getUserAccountAndSlot()?.slot;
669
+ for (const [
670
+ marketIndex,
671
+ slot,
672
+ ] of this.perpMarketLastSlotCache.entries()) {
669
673
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
670
674
  // otherwise remove from slot
671
- if (slot > lastUserPositionsSlot) {
675
+ if (slot > lastUserSlot) {
672
676
  const marketAccount = this.getPerpMarketAccount(marketIndex);
673
677
  perpMarketAccountMap.set(marketIndex, {
674
678
  pubkey: marketAccount.pubkey,
@@ -681,7 +685,32 @@ export class ClearingHouse {
681
685
  isWritable: false,
682
686
  });
683
687
  } else {
684
- this.marketLastSlotCache.delete(marketIndex);
688
+ this.perpMarketLastSlotCache.delete(marketIndex);
689
+ }
690
+ }
691
+
692
+ for (const [
693
+ marketIndex,
694
+ slot,
695
+ ] of this.spotMarketLastSlotCache.entries()) {
696
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
697
+ // otherwise remove from slot
698
+ if (slot > lastUserSlot) {
699
+ const marketAccount = this.getSpotMarketAccount(marketIndex);
700
+ spotMarketAccountMap.set(marketIndex, {
701
+ pubkey: marketAccount.pubkey,
702
+ isSigner: false,
703
+ isWritable: false,
704
+ });
705
+ if (!marketAccount.oracle.equals(PublicKey.default)) {
706
+ oracleAccountMap.set(marketAccount.oracle.toString(), {
707
+ pubkey: marketAccount.oracle,
708
+ isSigner: false,
709
+ isWritable: false,
710
+ });
711
+ }
712
+ } else {
713
+ this.spotMarketLastSlotCache.delete(marketIndex);
685
714
  }
686
715
  }
687
716
  }
@@ -720,21 +749,23 @@ export class ClearingHouse {
720
749
  }
721
750
  }
722
751
 
723
- if (params.readableSpotMarketIndex !== undefined) {
724
- const spotMarketAccount = this.getSpotMarketAccount(
725
- params.readableSpotMarketIndex
726
- );
727
- spotMarketAccountMap.set(params.readableSpotMarketIndex, {
728
- pubkey: spotMarketAccount.pubkey,
729
- isSigner: false,
730
- isWritable: false,
731
- });
732
- if (spotMarketAccount.marketIndex !== 0) {
733
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
734
- pubkey: spotMarketAccount.oracle,
752
+ if (params.readableSpotMarketIndexes !== undefined) {
753
+ for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
754
+ const spotMarketAccount = this.getSpotMarketAccount(
755
+ readableSpotMarketIndex
756
+ );
757
+ spotMarketAccountMap.set(readableSpotMarketIndex, {
758
+ pubkey: spotMarketAccount.pubkey,
735
759
  isSigner: false,
736
760
  isWritable: false,
737
761
  });
762
+ if (spotMarketAccount.marketIndex !== 0) {
763
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
764
+ pubkey: spotMarketAccount.oracle,
765
+ isSigner: false,
766
+ isWritable: false,
767
+ });
768
+ }
738
769
  }
739
770
  }
740
771
 
@@ -793,6 +824,17 @@ export class ClearingHouse {
793
824
  isWritable: false,
794
825
  });
795
826
  }
827
+
828
+ if (
829
+ !spotPosition.openAsks.eq(ZERO) ||
830
+ !spotPosition.openBids.eq(ZERO)
831
+ ) {
832
+ spotMarketAccountMap.set(spotPosition.marketIndex, {
833
+ pubkey: this.getQuoteSpotMarketAccount().pubkey,
834
+ isSigner: false,
835
+ isWritable: false,
836
+ });
837
+ }
796
838
  }
797
839
  }
798
840
  for (const position of userAccount.perpPositions) {
@@ -894,11 +936,12 @@ export class ClearingHouse {
894
936
  );
895
937
  }
896
938
 
897
- const { txSig } = await this.txSender.send(
939
+ const { txSig, slot } = await this.txSender.send(
898
940
  tx,
899
941
  additionalSigners,
900
942
  this.opts
901
943
  );
944
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
902
945
  return txSig;
903
946
  }
904
947
 
@@ -1119,12 +1162,12 @@ export class ClearingHouse {
1119
1162
  );
1120
1163
  }
1121
1164
 
1122
- const { txSig } = await this.txSender.send(
1165
+ const { txSig, slot } = await this.txSender.send(
1123
1166
  tx,
1124
1167
  additionalSigners,
1125
1168
  this.opts
1126
1169
  );
1127
-
1170
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1128
1171
  return [txSig, userAccountPublicKey];
1129
1172
  }
1130
1173
 
@@ -1242,11 +1285,12 @@ export class ClearingHouse {
1242
1285
  );
1243
1286
  }
1244
1287
 
1245
- const { txSig } = await this.txSender.send(
1288
+ const { txSig, slot } = await this.txSender.send(
1246
1289
  tx,
1247
1290
  additionalSigners,
1248
1291
  this.opts
1249
1292
  );
1293
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1250
1294
  return txSig;
1251
1295
  }
1252
1296
 
@@ -1262,6 +1306,7 @@ export class ClearingHouse {
1262
1306
  userAccounts: [this.getUserAccount()],
1263
1307
  useMarketLastSlotCache: true,
1264
1308
  writableSpotMarketIndexes: [marketIndex],
1309
+ readableSpotMarketIndexes: [QUOTE_SPOT_MARKET_INDEX],
1265
1310
  });
1266
1311
 
1267
1312
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
@@ -1293,7 +1338,7 @@ export class ClearingHouse {
1293
1338
  fromSubAccountId: number,
1294
1339
  toSubAccountId: number
1295
1340
  ): Promise<TransactionSignature> {
1296
- const { txSig } = await this.txSender.send(
1341
+ const { txSig, slot } = await this.txSender.send(
1297
1342
  wrapInTx(
1298
1343
  await this.getTransferDepositIx(
1299
1344
  amount,
@@ -1305,6 +1350,12 @@ export class ClearingHouse {
1305
1350
  [],
1306
1351
  this.opts
1307
1352
  );
1353
+ if (
1354
+ fromSubAccountId === this.activeSubAccountId ||
1355
+ toSubAccountId === this.activeSubAccountId
1356
+ ) {
1357
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
1358
+ }
1308
1359
  return txSig;
1309
1360
  }
1310
1361
 
@@ -1356,6 +1407,7 @@ export class ClearingHouse {
1356
1407
  toUser,
1357
1408
  userStats: this.getUserStatsAccountPublicKey(),
1358
1409
  state: await this.getStatePublicKey(),
1410
+ spotMarketVault: this.getQuoteSpotMarketAccount().vault,
1359
1411
  },
1360
1412
  remainingAccounts,
1361
1413
  });
@@ -1530,7 +1582,7 @@ export class ClearingHouse {
1530
1582
  [],
1531
1583
  this.opts
1532
1584
  );
1533
- this.marketLastSlotCache.set(marketIndex, slot);
1585
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
1534
1586
  return txSig;
1535
1587
  }
1536
1588
 
@@ -1619,7 +1671,7 @@ export class ClearingHouse {
1619
1671
  true
1620
1672
  );
1621
1673
 
1622
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
1674
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1623
1675
 
1624
1676
  return { txSig, signedFillTx };
1625
1677
  }
@@ -1632,7 +1684,7 @@ export class ClearingHouse {
1632
1684
  [],
1633
1685
  this.opts
1634
1686
  );
1635
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
1687
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1636
1688
  return txSig;
1637
1689
  }
1638
1690
 
@@ -1993,11 +2045,13 @@ export class ClearingHouse {
1993
2045
  public async placeSpotOrder(
1994
2046
  orderParams: OptionalOrderParams
1995
2047
  ): Promise<TransactionSignature> {
1996
- const { txSig } = await this.txSender.send(
2048
+ const { txSig, slot } = await this.txSender.send(
1997
2049
  wrapInTx(await this.getPlaceSpotOrderIx(orderParams)),
1998
2050
  [],
1999
2051
  this.opts
2000
2052
  );
2053
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2054
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2001
2055
  return txSig;
2002
2056
  }
2003
2057
 
@@ -2010,7 +2064,10 @@ export class ClearingHouse {
2010
2064
  const remainingAccounts = this.getRemainingAccounts({
2011
2065
  userAccounts: [this.getUserAccount()],
2012
2066
  useMarketLastSlotCache: true,
2013
- readableSpotMarketIndex: orderParams.marketIndex,
2067
+ readableSpotMarketIndexes: [
2068
+ orderParams.marketIndex,
2069
+ QUOTE_SPOT_MARKET_INDEX,
2070
+ ],
2014
2071
  });
2015
2072
 
2016
2073
  return await this.program.instruction.placeSpotOrder(orderParams, {
@@ -2109,13 +2166,11 @@ export class ClearingHouse {
2109
2166
  const orderId = order.orderId;
2110
2167
  const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
2111
2168
 
2112
- if (fulfillmentConfig) {
2113
- this.addSerumRemainingAccounts(
2114
- marketIndex,
2115
- remainingAccounts,
2116
- fulfillmentConfig
2117
- );
2118
- }
2169
+ this.addSpotFulfillmentAccounts(
2170
+ marketIndex,
2171
+ remainingAccounts,
2172
+ fulfillmentConfig
2173
+ );
2119
2174
 
2120
2175
  return await this.program.instruction.fillSpotOrder(
2121
2176
  orderId,
@@ -2135,6 +2190,31 @@ export class ClearingHouse {
2135
2190
  );
2136
2191
  }
2137
2192
 
2193
+ addSpotFulfillmentAccounts(
2194
+ marketIndex: number,
2195
+ remainingAccounts: AccountMeta[],
2196
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount
2197
+ ) {
2198
+ if (fulfillmentConfig) {
2199
+ this.addSerumRemainingAccounts(
2200
+ marketIndex,
2201
+ remainingAccounts,
2202
+ fulfillmentConfig
2203
+ );
2204
+ } else {
2205
+ remainingAccounts.push({
2206
+ pubkey: this.getSpotMarketAccount(marketIndex).vault,
2207
+ isWritable: false,
2208
+ isSigner: false,
2209
+ });
2210
+ remainingAccounts.push({
2211
+ pubkey: this.getQuoteSpotMarketAccount().vault,
2212
+ isWritable: false,
2213
+ isSigner: false,
2214
+ });
2215
+ }
2216
+ }
2217
+
2138
2218
  addSerumRemainingAccounts(
2139
2219
  marketIndex: number,
2140
2220
  remainingAccounts: AccountMeta[],
@@ -2291,7 +2371,7 @@ export class ClearingHouse {
2291
2371
  [],
2292
2372
  this.opts
2293
2373
  );
2294
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
2374
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2295
2375
  return txSig;
2296
2376
  }
2297
2377
 
@@ -2374,7 +2454,7 @@ export class ClearingHouse {
2374
2454
  this.opts
2375
2455
  );
2376
2456
 
2377
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
2457
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
2378
2458
 
2379
2459
  return txSig;
2380
2460
  }
@@ -2431,7 +2511,7 @@ export class ClearingHouse {
2431
2511
  makerInfo?: MakerInfo,
2432
2512
  referrerInfo?: ReferrerInfo
2433
2513
  ): Promise<TransactionSignature> {
2434
- const { txSig } = await this.txSender.send(
2514
+ const { txSig, slot } = await this.txSender.send(
2435
2515
  wrapInTx(
2436
2516
  await this.getPlaceAndTakeSpotOrderIx(
2437
2517
  orderParams,
@@ -2443,6 +2523,8 @@ export class ClearingHouse {
2443
2523
  [],
2444
2524
  this.opts
2445
2525
  );
2526
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2527
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2446
2528
  return txSig;
2447
2529
  }
2448
2530
 
@@ -2497,13 +2579,11 @@ export class ClearingHouse {
2497
2579
  });
2498
2580
  }
2499
2581
 
2500
- if (fulfillmentConfig) {
2501
- this.addSerumRemainingAccounts(
2502
- orderParams.marketIndex,
2503
- remainingAccounts,
2504
- fulfillmentConfig
2505
- );
2506
- }
2582
+ this.addSpotFulfillmentAccounts(
2583
+ orderParams.marketIndex,
2584
+ remainingAccounts,
2585
+ fulfillmentConfig
2586
+ );
2507
2587
 
2508
2588
  return await this.program.instruction.placeAndTakeSpotOrder(
2509
2589
  orderParams,
@@ -2524,26 +2604,30 @@ export class ClearingHouse {
2524
2604
  public async placeAndMakeSpotOrder(
2525
2605
  orderParams: OptionalOrderParams,
2526
2606
  takerInfo: TakerInfo,
2607
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2527
2608
  referrerInfo?: ReferrerInfo
2528
2609
  ): Promise<TransactionSignature> {
2529
- const { txSig } = await this.txSender.send(
2610
+ const { txSig, slot } = await this.txSender.send(
2530
2611
  wrapInTx(
2531
2612
  await this.getPlaceAndMakeSpotOrderIx(
2532
2613
  orderParams,
2533
2614
  takerInfo,
2615
+ fulfillmentConfig,
2534
2616
  referrerInfo
2535
2617
  )
2536
2618
  ),
2537
2619
  [],
2538
2620
  this.opts
2539
2621
  );
2540
-
2622
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
2623
+ this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
2541
2624
  return txSig;
2542
2625
  }
2543
2626
 
2544
2627
  public async getPlaceAndMakeSpotOrderIx(
2545
2628
  orderParams: OptionalOrderParams,
2546
2629
  takerInfo: TakerInfo,
2630
+ fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
2547
2631
  referrerInfo?: ReferrerInfo
2548
2632
  ): Promise<TransactionInstruction> {
2549
2633
  orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
@@ -2572,10 +2656,17 @@ export class ClearingHouse {
2572
2656
  });
2573
2657
  }
2574
2658
 
2659
+ this.addSpotFulfillmentAccounts(
2660
+ orderParams.marketIndex,
2661
+ remainingAccounts,
2662
+ fulfillmentConfig
2663
+ );
2664
+
2575
2665
  const takerOrderId = takerInfo.order.orderId;
2576
2666
  return await this.program.instruction.placeAndMakeSpotOrder(
2577
2667
  orderParams,
2578
2668
  takerOrderId,
2669
+ fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null,
2579
2670
  {
2580
2671
  accounts: {
2581
2672
  state: await this.getStatePublicKey(),
@@ -2614,6 +2705,66 @@ export class ClearingHouse {
2614
2705
  });
2615
2706
  }
2616
2707
 
2708
+ /**
2709
+ * Modifies an open order by closing it and replacing it with a new order.
2710
+ * @param orderId: The open order to modify
2711
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2712
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2713
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
2714
+ * @returns
2715
+ */
2716
+ public async modifyPerpOrder(
2717
+ orderId: number,
2718
+ newBaseAmount?: BN,
2719
+ newLimitPrice?: BN,
2720
+ newOraclePriceOffset?: number
2721
+ ): Promise<TransactionSignature> {
2722
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
2723
+ throw new Error(
2724
+ `Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`
2725
+ );
2726
+ }
2727
+
2728
+ const openOrder = this.getUser().getOrder(orderId);
2729
+ if (!openOrder) {
2730
+ throw new Error(`No open order with id ${orderId.toString()}`);
2731
+ }
2732
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
2733
+
2734
+ const newOrderParams: OptionalOrderParams = {
2735
+ orderType: openOrder.orderType,
2736
+ marketType: openOrder.marketType,
2737
+ direction: openOrder.direction,
2738
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
2739
+ price: newLimitPrice || openOrder.price,
2740
+ marketIndex: openOrder.marketIndex,
2741
+ reduceOnly: openOrder.reduceOnly,
2742
+ postOnly: openOrder.postOnly,
2743
+ immediateOrCancel: openOrder.immediateOrCancel,
2744
+ triggerPrice: openOrder.triggerPrice,
2745
+ triggerCondition: openOrder.triggerCondition,
2746
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
2747
+ auctionDuration: openOrder.auctionDuration,
2748
+ maxTs: openOrder.maxTs,
2749
+ auctionStartPrice: openOrder.auctionStartPrice,
2750
+ auctionEndPrice: openOrder.auctionEndPrice,
2751
+ };
2752
+ const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
2753
+
2754
+ const tx = new Transaction();
2755
+ tx.add(
2756
+ ComputeBudgetProgram.requestUnits({
2757
+ units: 1_000_000,
2758
+ additionalFee: 0,
2759
+ })
2760
+ );
2761
+ tx.add(cancelOrderIx);
2762
+ tx.add(placeOrderIx);
2763
+ const { txSig, slot } = await this.txSender.send(tx, [], this.opts);
2764
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
2765
+ return txSig;
2766
+ }
2767
+
2617
2768
  public async settlePNLs(
2618
2769
  users: {
2619
2770
  settleeUserAccountPublicKey: PublicKey;
@@ -2680,6 +2831,7 @@ export class ClearingHouse {
2680
2831
  state: await this.getStatePublicKey(),
2681
2832
  authority: this.wallet.publicKey,
2682
2833
  user: settleeUserAccountPublicKey,
2834
+ spotMarketVault: this.getQuoteSpotMarketAccount().vault,
2683
2835
  },
2684
2836
  remainingAccounts: remainingAccounts,
2685
2837
  });
@@ -2689,7 +2841,8 @@ export class ClearingHouse {
2689
2841
  userAccountPublicKey: PublicKey,
2690
2842
  userAccount: UserAccount,
2691
2843
  marketIndex: number,
2692
- maxBaseAssetAmount: BN
2844
+ maxBaseAssetAmount: BN,
2845
+ limitPrice?: BN
2693
2846
  ): Promise<TransactionSignature> {
2694
2847
  const { txSig, slot } = await this.txSender.send(
2695
2848
  wrapInTx(
@@ -2697,13 +2850,14 @@ export class ClearingHouse {
2697
2850
  userAccountPublicKey,
2698
2851
  userAccount,
2699
2852
  marketIndex,
2700
- maxBaseAssetAmount
2853
+ maxBaseAssetAmount,
2854
+ limitPrice
2701
2855
  )
2702
2856
  ),
2703
2857
  [],
2704
2858
  this.opts
2705
2859
  );
2706
- this.marketLastSlotCache.set(marketIndex, slot);
2860
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
2707
2861
  return txSig;
2708
2862
  }
2709
2863
 
@@ -2711,7 +2865,8 @@ export class ClearingHouse {
2711
2865
  userAccountPublicKey: PublicKey,
2712
2866
  userAccount: UserAccount,
2713
2867
  marketIndex: number,
2714
- maxBaseAssetAmount: BN
2868
+ maxBaseAssetAmount: BN,
2869
+ limitPrice?: BN
2715
2870
  ): Promise<TransactionInstruction> {
2716
2871
  const userStatsPublicKey = getUserStatsAccountPublicKey(
2717
2872
  this.program.programId,
@@ -2730,6 +2885,7 @@ export class ClearingHouse {
2730
2885
  return await this.program.instruction.liquidatePerp(
2731
2886
  marketIndex,
2732
2887
  maxBaseAssetAmount,
2888
+ limitPrice ?? null,
2733
2889
  {
2734
2890
  accounts: {
2735
2891
  state: await this.getStatePublicKey(),
@@ -2751,7 +2907,7 @@ export class ClearingHouse {
2751
2907
  liabilityMarketIndex: number,
2752
2908
  maxLiabilityTransfer: BN
2753
2909
  ): Promise<TransactionSignature> {
2754
- const { txSig } = await this.txSender.send(
2910
+ const { txSig, slot } = await this.txSender.send(
2755
2911
  wrapInTx(
2756
2912
  await this.getLiquidateSpotIx(
2757
2913
  userAccountPublicKey,
@@ -2764,6 +2920,8 @@ export class ClearingHouse {
2764
2920
  [],
2765
2921
  this.opts
2766
2922
  );
2923
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
2924
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
2767
2925
  return txSig;
2768
2926
  }
2769
2927
 
@@ -2826,7 +2984,8 @@ export class ClearingHouse {
2826
2984
  [],
2827
2985
  this.opts
2828
2986
  );
2829
- this.marketLastSlotCache.set(perpMarketIndex, slot);
2987
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
2988
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
2830
2989
  return txSig;
2831
2990
  }
2832
2991
 
@@ -2889,7 +3048,8 @@ export class ClearingHouse {
2889
3048
  [],
2890
3049
  this.opts
2891
3050
  );
2892
- this.marketLastSlotCache.set(perpMarketIndex, slot);
3051
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
3052
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
2893
3053
  return txSig;
2894
3054
  }
2895
3055
 
@@ -143,6 +143,7 @@ export class ClearingHouseUser {
143
143
  marketIndex,
144
144
  quoteAssetAmount: ZERO,
145
145
  quoteEntryAmount: ZERO,
146
+ quoteBreakEvenAmount: ZERO,
146
147
  openOrders: 0,
147
148
  openBids: ZERO,
148
149
  openAsks: ZERO,
@@ -213,7 +214,7 @@ export class ClearingHouseUser {
213
214
  */
214
215
  public getLPBidAsks(marketIndex: number): [BN, BN] {
215
216
  const position = this.getUserPosition(marketIndex);
216
- if (position.lpShares.eq(ZERO)) {
217
+ if (position === undefined || position.lpShares.eq(ZERO)) {
217
218
  return [ZERO, ZERO];
218
219
  }
219
220
 
@@ -773,30 +774,12 @@ export class ClearingHouseUser {
773
774
  settledPosition.baseAssetAmount.add(dustBaa);
774
775
  perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
775
776
 
776
- // open orders
777
- let openAsks;
778
- if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
779
- openAsks = market.amm.maxBaseAssetReserve
780
- .sub(market.amm.baseAssetReserve)
781
- .mul(perpPosition.lpShares)
782
- .div(market.amm.sqrtK)
783
- .mul(new BN(-1));
784
- } else {
785
- openAsks = ZERO;
786
- }
787
-
788
- let openBids;
789
- if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
790
- openBids = market.amm.baseAssetReserve
791
- .sub(market.amm.minBaseAssetReserve)
792
- .mul(perpPosition.lpShares)
793
- .div(market.amm.sqrtK);
794
- } else {
795
- openBids = ZERO;
796
- }
777
+ const [totalOpenBids, totalOpenAsks] = this.getPerpBidAsks(
778
+ market.marketIndex
779
+ );
797
780
 
798
- perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
799
- perpPosition.openBids = perpPosition.openBids.add(openBids);
781
+ perpPosition.openAsks = totalOpenAsks;
782
+ perpPosition.openBids = totalOpenBids;
800
783
  }
801
784
 
802
785
  let valuationPrice = this.getOracleDataForPerpMarket(
@@ -1118,6 +1101,7 @@ export class ClearingHouseUser {
1118
1101
  remainderBaseAssetAmount: 0,
1119
1102
  quoteAssetAmount: new BN(0),
1120
1103
  lastCumulativeFundingRate: ZERO,
1104
+ quoteBreakEvenAmount: new BN(0),
1121
1105
  quoteEntryAmount: new BN(0),
1122
1106
  openOrders: 0,
1123
1107
  openBids: new BN(0),
@@ -1487,7 +1471,7 @@ export class ClearingHouseUser {
1487
1471
 
1488
1472
  const amountWithdrawable = freeCollateral
1489
1473
  .mul(MARGIN_PRECISION)
1490
- .div(spotMarket.initialAssetWeight)
1474
+ .div(new BN(spotMarket.initialAssetWeight))
1491
1475
  .mul(PRICE_PRECISION)
1492
1476
  .div(oracleData.price)
1493
1477
  .mul(precisionIncrease);
@@ -1526,7 +1510,7 @@ export class ClearingHouseUser {
1526
1510
 
1527
1511
  const maxLiabilityAllowed = freeCollatAfterWithdraw
1528
1512
  .mul(MARGIN_PRECISION)
1529
- .div(spotMarket.initialLiabilityWeight)
1513
+ .div(new BN(spotMarket.initialLiabilityWeight))
1530
1514
  .mul(PRICE_PRECISION)
1531
1515
  .div(oracleData.price)
1532
1516
  .mul(precisionIncrease);
package/src/config.ts CHANGED
@@ -29,7 +29,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
29
29
  devnet: {
30
30
  ENV: 'devnet',
31
31
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
32
- CLEARING_HOUSE_PROGRAM_ID: 'BJG3o4CURrokB7huNyiHptYepAtHXj4YfefJiPgzUULV',
32
+ CLEARING_HOUSE_PROGRAM_ID: 'jAEeKs9twxAJmXZHqS2p459xW7FMDjoyvuqthRo9qGS',
33
33
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
34
34
  SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
35
35
  V2_ALPHA_TICKET_MINT_ADDRESS:
@@ -9,6 +9,11 @@ export class BigNum {
9
9
  static delim = '.';
10
10
  static spacer = ',';
11
11
 
12
+ public static setLocale(locale: string): void {
13
+ BigNum.delim = (1.1).toLocaleString(locale).slice(1, 2) || '.';
14
+ BigNum.spacer = (1000).toLocaleString(locale).slice(1, 2) || ',';
15
+ }
16
+
12
17
  constructor(
13
18
  val: BN | number | string,
14
19
  precisionVal: BN | number | string = new BN(0)
@@ -566,7 +571,21 @@ export class BigNum {
566
571
  * @returns
567
572
  */
568
573
  public toNum() {
569
- return parseFloat(this.print());
574
+ let printedValue = this.print();
575
+
576
+ // Must convert any non-US delimiters and spacers to US format before using parseFloat
577
+ if (BigNum.delim !== '.' || BigNum.spacer !== ',') {
578
+ printedValue = printedValue
579
+ .split('')
580
+ .map((char) => {
581
+ if (char === BigNum.delim) return '.';
582
+ if (char === BigNum.spacer) return ',';
583
+ return char;
584
+ })
585
+ .join('');
586
+ }
587
+
588
+ return parseFloat(printedValue);
570
589
  }
571
590
 
572
591
  static fromJSON(json: { val: string; precision: string }) {
@@ -599,12 +618,16 @@ export class BigNum {
599
618
  static fromPrint(val: string, precisionShift?: BN): BigNum {
600
619
  // Handle empty number edge cases
601
620
  if (!val) return BigNum.from(ZERO, precisionShift);
602
- if (!val.replace(BigNum.delim, ''))
621
+ if (!val.replace(BigNum.delim, '')) {
603
622
  return BigNum.from(ZERO, precisionShift);
623
+ }
604
624
 
605
- const [leftSide, rightSide] = val.split(BigNum.delim);
625
+ const sides = val.split(BigNum.delim);
626
+ const rightSide = sides[1];
627
+ const leftSide = sides[0].replace(/\D/g, '');
628
+ const bnInput = `${leftSide ?? ''}${rightSide ?? ''}`;
606
629
 
607
- const rawBn = new BN(`${leftSide ?? ''}${rightSide ?? ''}`);
630
+ const rawBn = new BN(bnInput);
608
631
 
609
632
  const rightSideLength = rightSide?.length ?? 0;
610
633