@drift-labs/sdk 2.87.0-beta.5 → 2.87.0-beta.6

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/index.d.ts CHANGED
@@ -72,6 +72,8 @@ export * from './serum/serumFulfillmentConfigMap';
72
72
  export * from './phoenix/phoenixSubscriber';
73
73
  export * from './priorityFee';
74
74
  export * from './phoenix/phoenixFulfillmentConfigMap';
75
+ export * from './openbook/openbookV2Subscriber';
76
+ export * from './openbook/openbookV2FulfillmentConfigMap';
75
77
  export * from './tx/fastSingleTxSender';
76
78
  export * from './tx/retryTxSender';
77
79
  export * from './tx/whileValidTxSender';
package/lib/index.js CHANGED
@@ -95,6 +95,8 @@ __exportStar(require("./serum/serumFulfillmentConfigMap"), exports);
95
95
  __exportStar(require("./phoenix/phoenixSubscriber"), exports);
96
96
  __exportStar(require("./priorityFee"), exports);
97
97
  __exportStar(require("./phoenix/phoenixFulfillmentConfigMap"), exports);
98
+ __exportStar(require("./openbook/openbookV2Subscriber"), exports);
99
+ __exportStar(require("./openbook/openbookV2FulfillmentConfigMap"), exports);
98
100
  __exportStar(require("./tx/fastSingleTxSender"), exports);
99
101
  __exportStar(require("./tx/retryTxSender"), exports);
100
102
  __exportStar(require("./tx/whileValidTxSender"), exports);
@@ -3,3 +3,9 @@ import { BN } from '@coral-xyz/anchor';
3
3
  import { MarginCategory, SpotBalanceType, SpotMarketAccount } from '../types';
4
4
  export declare function castNumberToSpotPrecision(value: number | BN, spotMarket: SpotMarketAccount): BN;
5
5
  export declare function calculateSpotMarketMarginRatio(market: SpotMarketAccount, oraclePrice: BN, marginCategory: MarginCategory, size: BN, balanceType: SpotBalanceType, customMarginRatio?: number): number;
6
+ /**
7
+ * Returns the maximum remaining deposit that can be made to the spot market. If the maxTokenDeposits on the market is zero then there is no limit and this function will also return zero. (so that needs to be checked)
8
+ * @param market
9
+ * @returns
10
+ */
11
+ export declare function calculateMaxRemainingDeposit(market: SpotMarketAccount): BN;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateSpotMarketMarginRatio = exports.castNumberToSpotPrecision = void 0;
3
+ exports.calculateMaxRemainingDeposit = exports.calculateSpotMarketMarginRatio = exports.castNumberToSpotPrecision = void 0;
4
4
  const anchor_1 = require("@coral-xyz/anchor");
5
5
  const types_1 = require("../types");
6
6
  const spotBalance_1 = require("./spotBalance");
@@ -32,3 +32,18 @@ function calculateSpotMarketMarginRatio(market, oraclePrice, marginCategory, siz
32
32
  return marginRatio;
33
33
  }
34
34
  exports.calculateSpotMarketMarginRatio = calculateSpotMarketMarginRatio;
35
+ /**
36
+ * Returns the maximum remaining deposit that can be made to the spot market. If the maxTokenDeposits on the market is zero then there is no limit and this function will also return zero. (so that needs to be checked)
37
+ * @param market
38
+ * @returns
39
+ */
40
+ function calculateMaxRemainingDeposit(market) {
41
+ const marketMaxTokenDeposits = market.maxTokenDeposits;
42
+ if (marketMaxTokenDeposits.eq(numericConstants_1.ZERO)) {
43
+ // If the maxTokenDeposits is set to zero then that means there is no limit. Return the largest number we can to represent infinite available deposit.
44
+ return numericConstants_1.ZERO;
45
+ }
46
+ const totalDepositsTokenAmount = (0, spotBalance_1.getTokenAmount)(market.depositBalance, market, types_1.SpotBalanceType.DEPOSIT);
47
+ return marketMaxTokenDeposits.sub(totalDepositsTokenAmount);
48
+ }
49
+ exports.calculateMaxRemainingDeposit = calculateMaxRemainingDeposit;
@@ -0,0 +1,10 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { OpenbookV2FulfillmentConfigAccount } from '../types';
3
+ import { DriftClient } from '../driftClient';
4
+ export declare class OpenbookV2FulfillmentConfigMap {
5
+ driftClient: DriftClient;
6
+ map: Map<number, OpenbookV2FulfillmentConfigAccount>;
7
+ constructor(driftClient: DriftClient);
8
+ add(marketIndex: number, openbookV2MarketAddress: PublicKey): Promise<void>;
9
+ get(marketIndex: number): OpenbookV2FulfillmentConfigAccount | undefined;
10
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenbookV2FulfillmentConfigMap = void 0;
4
+ class OpenbookV2FulfillmentConfigMap {
5
+ constructor(driftClient) {
6
+ this.map = new Map();
7
+ this.driftClient = driftClient;
8
+ }
9
+ async add(marketIndex, openbookV2MarketAddress) {
10
+ const account = await this.driftClient.getOpenbookV2FulfillmentConfig(openbookV2MarketAddress);
11
+ this.map.set(marketIndex, account);
12
+ }
13
+ get(marketIndex) {
14
+ return this.map.get(marketIndex);
15
+ }
16
+ }
17
+ exports.OpenbookV2FulfillmentConfigMap = OpenbookV2FulfillmentConfigMap;
@@ -0,0 +1,36 @@
1
+ /// <reference types="bn.js" />
2
+ import { Connection, PublicKey } from '@solana/web3.js';
3
+ import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
4
+ import { BN } from '@coral-xyz/anchor';
5
+ import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
6
+ import { Market, OpenBookV2Client } from '@openbook-dex/openbook-v2';
7
+ export type OpenbookV2SubscriberConfig = {
8
+ connection: Connection;
9
+ programId: PublicKey;
10
+ marketAddress: PublicKey;
11
+ accountSubscription: {
12
+ type: 'polling';
13
+ accountLoader: BulkAccountLoader;
14
+ } | {
15
+ type: 'websocket';
16
+ };
17
+ };
18
+ export declare class OpenbookV2Subscriber implements L2OrderBookGenerator {
19
+ connection: Connection;
20
+ programId: PublicKey;
21
+ marketAddress: PublicKey;
22
+ subscriptionType: 'polling' | 'websocket';
23
+ accountLoader: BulkAccountLoader | undefined;
24
+ subscribed: boolean;
25
+ market: Market;
26
+ marketCallbackId: string | number;
27
+ client: OpenBookV2Client;
28
+ constructor(config: OpenbookV2SubscriberConfig);
29
+ subscribe(): Promise<void>;
30
+ getBestBid(): Promise<BN | undefined>;
31
+ getBestAsk(): Promise<BN | undefined>;
32
+ getL2Bids(): Generator<L2Level>;
33
+ getL2Asks(): Generator<L2Level>;
34
+ getL2Levels(side: 'bids' | 'asks'): Generator<L2Level>;
35
+ unsubscribe(): Promise<void>;
36
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OpenbookV2Subscriber = void 0;
7
+ const web3_js_1 = require("@solana/web3.js");
8
+ const numericConstants_1 = require("../constants/numericConstants");
9
+ const anchor_1 = require("@coral-xyz/anchor");
10
+ const openbook_v2_1 = require("@openbook-dex/openbook-v2");
11
+ const openbook_json_1 = __importDefault(require("../idl/openbook.json"));
12
+ class OpenbookV2Subscriber {
13
+ constructor(config) {
14
+ this.connection = config.connection;
15
+ this.programId = config.programId;
16
+ this.marketAddress = config.marketAddress;
17
+ this.subscribed = false;
18
+ if (config.accountSubscription.type === 'polling') {
19
+ this.subscriptionType = 'polling';
20
+ this.accountLoader = config.accountSubscription.accountLoader;
21
+ }
22
+ else {
23
+ this.subscriptionType = 'websocket';
24
+ }
25
+ }
26
+ async subscribe() {
27
+ if (this.subscribed === true) {
28
+ return;
29
+ }
30
+ const anchorProvider = new anchor_1.AnchorProvider(this.connection, new anchor_1.Wallet(web3_js_1.Keypair.generate()), {});
31
+ const openbookV2Program = new anchor_1.Program(openbook_json_1.default, this.programId, anchorProvider);
32
+ this.client = new openbook_v2_1.OpenBookV2Client(anchorProvider);
33
+ const market = await openbook_v2_1.Market.load(this.client, this.marketAddress);
34
+ this.market = await market.loadOrderBook();
35
+ if (this.subscriptionType === 'websocket') {
36
+ this.marketCallbackId = this.connection.onAccountChange(this.marketAddress, async (accountInfo, _) => {
37
+ const marketRaw = openbookV2Program.coder.accounts.decode('Market', accountInfo.data);
38
+ this.market = new openbook_v2_1.Market(this.client, this.marketAddress, marketRaw);
39
+ await this.market.loadOrderBook();
40
+ });
41
+ }
42
+ else {
43
+ this.marketCallbackId = await this.accountLoader.addAccount(this.marketAddress, (buffer, _) => {
44
+ const marketRaw = openbookV2Program.coder.accounts.decode('Market', buffer);
45
+ this.market = new openbook_v2_1.Market(this.client, this.marketAddress, marketRaw);
46
+ (async () => {
47
+ await this.market.loadOrderBook();
48
+ })();
49
+ });
50
+ }
51
+ this.subscribed = true;
52
+ }
53
+ async getBestBid() {
54
+ const bids = await this.market.loadBids();
55
+ const bestBid = bids.best();
56
+ if (bestBid === undefined) {
57
+ return undefined;
58
+ }
59
+ return new anchor_1.BN(Math.floor(bestBid.price * numericConstants_1.PRICE_PRECISION.toNumber()));
60
+ }
61
+ async getBestAsk() {
62
+ const asks = await this.market.loadAsks();
63
+ const bestAsk = asks.best();
64
+ if (bestAsk === undefined) {
65
+ return undefined;
66
+ }
67
+ return new anchor_1.BN(Math.floor(bestAsk.price * numericConstants_1.PRICE_PRECISION.toNumber()));
68
+ }
69
+ getL2Bids() {
70
+ return this.getL2Levels('bids');
71
+ }
72
+ getL2Asks() {
73
+ return this.getL2Levels('asks');
74
+ }
75
+ *getL2Levels(side) {
76
+ const basePrecision = Math.ceil(1 / this.market.baseNativeFactor.toNumber());
77
+ const pricePrecision = numericConstants_1.PRICE_PRECISION.toNumber();
78
+ const levels = side === 'bids' ? this.market.bids : this.market.asks;
79
+ for (const order of levels.items()) {
80
+ const size = new anchor_1.BN(order.size * basePrecision);
81
+ const price = new anchor_1.BN(order.price * pricePrecision);
82
+ yield {
83
+ price,
84
+ size,
85
+ sources: {
86
+ openbook: size,
87
+ },
88
+ };
89
+ }
90
+ }
91
+ async unsubscribe() {
92
+ if (!this.subscribed) {
93
+ return;
94
+ }
95
+ if (this.subscriptionType === 'websocket') {
96
+ await this.connection.removeAccountChangeListener(this.marketCallbackId);
97
+ }
98
+ else {
99
+ this.accountLoader.removeAccount(this.marketAddress, this.marketCallbackId);
100
+ }
101
+ this.subscribed = false;
102
+ }
103
+ }
104
+ exports.OpenbookV2Subscriber = OpenbookV2Subscriber;
package/lib/types.d.ts CHANGED
@@ -824,6 +824,7 @@ export type SpotMarketAccount = {
824
824
  fuelBoostTaker: number;
825
825
  fuelBoostMaker: number;
826
826
  fuelBoostInsurance: number;
827
+ tokenProgram: number;
827
828
  };
828
829
  export type PoolBalance = {
829
830
  scaledBalance: BN;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.87.0-beta.5",
3
+ "version": "2.87.0-beta.6",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -37,6 +37,7 @@
37
37
  "@coral-xyz/anchor": "0.28.0",
38
38
  "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1",
39
39
  "@ellipsis-labs/phoenix-sdk": "^1.4.2",
40
+ "@openbook-dex/openbook-v2": "^0.2.10",
40
41
  "@project-serum/serum": "^0.13.38",
41
42
  "@pythnetwork/client": "2.5.3",
42
43
  "@pythnetwork/price-service-sdk": "^1.7.1",
@@ -77,6 +78,7 @@
77
78
  "node": ">=18"
78
79
  },
79
80
  "resolutions": {
80
- "@solana/errors": "2.0.0-preview.4"
81
+ "@solana/errors": "2.0.0-preview.4",
82
+ "@solana/codecs-data-structures": "2.0.0-preview.4"
81
83
  }
82
84
  }
@@ -1,6 +1,8 @@
1
1
  import { PublicKey } from '@solana/web3.js';
2
2
  import * as anchor from '@coral-xyz/anchor';
3
3
  import { BN } from '@coral-xyz/anchor';
4
+ import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
5
+ import { SpotMarketAccount } from '..';
4
6
 
5
7
  export async function getDriftStateAccountPublicKeyAndNonce(
6
8
  programId: PublicKey
@@ -264,3 +266,11 @@ export function getPythPullOraclePublicKey(
264
266
  progarmId
265
267
  )[0];
266
268
  }
269
+ export function getTokenProgramForSpotMarket(
270
+ spotMarketAccount: SpotMarketAccount
271
+ ): PublicKey {
272
+ if (spotMarketAccount.tokenProgram === 1) {
273
+ return TOKEN_2022_PROGRAM_ID;
274
+ }
275
+ return TOKEN_PROGRAM_ID;
276
+ }
@@ -198,6 +198,8 @@ export class AdminClient extends DriftClient {
198
198
  spotMarketIndex
199
199
  );
200
200
 
201
+ const tokenProgram = (await this.connection.getAccountInfo(mint)).owner;
202
+
201
203
  const nameBuffer = encodeName(name);
202
204
  const initializeIx = await this.program.instruction.initializeSpotMarket(
203
205
  optimalUtilization,
@@ -233,7 +235,7 @@ export class AdminClient extends DriftClient {
233
235
  oracle,
234
236
  rent: SYSVAR_RENT_PUBKEY,
235
237
  systemProgram: anchor.web3.SystemProgram.programId,
236
- tokenProgram: TOKEN_PROGRAM_ID,
238
+ tokenProgram,
237
239
  },
238
240
  }
239
241
  );
@@ -35,7 +35,7 @@ import {
35
35
  import { BankrunProvider } from 'anchor-bankrun';
36
36
  import bs58 from 'bs58';
37
37
  import { BN, Wallet } from '@coral-xyz/anchor';
38
- import { Account, TOKEN_PROGRAM_ID, unpackAccount } from '@solana/spl-token';
38
+ import { Account, unpackAccount } from '@solana/spl-token';
39
39
 
40
40
  export type Connection = SolanaConnection | BankrunConnection;
41
41
 
@@ -164,7 +164,7 @@ export class BankrunConnection {
164
164
 
165
165
  async getTokenAccount(publicKey: PublicKey): Promise<Account> {
166
166
  const info = await this.getAccountInfo(publicKey);
167
- return unpackAccount(publicKey, info, TOKEN_PROGRAM_ID);
167
+ return unpackAccount(publicKey, info, info.owner);
168
168
  }
169
169
 
170
170
  async getMultipleAccountsInfo(
@@ -67,6 +67,17 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
67
67
  pythFeedId:
68
68
  '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
69
69
  },
70
+ {
71
+ symbol: 'PYUSD',
72
+ marketIndex: 3,
73
+ oracle: new PublicKey('HpMoKp3TCd3QT4MWYUKk2zCBwmhr5Df45fB6wdxYqEeh'),
74
+ oracleSource: OracleSource.PYTH_PULL,
75
+ mint: new PublicKey('GLfF72ZCUnS6N9iDJw8kedHzd6WFVf3VbpwdKKy76FRk'),
76
+ precision: new BN(10).pow(SIX),
77
+ precisionExp: SIX,
78
+ pythFeedId:
79
+ '0xc1da1b73d7f01e7ddd54b3766cf7fcd644395ad14f70aa706ec5384c59e76692',
80
+ },
70
81
  ];
71
82
 
72
83
  export const MainnetSpotMarkets: SpotMarketConfig[] = [
@@ -21,7 +21,7 @@ import {
21
21
  import { PublicKey } from '@solana/web3.js';
22
22
  import { assert } from '../assert/assert';
23
23
 
24
- type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix';
24
+ type liquiditySource = 'serum' | 'vamm' | 'dlob' | 'phoenix' | 'openbook';
25
25
 
26
26
  export type L2Level = {
27
27
  price: BN;