@drift-labs/sdk 2.83.0-beta.8 → 2.84.0-beta.0

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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.83.0-beta.8
1
+ 2.84.0-beta.0
@@ -7,10 +7,10 @@ export declare class AdminClient extends DriftClient {
7
7
  initialize(usdcMint: PublicKey, _adminControlsPrices: boolean): Promise<[TransactionSignature]>;
8
8
  initializeSpotMarket(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidatorFee?: number, ifLiquidationFee?: number, activeStatus?: boolean, assetTier?: {
9
9
  collateral: {};
10
- }, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string): Promise<TransactionSignature>;
10
+ }, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string, marketIndex?: number): Promise<TransactionSignature>;
11
11
  getInitializeSpotMarketIx(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidatorFee?: number, ifLiquidationFee?: number, activeStatus?: boolean, assetTier?: {
12
12
  collateral: {};
13
- }, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string): Promise<TransactionInstruction>;
13
+ }, scaleInitialAssetWeightStart?: BN, withdrawGuardThreshold?: BN, orderTickSize?: BN, orderStepSize?: BN, ifTotalFactor?: number, name?: string, marketIndex?: number): Promise<TransactionInstruction>;
14
14
  deleteInitializedSpotMarket(marketIndex: number): Promise<TransactionSignature>;
15
15
  getDeleteInitializedSpotMarketIx(marketIndex: number): Promise<TransactionInstruction>;
16
16
  initializeSerumFulfillmentConfig(marketIndex: number, serumMarket: PublicKey, serumProgram: PublicKey): Promise<TransactionSignature>;
@@ -61,12 +61,11 @@ class AdminClient extends driftClient_1.DriftClient {
61
61
  const { txSig } = await super.sendTransaction(tx, [], this.opts);
62
62
  return [txSig];
63
63
  }
64
- async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME) {
65
- const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
66
- const initializeIx = await this.getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor, liquidatorFee, ifLiquidationFee, activeStatus, assetTier, scaleInitialAssetWeightStart, withdrawGuardThreshold, orderTickSize, orderStepSize, ifTotalFactor, name);
64
+ async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME, marketIndex) {
65
+ const spotMarketIndex = marketIndex !== null && marketIndex !== void 0 ? marketIndex : this.getStateAccount().numberOfSpotMarkets;
66
+ const initializeIx = await this.getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor, liquidatorFee, ifLiquidationFee, activeStatus, assetTier, scaleInitialAssetWeightStart, withdrawGuardThreshold, orderTickSize, orderStepSize, ifTotalFactor, name, marketIndex);
67
67
  const tx = await this.buildTransaction(initializeIx);
68
68
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
69
- // const { txSig } = await this.sendTransaction(initializeTx, [], this.opts);
70
69
  await this.accountSubscriber.addSpotMarket(spotMarketIndex);
71
70
  await this.accountSubscriber.addOracle({
72
71
  source: oracleSource,
@@ -75,8 +74,8 @@ class AdminClient extends driftClient_1.DriftClient {
75
74
  await this.accountSubscriber.setSpotOracleMap();
76
75
  return txSig;
77
76
  }
78
- async getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME) {
79
- const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
77
+ async getInitializeSpotMarketIx(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidatorFee = 0, ifLiquidationFee = 0, activeStatus = true, assetTier = types_1.AssetTier.COLLATERAL, scaleInitialAssetWeightStart = numericConstants_1.ZERO, withdrawGuardThreshold = numericConstants_1.ZERO, orderTickSize = numericConstants_1.ONE, orderStepSize = numericConstants_1.ONE, ifTotalFactor = 0, name = userName_1.DEFAULT_MARKET_NAME, marketIndex) {
78
+ const spotMarketIndex = marketIndex !== null && marketIndex !== void 0 ? marketIndex : this.getStateAccount().numberOfSpotMarkets;
80
79
  const spotMarket = await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, spotMarketIndex);
81
80
  const spotMarketVault = await (0, pda_1.getSpotMarketVaultPublicKey)(this.program.programId, spotMarketIndex);
82
81
  const insuranceFundVault = await (0, pda_1.getInsuranceFundVaultPublicKey)(this.program.programId, spotMarketIndex);
@@ -184,7 +183,7 @@ class AdminClient extends driftClient_1.DriftClient {
184
183
  while (this.getStateAccount().numberOfMarkets <= currentPerpMarketIndex) {
185
184
  await this.fetchAccounts();
186
185
  }
187
- await this.accountSubscriber.addPerpMarket(currentPerpMarketIndex);
186
+ await this.accountSubscriber.addPerpMarket(marketIndex);
188
187
  await this.accountSubscriber.addOracle({
189
188
  source: oracleSource,
190
189
  publicKey: priceOracle,
@@ -193,8 +192,7 @@ class AdminClient extends driftClient_1.DriftClient {
193
192
  return txSig;
194
193
  }
195
194
  async getInitializePerpMarketIx(marketIndex, priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION, oracleSource = types_1.OracleSource.PYTH, contractTier = types_1.ContractTier.SPECULATIVE, marginRatioInitial = 2000, marginRatioMaintenance = 500, liquidatorFee = 0, ifLiquidatorFee = 10000, imfFactor = 0, activeStatus = true, baseSpread = 0, maxSpread = 142500, maxOpenInterest = numericConstants_1.ZERO, maxRevenueWithdrawPerPeriod = numericConstants_1.ZERO, quoteMaxInsurance = numericConstants_1.ZERO, orderStepSize = numericConstants_1.BASE_PRECISION.divn(10000), orderTickSize = numericConstants_1.PRICE_PRECISION.divn(100000), minOrderSize = numericConstants_1.BASE_PRECISION.divn(10000), concentrationCoefScale = numericConstants_1.ONE, curveUpdateIntensity = 0, ammJitIntensity = 0, name = userName_1.DEFAULT_MARKET_NAME) {
196
- const currentPerpMarketIndex = this.getStateAccount().numberOfMarkets;
197
- const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, currentPerpMarketIndex);
195
+ const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, marketIndex);
198
196
  const nameBuffer = (0, userName_1.encodeName)(name);
199
197
  return await this.program.instruction.initializePerpMarket(marketIndex, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier, oracleSource, contractTier, marginRatioInitial, marginRatioMaintenance, liquidatorFee, ifLiquidatorFee, imfFactor, activeStatus, baseSpread, maxSpread, maxOpenInterest, maxRevenueWithdrawPerPeriod, quoteMaxInsurance, orderStepSize, orderTickSize, minOrderSize, concentrationCoefScale, curveUpdateIntensity, ammJitIntensity, nameBuffer, {
200
198
  accounts: {
@@ -11,11 +11,13 @@ export interface ClockSubscriberEvent {
11
11
  }
12
12
  export declare class ClockSubscriber {
13
13
  private connection;
14
- private latestSlot;
15
- currentTs: number;
14
+ private _latestSlot;
15
+ private _currentTs;
16
16
  private subscriptionId;
17
17
  commitment: Commitment;
18
18
  eventEmitter: StrictEventEmitter<EventEmitter, ClockSubscriberEvent>;
19
+ get latestSlot(): number;
20
+ get currentTs(): number;
19
21
  private timeoutId?;
20
22
  private resubTimeoutMs?;
21
23
  private isUnsubscribing;
@@ -5,6 +5,12 @@ const web3_js_1 = require("@solana/web3.js");
5
5
  const events_1 = require("events");
6
6
  const __1 = require("..");
7
7
  class ClockSubscriber {
8
+ get latestSlot() {
9
+ return this._latestSlot;
10
+ }
11
+ get currentTs() {
12
+ return this._currentTs;
13
+ }
8
14
  constructor(connection, config) {
9
15
  this.connection = connection;
10
16
  this.isUnsubscribing = false;
@@ -27,8 +33,8 @@ class ClockSubscriber {
27
33
  clearTimeout(this.timeoutId);
28
34
  this.setTimeout();
29
35
  }
30
- this.latestSlot = context.slot;
31
- this.currentTs = new __1.BN(acctInfo.data.subarray(32, 39), undefined, 'le').toNumber();
36
+ this._latestSlot = context.slot;
37
+ this._currentTs = new __1.BN(acctInfo.data.subarray(32, 39), undefined, 'le').toNumber();
32
38
  this.eventEmitter.emit('clockUpdate', this.currentTs);
33
39
  }
34
40
  }, this.commitment);
@@ -566,6 +566,16 @@ exports.MainnetPerpMarkets = [
566
566
  launchTs: 1716595200000,
567
567
  oracleSource: __1.OracleSource.SWITCHBOARD,
568
568
  },
569
+ {
570
+ fullName: 'Sanctum',
571
+ category: ['LST', 'Solana'],
572
+ symbol: 'CLOUD-PERP',
573
+ baseAssetSymbol: 'CLOUD',
574
+ marketIndex: 31,
575
+ oracle: new web3_js_1.PublicKey('C7UxgCodaEy4yqwTe3a4QXfsG7LnpMGGQdEqaxDae4b8'),
576
+ launchTs: 1717597648000,
577
+ oracleSource: __1.OracleSource.Prelaunch,
578
+ },
569
579
  ];
570
580
  exports.PerpMarkets = {
571
581
  devnet: exports.DevnetPerpMarkets,
@@ -31,7 +31,7 @@ type OrderBookCallback = () => void;
31
31
  * Receives a DLOBNode and is expected to return true if the node should
32
32
  * be taken into account when generating, or false otherwise.
33
33
  *
34
- * Currently used in getRestingLimitBids and getRestingLimitAsks.
34
+ * Currently used in functions that rely on getBestNode
35
35
  */
36
36
  export type DLOBFilterFcn = (node: DLOBNode) => boolean;
37
37
  export type NodeToFill = {
@@ -79,13 +79,13 @@ export declare class DLOB {
79
79
  findNodesCrossingFallbackLiquidity(marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, nodeGenerator: Generator<DLOBNode>, doesCross: (nodePrice: BN | undefined) => boolean, minAuctionDuration: number): NodeToFill[];
80
80
  findExpiredNodesToFill(marketIndex: number, ts: number, marketType: MarketType): NodeToFill[];
81
81
  findJitAuctionNodesToFill(marketIndex: number, slot: number, oraclePriceData: OraclePriceData, marketType: MarketType): NodeToFill[];
82
- getTakingBids(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
83
- getTakingAsks(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
82
+ getTakingBids(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
83
+ getTakingAsks(marketIndex: number, marketType: MarketType, slot: number, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
84
84
  private getBestNode;
85
85
  getRestingLimitAsks(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
86
86
  getRestingLimitBids(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
87
- getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
88
- getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
87
+ getAsks(marketIndex: number, fallbackAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
88
+ getBids(marketIndex: number, fallbackBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, filterFcn?: DLOBFilterFcn): Generator<DLOBNode>;
89
89
  findCrossingRestingLimitOrders(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
90
90
  determineMakerAndTaker(askNode: DLOBNode, bidNode: DLOBNode): {
91
91
  takerNode: DLOBNode;
package/lib/dlob/DLOB.js CHANGED
@@ -565,7 +565,7 @@ class DLOB {
565
565
  }
566
566
  return nodesToFill;
567
567
  }
568
- *getTakingBids(marketIndex, marketType, slot, oraclePriceData) {
568
+ *getTakingBids(marketIndex, marketType, slot, oraclePriceData, filterFcn) {
569
569
  const marketTypeStr = (0, __1.getVariant)(marketType);
570
570
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
571
571
  if (!orderLists) {
@@ -578,9 +578,9 @@ class DLOB {
578
578
  ];
579
579
  yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
580
580
  return bestNode.order.slot.lt(currentNode.order.slot);
581
- });
581
+ }, filterFcn);
582
582
  }
583
- *getTakingAsks(marketIndex, marketType, slot, oraclePriceData) {
583
+ *getTakingAsks(marketIndex, marketType, slot, oraclePriceData, filterFcn) {
584
584
  const marketTypeStr = (0, __1.getVariant)(marketType);
585
585
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
586
586
  if (!orderLists) {
@@ -593,7 +593,7 @@ class DLOB {
593
593
  ];
594
594
  yield* this.getBestNode(generatorList, oraclePriceData, slot, (bestNode, currentNode) => {
595
595
  return bestNode.order.slot.lt(currentNode.order.slot);
596
- });
596
+ }, filterFcn);
597
597
  }
598
598
  *getBestNode(generatorList, oraclePriceData, slot, compareFcn, filterFcn) {
599
599
  const generators = generatorList.map((generator) => {
@@ -675,7 +675,7 @@ class DLOB {
675
675
  .gt(currentNode.getPrice(oraclePriceData, slot));
676
676
  }, filterFcn);
677
677
  }
678
- *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
678
+ *getAsks(marketIndex, fallbackAsk, slot, marketType, oraclePriceData, filterFcn) {
679
679
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
680
680
  throw new Error('Must provide OraclePriceData to get spot asks');
681
681
  }
@@ -706,9 +706,9 @@ class DLOB {
706
706
  return bestNode
707
707
  .getPrice(oraclePriceData, slot)
708
708
  .lt(currentNode.getPrice(oraclePriceData, slot));
709
- });
709
+ }, filterFcn);
710
710
  }
711
- *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData) {
711
+ *getBids(marketIndex, fallbackBid, slot, marketType, oraclePriceData, filterFcn) {
712
712
  if ((0, __1.isVariant)(marketType, 'spot') && !oraclePriceData) {
713
713
  throw new Error('Must provide OraclePriceData to get spot bids');
714
714
  }
@@ -739,7 +739,7 @@ class DLOB {
739
739
  return bestNode
740
740
  .getPrice(oraclePriceData, slot)
741
741
  .gt(currentNode.getPrice(oraclePriceData, slot));
742
- });
742
+ }, filterFcn);
743
743
  }
744
744
  findCrossingRestingLimitOrders(marketIndex, slot, marketType, oraclePriceData) {
745
745
  const nodesToFill = new Array();
@@ -593,7 +593,7 @@ export declare class DriftClient {
593
593
  settleeUserAccount: UserAccount;
594
594
  }[], marketIndexes: number[], opts?: {
595
595
  filterInvalidMarkets?: boolean;
596
- }): Promise<TransactionSignature>;
596
+ }, txParams?: TxParams): Promise<TransactionSignature>;
597
597
  getSettlePNLsIxs(users: {
598
598
  settleeUserAccountPublicKey: PublicKey;
599
599
  settleeUserAccount: UserAccount;
@@ -2949,7 +2949,7 @@ class DriftClient {
2949
2949
  remainingAccounts,
2950
2950
  });
2951
2951
  }
2952
- async settlePNLs(users, marketIndexes, opts) {
2952
+ async settlePNLs(users, marketIndexes, opts, txParams) {
2953
2953
  const filterInvalidMarkets = opts === null || opts === void 0 ? void 0 : opts.filterInvalidMarkets;
2954
2954
  // # Filter market indexes by markets with valid oracle
2955
2955
  const marketIndexToSettle = filterInvalidMarkets
@@ -2969,7 +2969,7 @@ class DriftClient {
2969
2969
  }
2970
2970
  // # Settle filtered market indexes
2971
2971
  const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
2972
- const tx = await this.buildTransaction(ixs, {
2972
+ const tx = await this.buildTransaction(ixs, txParams !== null && txParams !== void 0 ? txParams : {
2973
2973
  computeUnits: 1400000,
2974
2974
  });
2975
2975
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.82.0",
2
+ "version": "2.83.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -69,7 +69,7 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
69
69
  const deltaDenominator = new _1.BN(order.auctionDuration);
70
70
  const deltaNumerator = _1.BN.min(slotsElapsed, deltaDenominator);
71
71
  if (deltaDenominator.eq(_1.ZERO)) {
72
- return oraclePrice.add(order.auctionEndPrice);
72
+ return _1.BN.max(oraclePrice.add(order.auctionEndPrice), _1.ONE);
73
73
  }
74
74
  let priceOffsetDelta;
75
75
  if ((0, types_1.isVariant)(order.direction, 'long')) {
@@ -91,7 +91,7 @@ function getAuctionPriceForOracleOffsetAuction(order, slot, oraclePrice) {
91
91
  else {
92
92
  priceOffset = order.auctionStartPrice.sub(priceOffsetDelta);
93
93
  }
94
- return oraclePrice.add(priceOffset);
94
+ return _1.BN.max(oraclePrice.add(priceOffset), _1.ONE);
95
95
  }
96
96
  exports.getAuctionPriceForOracleOffsetAuction = getAuctionPriceForOracleOffsetAuction;
97
97
  function deriveOracleAuctionParams({ direction, oraclePrice, auctionStartPrice, auctionEndPrice, limitPrice, }) {
@@ -102,7 +102,7 @@ function getLimitPrice(order, oraclePriceData, slot, fallbackPrice) {
102
102
  limitPrice = (0, auction_1.getAuctionPrice)(order, slot, oraclePriceData.price);
103
103
  }
104
104
  else if (order.oraclePriceOffset !== 0) {
105
- limitPrice = oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset));
105
+ limitPrice = anchor_1.BN.max(oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset)), numericConstants_1.ONE);
106
106
  }
107
107
  else if (order.price.eq(numericConstants_1.ZERO)) {
108
108
  limitPrice = fallbackPrice;
@@ -29,6 +29,7 @@ export declare class UserMap implements UserMapInterface {
29
29
  private stateAccountUpdateCallback;
30
30
  private decode;
31
31
  private mostRecentSlot;
32
+ private syncConfig;
32
33
  private syncPromise?;
33
34
  private syncPromiseResolver;
34
35
  /**
@@ -78,6 +79,8 @@ export declare class UserMap implements UserMapInterface {
78
79
  */
79
80
  getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
80
81
  sync(): Promise<void>;
82
+ private defaultSync;
83
+ private paginatedSync;
81
84
  unsubscribe(): Promise<void>;
82
85
  updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
83
86
  updateLatestSlot(slot: number): void;
@@ -15,7 +15,7 @@ class UserMap {
15
15
  * Constructs a new UserMap instance.
16
16
  */
17
17
  constructor(config) {
18
- var _a, _b, _c, _d;
18
+ var _a, _b, _c, _d, _e;
19
19
  this.userMap = new Map();
20
20
  this.stateAccountUpdateCallback = async (state) => {
21
21
  if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
@@ -64,6 +64,9 @@ class UserMap {
64
64
  decodeFn,
65
65
  });
66
66
  }
67
+ this.syncConfig = (_e = config.syncConfig) !== null && _e !== void 0 ? _e : {
68
+ type: 'default',
69
+ };
67
70
  }
68
71
  async subscribe() {
69
72
  if (this.size() > 0) {
@@ -230,6 +233,14 @@ class UserMap {
230
233
  return userAuthKeys;
231
234
  }
232
235
  async sync() {
236
+ if (this.syncConfig.type === 'default') {
237
+ return this.defaultSync();
238
+ }
239
+ else {
240
+ return this.paginatedSync();
241
+ }
242
+ }
243
+ async defaultSync() {
233
244
  var _a;
234
245
  if (this.syncPromise) {
235
246
  return this.syncPromise;
@@ -298,6 +309,97 @@ class UserMap {
298
309
  this.syncPromise = undefined;
299
310
  }
300
311
  }
312
+ async paginatedSync() {
313
+ var _a, _b;
314
+ if (this.syncPromise) {
315
+ return this.syncPromise;
316
+ }
317
+ this.syncPromise = new Promise((resolve) => {
318
+ this.syncPromiseResolver = resolve;
319
+ });
320
+ try {
321
+ const accountsPrefetch = await this.connection.getProgramAccounts(this.driftClient.program.programId, {
322
+ dataSlice: { offset: 0, length: 0 },
323
+ filters: [
324
+ (0, memcmp_1.getUserFilter)(),
325
+ ...(!this.includeIdle ? [(0, memcmp_1.getNonIdleUserFilter)()] : []),
326
+ ],
327
+ });
328
+ const accountPublicKeys = accountsPrefetch.map((account) => account.pubkey);
329
+ const limitConcurrency = async (tasks, limit) => {
330
+ const executing = [];
331
+ const results = [];
332
+ for (let i = 0; i < tasks.length; i++) {
333
+ const executor = Promise.resolve().then(tasks[i]);
334
+ results.push(executor);
335
+ if (executing.length < limit) {
336
+ executing.push(executor);
337
+ executor.finally(() => {
338
+ const index = executing.indexOf(executor);
339
+ if (index > -1) {
340
+ executing.splice(index, 1);
341
+ }
342
+ });
343
+ }
344
+ else {
345
+ await Promise.race(executing);
346
+ }
347
+ }
348
+ return Promise.all(results);
349
+ };
350
+ const programAccountBufferMap = new Map();
351
+ // @ts-ignore
352
+ const chunkSize = (_a = this.syncConfig.chunkSize) !== null && _a !== void 0 ? _a : 100;
353
+ const tasks = [];
354
+ for (let i = 0; i < accountPublicKeys.length; i += chunkSize) {
355
+ const chunk = accountPublicKeys.slice(i, i + chunkSize);
356
+ tasks.push(async () => {
357
+ const accountInfos = await this.connection.getMultipleAccountsInfoAndContext(chunk, {
358
+ commitment: this.commitment,
359
+ });
360
+ const accountInfosSlot = accountInfos.context.slot;
361
+ for (let j = 0; j < accountInfos.value.length; j += 1) {
362
+ const accountInfo = accountInfos.value[j];
363
+ if (accountInfo === null)
364
+ continue;
365
+ const publicKeyString = chunk[j].toString();
366
+ const buffer = buffer_1.Buffer.from(accountInfo.data);
367
+ programAccountBufferMap.set(publicKeyString, buffer);
368
+ const decodedUser = this.decode('User', buffer);
369
+ const currAccountWithSlot = this.getWithSlot(publicKeyString);
370
+ if (currAccountWithSlot &&
371
+ currAccountWithSlot.slot <= accountInfosSlot) {
372
+ this.updateUserAccount(publicKeyString, decodedUser, accountInfosSlot);
373
+ }
374
+ else {
375
+ await this.addPubkey(new web3_js_1.PublicKey(publicKeyString), decodedUser, accountInfosSlot);
376
+ }
377
+ }
378
+ });
379
+ }
380
+ // @ts-ignore
381
+ const concurrencyLimit = (_b = this.syncConfig.concurrencyLimit) !== null && _b !== void 0 ? _b : 10;
382
+ await limitConcurrency(tasks, concurrencyLimit);
383
+ for (const [key] of this.entries()) {
384
+ if (!programAccountBufferMap.has(key)) {
385
+ const user = this.get(key);
386
+ if (user) {
387
+ await user.unsubscribe();
388
+ this.userMap.delete(key);
389
+ }
390
+ }
391
+ }
392
+ }
393
+ catch (err) {
394
+ console.error(`Error in UserMap.sync():`, err);
395
+ }
396
+ finally {
397
+ if (this.syncPromiseResolver) {
398
+ this.syncPromiseResolver();
399
+ }
400
+ this.syncPromise = undefined;
401
+ }
402
+ }
301
403
  async unsubscribe() {
302
404
  await this.subscription.unsubscribe();
303
405
  for (const [key, user] of this.entries()) {
@@ -3,6 +3,13 @@ import { DriftClient } from '../driftClient';
3
3
  export type UserAccountFilterCriteria = {
4
4
  hasOpenOrders: boolean;
5
5
  };
6
+ export type SyncConfig = {
7
+ type: 'default';
8
+ } | {
9
+ type: 'paginated';
10
+ chunkSize?: number;
11
+ concurrencyLimit?: number;
12
+ };
6
13
  export type UserMapConfig = {
7
14
  driftClient: DriftClient;
8
15
  connection?: Connection;
@@ -20,4 +27,5 @@ export type UserMapConfig = {
20
27
  includeIdle?: boolean;
21
28
  fastDecode?: boolean;
22
29
  disableSyncOnTotalAccountsChange?: boolean;
30
+ syncConfig?: SyncConfig;
23
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.83.0-beta.8",
3
+ "version": "2.84.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -102,9 +102,11 @@ export class AdminClient extends DriftClient {
102
102
  orderTickSize = ONE,
103
103
  orderStepSize = ONE,
104
104
  ifTotalFactor = 0,
105
- name = DEFAULT_MARKET_NAME
105
+ name = DEFAULT_MARKET_NAME,
106
+ marketIndex?: number
106
107
  ): Promise<TransactionSignature> {
107
- const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
108
+ const spotMarketIndex =
109
+ marketIndex ?? this.getStateAccount().numberOfSpotMarkets;
108
110
 
109
111
  const initializeIx = await this.getInitializeSpotMarketIx(
110
112
  mint,
@@ -127,15 +129,14 @@ export class AdminClient extends DriftClient {
127
129
  orderTickSize,
128
130
  orderStepSize,
129
131
  ifTotalFactor,
130
- name
132
+ name,
133
+ marketIndex
131
134
  );
132
135
 
133
136
  const tx = await this.buildTransaction(initializeIx);
134
137
 
135
138
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
136
139
 
137
- // const { txSig } = await this.sendTransaction(initializeTx, [], this.opts);
138
-
139
140
  await this.accountSubscriber.addSpotMarket(spotMarketIndex);
140
141
  await this.accountSubscriber.addOracle({
141
142
  source: oracleSource,
@@ -167,9 +168,11 @@ export class AdminClient extends DriftClient {
167
168
  orderTickSize = ONE,
168
169
  orderStepSize = ONE,
169
170
  ifTotalFactor = 0,
170
- name = DEFAULT_MARKET_NAME
171
+ name = DEFAULT_MARKET_NAME,
172
+ marketIndex?: number
171
173
  ): Promise<TransactionInstruction> {
172
- const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
174
+ const spotMarketIndex =
175
+ marketIndex ?? this.getStateAccount().numberOfSpotMarkets;
173
176
  const spotMarket = await getSpotMarketPublicKey(
174
177
  this.program.programId,
175
178
  spotMarketIndex
@@ -444,7 +447,7 @@ export class AdminClient extends DriftClient {
444
447
  await this.fetchAccounts();
445
448
  }
446
449
 
447
- await this.accountSubscriber.addPerpMarket(currentPerpMarketIndex);
450
+ await this.accountSubscriber.addPerpMarket(marketIndex);
448
451
  await this.accountSubscriber.addOracle({
449
452
  source: oracleSource,
450
453
  publicKey: priceOracle,
@@ -482,10 +485,9 @@ export class AdminClient extends DriftClient {
482
485
  ammJitIntensity = 0,
483
486
  name = DEFAULT_MARKET_NAME
484
487
  ): Promise<TransactionInstruction> {
485
- const currentPerpMarketIndex = this.getStateAccount().numberOfMarkets;
486
488
  const perpMarketPublicKey = await getPerpMarketPublicKey(
487
489
  this.program.programId,
488
- currentPerpMarketIndex
490
+ marketIndex
489
491
  );
490
492
 
491
493
  const nameBuffer = encodeName(name);
@@ -14,12 +14,20 @@ export interface ClockSubscriberEvent {
14
14
  }
15
15
 
16
16
  export class ClockSubscriber {
17
- private latestSlot: number;
18
- currentTs: number;
17
+ private _latestSlot: number;
18
+ private _currentTs: number;
19
19
  private subscriptionId: number;
20
20
  commitment: Commitment;
21
21
  eventEmitter: StrictEventEmitter<EventEmitter, ClockSubscriberEvent>;
22
22
 
23
+ public get latestSlot(): number {
24
+ return this._latestSlot;
25
+ }
26
+
27
+ public get currentTs(): number {
28
+ return this._currentTs;
29
+ }
30
+
23
31
  // Reconnection
24
32
  private timeoutId?: NodeJS.Timeout;
25
33
  private resubTimeoutMs?: number;
@@ -54,8 +62,8 @@ export class ClockSubscriber {
54
62
  clearTimeout(this.timeoutId);
55
63
  this.setTimeout();
56
64
  }
57
- this.latestSlot = context.slot;
58
- this.currentTs = new BN(
65
+ this._latestSlot = context.slot;
66
+ this._currentTs = new BN(
59
67
  acctInfo.data.subarray(32, 39),
60
68
  undefined,
61
69
  'le'
@@ -577,6 +577,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
577
577
  launchTs: 1716595200000,
578
578
  oracleSource: OracleSource.SWITCHBOARD,
579
579
  },
580
+ {
581
+ fullName: 'Sanctum',
582
+ category: ['LST', 'Solana'],
583
+ symbol: 'CLOUD-PERP',
584
+ baseAssetSymbol: 'CLOUD',
585
+ marketIndex: 31,
586
+ oracle: new PublicKey('C7UxgCodaEy4yqwTe3a4QXfsG7LnpMGGQdEqaxDae4b8'),
587
+ launchTs: 1717597648000,
588
+ oracleSource: OracleSource.Prelaunch,
589
+ },
580
590
  ];
581
591
 
582
592
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
package/src/dlob/DLOB.ts CHANGED
@@ -76,7 +76,7 @@ type OrderBookCallback = () => void;
76
76
  * Receives a DLOBNode and is expected to return true if the node should
77
77
  * be taken into account when generating, or false otherwise.
78
78
  *
79
- * Currently used in getRestingLimitBids and getRestingLimitAsks.
79
+ * Currently used in functions that rely on getBestNode
80
80
  */
81
81
  export type DLOBFilterFcn = (node: DLOBNode) => boolean;
82
82
 
@@ -1046,7 +1046,8 @@ export class DLOB {
1046
1046
  marketIndex: number,
1047
1047
  marketType: MarketType,
1048
1048
  slot: number,
1049
- oraclePriceData: OraclePriceData
1049
+ oraclePriceData: OraclePriceData,
1050
+ filterFcn?: DLOBFilterFcn
1050
1051
  ): Generator<DLOBNode> {
1051
1052
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
1052
1053
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
@@ -1067,7 +1068,8 @@ export class DLOB {
1067
1068
  slot,
1068
1069
  (bestNode, currentNode) => {
1069
1070
  return bestNode.order.slot.lt(currentNode.order.slot);
1070
- }
1071
+ },
1072
+ filterFcn
1071
1073
  );
1072
1074
  }
1073
1075
 
@@ -1075,7 +1077,8 @@ export class DLOB {
1075
1077
  marketIndex: number,
1076
1078
  marketType: MarketType,
1077
1079
  slot: number,
1078
- oraclePriceData: OraclePriceData
1080
+ oraclePriceData: OraclePriceData,
1081
+ filterFcn?: DLOBFilterFcn
1079
1082
  ): Generator<DLOBNode> {
1080
1083
  const marketTypeStr = getVariant(marketType) as MarketTypeStr;
1081
1084
  const orderLists = this.orderLists.get(marketTypeStr).get(marketIndex);
@@ -1096,7 +1099,8 @@ export class DLOB {
1096
1099
  slot,
1097
1100
  (bestNode, currentNode) => {
1098
1101
  return bestNode.order.slot.lt(currentNode.order.slot);
1099
- }
1102
+ },
1103
+ filterFcn
1100
1104
  );
1101
1105
  }
1102
1106
 
@@ -1241,7 +1245,8 @@ export class DLOB {
1241
1245
  fallbackAsk: BN | undefined,
1242
1246
  slot: number,
1243
1247
  marketType: MarketType,
1244
- oraclePriceData: OraclePriceData
1248
+ oraclePriceData: OraclePriceData,
1249
+ filterFcn?: DLOBFilterFcn
1245
1250
  ): Generator<DLOBNode> {
1246
1251
  if (isVariant(marketType, 'spot') && !oraclePriceData) {
1247
1252
  throw new Error('Must provide OraclePriceData to get spot asks');
@@ -1284,7 +1289,8 @@ export class DLOB {
1284
1289
  return bestNode
1285
1290
  .getPrice(oraclePriceData, slot)
1286
1291
  .lt(currentNode.getPrice(oraclePriceData, slot));
1287
- }
1292
+ },
1293
+ filterFcn
1288
1294
  );
1289
1295
  }
1290
1296
 
@@ -1293,7 +1299,8 @@ export class DLOB {
1293
1299
  fallbackBid: BN | undefined,
1294
1300
  slot: number,
1295
1301
  marketType: MarketType,
1296
- oraclePriceData: OraclePriceData
1302
+ oraclePriceData: OraclePriceData,
1303
+ filterFcn?: DLOBFilterFcn
1297
1304
  ): Generator<DLOBNode> {
1298
1305
  if (isVariant(marketType, 'spot') && !oraclePriceData) {
1299
1306
  throw new Error('Must provide OraclePriceData to get spot bids');
@@ -1336,7 +1343,8 @@ export class DLOB {
1336
1343
  return bestNode
1337
1344
  .getPrice(oraclePriceData, slot)
1338
1345
  .gt(currentNode.getPrice(oraclePriceData, slot));
1339
- }
1346
+ },
1347
+ filterFcn
1340
1348
  );
1341
1349
  }
1342
1350
 
@@ -5385,7 +5385,8 @@ export class DriftClient {
5385
5385
  marketIndexes: number[],
5386
5386
  opts?: {
5387
5387
  filterInvalidMarkets?: boolean;
5388
- }
5388
+ },
5389
+ txParams?: TxParams
5389
5390
  ): Promise<TransactionSignature> {
5390
5391
  const filterInvalidMarkets = opts?.filterInvalidMarkets;
5391
5392
 
@@ -5418,9 +5419,12 @@ export class DriftClient {
5418
5419
  // # Settle filtered market indexes
5419
5420
  const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
5420
5421
 
5421
- const tx = await this.buildTransaction(ixs, {
5422
- computeUnits: 1_400_000,
5423
- });
5422
+ const tx = await this.buildTransaction(
5423
+ ixs,
5424
+ txParams ?? {
5425
+ computeUnits: 1_400_000,
5426
+ }
5427
+ );
5424
5428
 
5425
5429
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
5426
5430
  return txSig;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.82.0",
2
+ "version": "2.83.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -88,7 +88,7 @@ export function getAuctionPriceForOracleOffsetAuction(
88
88
  const deltaNumerator = BN.min(slotsElapsed, deltaDenominator);
89
89
 
90
90
  if (deltaDenominator.eq(ZERO)) {
91
- return oraclePrice.add(order.auctionEndPrice);
91
+ return BN.max(oraclePrice.add(order.auctionEndPrice), ONE);
92
92
  }
93
93
 
94
94
  let priceOffsetDelta;
@@ -111,7 +111,7 @@ export function getAuctionPriceForOracleOffsetAuction(
111
111
  priceOffset = order.auctionStartPrice.sub(priceOffsetDelta);
112
112
  }
113
113
 
114
- return oraclePrice.add(priceOffset);
114
+ return BN.max(oraclePrice.add(priceOffset), ONE);
115
115
  }
116
116
 
117
117
  export function deriveOracleAuctionParams({
@@ -7,7 +7,7 @@ import {
7
7
  Order,
8
8
  PositionDirection,
9
9
  } from '../types';
10
- import { ZERO, TWO } from '../constants/numericConstants';
10
+ import { ZERO, TWO, ONE } from '../constants/numericConstants';
11
11
  import { BN } from '@coral-xyz/anchor';
12
12
  import { OraclePriceData } from '../oracles/types';
13
13
  import {
@@ -160,7 +160,10 @@ export function getLimitPrice(
160
160
  if (hasAuctionPrice(order, slot)) {
161
161
  limitPrice = getAuctionPrice(order, slot, oraclePriceData.price);
162
162
  } else if (order.oraclePriceOffset !== 0) {
163
- limitPrice = oraclePriceData.price.add(new BN(order.oraclePriceOffset));
163
+ limitPrice = BN.max(
164
+ oraclePriceData.price.add(new BN(order.oraclePriceOffset)),
165
+ ONE
166
+ );
164
167
  } else if (order.price.eq(ZERO)) {
165
168
  limitPrice = fallbackPrice;
166
169
  } else {
@@ -29,6 +29,7 @@ import { Buffer } from 'buffer';
29
29
  import { ZSTDDecoder } from 'zstddec';
30
30
  import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
31
31
  import {
32
+ SyncConfig,
32
33
  UserAccountFilterCriteria as UserFilterCriteria,
33
34
  UserMapConfig,
34
35
  } from './userMapConfig';
@@ -83,6 +84,7 @@ export class UserMap implements UserMapInterface {
83
84
  };
84
85
  private decode;
85
86
  private mostRecentSlot = 0;
87
+ private syncConfig: SyncConfig;
86
88
 
87
89
  private syncPromise?: Promise<void>;
88
90
  private syncPromiseResolver: () => void;
@@ -132,6 +134,10 @@ export class UserMap implements UserMapInterface {
132
134
  decodeFn,
133
135
  });
134
136
  }
137
+
138
+ this.syncConfig = config.syncConfig ?? {
139
+ type: 'default',
140
+ };
135
141
  }
136
142
 
137
143
  public async subscribe() {
@@ -345,6 +351,14 @@ export class UserMap implements UserMapInterface {
345
351
  }
346
352
 
347
353
  public async sync() {
354
+ if (this.syncConfig.type === 'default') {
355
+ return this.defaultSync();
356
+ } else {
357
+ return this.paginatedSync();
358
+ }
359
+ }
360
+
361
+ private async defaultSync() {
348
362
  if (this.syncPromise) {
349
363
  return this.syncPromise;
350
364
  }
@@ -438,6 +452,123 @@ export class UserMap implements UserMapInterface {
438
452
  }
439
453
  }
440
454
 
455
+ private async paginatedSync() {
456
+ if (this.syncPromise) {
457
+ return this.syncPromise;
458
+ }
459
+
460
+ this.syncPromise = new Promise<void>((resolve) => {
461
+ this.syncPromiseResolver = resolve;
462
+ });
463
+
464
+ try {
465
+ const accountsPrefetch = await this.connection.getProgramAccounts(
466
+ this.driftClient.program.programId,
467
+ {
468
+ dataSlice: { offset: 0, length: 0 },
469
+ filters: [
470
+ getUserFilter(),
471
+ ...(!this.includeIdle ? [getNonIdleUserFilter()] : []),
472
+ ],
473
+ }
474
+ );
475
+ const accountPublicKeys = accountsPrefetch.map(
476
+ (account) => account.pubkey
477
+ );
478
+
479
+ const limitConcurrency = async (tasks, limit) => {
480
+ const executing = [];
481
+ const results = [];
482
+
483
+ for (let i = 0; i < tasks.length; i++) {
484
+ const executor = Promise.resolve().then(tasks[i]);
485
+ results.push(executor);
486
+
487
+ if (executing.length < limit) {
488
+ executing.push(executor);
489
+ executor.finally(() => {
490
+ const index = executing.indexOf(executor);
491
+ if (index > -1) {
492
+ executing.splice(index, 1);
493
+ }
494
+ });
495
+ } else {
496
+ await Promise.race(executing);
497
+ }
498
+ }
499
+
500
+ return Promise.all(results);
501
+ };
502
+
503
+ const programAccountBufferMap = new Map<string, Buffer>();
504
+
505
+ // @ts-ignore
506
+ const chunkSize = this.syncConfig.chunkSize ?? 100;
507
+ const tasks = [];
508
+ for (let i = 0; i < accountPublicKeys.length; i += chunkSize) {
509
+ const chunk = accountPublicKeys.slice(i, i + chunkSize);
510
+ tasks.push(async () => {
511
+ const accountInfos =
512
+ await this.connection.getMultipleAccountsInfoAndContext(chunk, {
513
+ commitment: this.commitment,
514
+ });
515
+
516
+ const accountInfosSlot = accountInfos.context.slot;
517
+
518
+ for (let j = 0; j < accountInfos.value.length; j += 1) {
519
+ const accountInfo = accountInfos.value[j];
520
+ if (accountInfo === null) continue;
521
+
522
+ const publicKeyString = chunk[j].toString();
523
+ const buffer = Buffer.from(accountInfo.data);
524
+ programAccountBufferMap.set(publicKeyString, buffer);
525
+
526
+ const decodedUser = this.decode('User', buffer);
527
+
528
+ const currAccountWithSlot = this.getWithSlot(publicKeyString);
529
+ if (
530
+ currAccountWithSlot &&
531
+ currAccountWithSlot.slot <= accountInfosSlot
532
+ ) {
533
+ this.updateUserAccount(
534
+ publicKeyString,
535
+ decodedUser,
536
+ accountInfosSlot
537
+ );
538
+ } else {
539
+ await this.addPubkey(
540
+ new PublicKey(publicKeyString),
541
+ decodedUser,
542
+ accountInfosSlot
543
+ );
544
+ }
545
+ }
546
+ });
547
+ }
548
+
549
+ // @ts-ignore
550
+ const concurrencyLimit = this.syncConfig.concurrencyLimit ?? 10;
551
+ await limitConcurrency(tasks, concurrencyLimit);
552
+
553
+ for (const [key] of this.entries()) {
554
+ if (!programAccountBufferMap.has(key)) {
555
+ const user = this.get(key);
556
+ if (user) {
557
+ await user.unsubscribe();
558
+ this.userMap.delete(key);
559
+ }
560
+ }
561
+ }
562
+ } catch (err) {
563
+ console.error(`Error in UserMap.sync():`, err);
564
+ } finally {
565
+ if (this.syncPromiseResolver) {
566
+ this.syncPromiseResolver();
567
+ }
568
+ this.syncPromise = undefined;
569
+ }
570
+ }
571
+
441
572
  public async unsubscribe() {
442
573
  await this.subscription.unsubscribe();
443
574
 
@@ -7,6 +7,16 @@ export type UserAccountFilterCriteria = {
7
7
  hasOpenOrders: boolean;
8
8
  };
9
9
 
10
+ export type SyncConfig =
11
+ | {
12
+ type: 'default';
13
+ }
14
+ | {
15
+ type: 'paginated';
16
+ chunkSize?: number;
17
+ concurrencyLimit?: number;
18
+ };
19
+
10
20
  export type UserMapConfig = {
11
21
  driftClient: DriftClient;
12
22
  // connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection
@@ -36,4 +46,6 @@ export type UserMapConfig = {
36
46
  // If true, will not do a full sync whenever StateAccount.numberOfSubAccounts changes.
37
47
  // default behavior is to do a full sync on changes.
38
48
  disableSyncOnTotalAccountsChange?: boolean;
49
+
50
+ syncConfig?: SyncConfig;
39
51
  };