@drift-labs/sdk 2.26.0-beta.2 → 2.26.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/lib/config.d.ts CHANGED
@@ -7,6 +7,7 @@ type DriftConfig = {
7
7
  DRIFT_PROGRAM_ID: string;
8
8
  USDC_MINT_ADDRESS: string;
9
9
  SERUM_V3: string;
10
+ PHOENIX: string;
10
11
  V2_ALPHA_TICKET_MINT_ADDRESS: string;
11
12
  PERP_MARKETS: PerpMarketConfig[];
12
13
  SPOT_MARKETS: SpotMarketConfig[];
package/lib/config.js CHANGED
@@ -10,6 +10,7 @@ exports.configs = {
10
10
  DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
11
11
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
12
12
  SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
13
+ PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
13
14
  V2_ALPHA_TICKET_MINT_ADDRESS: 'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
14
15
  PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
15
16
  SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
@@ -21,6 +22,7 @@ exports.configs = {
21
22
  DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
22
23
  USDC_MINT_ADDRESS: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
23
24
  SERUM_V3: 'srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX',
25
+ PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
24
26
  V2_ALPHA_TICKET_MINT_ADDRESS: 'Cmvhycb6LQvvzaShGw4iDHRLzeSSryioAsU98DSSkMNa',
25
27
  PERP_MARKETS: perpMarkets_1.MainnetPerpMarkets,
26
28
  SPOT_MARKETS: spotMarkets_1.MainnetSpotMarkets,
@@ -9,6 +9,7 @@ export type SpotMarketConfig = {
9
9
  precision: BN;
10
10
  precisionExp: BN;
11
11
  serumMarket?: PublicKey;
12
+ phoenixMarket?: PublicKey;
12
13
  };
13
14
  export declare const WRAPPED_SOL_MINT: PublicKey;
14
15
  export declare const DevnetSpotMarkets: SpotMarketConfig[];
@@ -24,6 +24,7 @@ exports.DevnetSpotMarkets = [
24
24
  precision: numericConstants_1.LAMPORTS_PRECISION,
25
25
  precisionExp: numericConstants_1.LAMPORTS_EXP,
26
26
  serumMarket: new web3_js_1.PublicKey('8N37SsnTu8RYxtjrV9SStjkkwVhmU8aCWhLvwduAPEKW'),
27
+ phoenixMarket: new web3_js_1.PublicKey('78ehDnHgbkFxqXZwdFxa8HK7saX58GymeX2wNGdkqYLp'),
27
28
  },
28
29
  {
29
30
  symbol: 'BTC',
@@ -1,6 +1,6 @@
1
1
  import { BN, DLOBNode, OraclePriceData, PerpMarketAccount } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
- type liquiditySource = 'serum' | 'vamm' | 'dlob';
3
+ type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix';
4
4
  export type L2Level = {
5
5
  price: BN;
6
6
  size: BN;
@@ -34,7 +34,6 @@ export declare class DriftClient {
34
34
  users: Map<string, User>;
35
35
  userStats?: UserStats;
36
36
  activeSubAccountId: number;
37
- activeAuthority: PublicKey;
38
37
  userAccountSubscriptionConfig: UserSubscriptionConfig;
39
38
  accountSubscriber: DriftClientAccountSubscriber;
40
39
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
@@ -46,7 +45,7 @@ export declare class DriftClient {
46
45
  marketLookupTable: PublicKey;
47
46
  lookupTableAccount: AddressLookupTableAccount;
48
47
  includeDelegates?: boolean;
49
- authoritySubaccountMap?: Map<string, number[]>;
48
+ authoritySubAccountMap?: Map<string, number[]>;
50
49
  skipLoadUsers?: boolean;
51
50
  get isSubscribed(): boolean;
52
51
  set isSubscribed(val: boolean);
@@ -98,7 +97,7 @@ export declare class DriftClient {
98
97
  */
99
98
  updateWallet(newWallet: IWallet, subAccountIds?: number[], activeSubAccountId?: number, includeDelegates?: boolean, authoritySubaccountMap?: Map<string, number[]>): Promise<boolean>;
100
99
  switchActiveUser(subAccountId: number, authority?: PublicKey): void;
101
- addUser(subAccountId: number, authority?: PublicKey): Promise<boolean>;
100
+ addUser(subAccountId: number, authority?: PublicKey, userAccount?: UserAccount): Promise<boolean>;
102
101
  /**
103
102
  * Adds and subscribes to users based on params set by the constructor or by updateWallet.
104
103
  */
@@ -77,19 +77,18 @@ class DriftClient {
77
77
  if (config.includeDelegates && config.subAccountIds) {
78
78
  throw new Error('Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead');
79
79
  }
80
- if (config.authoritySubaccountMap && config.subAccountIds) {
80
+ if (config.authoritySubAccountMap && config.subAccountIds) {
81
81
  throw new Error('Can only pass one of authoritySubaccountMap or subAccountIds');
82
82
  }
83
- if (config.authoritySubaccountMap && config.includeDelegates) {
83
+ if (config.authoritySubAccountMap && config.includeDelegates) {
84
84
  throw new Error('Can only pass one of authoritySubaccountMap or includeDelegates');
85
85
  }
86
- this.authoritySubaccountMap = config.authoritySubaccountMap
87
- ? config.authoritySubaccountMap
86
+ this.authoritySubAccountMap = config.authoritySubAccountMap
87
+ ? config.authoritySubAccountMap
88
88
  : config.subAccountIds
89
89
  ? new Map([[this.authority.toString(), config.subAccountIds]])
90
90
  : new Map();
91
91
  this.includeDelegates = (_d = config.includeDelegates) !== null && _d !== void 0 ? _d : false;
92
- this.activeAuthority = this.authority;
93
92
  this.userAccountSubscriptionConfig =
94
93
  ((_e = config.accountSubscription) === null || _e === void 0 ? void 0 : _e.type) === 'polling'
95
94
  ? {
@@ -298,7 +297,7 @@ class DriftClient {
298
297
  if (authoritySubaccountMap && includeDelegates) {
299
298
  throw new Error('Can only pass one of authoritySubaccountMap or includeDelegates');
300
299
  }
301
- this.authoritySubaccountMap = authoritySubaccountMap
300
+ this.authoritySubAccountMap = authoritySubaccountMap
302
301
  ? authoritySubaccountMap
303
302
  : subAccountIds
304
303
  ? new Map([[this.authority.toString(), subAccountIds]])
@@ -322,16 +321,17 @@ class DriftClient {
322
321
  }
323
322
  switchActiveUser(subAccountId, authority) {
324
323
  this.activeSubAccountId = subAccountId;
325
- this.activeAuthority = authority !== null && authority !== void 0 ? authority : this.authority;
324
+ this.authority = authority !== null && authority !== void 0 ? authority : this.authority;
325
+ this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.authority);
326
326
  }
327
- async addUser(subAccountId, authority) {
327
+ async addUser(subAccountId, authority, userAccount) {
328
328
  authority = authority !== null && authority !== void 0 ? authority : this.authority;
329
329
  const userKey = this.getUserMapKey(subAccountId, authority);
330
330
  if (this.users.has(userKey) && this.users.get(userKey).isSubscribed) {
331
331
  return true;
332
332
  }
333
333
  const user = this.createUser(subAccountId, this.userAccountSubscriptionConfig, authority);
334
- const result = await user.subscribe();
334
+ const result = await user.subscribe(userAccount);
335
335
  if (result) {
336
336
  this.users.set(userKey, user);
337
337
  return true;
@@ -349,28 +349,28 @@ class DriftClient {
349
349
  if (this.skipLoadUsers)
350
350
  return true;
351
351
  let result = true;
352
- if (this.authoritySubaccountMap && this.authoritySubaccountMap.size > 0) {
353
- this.authoritySubaccountMap.forEach(async (value, key) => {
352
+ if (this.authoritySubAccountMap && this.authoritySubAccountMap.size > 0) {
353
+ this.authoritySubAccountMap.forEach(async (value, key) => {
354
354
  for (const subAccountId of value) {
355
355
  result =
356
356
  result && (await this.addUser(subAccountId, new web3_js_1.PublicKey(key)));
357
357
  }
358
358
  });
359
359
  if (this.activeSubAccountId == undefined) {
360
- this.switchActiveUser((_a = [...this.authoritySubaccountMap.values()][0][0]) !== null && _a !== void 0 ? _a : 0, new web3_js_1.PublicKey((_b = [...this.authoritySubaccountMap.keys()][0]) !== null && _b !== void 0 ? _b : this.authority.toString()));
360
+ this.switchActiveUser((_a = [...this.authoritySubAccountMap.values()][0][0]) !== null && _a !== void 0 ? _a : 0, new web3_js_1.PublicKey((_b = [...this.authoritySubAccountMap.keys()][0]) !== null && _b !== void 0 ? _b : this.authority.toString()));
361
361
  }
362
362
  }
363
363
  else {
364
- const userAccounts = (_c = (await this.getUserAccountsForAuthority(this.authority))) !== null && _c !== void 0 ? _c : [];
364
+ const userAccounts = (_c = (await this.getUserAccountsForAuthority(this.wallet.publicKey))) !== null && _c !== void 0 ? _c : [];
365
365
  let delegatedAccounts = [];
366
366
  if (this.includeDelegates) {
367
367
  delegatedAccounts =
368
- (_d = (await this.getUserAccountsForDelegate(this.authority))) !== null && _d !== void 0 ? _d : [];
368
+ (_d = (await this.getUserAccountsForDelegate(this.wallet.publicKey))) !== null && _d !== void 0 ? _d : [];
369
369
  }
370
370
  for (const account of userAccounts.concat(delegatedAccounts)) {
371
371
  result =
372
372
  result &&
373
- (await this.addUser(account.subAccountId, account.authority));
373
+ (await this.addUser(account.subAccountId, account.authority, account));
374
374
  }
375
375
  if (this.activeSubAccountId == undefined) {
376
376
  this.switchActiveUser((_f = (_e = userAccounts.concat(delegatedAccounts)[0]) === null || _e === void 0 ? void 0 : _e.subAccountId) !== null && _f !== void 0 ? _f : 0, (_h = (_g = userAccounts.concat(delegatedAccounts)[0]) === null || _g === void 0 ? void 0 : _g.authority) !== null && _h !== void 0 ? _h : this.authority);
@@ -388,6 +388,7 @@ class DriftClient {
388
388
  }
389
389
  tx.add(initializeUserAccountIx);
390
390
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
391
+ await this.addUser(subAccountId);
391
392
  return [txSig, userAccountPublicKey];
392
393
  }
393
394
  async getInitializeUserInstructions(subAccountId = 0, name = userName_1.DEFAULT_USER_NAME, referrerInfo) {
@@ -604,18 +605,18 @@ class DriftClient {
604
605
  }
605
606
  getUser(subAccountId, authority) {
606
607
  subAccountId = subAccountId !== null && subAccountId !== void 0 ? subAccountId : this.activeSubAccountId;
607
- authority = authority !== null && authority !== void 0 ? authority : this.activeAuthority;
608
+ authority = authority !== null && authority !== void 0 ? authority : this.authority;
608
609
  const userMapKey = this.getUserMapKey(subAccountId, authority);
609
610
  if (!this.users.has(userMapKey)) {
610
- throw new Error(`Clearing House has no user for user id ${subAccountId}`);
611
+ throw new Error(`Clearing House has no user for user id ${userMapKey}`);
611
612
  }
612
613
  return this.users.get(userMapKey);
613
614
  }
614
615
  getUsers() {
615
616
  // delegate users get added to the end
616
617
  return [...this.users.values()]
617
- .filter((acct) => acct.getUserAccount().authority.equals(this.authority))
618
- .concat([...this.users.values()].filter((acct) => !acct.getUserAccount().authority.equals(this.authority)));
618
+ .filter((acct) => acct.getUserAccount().authority.equals(this.wallet.publicKey))
619
+ .concat([...this.users.values()].filter((acct) => !acct.getUserAccount().authority.equals(this.wallet.publicKey)));
619
620
  }
620
621
  getUserStats() {
621
622
  return this.userStats;
@@ -629,7 +630,7 @@ class DriftClient {
629
630
  if (this.userStatsAccountPublicKey) {
630
631
  return this.userStatsAccountPublicKey;
631
632
  }
632
- this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.activeAuthority);
633
+ this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.authority);
633
634
  return this.userStatsAccountPublicKey;
634
635
  }
635
636
  async getUserAccountPublicKey(subAccountId, authority) {
@@ -909,8 +910,8 @@ class DriftClient {
909
910
  const additionalSigners = [];
910
911
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
911
912
  const isSolMarket = spotMarketAccount.mint.equals(spotMarkets_1.WRAPPED_SOL_MINT);
912
- const authority = this.authority;
913
- const createWSOLTokenAccount = isSolMarket && collateralAccountPublicKey.equals(authority);
913
+ const signerAuthority = this.wallet.publicKey;
914
+ const createWSOLTokenAccount = isSolMarket && collateralAccountPublicKey.equals(signerAuthority);
914
915
  if (createWSOLTokenAccount) {
915
916
  const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(amount, true);
916
917
  collateralAccountPublicKey = pubkey;
@@ -923,16 +924,14 @@ class DriftClient {
923
924
  tx.add(depositCollateralIx);
924
925
  // Close the wrapped sol account at the end of the transaction
925
926
  if (createWSOLTokenAccount) {
926
- tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, collateralAccountPublicKey, authority, authority, []));
927
+ tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, collateralAccountPublicKey, signerAuthority, signerAuthority, []));
927
928
  }
928
929
  const { txSig, slot } = await this.sendTransaction(tx, additionalSigners, this.opts);
929
930
  this.spotMarketLastSlotCache.set(marketIndex, slot);
930
931
  return txSig;
931
932
  }
932
933
  async getDepositInstruction(amount, marketIndex, userTokenAccount, subAccountId, reduceOnly = false, userInitialized = true) {
933
- const userAccountPublicKey = subAccountId
934
- ? await (0, pda_1.getUserAccountPublicKey)(this.program.programId, this.authority, subAccountId)
935
- : await this.getUserAccountPublicKey();
934
+ const userAccountPublicKey = await (0, pda_1.getUserAccountPublicKey)(this.program.programId, this.authority, subAccountId !== null && subAccountId !== void 0 ? subAccountId : this.activeSubAccountId);
936
935
  let remainingAccounts = [];
937
936
  if (userInitialized) {
938
937
  remainingAccounts = this.getRemainingAccounts({
@@ -1049,6 +1048,7 @@ class DriftClient {
1049
1048
  }
1050
1049
  const { txSig, slot } = await this.sendTransaction(tx, additionalSigners, this.opts);
1051
1050
  this.spotMarketLastSlotCache.set(marketIndex, slot);
1051
+ await this.addUser(subAccountId);
1052
1052
  return [txSig, userAccountPublicKey];
1053
1053
  }
1054
1054
  async initializeUserAccountForDevnet(subAccountId = 0, name = userName_1.DEFAULT_USER_NAME, marketIndex, tokenFaucet, amount, referrerInfo) {
@@ -1063,6 +1063,7 @@ class DriftClient {
1063
1063
  }
1064
1064
  tx.add(initializeUserAccountIx).add(depositCollateralIx);
1065
1065
  const txSig = await this.program.provider.sendAndConfirm(tx, []);
1066
+ await this.addUser(subAccountId);
1066
1067
  return [txSig, userAccountPublicKey];
1067
1068
  }
1068
1069
  async withdraw(amount, marketIndex, userTokenAccount, reduceOnly = false) {
@@ -20,7 +20,7 @@ export type DriftClientConfig = {
20
20
  userStats?: boolean;
21
21
  authority?: PublicKey;
22
22
  includeDelegates?: boolean;
23
- authoritySubaccountMap?: Map<string, number[]>;
23
+ authoritySubAccountMap?: Map<string, number[]>;
24
24
  skipLoadUsers?: boolean;
25
25
  };
26
26
  export type DriftClientSubscriptionConfig = {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.26.0-beta.2",
2
+ "version": "2.26.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -2,6 +2,7 @@ import { Connection, PublicKey } from '@solana/web3.js';
2
2
  import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
3
3
  import { MarketData, Client } from '@ellipsis-labs/phoenix-sdk';
4
4
  import { BN } from '@coral-xyz/anchor';
5
+ import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
5
6
  export type PhoenixMarketSubscriberConfig = {
6
7
  connection: Connection;
7
8
  programId: PublicKey;
@@ -13,7 +14,7 @@ export type PhoenixMarketSubscriberConfig = {
13
14
  type: 'websocket';
14
15
  };
15
16
  };
16
- export declare class PhoenixSubscriber {
17
+ export declare class PhoenixSubscriber implements L2OrderBookGenerator {
17
18
  connection: Connection;
18
19
  client: Client;
19
20
  programId: PublicKey;
@@ -30,5 +31,8 @@ export declare class PhoenixSubscriber {
30
31
  subscribe(): Promise<void>;
31
32
  getBestBid(): BN;
32
33
  getBestAsk(): BN;
34
+ getL2Bids(): Generator<L2Level>;
35
+ getL2Asks(): Generator<L2Level>;
36
+ getL2Levels(side: 'bids' | 'asks'): Generator<L2Level>;
33
37
  unsubscribe(): Promise<void>;
34
38
  }
@@ -60,6 +60,29 @@ class PhoenixSubscriber {
60
60
  const ladder = (0, phoenix_sdk_1.getMarketUiLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 1);
61
61
  return new anchor_1.BN(Math.floor(ladder.asks[0][0] * numericConstants_1.PRICE_PRECISION.toNumber()));
62
62
  }
63
+ getL2Bids() {
64
+ return this.getL2Levels('bids');
65
+ }
66
+ getL2Asks() {
67
+ return this.getL2Levels('asks');
68
+ }
69
+ *getL2Levels(side) {
70
+ // @ts-ignore
71
+ const basePrecision = Math.pow(10, this.market.header.baseParams.decimals);
72
+ const pricePrecision = numericConstants_1.PRICE_PRECISION.toNumber();
73
+ const ladder = (0, phoenix_sdk_1.getMarketUiLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
74
+ for (let i = 0; i < ladder[side].length; i++) {
75
+ const [priceNum, sizeNum] = ladder[side][i];
76
+ const size = new anchor_1.BN(Math.floor(sizeNum * basePrecision));
77
+ yield {
78
+ price: new anchor_1.BN(Math.floor(priceNum * pricePrecision)),
79
+ size,
80
+ sources: {
81
+ phoenix: size,
82
+ },
83
+ };
84
+ }
85
+ }
63
86
  async unsubscribe() {
64
87
  if (!this.subscribed) {
65
88
  return;
@@ -76,9 +76,9 @@ class SerumSubscriber {
76
76
  // @ts-ignore
77
77
  const basePrecision = Math.pow(10, this.market._baseSplTokenDecimals);
78
78
  const pricePrecision = numericConstants_1.PRICE_PRECISION.toNumber();
79
- for (let { price, size } of this[side].items(side === 'bids')) {
80
- price = new anchor_1.BN(price * pricePrecision);
81
- size = new anchor_1.BN(size * basePrecision);
79
+ for (const { price: priceNum, size: sizeNum } of this[side].items(side === 'bids')) {
80
+ const price = new anchor_1.BN(priceNum * pricePrecision);
81
+ const size = new anchor_1.BN(sizeNum * basePrecision);
82
82
  yield {
83
83
  price,
84
84
  size,
package/lib/wallet.d.ts CHANGED
@@ -7,3 +7,4 @@ export declare class Wallet implements IWallet {
7
7
  signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;
8
8
  get publicKey(): PublicKey;
9
9
  }
10
+ export declare function loadKeypair(privateKey: string): Keypair;
package/lib/wallet.js CHANGED
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Wallet = void 0;
6
+ exports.loadKeypair = exports.Wallet = void 0;
7
+ const web3_js_1 = require("@solana/web3.js");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const bs58_1 = __importDefault(require("bs58"));
4
10
  class Wallet {
5
11
  constructor(payer) {
6
12
  this.payer = payer;
@@ -20,3 +26,20 @@ class Wallet {
20
26
  }
21
27
  }
22
28
  exports.Wallet = Wallet;
29
+ function loadKeypair(privateKey) {
30
+ // try to load privateKey as a filepath
31
+ let loadedKey;
32
+ if (fs_1.default.existsSync(privateKey)) {
33
+ loadedKey = new Uint8Array(JSON.parse(fs_1.default.readFileSync(privateKey).toString()));
34
+ }
35
+ else {
36
+ if (privateKey.includes(',')) {
37
+ loadedKey = Uint8Array.from(privateKey.split(',').map((val) => Number(val)));
38
+ }
39
+ else {
40
+ loadedKey = new Uint8Array(bs58_1.default.decode(privateKey));
41
+ }
42
+ }
43
+ return web3_js_1.Keypair.fromSecretKey(Uint8Array.from(loadedKey));
44
+ }
45
+ exports.loadKeypair = loadKeypair;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.26.0-beta.2",
3
+ "version": "2.26.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
package/src/config.ts CHANGED
@@ -18,6 +18,7 @@ type DriftConfig = {
18
18
  DRIFT_PROGRAM_ID: string;
19
19
  USDC_MINT_ADDRESS: string;
20
20
  SERUM_V3: string;
21
+ PHOENIX: string;
21
22
  V2_ALPHA_TICKET_MINT_ADDRESS: string;
22
23
  PERP_MARKETS: PerpMarketConfig[];
23
24
  SPOT_MARKETS: SpotMarketConfig[];
@@ -33,6 +34,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
33
34
  DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
34
35
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
35
36
  SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
37
+ PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
36
38
  V2_ALPHA_TICKET_MINT_ADDRESS:
37
39
  'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
38
40
  PERP_MARKETS: DevnetPerpMarkets,
@@ -45,6 +47,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
45
47
  DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
46
48
  USDC_MINT_ADDRESS: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
47
49
  SERUM_V3: 'srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX',
50
+ PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
48
51
  V2_ALPHA_TICKET_MINT_ADDRESS:
49
52
  'Cmvhycb6LQvvzaShGw4iDHRLzeSSryioAsU98DSSkMNa',
50
53
  PERP_MARKETS: MainnetPerpMarkets,
@@ -17,6 +17,7 @@ export type SpotMarketConfig = {
17
17
  precision: BN;
18
18
  precisionExp: BN;
19
19
  serumMarket?: PublicKey;
20
+ phoenixMarket?: PublicKey;
20
21
  };
21
22
 
22
23
  export const WRAPPED_SOL_MINT = new PublicKey(
@@ -42,6 +43,9 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
42
43
  precision: LAMPORTS_PRECISION,
43
44
  precisionExp: LAMPORTS_EXP,
44
45
  serumMarket: new PublicKey('8N37SsnTu8RYxtjrV9SStjkkwVhmU8aCWhLvwduAPEKW'),
46
+ phoenixMarket: new PublicKey(
47
+ '78ehDnHgbkFxqXZwdFxa8HK7saX58GymeX2wNGdkqYLp'
48
+ ),
45
49
  },
46
50
  {
47
51
  symbol: 'BTC',
@@ -13,7 +13,7 @@ import {
13
13
  } from '..';
14
14
  import { PublicKey } from '@solana/web3.js';
15
15
 
16
- type liquiditySource = 'serum' | 'vamm' | 'dlob';
16
+ type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix';
17
17
 
18
18
  export type L2Level = {
19
19
  price: BN;
@@ -121,7 +121,6 @@ export class DriftClient {
121
121
  users = new Map<string, User>();
122
122
  userStats?: UserStats;
123
123
  activeSubAccountId: number;
124
- activeAuthority: PublicKey;
125
124
  userAccountSubscriptionConfig: UserSubscriptionConfig;
126
125
  accountSubscriber: DriftClientAccountSubscriber;
127
126
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
@@ -133,7 +132,7 @@ export class DriftClient {
133
132
  marketLookupTable: PublicKey;
134
133
  lookupTableAccount: AddressLookupTableAccount;
135
134
  includeDelegates?: boolean;
136
- authoritySubaccountMap?: Map<string, number[]>;
135
+ authoritySubAccountMap?: Map<string, number[]>;
137
136
  skipLoadUsers?: boolean;
138
137
 
139
138
  public get isSubscribed() {
@@ -169,26 +168,25 @@ export class DriftClient {
169
168
  );
170
169
  }
171
170
 
172
- if (config.authoritySubaccountMap && config.subAccountIds) {
171
+ if (config.authoritySubAccountMap && config.subAccountIds) {
173
172
  throw new Error(
174
173
  'Can only pass one of authoritySubaccountMap or subAccountIds'
175
174
  );
176
175
  }
177
176
 
178
- if (config.authoritySubaccountMap && config.includeDelegates) {
177
+ if (config.authoritySubAccountMap && config.includeDelegates) {
179
178
  throw new Error(
180
179
  'Can only pass one of authoritySubaccountMap or includeDelegates'
181
180
  );
182
181
  }
183
182
 
184
- this.authoritySubaccountMap = config.authoritySubaccountMap
185
- ? config.authoritySubaccountMap
183
+ this.authoritySubAccountMap = config.authoritySubAccountMap
184
+ ? config.authoritySubAccountMap
186
185
  : config.subAccountIds
187
186
  ? new Map([[this.authority.toString(), config.subAccountIds]])
188
187
  : new Map<string, number[]>();
189
188
 
190
189
  this.includeDelegates = config.includeDelegates ?? false;
191
- this.activeAuthority = this.authority;
192
190
  this.userAccountSubscriptionConfig =
193
191
  config.accountSubscription?.type === 'polling'
194
192
  ? {
@@ -525,7 +523,7 @@ export class DriftClient {
525
523
  );
526
524
  }
527
525
 
528
- this.authoritySubaccountMap = authoritySubaccountMap
526
+ this.authoritySubAccountMap = authoritySubaccountMap
529
527
  ? authoritySubaccountMap
530
528
  : subAccountIds
531
529
  ? new Map([[this.authority.toString(), subAccountIds]])
@@ -557,12 +555,17 @@ export class DriftClient {
557
555
 
558
556
  public switchActiveUser(subAccountId: number, authority?: PublicKey) {
559
557
  this.activeSubAccountId = subAccountId;
560
- this.activeAuthority = authority ?? this.authority;
558
+ this.authority = authority ?? this.authority;
559
+ this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
560
+ this.program.programId,
561
+ this.authority
562
+ );
561
563
  }
562
564
 
563
565
  public async addUser(
564
566
  subAccountId: number,
565
- authority?: PublicKey
567
+ authority?: PublicKey,
568
+ userAccount?: UserAccount
566
569
  ): Promise<boolean> {
567
570
  authority = authority ?? this.authority;
568
571
  const userKey = this.getUserMapKey(subAccountId, authority);
@@ -577,7 +580,7 @@ export class DriftClient {
577
580
  authority
578
581
  );
579
582
 
580
- const result = await user.subscribe();
583
+ const result = await user.subscribe(userAccount);
581
584
 
582
585
  if (result) {
583
586
  this.users.set(userKey, user);
@@ -596,8 +599,8 @@ export class DriftClient {
596
599
 
597
600
  let result = true;
598
601
 
599
- if (this.authoritySubaccountMap && this.authoritySubaccountMap.size > 0) {
600
- this.authoritySubaccountMap.forEach(async (value, key) => {
602
+ if (this.authoritySubAccountMap && this.authoritySubAccountMap.size > 0) {
603
+ this.authoritySubAccountMap.forEach(async (value, key) => {
601
604
  for (const subAccountId of value) {
602
605
  result =
603
606
  result && (await this.addUser(subAccountId, new PublicKey(key)));
@@ -606,27 +609,31 @@ export class DriftClient {
606
609
 
607
610
  if (this.activeSubAccountId == undefined) {
608
611
  this.switchActiveUser(
609
- [...this.authoritySubaccountMap.values()][0][0] ?? 0,
612
+ [...this.authoritySubAccountMap.values()][0][0] ?? 0,
610
613
  new PublicKey(
611
- [...this.authoritySubaccountMap.keys()][0] ??
614
+ [...this.authoritySubAccountMap.keys()][0] ??
612
615
  this.authority.toString()
613
616
  )
614
617
  );
615
618
  }
616
619
  } else {
617
620
  const userAccounts =
618
- (await this.getUserAccountsForAuthority(this.authority)) ?? [];
621
+ (await this.getUserAccountsForAuthority(this.wallet.publicKey)) ?? [];
619
622
  let delegatedAccounts = [];
620
623
 
621
624
  if (this.includeDelegates) {
622
625
  delegatedAccounts =
623
- (await this.getUserAccountsForDelegate(this.authority)) ?? [];
626
+ (await this.getUserAccountsForDelegate(this.wallet.publicKey)) ?? [];
624
627
  }
625
628
 
626
629
  for (const account of userAccounts.concat(delegatedAccounts)) {
627
630
  result =
628
631
  result &&
629
- (await this.addUser(account.subAccountId, account.authority));
632
+ (await this.addUser(
633
+ account.subAccountId,
634
+ account.authority,
635
+ account
636
+ ));
630
637
  }
631
638
 
632
639
  if (this.activeSubAccountId == undefined) {
@@ -662,6 +669,9 @@ export class DriftClient {
662
669
  }
663
670
  tx.add(initializeUserAccountIx);
664
671
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
672
+
673
+ await this.addUser(subAccountId);
674
+
665
675
  return [txSig, userAccountPublicKey];
666
676
  }
667
677
 
@@ -1006,11 +1016,11 @@ export class DriftClient {
1006
1016
 
1007
1017
  public getUser(subAccountId?: number, authority?: PublicKey): User {
1008
1018
  subAccountId = subAccountId ?? this.activeSubAccountId;
1009
- authority = authority ?? this.activeAuthority;
1019
+ authority = authority ?? this.authority;
1010
1020
  const userMapKey = this.getUserMapKey(subAccountId, authority);
1011
1021
 
1012
1022
  if (!this.users.has(userMapKey)) {
1013
- throw new Error(`Clearing House has no user for user id ${subAccountId}`);
1023
+ throw new Error(`Clearing House has no user for user id ${userMapKey}`);
1014
1024
  }
1015
1025
  return this.users.get(userMapKey);
1016
1026
  }
@@ -1018,10 +1028,13 @@ export class DriftClient {
1018
1028
  public getUsers(): User[] {
1019
1029
  // delegate users get added to the end
1020
1030
  return [...this.users.values()]
1021
- .filter((acct) => acct.getUserAccount().authority.equals(this.authority))
1031
+ .filter((acct) =>
1032
+ acct.getUserAccount().authority.equals(this.wallet.publicKey)
1033
+ )
1022
1034
  .concat(
1023
1035
  [...this.users.values()].filter(
1024
- (acct) => !acct.getUserAccount().authority.equals(this.authority)
1036
+ (acct) =>
1037
+ !acct.getUserAccount().authority.equals(this.wallet.publicKey)
1025
1038
  )
1026
1039
  );
1027
1040
  }
@@ -1051,7 +1064,7 @@ export class DriftClient {
1051
1064
 
1052
1065
  this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
1053
1066
  this.program.programId,
1054
- this.activeAuthority
1067
+ this.authority
1055
1068
  );
1056
1069
  return this.userStatsAccountPublicKey;
1057
1070
  }
@@ -1419,10 +1432,10 @@ export class DriftClient {
1419
1432
 
1420
1433
  const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT);
1421
1434
 
1422
- const authority = this.authority;
1435
+ const signerAuthority = this.wallet.publicKey;
1423
1436
 
1424
1437
  const createWSOLTokenAccount =
1425
- isSolMarket && collateralAccountPublicKey.equals(authority);
1438
+ isSolMarket && collateralAccountPublicKey.equals(signerAuthority);
1426
1439
 
1427
1440
  if (createWSOLTokenAccount) {
1428
1441
  const { ixs, signers, pubkey } =
@@ -1454,8 +1467,8 @@ export class DriftClient {
1454
1467
  Token.createCloseAccountInstruction(
1455
1468
  TOKEN_PROGRAM_ID,
1456
1469
  collateralAccountPublicKey,
1457
- authority,
1458
- authority,
1470
+ signerAuthority,
1471
+ signerAuthority,
1459
1472
  []
1460
1473
  )
1461
1474
  );
@@ -1478,13 +1491,11 @@ export class DriftClient {
1478
1491
  reduceOnly = false,
1479
1492
  userInitialized = true
1480
1493
  ): Promise<TransactionInstruction> {
1481
- const userAccountPublicKey = subAccountId
1482
- ? await getUserAccountPublicKey(
1483
- this.program.programId,
1484
- this.authority,
1485
- subAccountId
1486
- )
1487
- : await this.getUserAccountPublicKey();
1494
+ const userAccountPublicKey = await getUserAccountPublicKey(
1495
+ this.program.programId,
1496
+ this.authority,
1497
+ subAccountId ?? this.activeSubAccountId
1498
+ );
1488
1499
 
1489
1500
  let remainingAccounts = [];
1490
1501
  if (userInitialized) {
@@ -1713,6 +1724,8 @@ export class DriftClient {
1713
1724
  );
1714
1725
  this.spotMarketLastSlotCache.set(marketIndex, slot);
1715
1726
 
1727
+ await this.addUser(subAccountId);
1728
+
1716
1729
  return [txSig, userAccountPublicKey];
1717
1730
  }
1718
1731
 
@@ -1759,6 +1772,8 @@ export class DriftClient {
1759
1772
 
1760
1773
  const txSig = await this.program.provider.sendAndConfirm(tx, []);
1761
1774
 
1775
+ await this.addUser(subAccountId);
1776
+
1762
1777
  return [txSig, userAccountPublicKey];
1763
1778
  }
1764
1779
 
@@ -21,7 +21,7 @@ export type DriftClientConfig = {
21
21
  userStats?: boolean;
22
22
  authority?: PublicKey; // explicitly pass an authority if signer is delegate
23
23
  includeDelegates?: boolean; // flag for whether to load delegate accounts as well
24
- authoritySubaccountMap?: Map<string, number[]>; // if passed this will override subAccountIds and includeDelegates
24
+ authoritySubAccountMap?: Map<string, number[]>; // if passed this will override subAccountIds and includeDelegates
25
25
  skipLoadUsers?: boolean; // if passed to constructor, no user accounts will be loaded. they will load if updateWallet is called afterwards.
26
26
  };
27
27
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.26.0-beta.2",
2
+ "version": "2.26.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -10,6 +10,7 @@ import {
10
10
  } from '@ellipsis-labs/phoenix-sdk';
11
11
  import { PRICE_PRECISION } from '../constants/numericConstants';
12
12
  import { BN } from '@coral-xyz/anchor';
13
+ import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
13
14
 
14
15
  export type PhoenixMarketSubscriberConfig = {
15
16
  connection: Connection;
@@ -26,7 +27,7 @@ export type PhoenixMarketSubscriberConfig = {
26
27
  };
27
28
  };
28
29
 
29
- export class PhoenixSubscriber {
30
+ export class PhoenixSubscriber implements L2OrderBookGenerator {
30
31
  connection: Connection;
31
32
  client: Client;
32
33
  programId: PublicKey;
@@ -127,6 +128,39 @@ export class PhoenixSubscriber {
127
128
  return new BN(Math.floor(ladder.asks[0][0] * PRICE_PRECISION.toNumber()));
128
129
  }
129
130
 
131
+ public getL2Bids(): Generator<L2Level> {
132
+ return this.getL2Levels('bids');
133
+ }
134
+
135
+ public getL2Asks(): Generator<L2Level> {
136
+ return this.getL2Levels('asks');
137
+ }
138
+
139
+ *getL2Levels(side: 'bids' | 'asks'): Generator<L2Level> {
140
+ // @ts-ignore
141
+ const basePrecision = Math.pow(10, this.market.header.baseParams.decimals);
142
+ const pricePrecision = PRICE_PRECISION.toNumber();
143
+
144
+ const ladder = getMarketUiLadder(
145
+ this.market,
146
+ this.lastSlot,
147
+ this.lastUnixTimestamp,
148
+ 20
149
+ );
150
+
151
+ for (let i = 0; i < ladder[side].length; i++) {
152
+ const [priceNum, sizeNum] = ladder[side][i];
153
+ const size = new BN(Math.floor(sizeNum * basePrecision));
154
+ yield {
155
+ price: new BN(Math.floor(priceNum * pricePrecision)),
156
+ size,
157
+ sources: {
158
+ phoenix: size,
159
+ },
160
+ };
161
+ }
162
+ }
163
+
130
164
  public async unsubscribe(): Promise<void> {
131
165
  if (!this.subscribed) {
132
166
  return;
@@ -125,9 +125,11 @@ export class SerumSubscriber implements L2OrderBookGenerator {
125
125
  // @ts-ignore
126
126
  const basePrecision = Math.pow(10, this.market._baseSplTokenDecimals);
127
127
  const pricePrecision = PRICE_PRECISION.toNumber();
128
- for (let { price, size } of this[side].items(side === 'bids')) {
129
- price = new BN(price * pricePrecision);
130
- size = new BN(size * basePrecision);
128
+ for (const { price: priceNum, size: sizeNum } of this[side].items(
129
+ side === 'bids'
130
+ )) {
131
+ const price = new BN(priceNum * pricePrecision);
132
+ const size = new BN(sizeNum * basePrecision);
131
133
  yield {
132
134
  price,
133
135
  size,
package/src/wallet.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Keypair, PublicKey, Transaction } from '@solana/web3.js';
2
2
  import { IWallet } from './types';
3
+ import fs from 'fs';
4
+ import bs58 from 'bs58';
3
5
 
4
6
  export class Wallet implements IWallet {
5
7
  constructor(readonly payer: Keypair) {}
@@ -20,3 +22,23 @@ export class Wallet implements IWallet {
20
22
  return this.payer.publicKey;
21
23
  }
22
24
  }
25
+
26
+ export function loadKeypair(privateKey: string): Keypair {
27
+ // try to load privateKey as a filepath
28
+ let loadedKey: Uint8Array;
29
+ if (fs.existsSync(privateKey)) {
30
+ loadedKey = new Uint8Array(
31
+ JSON.parse(fs.readFileSync(privateKey).toString())
32
+ );
33
+ } else {
34
+ if (privateKey.includes(',')) {
35
+ loadedKey = Uint8Array.from(
36
+ privateKey.split(',').map((val) => Number(val))
37
+ );
38
+ } else {
39
+ loadedKey = new Uint8Array(bs58.decode(privateKey));
40
+ }
41
+ }
42
+
43
+ return Keypair.fromSecretKey(Uint8Array.from(loadedKey));
44
+ }