@drift-labs/sdk 2.49.0-beta.8 → 2.52.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.
Files changed (64) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +2 -2
  3. package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +9 -6
  4. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +0 -1
  5. package/lib/adminClient.d.ts +1 -0
  6. package/lib/adminClient.js +8 -0
  7. package/lib/constants/perpMarkets.js +40 -0
  8. package/lib/constants/spotMarkets.js +10 -0
  9. package/lib/decode/user.d.ts +3 -0
  10. package/lib/decode/user.js +329 -0
  11. package/lib/driftClient.d.ts +5 -1
  12. package/lib/driftClient.js +28 -7
  13. package/lib/examples/loadDlob.js +10 -5
  14. package/lib/idl/drift.json +32 -2
  15. package/lib/index.d.ts +2 -1
  16. package/lib/index.js +2 -1
  17. package/lib/math/state.d.ts +5 -0
  18. package/lib/math/state.js +27 -0
  19. package/lib/math/superStake.d.ts +43 -0
  20. package/lib/math/superStake.js +64 -22
  21. package/lib/orderSubscriber/OrderSubscriber.d.ts +3 -0
  22. package/lib/orderSubscriber/OrderSubscriber.js +17 -3
  23. package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
  24. package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
  25. package/lib/orderSubscriber/types.d.ts +4 -1
  26. package/lib/types.d.ts +2 -1
  27. package/lib/user.d.ts +2 -1
  28. package/lib/user.js +13 -5
  29. package/lib/userMap/PollingSubscription.d.ts +15 -0
  30. package/lib/userMap/PollingSubscription.js +28 -0
  31. package/lib/userMap/WebsocketSubscription.d.ts +23 -0
  32. package/lib/userMap/WebsocketSubscription.js +41 -0
  33. package/lib/userMap/userMap.d.ts +16 -18
  34. package/lib/userMap/userMap.js +73 -34
  35. package/lib/userMap/userMapConfig.d.ts +21 -0
  36. package/lib/userMap/userMapConfig.js +2 -0
  37. package/package.json +2 -1
  38. package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +8 -6
  39. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -1
  40. package/src/adminClient.ts +14 -0
  41. package/src/constants/perpMarkets.ts +40 -0
  42. package/src/constants/spotMarkets.ts +10 -0
  43. package/src/decode/user.ts +358 -0
  44. package/src/driftClient.ts +48 -7
  45. package/src/examples/loadDlob.ts +11 -6
  46. package/src/idl/drift.json +33 -3
  47. package/src/index.ts +2 -1
  48. package/src/math/state.ts +26 -0
  49. package/src/math/superStake.ts +108 -20
  50. package/src/orderSubscriber/OrderSubscriber.ts +33 -7
  51. package/src/orderSubscriber/WebsocketSubscription.ts +17 -16
  52. package/src/orderSubscriber/types.ts +15 -2
  53. package/src/types.ts +2 -1
  54. package/src/user.ts +19 -6
  55. package/src/userMap/PollingSubscription.ts +48 -0
  56. package/src/userMap/WebsocketSubscription.ts +76 -0
  57. package/src/userMap/userMap.ts +103 -70
  58. package/src/userMap/userMapConfig.ts +34 -0
  59. package/tests/amm/test.ts +6 -3
  60. package/tests/decode/test.ts +266 -0
  61. package/tests/decode/userAccountBufferStrings.ts +102 -0
  62. package/tests/dlob/helpers.ts +1 -0
  63. package/tests/dlob/test.ts +10 -8
  64. package/tests/user/helpers.ts +0 -1
@@ -1,5 +1,6 @@
1
- import { User, DriftClient, UserAccount, OrderRecord, UserSubscriptionConfig, WrappedEvent, DLOB } from '..';
1
+ import { User, DriftClient, UserAccount, OrderRecord, WrappedEvent, DLOB } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
+ import { UserAccountFilterCriteria as UserFilterCriteria, UserMapConfig } from './userMapConfig';
3
4
  export interface UserMapInterface {
4
5
  subscribe(): Promise<void>;
5
6
  unsubscribe(): Promise<void>;
@@ -11,33 +12,24 @@ export interface UserMapInterface {
11
12
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
12
13
  values(): IterableIterator<User>;
13
14
  }
14
- export type SyncCallbackCriteria = {
15
- hasOpenOrders: boolean;
16
- };
17
15
  export declare class UserMap implements UserMapInterface {
18
16
  private userMap;
19
- private driftClient;
20
- private accountSubscription;
17
+ driftClient: DriftClient;
18
+ private connection;
19
+ private commitment;
21
20
  private includeIdle;
22
21
  private lastNumberOfSubAccounts;
22
+ private subscription;
23
23
  private stateAccountUpdateCallback;
24
- private syncCallback;
25
- private syncCallbackCriteria;
24
+ private decode;
26
25
  private syncPromise?;
27
26
  private syncPromiseResolver;
28
27
  /**
29
28
  * Constructs a new UserMap instance.
30
- *
31
- * @param {DriftClient} driftClient - The DriftClient instance.
32
- * @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
33
- * @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
34
- * @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
35
- * @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
36
29
  */
37
- constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean, syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria);
38
- addSyncCallback(syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria): void;
30
+ constructor(config: UserMapConfig);
39
31
  subscribe(): Promise<void>;
40
- addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
32
+ addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount, slot?: number): Promise<void>;
41
33
  has(key: string): boolean;
42
34
  /**
43
35
  * gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
@@ -67,7 +59,13 @@ export declare class UserMap implements UserMapInterface {
67
59
  updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
68
60
  values(): IterableIterator<User>;
69
61
  size(): number;
70
- getUniqueAuthorities(useSyncCallbackCriteria?: boolean): PublicKey[];
62
+ /**
63
+ * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
64
+ * @param filterCriteria: Users must meet these criteria to be included
65
+ * @returns
66
+ */
67
+ getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
71
68
  sync(): Promise<void>;
72
69
  unsubscribe(): Promise<void>;
70
+ updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
73
71
  }
@@ -5,33 +5,57 @@ const __1 = require("..");
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  const buffer_1 = require("buffer");
7
7
  const memcmp_1 = require("../memcmp");
8
+ const WebsocketSubscription_1 = require("./WebsocketSubscription");
9
+ const PollingSubscription_1 = require("./PollingSubscription");
10
+ const user_1 = require("../decode/user");
8
11
  class UserMap {
9
12
  /**
10
13
  * Constructs a new UserMap instance.
11
- *
12
- * @param {DriftClient} driftClient - The DriftClient instance.
13
- * @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
14
- * @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
15
- * @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
16
- * @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
17
14
  */
18
- constructor(driftClient, accountSubscription, includeIdle = false, syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
15
+ constructor(config) {
16
+ var _a, _b, _c;
19
17
  this.userMap = new Map();
20
18
  this.stateAccountUpdateCallback = async (state) => {
21
- if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
19
+ if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
22
20
  await this.sync();
23
21
  this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
24
22
  }
25
23
  };
26
- this.driftClient = driftClient;
27
- this.accountSubscription = accountSubscription;
28
- this.includeIdle = includeIdle;
29
- this.syncCallback = syncCallback;
30
- this.syncCallbackCriteria = syncCallbackCriteria;
31
- }
32
- addSyncCallback(syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
33
- this.syncCallback = syncCallback;
34
- this.syncCallbackCriteria = syncCallbackCriteria;
24
+ this.driftClient = config.driftClient;
25
+ if (config.connection) {
26
+ this.connection = config.connection;
27
+ }
28
+ else {
29
+ this.connection = this.driftClient.connection;
30
+ }
31
+ this.commitment =
32
+ (_a = config.subscriptionConfig.commitment) !== null && _a !== void 0 ? _a : this.driftClient.opts.commitment;
33
+ this.includeIdle = (_b = config.includeIdle) !== null && _b !== void 0 ? _b : false;
34
+ let decodeFn;
35
+ if ((_c = config.fastDecode) !== null && _c !== void 0 ? _c : true) {
36
+ decodeFn = (name, buffer) => (0, user_1.decodeUser)(buffer);
37
+ }
38
+ else {
39
+ decodeFn =
40
+ this.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.driftClient.program.account.user.coder.accounts);
41
+ }
42
+ this.decode = decodeFn;
43
+ if (config.subscriptionConfig.type === 'polling') {
44
+ this.subscription = new PollingSubscription_1.PollingSubscription({
45
+ userMap: this,
46
+ frequency: config.subscriptionConfig.frequency,
47
+ skipInitialLoad: config.skipInitialLoad,
48
+ });
49
+ }
50
+ else {
51
+ this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
52
+ userMap: this,
53
+ commitment: this.commitment,
54
+ resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
55
+ skipInitialLoad: config.skipInitialLoad,
56
+ decodeFn,
57
+ });
58
+ }
35
59
  }
36
60
  async subscribe() {
37
61
  if (this.size() > 0) {
@@ -41,13 +65,16 @@ class UserMap {
41
65
  this.lastNumberOfSubAccounts =
42
66
  this.driftClient.getStateAccount().numberOfSubAccounts;
43
67
  this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
44
- await this.sync();
68
+ await this.subscription.subscribe();
45
69
  }
46
- async addPubkey(userAccountPublicKey, userAccount) {
70
+ async addPubkey(userAccountPublicKey, userAccount, slot) {
47
71
  const user = new __1.User({
48
72
  driftClient: this.driftClient,
49
73
  userAccountPublicKey,
50
- accountSubscription: this.accountSubscription,
74
+ accountSubscription: {
75
+ type: 'custom',
76
+ userAccountSubscriber: new __1.BasicUserAccountSubscriber(userAccountPublicKey, userAccount, slot),
77
+ },
51
78
  });
52
79
  await user.subscribe(userAccount);
53
80
  this.userMap.set(userAccountPublicKey.toString(), user);
@@ -148,11 +175,15 @@ class UserMap {
148
175
  size() {
149
176
  return this.userMap.size;
150
177
  }
151
- getUniqueAuthorities(useSyncCallbackCriteria = true) {
178
+ /**
179
+ * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
180
+ * @param filterCriteria: Users must meet these criteria to be included
181
+ * @returns
182
+ */
183
+ getUniqueAuthorities(filterCriteria) {
152
184
  const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
153
185
  let pass = true;
154
- if (useSyncCallbackCriteria &&
155
- this.syncCallbackCriteria.hasOpenOrders) {
186
+ if (filterCriteria && filterCriteria.hasOpenOrders) {
156
187
  pass = pass && user.getUserAccount().hasOpenOrder;
157
188
  }
158
189
  return pass;
@@ -176,7 +207,7 @@ class UserMap {
176
207
  const rpcRequestArgs = [
177
208
  this.driftClient.program.programId.toBase58(),
178
209
  {
179
- commitment: this.driftClient.connection.commitment,
210
+ commitment: this.commitment,
180
211
  filters,
181
212
  encoding: 'base64',
182
213
  withContext: true,
@@ -184,7 +215,7 @@ class UserMap {
184
215
  ];
185
216
  const rpcJSONResponse =
186
217
  // @ts-ignore
187
- await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
218
+ await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
188
219
  const rpcResponseAndContext = rpcJSONResponse.result;
189
220
  const slot = rpcResponseAndContext.context.slot;
190
221
  const programAccountBufferMap = new Map();
@@ -195,26 +226,24 @@ class UserMap {
195
226
  }
196
227
  for (const [key, buffer] of programAccountBufferMap.entries()) {
197
228
  if (!this.has(key)) {
198
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
229
+ const userAccount = this.decode('User', buffer);
199
230
  await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
231
+ this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
200
232
  }
233
+ // give event loop a chance to breathe
234
+ await new Promise((resolve) => setTimeout(resolve, 0));
201
235
  }
202
236
  for (const [key, user] of this.userMap.entries()) {
203
237
  if (!programAccountBufferMap.has(key)) {
204
238
  await user.unsubscribe();
205
239
  this.userMap.delete(key);
206
240
  }
207
- else {
208
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
209
- user.accountSubscriber.updateData(userAccount, slot);
210
- }
211
- }
212
- if (this.syncCallback) {
213
- await this.syncCallback(this.getUniqueAuthorities());
241
+ // give event loop a chance to breathe
242
+ await new Promise((resolve) => setTimeout(resolve, 0));
214
243
  }
215
244
  }
216
245
  catch (e) {
217
- console.error(`Error in UserMap.sync()`);
246
+ console.error(`Error in UserMap.sync():`);
218
247
  console.error(e);
219
248
  }
220
249
  finally {
@@ -223,6 +252,7 @@ class UserMap {
223
252
  }
224
253
  }
225
254
  async unsubscribe() {
255
+ await this.subscription.unsubscribe();
226
256
  for (const [key, user] of this.userMap.entries()) {
227
257
  await user.unsubscribe();
228
258
  this.userMap.delete(key);
@@ -232,5 +262,14 @@ class UserMap {
232
262
  this.lastNumberOfSubAccounts = undefined;
233
263
  }
234
264
  }
265
+ async updateUserAccount(key, userAccount, slot) {
266
+ if (!this.userMap.has(key)) {
267
+ this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
268
+ }
269
+ else {
270
+ const user = this.userMap.get(key);
271
+ user.accountSubscriber.updateData(userAccount, slot);
272
+ }
273
+ }
235
274
  }
236
275
  exports.UserMap = UserMap;
@@ -0,0 +1,21 @@
1
+ import { Commitment, Connection } from '@solana/web3.js';
2
+ import { DriftClient } from '../driftClient';
3
+ export type UserAccountFilterCriteria = {
4
+ hasOpenOrders: boolean;
5
+ };
6
+ export type UserMapConfig = {
7
+ driftClient: DriftClient;
8
+ connection?: Connection;
9
+ subscriptionConfig: {
10
+ type: 'polling';
11
+ frequency: number;
12
+ commitment?: Commitment;
13
+ } | {
14
+ type: 'websocket';
15
+ resubTimeoutMs?: number;
16
+ commitment?: Commitment;
17
+ };
18
+ skipInitialLoad?: boolean;
19
+ includeIdle?: boolean;
20
+ fastDecode?: boolean;
21
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.49.0-beta.8",
3
+ "version": "2.52.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -44,6 +44,7 @@
44
44
  "uuid": "^8.3.2"
45
45
  },
46
46
  "devDependencies": {
47
+ "object-sizeof": "^2.6.3",
47
48
  "@types/big.js": "^6.2.0",
48
49
  "@types/bn.js": "^5.1.3",
49
50
  "@types/chai": "^4.3.1",
@@ -4,7 +4,7 @@ import StrictEventEmitter from 'strict-event-emitter-types';
4
4
  import { EventEmitter } from 'events';
5
5
  import { UserAccount } from '../types';
6
6
 
7
- export class MockUserAccountSubscriber implements UserAccountSubscriber {
7
+ export class BasicUserAccountSubscriber implements UserAccountSubscriber {
8
8
  isSubscribed: boolean;
9
9
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
10
10
  userAccountPublicKey: PublicKey;
@@ -16,8 +16,8 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
16
16
 
17
17
  public constructor(
18
18
  userAccountPublicKey: PublicKey,
19
- data: UserAccount,
20
- slot: number
19
+ data?: UserAccount,
20
+ slot?: number
21
21
  ) {
22
22
  this.isSubscribed = true;
23
23
  this.eventEmitter = new EventEmitter();
@@ -46,8 +46,10 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
46
46
  }
47
47
 
48
48
  public updateData(userAccount: UserAccount, slot: number): void {
49
- this.user = { data: userAccount, slot };
50
- this.eventEmitter.emit('userAccountUpdate', userAccount);
51
- this.eventEmitter.emit('update');
49
+ if (!this.user || slot >= (this.user.slot ?? 0)) {
50
+ this.user = { data: userAccount, slot };
51
+ this.eventEmitter.emit('userAccountUpdate', userAccount);
52
+ this.eventEmitter.emit('update');
53
+ }
52
54
  }
53
55
  }
@@ -54,7 +54,6 @@ export class PollingInsuranceFundStakeAccountSubscriber
54
54
 
55
55
  await this.addToAccountLoader();
56
56
 
57
- await this.fetchIfUnloaded();
58
57
  if (this.doesAccountExist()) {
59
58
  this.eventEmitter.emit('update');
60
59
  }
@@ -846,6 +846,20 @@ export class AdminClient extends DriftClient {
846
846
  );
847
847
  }
848
848
 
849
+ public async updateStateMaxInitializeUserFee(
850
+ maxInitializeUserFee: number
851
+ ): Promise<TransactionSignature> {
852
+ return await this.program.rpc.updateStateMaxInitializeUserFee(
853
+ maxInitializeUserFee,
854
+ {
855
+ accounts: {
856
+ admin: this.wallet.publicKey,
857
+ state: await this.getStatePublicKey(),
858
+ },
859
+ }
860
+ );
861
+ }
862
+
849
863
  public async updateWithdrawGuardThreshold(
850
864
  spotMarketIndex: number,
851
865
  withdrawGuardThreshold: BN
@@ -204,6 +204,26 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
204
204
  launchTs: 1700542800000,
205
205
  oracleSource: OracleSource.PYTH,
206
206
  },
207
+ {
208
+ fullName: 'Celestia',
209
+ category: ['Data'],
210
+ symbol: 'TIA-PERP',
211
+ baseAssetSymbol: 'TIA',
212
+ marketIndex: 19,
213
+ oracle: new PublicKey('4GiL1Y6u6JkPb7ckakzJgc414h6P7qoYnEKFcd1YtSB9'),
214
+ launchTs: 1701880540000,
215
+ oracleSource: OracleSource.PYTH,
216
+ },
217
+ {
218
+ fullName: 'Jito',
219
+ category: ['MEV'],
220
+ symbol: 'JTO-PERP',
221
+ baseAssetSymbol: 'JTO',
222
+ marketIndex: 20,
223
+ oracle: new PublicKey('29xQnTzyyuRtgJ7RtSKEgBWwRzZqtrrKmyQQ5m3x629f'),
224
+ launchTs: 1701967240000,
225
+ oracleSource: OracleSource.PYTH,
226
+ },
207
227
  ];
208
228
 
209
229
  export const MainnetPerpMarkets: PerpMarketConfig[] = [
@@ -397,6 +417,26 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
397
417
  launchTs: 1700542800000,
398
418
  oracleSource: OracleSource.PYTH,
399
419
  },
420
+ {
421
+ fullName: 'Celestia',
422
+ category: ['Data'],
423
+ symbol: 'TIA-PERP',
424
+ baseAssetSymbol: 'TIA',
425
+ marketIndex: 19,
426
+ oracle: new PublicKey('funeUsHgi2QKkLdUPASRLuYkaK8JaazCEz3HikbkhVt'),
427
+ launchTs: 1701880540000,
428
+ oracleSource: OracleSource.PYTH,
429
+ },
430
+ {
431
+ fullName: 'Jito',
432
+ category: ['MEV'],
433
+ symbol: 'JTO-PERP',
434
+ baseAssetSymbol: 'JTO',
435
+ marketIndex: 20,
436
+ oracle: new PublicKey('D8UUgr8a3aR3yUeHLu7v8FWK7E8Y5sSU7qrYBXUJXBQ5'),
437
+ launchTs: 1701967240000,
438
+ oracleSource: OracleSource.PYTH,
439
+ },
400
440
  ];
401
441
 
402
442
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
@@ -160,6 +160,16 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
160
160
  precisionExp: NINE,
161
161
  serumMarket: new PublicKey('ARjaHVxGCQfTvvKjLd7U7srvk6orthZSE6uqWchCczZc'),
162
162
  },
163
+ {
164
+ symbol: 'JTO',
165
+ marketIndex: 9,
166
+ oracle: new PublicKey('D8UUgr8a3aR3yUeHLu7v8FWK7E8Y5sSU7qrYBXUJXBQ5'),
167
+ oracleSource: OracleSource.PYTH,
168
+ mint: new PublicKey('jtojtomepa8beP8AuQc6eXt5FriJwfFMwQx2v2f9mCL'),
169
+ precision: new BN(10).pow(NINE),
170
+ precisionExp: NINE,
171
+ serumMarket: new PublicKey('H87FfmHABiZLRGrDsXRZtqq25YpARzaokCzL1vMYGiep'),
172
+ },
163
173
  ];
164
174
 
165
175
  export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = {