@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/VERSION +1 -1
- package/lib/addresses/pda.d.ts +2 -0
- package/lib/addresses/pda.js +9 -1
- package/lib/adminClient.js +2 -1
- package/lib/bankrun/bankrunConnection.js +1 -1
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/dlob/orderBookLevels.d.ts +1 -1
- package/lib/driftClient.d.ts +6 -5
- package/lib/driftClient.js +77 -45
- package/lib/idl/drift.json +38 -5
- package/lib/idl/openbook.json +3854 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/math/spotMarket.d.ts +6 -0
- package/lib/math/spotMarket.js +16 -1
- package/lib/openbook/openbookV2FulfillmentConfigMap.d.ts +10 -0
- package/lib/openbook/openbookV2FulfillmentConfigMap.js +17 -0
- package/lib/openbook/openbookV2Subscriber.d.ts +36 -0
- package/lib/openbook/openbookV2Subscriber.js +104 -0
- package/lib/types.d.ts +1 -0
- package/package.json +4 -2
- package/src/addresses/pda.ts +10 -0
- package/src/adminClient.ts +3 -1
- package/src/bankrun/bankrunConnection.ts +2 -2
- package/src/constants/spotMarkets.ts +11 -0
- package/src/dlob/orderBookLevels.ts +1 -1
- package/src/driftClient.ts +93 -57
- package/src/idl/drift.json +39 -6
- package/src/index.ts +2 -0
- package/src/math/spotMarket.ts +28 -2
- package/src/openbook/openbookV2FulfillmentConfigMap.ts +29 -0
- package/src/openbook/openbookV2Subscriber.ts +167 -0
- package/src/types.ts +2 -0
- package/tests/subscriber/openbook.ts +58 -0
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);
|
package/lib/math/spotMarket.d.ts
CHANGED
|
@@ -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;
|
package/lib/math/spotMarket.js
CHANGED
|
@@ -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.87.0-beta.
|
|
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
|
}
|
package/src/addresses/pda.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/adminClient.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
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;
|