@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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
3
|
+
import { PRICE_PRECISION } from '../constants/numericConstants';
|
|
4
|
+
import { AnchorProvider, BN, Idl, Program, Wallet } from '@coral-xyz/anchor';
|
|
5
|
+
import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
|
|
6
|
+
import { Market, OpenBookV2Client } from '@openbook-dex/openbook-v2';
|
|
7
|
+
import openbookV2Idl from '../idl/openbook.json';
|
|
8
|
+
|
|
9
|
+
export type OpenbookV2SubscriberConfig = {
|
|
10
|
+
connection: Connection;
|
|
11
|
+
programId: PublicKey;
|
|
12
|
+
marketAddress: PublicKey;
|
|
13
|
+
accountSubscription:
|
|
14
|
+
| {
|
|
15
|
+
// enables use to add web sockets in the future
|
|
16
|
+
type: 'polling';
|
|
17
|
+
accountLoader: BulkAccountLoader;
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
type: 'websocket';
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class OpenbookV2Subscriber implements L2OrderBookGenerator {
|
|
25
|
+
connection: Connection;
|
|
26
|
+
programId: PublicKey;
|
|
27
|
+
marketAddress: PublicKey;
|
|
28
|
+
subscriptionType: 'polling' | 'websocket';
|
|
29
|
+
accountLoader: BulkAccountLoader | undefined;
|
|
30
|
+
subscribed: boolean;
|
|
31
|
+
market: Market;
|
|
32
|
+
marketCallbackId: string | number;
|
|
33
|
+
client: OpenBookV2Client;
|
|
34
|
+
|
|
35
|
+
public constructor(config: OpenbookV2SubscriberConfig) {
|
|
36
|
+
this.connection = config.connection;
|
|
37
|
+
this.programId = config.programId;
|
|
38
|
+
this.marketAddress = config.marketAddress;
|
|
39
|
+
this.subscribed = false;
|
|
40
|
+
if (config.accountSubscription.type === 'polling') {
|
|
41
|
+
this.subscriptionType = 'polling';
|
|
42
|
+
this.accountLoader = config.accountSubscription.accountLoader;
|
|
43
|
+
} else {
|
|
44
|
+
this.subscriptionType = 'websocket';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async subscribe(): Promise<void> {
|
|
49
|
+
if (this.subscribed === true) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const anchorProvider = new AnchorProvider(
|
|
54
|
+
this.connection,
|
|
55
|
+
new Wallet(Keypair.generate()),
|
|
56
|
+
{}
|
|
57
|
+
);
|
|
58
|
+
const openbookV2Program = new Program(
|
|
59
|
+
openbookV2Idl as Idl,
|
|
60
|
+
this.programId,
|
|
61
|
+
anchorProvider
|
|
62
|
+
);
|
|
63
|
+
this.client = new OpenBookV2Client(anchorProvider);
|
|
64
|
+
const market = await Market.load(this.client, this.marketAddress);
|
|
65
|
+
this.market = await market.loadOrderBook();
|
|
66
|
+
|
|
67
|
+
if (this.subscriptionType === 'websocket') {
|
|
68
|
+
this.marketCallbackId = this.connection.onAccountChange(
|
|
69
|
+
this.marketAddress,
|
|
70
|
+
async (accountInfo, _) => {
|
|
71
|
+
const marketRaw = openbookV2Program.coder.accounts.decode(
|
|
72
|
+
'Market',
|
|
73
|
+
accountInfo.data
|
|
74
|
+
);
|
|
75
|
+
this.market = new Market(this.client, this.marketAddress, marketRaw);
|
|
76
|
+
await this.market.loadOrderBook();
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
this.marketCallbackId = await this.accountLoader.addAccount(
|
|
81
|
+
this.marketAddress,
|
|
82
|
+
(buffer, _) => {
|
|
83
|
+
const marketRaw = openbookV2Program.coder.accounts.decode(
|
|
84
|
+
'Market',
|
|
85
|
+
buffer
|
|
86
|
+
);
|
|
87
|
+
this.market = new Market(this.client, this.marketAddress, marketRaw);
|
|
88
|
+
(async () => {
|
|
89
|
+
await this.market.loadOrderBook();
|
|
90
|
+
})();
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.subscribed = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async getBestBid(): Promise<BN | undefined> {
|
|
99
|
+
const bids = await this.market.loadBids();
|
|
100
|
+
const bestBid = bids.best();
|
|
101
|
+
|
|
102
|
+
if (bestBid === undefined) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return new BN(Math.floor(bestBid.price * PRICE_PRECISION.toNumber()));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async getBestAsk(): Promise<BN | undefined> {
|
|
110
|
+
const asks = await this.market.loadAsks();
|
|
111
|
+
const bestAsk = asks.best();
|
|
112
|
+
|
|
113
|
+
if (bestAsk === undefined) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return new BN(Math.floor(bestAsk.price * PRICE_PRECISION.toNumber()));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public getL2Bids(): Generator<L2Level> {
|
|
121
|
+
return this.getL2Levels('bids');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public getL2Asks(): Generator<L2Level> {
|
|
125
|
+
return this.getL2Levels('asks');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
*getL2Levels(side: 'bids' | 'asks'): Generator<L2Level> {
|
|
129
|
+
const basePrecision = Math.ceil(
|
|
130
|
+
1 / this.market.baseNativeFactor.toNumber()
|
|
131
|
+
);
|
|
132
|
+
const pricePrecision = PRICE_PRECISION.toNumber();
|
|
133
|
+
|
|
134
|
+
const levels = side === 'bids' ? this.market.bids : this.market.asks;
|
|
135
|
+
|
|
136
|
+
for (const order of levels.items()) {
|
|
137
|
+
const size = new BN(order.size * basePrecision);
|
|
138
|
+
const price = new BN(order.price * pricePrecision);
|
|
139
|
+
yield {
|
|
140
|
+
price,
|
|
141
|
+
size,
|
|
142
|
+
sources: {
|
|
143
|
+
openbook: size,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public async unsubscribe(): Promise<void> {
|
|
150
|
+
if (!this.subscribed) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.subscriptionType === 'websocket') {
|
|
155
|
+
await this.connection.removeAccountChangeListener(
|
|
156
|
+
this.marketCallbackId as number
|
|
157
|
+
);
|
|
158
|
+
} else {
|
|
159
|
+
this.accountLoader.removeAccount(
|
|
160
|
+
this.marketAddress,
|
|
161
|
+
this.marketCallbackId as string
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.subscribed = false;
|
|
166
|
+
}
|
|
167
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { OpenbookV2Subscriber, PRICE_PRECISION } from '../../lib';
|
|
2
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
3
|
+
|
|
4
|
+
describe('openbook v2 subscriber', function () {
|
|
5
|
+
this.timeout(100_000);
|
|
6
|
+
|
|
7
|
+
it('works', async function () {
|
|
8
|
+
const connection = new Connection(
|
|
9
|
+
process.env.MAINNET_RPC_ENDPOINT as string
|
|
10
|
+
);
|
|
11
|
+
const solUsdc = new PublicKey(
|
|
12
|
+
'AFgkED1FUVfBe2trPUDqSqK9QKd4stJrfzq5q1RwAFTa'
|
|
13
|
+
);
|
|
14
|
+
const openbook = new PublicKey(
|
|
15
|
+
'opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const openbookV2Subscriber = new OpenbookV2Subscriber({
|
|
19
|
+
connection,
|
|
20
|
+
programId: openbook,
|
|
21
|
+
marketAddress: solUsdc,
|
|
22
|
+
accountSubscription: {
|
|
23
|
+
type: 'websocket',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await openbookV2Subscriber.subscribe();
|
|
28
|
+
|
|
29
|
+
// wait for updates
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 5_000));
|
|
31
|
+
|
|
32
|
+
const basePrecision = Math.ceil(
|
|
33
|
+
1 / openbookV2Subscriber.market.baseNativeFactor.toNumber()
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log('Bids');
|
|
37
|
+
for (const bid of openbookV2Subscriber.getL2Bids()) {
|
|
38
|
+
console.log('Price: ', bid.price.toNumber() / PRICE_PRECISION.toNumber());
|
|
39
|
+
console.log('Size: ', bid.size.toNumber() / basePrecision);
|
|
40
|
+
console.log('Source: ', bid.sources);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log('Asks');
|
|
44
|
+
for (const ask of openbookV2Subscriber.getL2Asks()) {
|
|
45
|
+
console.log('Price: ', ask.price.toNumber() / PRICE_PRECISION.toNumber());
|
|
46
|
+
console.log('Size: ', ask.size.toNumber() / basePrecision);
|
|
47
|
+
console.log('Source: ', ask.sources);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const bestBid = await openbookV2Subscriber.getBestBid();
|
|
51
|
+
console.log('Best bid:', bestBid.toNumber());
|
|
52
|
+
|
|
53
|
+
const bestAsk = await openbookV2Subscriber.getBestAsk();
|
|
54
|
+
console.log('Best ask:', bestAsk.toNumber());
|
|
55
|
+
|
|
56
|
+
await openbookV2Subscriber.unsubscribe();
|
|
57
|
+
});
|
|
58
|
+
});
|