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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ export declare class PollingDriftClientAccountSubscriber implements DriftClientA
15
15
  spotMarketIndexes: number[];
16
16
  oracleInfos: OracleInfo[];
17
17
  oracleClientCache: OracleClientCache;
18
+ shouldFindAllMarketsAndOracles: boolean;
18
19
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
19
20
  accountLoader: BulkAccountLoader;
20
21
  accountsToPoll: Map<string, AccountToPoll>;
@@ -28,7 +29,7 @@ export declare class PollingDriftClientAccountSubscriber implements DriftClientA
28
29
  private isSubscribing;
29
30
  private subscriptionPromise;
30
31
  private subscriptionPromiseResolver;
31
- constructor(program: Program, accountLoader: BulkAccountLoader, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[]);
32
+ constructor(program: Program, accountLoader: BulkAccountLoader, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean);
32
33
  subscribe(): Promise<boolean>;
33
34
  updateAccountsToPoll(): Promise<void>;
34
35
  updatePerpMarketAccountsToPoll(): Promise<boolean>;
@@ -8,8 +8,9 @@ const utils_1 = require("./utils");
8
8
  const web3_js_1 = require("@solana/web3.js");
9
9
  const oracleClientCache_1 = require("../oracles/oracleClientCache");
10
10
  const quoteAssetOracleClient_1 = require("../oracles/quoteAssetOracleClient");
11
+ const config_1 = require("../config");
11
12
  class PollingDriftClientAccountSubscriber {
12
- constructor(program, accountLoader, perpMarketIndexes, spotMarketIndexes, oracleInfos) {
13
+ constructor(program, accountLoader, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles) {
13
14
  this.oracleClientCache = new oracleClientCache_1.OracleClientCache();
14
15
  this.accountsToPoll = new Map();
15
16
  this.oraclesToPoll = new Map();
@@ -24,6 +25,7 @@ class PollingDriftClientAccountSubscriber {
24
25
  this.perpMarketIndexes = perpMarketIndexes;
25
26
  this.spotMarketIndexes = spotMarketIndexes;
26
27
  this.oracleInfos = oracleInfos;
28
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
27
29
  }
28
30
  async subscribe() {
29
31
  if (this.isSubscribed) {
@@ -36,6 +38,12 @@ class PollingDriftClientAccountSubscriber {
36
38
  this.subscriptionPromise = new Promise((res) => {
37
39
  this.subscriptionPromiseResolver = res;
38
40
  });
41
+ if (this.shouldFindAllMarketsAndOracles) {
42
+ const { perpMarketIndexes, spotMarketIndexes, oracleInfos } = await (0, config_1.findAllMarketAndOracles)(this.program);
43
+ this.perpMarketIndexes = perpMarketIndexes;
44
+ this.spotMarketIndexes = spotMarketIndexes;
45
+ this.oracleInfos = oracleInfos;
46
+ }
39
47
  await this.updateAccountsToPoll();
40
48
  await this.updateOraclesToPoll();
41
49
  await this.addToAccountLoader();
@@ -15,6 +15,7 @@ export declare class WebSocketDriftClientAccountSubscriber implements DriftClien
15
15
  spotMarketIndexes: number[];
16
16
  oracleInfos: OracleInfo[];
17
17
  oracleClientCache: OracleClientCache;
18
+ shouldFindAllMarketsAndOracles: boolean;
18
19
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
19
20
  stateAccountSubscriber?: AccountSubscriber<StateAccount>;
20
21
  perpMarketAccountSubscribers: Map<number, AccountSubscriber<PerpMarketAccount>>;
@@ -23,7 +24,7 @@ export declare class WebSocketDriftClientAccountSubscriber implements DriftClien
23
24
  private isSubscribing;
24
25
  private subscriptionPromise;
25
26
  private subscriptionPromiseResolver;
26
- constructor(program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[]);
27
+ constructor(program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean);
27
28
  subscribe(): Promise<boolean>;
28
29
  subscribeToPerpMarketAccounts(): Promise<boolean>;
29
30
  subscribeToPerpMarketAccount(marketIndex: number): Promise<boolean>;
@@ -8,8 +8,9 @@ const webSocketAccountSubscriber_1 = require("./webSocketAccountSubscriber");
8
8
  const web3_js_1 = require("@solana/web3.js");
9
9
  const oracleClientCache_1 = require("../oracles/oracleClientCache");
10
10
  const quoteAssetOracleClient_1 = require("../oracles/quoteAssetOracleClient");
11
+ const config_1 = require("../config");
11
12
  class WebSocketDriftClientAccountSubscriber {
12
- constructor(program, perpMarketIndexes, spotMarketIndexes, oracleInfos) {
13
+ constructor(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles) {
13
14
  this.oracleClientCache = new oracleClientCache_1.OracleClientCache();
14
15
  this.perpMarketAccountSubscribers = new Map();
15
16
  this.spotMarketAccountSubscribers = new Map();
@@ -21,6 +22,7 @@ class WebSocketDriftClientAccountSubscriber {
21
22
  this.perpMarketIndexes = perpMarketIndexes;
22
23
  this.spotMarketIndexes = spotMarketIndexes;
23
24
  this.oracleInfos = oracleInfos;
25
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
24
26
  }
25
27
  async subscribe() {
26
28
  if (this.isSubscribed) {
@@ -33,6 +35,12 @@ class WebSocketDriftClientAccountSubscriber {
33
35
  this.subscriptionPromise = new Promise((res) => {
34
36
  this.subscriptionPromiseResolver = res;
35
37
  });
38
+ if (this.shouldFindAllMarketsAndOracles) {
39
+ const { perpMarketIndexes, spotMarketIndexes, oracleInfos } = await (0, config_1.findAllMarketAndOracles)(this.program);
40
+ this.perpMarketIndexes = perpMarketIndexes;
41
+ this.spotMarketIndexes = spotMarketIndexes;
42
+ this.oracleInfos = oracleInfos;
43
+ }
36
44
  const statePublicKey = await (0, pda_1.getDriftStateAccountPublicKey)(this.program.programId);
37
45
  // create and activate main state account subscription
38
46
  this.stateAccountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('state', this.program, statePublicKey);
package/lib/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { PerpMarketConfig } from './constants/perpMarkets';
2
2
  import { SpotMarketConfig } from './constants/spotMarkets';
3
3
  import { OracleInfo } from './oracles/types';
4
+ import { Program } from '@coral-xyz/anchor';
4
5
  type DriftConfig = {
5
6
  ENV: DriftEnv;
6
7
  PYTH_ORACLE_MAPPING_ADDRESS: string;
@@ -35,4 +36,9 @@ export declare function getMarketsAndOraclesForSubscription(env: DriftEnv): {
35
36
  spotMarketIndexes: number[];
36
37
  oracleInfos: OracleInfo[];
37
38
  };
39
+ export declare function findAllMarketAndOracles(program: Program): Promise<{
40
+ perpMarketIndexes: number[];
41
+ spotMarketIndexes: number[];
42
+ oracleInfos: OracleInfo[];
43
+ }>;
38
44
  export {};
package/lib/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getMarketsAndOraclesForSubscription = exports.initialize = exports.getConfig = exports.configs = exports.DRIFT_PROGRAM_ID = void 0;
3
+ exports.findAllMarketAndOracles = exports.getMarketsAndOraclesForSubscription = exports.initialize = exports.getConfig = exports.configs = exports.DRIFT_PROGRAM_ID = void 0;
4
4
  const perpMarkets_1 = require("./constants/perpMarkets");
5
5
  const spotMarkets_1 = require("./constants/spotMarkets");
6
6
  exports.DRIFT_PROGRAM_ID = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
@@ -74,3 +74,32 @@ function getMarketsAndOraclesForSubscription(env) {
74
74
  };
75
75
  }
76
76
  exports.getMarketsAndOraclesForSubscription = getMarketsAndOraclesForSubscription;
77
+ async function findAllMarketAndOracles(program) {
78
+ const perpMarketIndexes = [];
79
+ const spotMarketIndexes = [];
80
+ const oracleInfos = new Map();
81
+ const perpMarketProgramAccounts = await program.account.perpMarket.all();
82
+ const spotMarketProgramAccounts = await program.account.spotMarket.all();
83
+ for (const perpMarketProgramAccount of perpMarketProgramAccounts) {
84
+ const perpMarket = perpMarketProgramAccount.account;
85
+ perpMarketIndexes.push(perpMarket.marketIndex);
86
+ oracleInfos.set(perpMarket.amm.oracle.toString(), {
87
+ publicKey: perpMarket.amm.oracle,
88
+ source: perpMarket.amm.oracleSource,
89
+ });
90
+ }
91
+ for (const spotMarketProgramAccount of spotMarketProgramAccounts) {
92
+ const spotMarket = spotMarketProgramAccount.account;
93
+ spotMarketIndexes.push(spotMarket.marketIndex);
94
+ oracleInfos.set(spotMarket.oracle.toString(), {
95
+ publicKey: spotMarket.oracle,
96
+ source: spotMarket.oracleSource,
97
+ });
98
+ }
99
+ return {
100
+ perpMarketIndexes,
101
+ spotMarketIndexes,
102
+ oracleInfos: Array.from(oracleInfos.values()),
103
+ };
104
+ }
105
+ exports.findAllMarketAndOracles = findAllMarketAndOracles;
@@ -41,4 +41,5 @@ export declare function getVammL2Generator({ marketAccount, oraclePriceData, num
41
41
  numOrders: number;
42
42
  now?: BN;
43
43
  }): L2OrderBookGenerator;
44
+ export declare function groupL2(l2: L2OrderBook, grouping: BN): L2OrderBook;
44
45
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getVammL2Generator = exports.createL2Levels = exports.mergeL2LevelGenerators = exports.getL2GeneratorFromDLOBNodes = void 0;
3
+ exports.groupL2 = exports.getVammL2Generator = exports.createL2Levels = exports.mergeL2LevelGenerators = exports.getL2GeneratorFromDLOBNodes = void 0;
4
4
  const __1 = require("..");
5
5
  /**
6
6
  * Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
@@ -58,7 +58,7 @@ function createL2Levels(generator, depth) {
58
58
  const size = level.size;
59
59
  if (levels.length > 0 && levels[levels.length - 1].price.eq(price)) {
60
60
  const currentLevel = levels[levels.length - 1];
61
- currentLevel.size.add(size);
61
+ currentLevel.size = currentLevel.size.add(size);
62
62
  for (const [source, size] of Object.entries(level.sources)) {
63
63
  if (currentLevel.sources[source]) {
64
64
  currentLevel.sources[source] = currentLevel.sources[source].add(size);
@@ -135,3 +135,39 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, })
135
135
  };
136
136
  }
137
137
  exports.getVammL2Generator = getVammL2Generator;
138
+ function groupL2(l2, grouping) {
139
+ return {
140
+ bids: groupL2Levels(l2.bids, grouping, __1.PositionDirection.LONG),
141
+ asks: groupL2Levels(l2.asks, grouping, __1.PositionDirection.SHORT),
142
+ };
143
+ }
144
+ exports.groupL2 = groupL2;
145
+ function groupL2Levels(levels, grouping, direction) {
146
+ const groupedLevels = [];
147
+ for (const level of levels) {
148
+ const price = (0, __1.standardizePrice)(level.price, grouping, direction);
149
+ const size = level.size;
150
+ if (groupedLevels.length > 0 &&
151
+ groupedLevels[groupedLevels.length - 1].price.eq(price)) {
152
+ const currentLevel = groupedLevels[groupedLevels.length - 1];
153
+ currentLevel.size = currentLevel.size.add(size);
154
+ for (const [source, size] of Object.entries(level.sources)) {
155
+ if (currentLevel.sources[source]) {
156
+ currentLevel.sources[source] = currentLevel.sources[source].add(size);
157
+ }
158
+ else {
159
+ currentLevel.sources[source] = size;
160
+ }
161
+ }
162
+ }
163
+ else {
164
+ const groupedLevel = {
165
+ price: price,
166
+ size,
167
+ sources: level.sources,
168
+ };
169
+ groupedLevels.push(groupedLevel);
170
+ }
171
+ }
172
+ return groupedLevels;
173
+ }
@@ -63,7 +63,7 @@ class DriftClient {
63
63
  this._isSubscribed = val;
64
64
  }
65
65
  constructor(config) {
66
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
66
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
67
67
  this.users = new Map();
68
68
  this._isSubscribed = false;
69
69
  this.perpMarketLastSlotCache = new Map();
@@ -108,31 +108,21 @@ class DriftClient {
108
108
  accountSubscription: this.userAccountSubscriptionConfig,
109
109
  });
110
110
  }
111
- let perpMarketIndexes = config.perpMarketIndexes;
112
- let spotMarketIndexes = config.spotMarketIndexes;
113
- let oracleInfos = config.oracleInfos;
114
- if (config.env) {
115
- const { perpMarketIndexes: envPerpMarketIndexes, spotMarketIndexes: envSpotMarketIndexes, oracleInfos: envOracleInfos, } = (0, config_1.getMarketsAndOraclesForSubscription)(config.env);
116
- perpMarketIndexes = perpMarketIndexes
117
- ? perpMarketIndexes
118
- : envPerpMarketIndexes;
119
- spotMarketIndexes = spotMarketIndexes
120
- ? spotMarketIndexes
121
- : envSpotMarketIndexes;
122
- oracleInfos = oracleInfos ? oracleInfos : envOracleInfos;
123
- }
124
111
  this.marketLookupTable = config.marketLookupTable;
125
112
  if (config.env && !this.marketLookupTable) {
126
113
  this.marketLookupTable = new web3_js_1.PublicKey(config_1.configs[config.env].MARKET_LOOKUP_TABLE);
127
114
  }
115
+ const noMarketsAndOraclesSpecified = config.perpMarketIndexes === undefined &&
116
+ config.spotMarketIndexes === undefined &&
117
+ config.oracleInfos === undefined;
128
118
  if (((_h = config.accountSubscription) === null || _h === void 0 ? void 0 : _h.type) === 'polling') {
129
- this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, perpMarketIndexes !== null && perpMarketIndexes !== void 0 ? perpMarketIndexes : [], spotMarketIndexes !== null && spotMarketIndexes !== void 0 ? spotMarketIndexes : [], oracleInfos !== null && oracleInfos !== void 0 ? oracleInfos : []);
119
+ this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_j = config.perpMarketIndexes) !== null && _j !== void 0 ? _j : [], (_k = config.spotMarketIndexes) !== null && _k !== void 0 ? _k : [], (_l = config.oracleInfos) !== null && _l !== void 0 ? _l : [], noMarketsAndOraclesSpecified);
130
120
  }
131
121
  else {
132
- this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, perpMarketIndexes !== null && perpMarketIndexes !== void 0 ? perpMarketIndexes : [], spotMarketIndexes !== null && spotMarketIndexes !== void 0 ? spotMarketIndexes : [], oracleInfos !== null && oracleInfos !== void 0 ? oracleInfos : []);
122
+ this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_m = config.perpMarketIndexes) !== null && _m !== void 0 ? _m : [], (_o = config.spotMarketIndexes) !== null && _o !== void 0 ? _o : [], (_p = config.oracleInfos) !== null && _p !== void 0 ? _p : [], noMarketsAndOraclesSpecified);
133
123
  }
134
124
  this.eventEmitter = this.accountSubscriber.eventEmitter;
135
- this.txSender = new retryTxSender_1.RetryTxSender(this.provider, (_j = config.txSenderConfig) === null || _j === void 0 ? void 0 : _j.timeout, (_k = config.txSenderConfig) === null || _k === void 0 ? void 0 : _k.retrySleep, (_l = config.txSenderConfig) === null || _l === void 0 ? void 0 : _l.additionalConnections);
125
+ this.txSender = new retryTxSender_1.RetryTxSender(this.provider, (_q = config.txSenderConfig) === null || _q === void 0 ? void 0 : _q.timeout, (_r = config.txSenderConfig) === null || _r === void 0 ? void 0 : _r.retrySleep, (_s = config.txSenderConfig) === null || _s === void 0 ? void 0 : _s.additionalConnections);
136
126
  }
137
127
  getUserMapKey(subAccountId, authority) {
138
128
  return `${subAccountId}_${authority.toString()}`;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.31.0-beta.0",
2
+ "version": "2.31.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -1,11 +1,12 @@
1
1
  import { User } from '../user';
2
- import { PerpMarketAccount, AMM, Order } from '../types';
2
+ import { PerpMarketAccount, AMM, Order, PositionDirection } from '../types';
3
3
  import { BN } from '@coral-xyz/anchor';
4
4
  import { OraclePriceData } from '../oracles/types';
5
5
  export declare function isOrderRiskIncreasing(user: User, order: Order): boolean;
6
6
  export declare function isOrderRiskIncreasingInSameDirection(user: User, order: Order): boolean;
7
7
  export declare function isOrderReduceOnly(user: User, order: Order): boolean;
8
8
  export declare function standardizeBaseAssetAmount(baseAssetAmount: BN, stepSize: BN): BN;
9
+ export declare function standardizePrice(price: BN, tickSize: BN, direction: PositionDirection): BN;
9
10
  export declare function getLimitPrice(order: Order, oraclePriceData: OraclePriceData, slot: number, fallbackPrice?: BN): BN | undefined;
10
11
  export declare function hasLimitPrice(order: Order, slot: number): boolean;
11
12
  export declare function hasAuctionPrice(order: Order, slot: number): boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.getLimitPrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
3
+ exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
4
4
  const types_1 = require("../types");
5
5
  const numericConstants_1 = require("../constants/numericConstants");
6
6
  const anchor_1 = require("@coral-xyz/anchor");
@@ -79,6 +79,23 @@ function standardizeBaseAssetAmount(baseAssetAmount, stepSize) {
79
79
  return baseAssetAmount.sub(remainder);
80
80
  }
81
81
  exports.standardizeBaseAssetAmount = standardizeBaseAssetAmount;
82
+ function standardizePrice(price, tickSize, direction) {
83
+ if (price.eq(numericConstants_1.ZERO)) {
84
+ console.log('price is zero');
85
+ return price;
86
+ }
87
+ const remainder = price.mod(tickSize);
88
+ if (remainder.eq(numericConstants_1.ZERO)) {
89
+ return price;
90
+ }
91
+ if ((0, types_1.isVariant)(direction, 'long')) {
92
+ return price.sub(remainder);
93
+ }
94
+ else {
95
+ return price.add(tickSize).sub(remainder);
96
+ }
97
+ }
98
+ exports.standardizePrice = standardizePrice;
82
99
  function getLimitPrice(order, oraclePriceData, slot, fallbackPrice) {
83
100
  let limitPrice;
84
101
  if (hasAuctionPrice(order, slot)) {
@@ -18,6 +18,12 @@ export declare class UserMap implements UserMapInterface {
18
18
  private includeIdle;
19
19
  private lastNumberOfSubAccounts;
20
20
  private syncCallback;
21
+ /**
22
+ *
23
+ * @param driftClient
24
+ * @param accountSubscription
25
+ * @param includeIdle whether idle users are subscribed to. defaults to false to decrease # of user subscriptions
26
+ */
21
27
  constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean);
22
28
  subscribe(): Promise<void>;
23
29
  addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
@@ -9,7 +9,13 @@ const web3_js_1 = require("@solana/web3.js");
9
9
  const buffer_1 = require("buffer");
10
10
  const bs58_1 = __importDefault(require("bs58"));
11
11
  class UserMap {
12
- constructor(driftClient, accountSubscription, includeIdle = true) {
12
+ /**
13
+ *
14
+ * @param driftClient
15
+ * @param accountSubscription
16
+ * @param includeIdle whether idle users are subscribed to. defaults to false to decrease # of user subscriptions
17
+ */
18
+ constructor(driftClient, accountSubscription, includeIdle = false) {
13
19
  this.userMap = new Map();
14
20
  this.syncCallback = async (state) => {
15
21
  if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.31.0-beta.0",
3
+ "version": "2.31.0-beta.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -26,6 +26,7 @@ import { PublicKey } from '@solana/web3.js';
26
26
  import { OracleInfo, OraclePriceData } from '../oracles/types';
27
27
  import { OracleClientCache } from '../oracles/oracleClientCache';
28
28
  import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient';
29
+ import { findAllMarketAndOracles } from '../config';
29
30
 
30
31
  export class PollingDriftClientAccountSubscriber
31
32
  implements DriftClientAccountSubscriber
@@ -37,6 +38,8 @@ export class PollingDriftClientAccountSubscriber
37
38
  oracleInfos: OracleInfo[];
38
39
  oracleClientCache = new OracleClientCache();
39
40
 
41
+ shouldFindAllMarketsAndOracles: boolean;
42
+
40
43
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
41
44
 
42
45
  accountLoader: BulkAccountLoader;
@@ -59,7 +62,8 @@ export class PollingDriftClientAccountSubscriber
59
62
  accountLoader: BulkAccountLoader,
60
63
  perpMarketIndexes: number[],
61
64
  spotMarketIndexes: number[],
62
- oracleInfos: OracleInfo[]
65
+ oracleInfos: OracleInfo[],
66
+ shouldFindAllMarketsAndOracles: boolean
63
67
  ) {
64
68
  this.isSubscribed = false;
65
69
  this.program = program;
@@ -68,6 +72,7 @@ export class PollingDriftClientAccountSubscriber
68
72
  this.perpMarketIndexes = perpMarketIndexes;
69
73
  this.spotMarketIndexes = spotMarketIndexes;
70
74
  this.oracleInfos = oracleInfos;
75
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
71
76
  }
72
77
 
73
78
  public async subscribe(): Promise<boolean> {
@@ -85,6 +90,14 @@ export class PollingDriftClientAccountSubscriber
85
90
  this.subscriptionPromiseResolver = res;
86
91
  });
87
92
 
93
+ if (this.shouldFindAllMarketsAndOracles) {
94
+ const { perpMarketIndexes, spotMarketIndexes, oracleInfos } =
95
+ await findAllMarketAndOracles(this.program);
96
+ this.perpMarketIndexes = perpMarketIndexes;
97
+ this.spotMarketIndexes = spotMarketIndexes;
98
+ this.oracleInfos = oracleInfos;
99
+ }
100
+
88
101
  await this.updateAccountsToPoll();
89
102
  await this.updateOraclesToPoll();
90
103
  await this.addToAccountLoader();
@@ -19,6 +19,7 @@ import { OracleInfo, OraclePriceData } from '../oracles/types';
19
19
  import { OracleClientCache } from '../oracles/oracleClientCache';
20
20
  import * as Buffer from 'buffer';
21
21
  import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient';
22
+ import { findAllMarketAndOracles } from '../config';
22
23
 
23
24
  export class WebSocketDriftClientAccountSubscriber
24
25
  implements DriftClientAccountSubscriber
@@ -30,6 +31,8 @@ export class WebSocketDriftClientAccountSubscriber
30
31
  oracleInfos: OracleInfo[];
31
32
  oracleClientCache = new OracleClientCache();
32
33
 
34
+ shouldFindAllMarketsAndOracles: boolean;
35
+
33
36
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
34
37
  stateAccountSubscriber?: AccountSubscriber<StateAccount>;
35
38
  perpMarketAccountSubscribers = new Map<
@@ -50,7 +53,8 @@ export class WebSocketDriftClientAccountSubscriber
50
53
  program: Program,
51
54
  perpMarketIndexes: number[],
52
55
  spotMarketIndexes: number[],
53
- oracleInfos: OracleInfo[]
56
+ oracleInfos: OracleInfo[],
57
+ shouldFindAllMarketsAndOracles: boolean
54
58
  ) {
55
59
  this.isSubscribed = false;
56
60
  this.program = program;
@@ -58,6 +62,7 @@ export class WebSocketDriftClientAccountSubscriber
58
62
  this.perpMarketIndexes = perpMarketIndexes;
59
63
  this.spotMarketIndexes = spotMarketIndexes;
60
64
  this.oracleInfos = oracleInfos;
65
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
61
66
  }
62
67
 
63
68
  public async subscribe(): Promise<boolean> {
@@ -75,6 +80,14 @@ export class WebSocketDriftClientAccountSubscriber
75
80
  this.subscriptionPromiseResolver = res;
76
81
  });
77
82
 
83
+ if (this.shouldFindAllMarketsAndOracles) {
84
+ const { perpMarketIndexes, spotMarketIndexes, oracleInfos } =
85
+ await findAllMarketAndOracles(this.program);
86
+ this.perpMarketIndexes = perpMarketIndexes;
87
+ this.spotMarketIndexes = spotMarketIndexes;
88
+ this.oracleInfos = oracleInfos;
89
+ }
90
+
78
91
  const statePublicKey = await getDriftStateAccountPublicKey(
79
92
  this.program.programId
80
93
  );
package/src/config.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  MainnetSpotMarkets,
12
12
  } from './constants/spotMarkets';
13
13
  import { OracleInfo } from './oracles/types';
14
+ import { Program } from '@coral-xyz/anchor';
14
15
 
15
16
  type DriftConfig = {
16
17
  ENV: DriftEnv;
@@ -113,3 +114,40 @@ export function getMarketsAndOraclesForSubscription(env: DriftEnv): {
113
114
  oracleInfos: Array.from(oracleInfos.values()),
114
115
  };
115
116
  }
117
+
118
+ export async function findAllMarketAndOracles(program: Program): Promise<{
119
+ perpMarketIndexes: number[];
120
+ spotMarketIndexes: number[];
121
+ oracleInfos: OracleInfo[];
122
+ }> {
123
+ const perpMarketIndexes = [];
124
+ const spotMarketIndexes = [];
125
+ const oracleInfos = new Map<string, OracleInfo>();
126
+
127
+ const perpMarketProgramAccounts = await program.account.perpMarket.all();
128
+ const spotMarketProgramAccounts = await program.account.spotMarket.all();
129
+
130
+ for (const perpMarketProgramAccount of perpMarketProgramAccounts) {
131
+ const perpMarket = perpMarketProgramAccount.account;
132
+ perpMarketIndexes.push(perpMarket.marketIndex);
133
+ oracleInfos.set(perpMarket.amm.oracle.toString(), {
134
+ publicKey: perpMarket.amm.oracle,
135
+ source: perpMarket.amm.oracleSource,
136
+ });
137
+ }
138
+
139
+ for (const spotMarketProgramAccount of spotMarketProgramAccounts) {
140
+ const spotMarket = spotMarketProgramAccount.account;
141
+ spotMarketIndexes.push(spotMarket.marketIndex);
142
+ oracleInfos.set(spotMarket.oracle.toString(), {
143
+ publicKey: spotMarket.oracle,
144
+ source: spotMarket.oracleSource,
145
+ });
146
+ }
147
+
148
+ return {
149
+ perpMarketIndexes,
150
+ spotMarketIndexes,
151
+ oracleInfos: Array.from(oracleInfos.values()),
152
+ };
153
+ }
@@ -9,6 +9,8 @@ import {
9
9
  DLOBNode,
10
10
  OraclePriceData,
11
11
  PerpMarketAccount,
12
+ PositionDirection,
13
+ standardizePrice,
12
14
  SwapDirection,
13
15
  } from '..';
14
16
  import { PublicKey } from '@solana/web3.js';
@@ -114,7 +116,7 @@ export function createL2Levels(
114
116
  const size = level.size;
115
117
  if (levels.length > 0 && levels[levels.length - 1].price.eq(price)) {
116
118
  const currentLevel = levels[levels.length - 1];
117
- currentLevel.size.add(size);
119
+ currentLevel.size = currentLevel.size.add(size);
118
120
  for (const [source, size] of Object.entries(level.sources)) {
119
121
  if (currentLevel.sources[source]) {
120
122
  currentLevel.sources[source] = currentLevel.sources[source].add(size);
@@ -241,3 +243,44 @@ export function getVammL2Generator({
241
243
  getL2Asks,
242
244
  };
243
245
  }
246
+
247
+ export function groupL2(l2: L2OrderBook, grouping: BN): L2OrderBook {
248
+ return {
249
+ bids: groupL2Levels(l2.bids, grouping, PositionDirection.LONG),
250
+ asks: groupL2Levels(l2.asks, grouping, PositionDirection.SHORT),
251
+ };
252
+ }
253
+
254
+ function groupL2Levels(
255
+ levels: L2Level[],
256
+ grouping: BN,
257
+ direction: PositionDirection
258
+ ): L2Level[] {
259
+ const groupedLevels = [];
260
+ for (const level of levels) {
261
+ const price = standardizePrice(level.price, grouping, direction);
262
+ const size = level.size;
263
+ if (
264
+ groupedLevels.length > 0 &&
265
+ groupedLevels[groupedLevels.length - 1].price.eq(price)
266
+ ) {
267
+ const currentLevel = groupedLevels[groupedLevels.length - 1];
268
+ currentLevel.size = currentLevel.size.add(size);
269
+ for (const [source, size] of Object.entries(level.sources)) {
270
+ if (currentLevel.sources[source]) {
271
+ currentLevel.sources[source] = currentLevel.sources[source].add(size);
272
+ } else {
273
+ currentLevel.sources[source] = size;
274
+ }
275
+ }
276
+ } else {
277
+ const groupedLevel = {
278
+ price: price,
279
+ size,
280
+ sources: level.sources,
281
+ };
282
+ groupedLevels.push(groupedLevel);
283
+ }
284
+ }
285
+ return groupedLevels;
286
+ }
@@ -106,11 +106,7 @@ import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDrift
106
106
  import { RetryTxSender } from './tx/retryTxSender';
107
107
  import { User } from './user';
108
108
  import { UserSubscriptionConfig } from './userConfig';
109
- import {
110
- configs,
111
- DRIFT_PROGRAM_ID,
112
- getMarketsAndOraclesForSubscription,
113
- } from './config';
109
+ import { configs, DRIFT_PROGRAM_ID } from './config';
114
110
  import { WRAPPED_SOL_MINT } from './constants/spotMarkets';
115
111
  import { UserStats } from './userStats';
116
112
  import { isSpotPositionAvailable } from './math/spotPosition';
@@ -230,24 +226,6 @@ export class DriftClient {
230
226
  });
231
227
  }
232
228
 
233
- let perpMarketIndexes = config.perpMarketIndexes;
234
- let spotMarketIndexes = config.spotMarketIndexes;
235
- let oracleInfos = config.oracleInfos;
236
- if (config.env) {
237
- const {
238
- perpMarketIndexes: envPerpMarketIndexes,
239
- spotMarketIndexes: envSpotMarketIndexes,
240
- oracleInfos: envOracleInfos,
241
- } = getMarketsAndOraclesForSubscription(config.env);
242
- perpMarketIndexes = perpMarketIndexes
243
- ? perpMarketIndexes
244
- : envPerpMarketIndexes;
245
- spotMarketIndexes = spotMarketIndexes
246
- ? spotMarketIndexes
247
- : envSpotMarketIndexes;
248
- oracleInfos = oracleInfos ? oracleInfos : envOracleInfos;
249
- }
250
-
251
229
  this.marketLookupTable = config.marketLookupTable;
252
230
  if (config.env && !this.marketLookupTable) {
253
231
  this.marketLookupTable = new PublicKey(
@@ -255,20 +233,26 @@ export class DriftClient {
255
233
  );
256
234
  }
257
235
 
236
+ const noMarketsAndOraclesSpecified =
237
+ config.perpMarketIndexes === undefined &&
238
+ config.spotMarketIndexes === undefined &&
239
+ config.oracleInfos === undefined;
258
240
  if (config.accountSubscription?.type === 'polling') {
259
241
  this.accountSubscriber = new PollingDriftClientAccountSubscriber(
260
242
  this.program,
261
243
  config.accountSubscription.accountLoader,
262
- perpMarketIndexes ?? [],
263
- spotMarketIndexes ?? [],
264
- oracleInfos ?? []
244
+ config.perpMarketIndexes ?? [],
245
+ config.spotMarketIndexes ?? [],
246
+ config.oracleInfos ?? [],
247
+ noMarketsAndOraclesSpecified
265
248
  );
266
249
  } else {
267
250
  this.accountSubscriber = new WebSocketDriftClientAccountSubscriber(
268
251
  this.program,
269
- perpMarketIndexes ?? [],
270
- spotMarketIndexes ?? [],
271
- oracleInfos ?? []
252
+ config.perpMarketIndexes ?? [],
253
+ config.spotMarketIndexes ?? [],
254
+ config.oracleInfos ?? [],
255
+ noMarketsAndOraclesSpecified
272
256
  );
273
257
  }
274
258
  this.eventEmitter = this.accountSubscriber.eventEmitter;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.31.0-beta.0",
2
+ "version": "2.31.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -124,6 +124,28 @@ export function standardizeBaseAssetAmount(
124
124
  return baseAssetAmount.sub(remainder);
125
125
  }
126
126
 
127
+ export function standardizePrice(
128
+ price: BN,
129
+ tickSize: BN,
130
+ direction: PositionDirection
131
+ ): BN {
132
+ if (price.eq(ZERO)) {
133
+ console.log('price is zero');
134
+ return price;
135
+ }
136
+
137
+ const remainder = price.mod(tickSize);
138
+ if (remainder.eq(ZERO)) {
139
+ return price;
140
+ }
141
+
142
+ if (isVariant(direction, 'long')) {
143
+ return price.sub(remainder);
144
+ } else {
145
+ return price.add(tickSize).sub(remainder);
146
+ }
147
+ }
148
+
127
149
  export function getLimitPrice(
128
150
  order: Order,
129
151
  oraclePriceData: OraclePriceData,
@@ -45,10 +45,16 @@ export class UserMap implements UserMapInterface {
45
45
  }
46
46
  };
47
47
 
48
+ /**
49
+ *
50
+ * @param driftClient
51
+ * @param accountSubscription
52
+ * @param includeIdle whether idle users are subscribed to. defaults to false to decrease # of user subscriptions
53
+ */
48
54
  constructor(
49
55
  driftClient: DriftClient,
50
56
  accountSubscription: UserSubscriptionConfig,
51
- includeIdle = true
57
+ includeIdle = false
52
58
  ) {
53
59
  this.driftClient = driftClient;
54
60
  this.accountSubscription = accountSubscription;