@drift-labs/sdk 2.142.0-beta.1 → 2.142.0-beta.11
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/bun.lock +25 -10
- package/lib/browser/accounts/grpcAccountSubscriber.d.ts +2 -1
- package/lib/browser/accounts/grpcAccountSubscriber.js +4 -2
- package/lib/browser/accounts/grpcDriftClientAccountSubscriber.d.ts +1 -1
- package/lib/browser/accounts/grpcDriftClientAccountSubscriber.js +3 -3
- package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.d.ts +24 -0
- package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +251 -0
- package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +34 -0
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +284 -0
- package/lib/browser/constants/spotMarkets.js +4 -4
- package/lib/browser/driftClient.js +11 -10
- package/lib/browser/driftClientConfig.d.ts +3 -0
- package/lib/browser/types.d.ts +3 -1
- package/lib/node/accounts/grpcAccountSubscriber.d.ts +2 -1
- package/lib/node/accounts/grpcAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcAccountSubscriber.js +4 -2
- package/lib/node/accounts/grpcDriftClientAccountSubscriber.d.ts +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriber.js +3 -3
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +25 -0
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -0
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +251 -0
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +35 -0
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -0
- package/lib/node/accounts/grpcMultiAccountSubscriber.js +284 -0
- package/lib/node/constants/spotMarkets.js +4 -4
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +11 -10
- package/lib/node/driftClientConfig.d.ts +3 -0
- package/lib/node/driftClientConfig.d.ts.map +1 -1
- package/lib/node/isomorphic/grpc.d.ts +5 -3
- package/lib/node/isomorphic/grpc.js +1 -3
- package/lib/node/isomorphic/grpc.node.d.ts +5 -3
- package/lib/node/isomorphic/grpc.node.d.ts.map +1 -1
- package/lib/node/isomorphic/grpc.node.js +1 -3
- package/lib/node/types.d.ts +3 -1
- package/lib/node/types.d.ts.map +1 -1
- package/package.json +10 -4
- package/scripts/client-test.ts +214 -0
- package/src/accounts/grpcAccountSubscriber.ts +9 -6
- package/src/accounts/grpcDriftClientAccountSubscriber.ts +1 -1
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +426 -0
- package/src/accounts/grpcMultiAccountSubscriber.ts +343 -0
- package/src/constants/spotMarkets.ts +4 -4
- package/src/driftClient.ts +5 -2
- package/src/driftClientConfig.ts +13 -0
- package/src/isomorphic/grpc.node.ts +11 -7
- package/src/types.ts +4 -2
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.142.0-beta.
|
|
3
|
+
"version": "2.142.0-beta.11",
|
|
4
4
|
"main": "lib/node/index.js",
|
|
5
5
|
"types": "lib/node/index.d.ts",
|
|
6
|
-
"
|
|
6
|
+
"module": "./lib/browser/index.js",
|
|
7
7
|
"author": "crispheaney",
|
|
8
8
|
"homepage": "https://www.drift.trade/",
|
|
9
9
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@coral-xyz/anchor": "0.29.0",
|
|
43
43
|
"@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1",
|
|
44
44
|
"@ellipsis-labs/phoenix-sdk": "1.4.5",
|
|
45
|
-
"@grpc/grpc-js": "1.
|
|
45
|
+
"@grpc/grpc-js": "1.14.0",
|
|
46
46
|
"@openbook-dex/openbook-v2": "0.2.10",
|
|
47
47
|
"@project-serum/serum": "0.13.65",
|
|
48
48
|
"@pythnetwork/client": "2.5.3",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@solana/web3.js": "1.98.0",
|
|
53
53
|
"@switchboard-xyz/common": "3.0.14",
|
|
54
54
|
"@switchboard-xyz/on-demand": "2.4.1",
|
|
55
|
-
"@triton-one/yellowstone-grpc": "1.
|
|
55
|
+
"@triton-one/yellowstone-grpc": "1.4.1",
|
|
56
56
|
"anchor-bankrun": "0.3.0",
|
|
57
57
|
"gill": "^0.10.2",
|
|
58
58
|
"helius-laserstream": "0.1.8",
|
|
@@ -137,5 +137,11 @@
|
|
|
137
137
|
"chalk-template": "<1.1.1",
|
|
138
138
|
"supports-hyperlinks": "<4.1.1",
|
|
139
139
|
"has-ansi": "<6.0.1"
|
|
140
|
+
},
|
|
141
|
+
"browser": {
|
|
142
|
+
"helius-laserstream": false,
|
|
143
|
+
"@triton-one/yellowstone-grpc": false,
|
|
144
|
+
"@grpc/grpc-js": false,
|
|
145
|
+
"zstddec": false
|
|
140
146
|
}
|
|
141
147
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { DriftClient } from '../src/driftClient';
|
|
2
|
+
import { grpcDriftClientAccountSubscriberV2 } from '../src/accounts/grpcDriftClientAccountSubscriberV2';
|
|
3
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { DriftClientConfig } from '../src/driftClientConfig';
|
|
5
|
+
import {
|
|
6
|
+
decodeName,
|
|
7
|
+
DRIFT_PROGRAM_ID,
|
|
8
|
+
PerpMarketAccount,
|
|
9
|
+
Wallet,
|
|
10
|
+
} from '../src';
|
|
11
|
+
import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
|
|
12
|
+
import dotenv from 'dotenv';
|
|
13
|
+
import {
|
|
14
|
+
AnchorProvider,
|
|
15
|
+
Idl,
|
|
16
|
+
Program,
|
|
17
|
+
ProgramAccount,
|
|
18
|
+
} from '@coral-xyz/anchor';
|
|
19
|
+
import driftIDL from '../src/idl/drift.json';
|
|
20
|
+
|
|
21
|
+
const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
|
|
22
|
+
const TOKEN = process.env.TOKEN;
|
|
23
|
+
|
|
24
|
+
async function initializeGrpcDriftClientV2() {
|
|
25
|
+
const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
26
|
+
const wallet = new Wallet(new Keypair());
|
|
27
|
+
dotenv.config({ path: '../' });
|
|
28
|
+
|
|
29
|
+
const programId = new PublicKey(DRIFT_PROGRAM_ID);
|
|
30
|
+
const provider = new AnchorProvider(
|
|
31
|
+
connection,
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
wallet,
|
|
34
|
+
{
|
|
35
|
+
commitment: 'confirmed',
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const program = new Program(driftIDL as Idl, programId, provider);
|
|
40
|
+
|
|
41
|
+
const perpMarketProgramAccounts =
|
|
42
|
+
(await program.account.perpMarket.all()) as ProgramAccount<PerpMarketAccount>[];
|
|
43
|
+
const solPerpMarket = perpMarketProgramAccounts.find(
|
|
44
|
+
(account) => account.account.marketIndex === 0
|
|
45
|
+
);
|
|
46
|
+
const solOracleInfo = {
|
|
47
|
+
publicKey: solPerpMarket.account.amm.oracle,
|
|
48
|
+
source: solPerpMarket.account.amm.oracleSource,
|
|
49
|
+
};
|
|
50
|
+
const ethPerpMarket = perpMarketProgramAccounts.find(
|
|
51
|
+
(account) => account.account.marketIndex === 2
|
|
52
|
+
);
|
|
53
|
+
const ethOracleInfo = {
|
|
54
|
+
publicKey: ethPerpMarket.account.amm.oracle,
|
|
55
|
+
source: ethPerpMarket.account.amm.oracleSource,
|
|
56
|
+
};
|
|
57
|
+
const btcPerpMarket = perpMarketProgramAccounts.find(
|
|
58
|
+
(account) => account.account.marketIndex === 1
|
|
59
|
+
);
|
|
60
|
+
const btcOracleInfo = {
|
|
61
|
+
publicKey: btcPerpMarket.account.amm.oracle,
|
|
62
|
+
source: btcPerpMarket.account.amm.oracleSource,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const config: DriftClientConfig = {
|
|
66
|
+
connection,
|
|
67
|
+
wallet,
|
|
68
|
+
programID: new PublicKey(DRIFT_PROGRAM_ID),
|
|
69
|
+
accountSubscription: {
|
|
70
|
+
type: 'grpc',
|
|
71
|
+
grpcConfigs: {
|
|
72
|
+
endpoint: GRPC_ENDPOINT,
|
|
73
|
+
token: TOKEN,
|
|
74
|
+
commitmentLevel: 'confirmed' as unknown as CommitmentLevel,
|
|
75
|
+
channelOptions: {
|
|
76
|
+
'grpc.keepalive_time_ms': 10_000,
|
|
77
|
+
'grpc.keepalive_timeout_ms': 1_000,
|
|
78
|
+
'grpc.keepalive_permit_without_calls': 1,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
driftClientAccountSubscriber: grpcDriftClientAccountSubscriberV2,
|
|
82
|
+
},
|
|
83
|
+
perpMarketIndexes: [0, 1, 2],
|
|
84
|
+
spotMarketIndexes: [0, 1, 2],
|
|
85
|
+
oracleInfos: [solOracleInfo, ethOracleInfo, btcOracleInfo],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const driftClient = new DriftClient(config);
|
|
89
|
+
|
|
90
|
+
let perpMarketUpdateCount = 0;
|
|
91
|
+
let spotMarketUpdateCount = 0;
|
|
92
|
+
let oraclePriceUpdateCount = 0;
|
|
93
|
+
let userAccountUpdateCount = 0;
|
|
94
|
+
|
|
95
|
+
const updatePromise = new Promise<void>((resolve) => {
|
|
96
|
+
driftClient.accountSubscriber.eventEmitter.on(
|
|
97
|
+
'perpMarketAccountUpdate',
|
|
98
|
+
(data) => {
|
|
99
|
+
console.log(
|
|
100
|
+
'Perp market account update:',
|
|
101
|
+
decodeName(data.name),
|
|
102
|
+
'mmOracleSequenceId:',
|
|
103
|
+
data.amm.mmOracleSequenceId.toString()
|
|
104
|
+
);
|
|
105
|
+
// const perpMarketData = driftClient.getPerpMarketAccount(
|
|
106
|
+
// data.marketIndex
|
|
107
|
+
// );
|
|
108
|
+
// console.log(
|
|
109
|
+
// 'Perp market data market index:',
|
|
110
|
+
// perpMarketData?.marketIndex
|
|
111
|
+
// );
|
|
112
|
+
// const oracle = driftClient.getOracleDataForPerpMarket(data.marketIndex);
|
|
113
|
+
// const mmOracle = driftClient.getMMOracleDataForPerpMarket(
|
|
114
|
+
// data.marketIndex
|
|
115
|
+
// );
|
|
116
|
+
// console.log('Perp oracle price:', oracle.price.toString());
|
|
117
|
+
// console.log('Perp MM oracle price:', mmOracle.price.toString());
|
|
118
|
+
// console.log(
|
|
119
|
+
// 'Perp MM oracle sequence id:',
|
|
120
|
+
// perpMarketData?.amm?.mmOracleSequenceId?.toString()
|
|
121
|
+
// );
|
|
122
|
+
perpMarketUpdateCount++;
|
|
123
|
+
if (
|
|
124
|
+
perpMarketUpdateCount >= 10 &&
|
|
125
|
+
spotMarketUpdateCount >= 10 &&
|
|
126
|
+
oraclePriceUpdateCount >= 10 &&
|
|
127
|
+
userAccountUpdateCount >= 2
|
|
128
|
+
) {
|
|
129
|
+
resolve();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
driftClient.accountSubscriber.eventEmitter.on(
|
|
135
|
+
'spotMarketAccountUpdate',
|
|
136
|
+
(data) => {
|
|
137
|
+
console.log('Spot market account update:', decodeName(data.name));
|
|
138
|
+
const spotMarketData = driftClient.getSpotMarketAccount(
|
|
139
|
+
data.marketIndex
|
|
140
|
+
);
|
|
141
|
+
console.log(
|
|
142
|
+
'Spot market data market index:',
|
|
143
|
+
spotMarketData?.marketIndex
|
|
144
|
+
);
|
|
145
|
+
const oracle = driftClient.getOracleDataForSpotMarket(data.marketIndex);
|
|
146
|
+
console.log('Spot oracle price:', oracle.price.toString());
|
|
147
|
+
spotMarketUpdateCount++;
|
|
148
|
+
if (
|
|
149
|
+
perpMarketUpdateCount >= 10 &&
|
|
150
|
+
spotMarketUpdateCount >= 10 &&
|
|
151
|
+
oraclePriceUpdateCount >= 10 &&
|
|
152
|
+
userAccountUpdateCount >= 2
|
|
153
|
+
) {
|
|
154
|
+
resolve();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
driftClient.accountSubscriber.eventEmitter.on(
|
|
160
|
+
'oraclePriceUpdate',
|
|
161
|
+
(data) => {
|
|
162
|
+
console.log('Oracle price update:', data.toBase58());
|
|
163
|
+
oraclePriceUpdateCount++;
|
|
164
|
+
if (
|
|
165
|
+
perpMarketUpdateCount >= 10 &&
|
|
166
|
+
spotMarketUpdateCount >= 10 &&
|
|
167
|
+
oraclePriceUpdateCount >= 10 &&
|
|
168
|
+
userAccountUpdateCount >= 2
|
|
169
|
+
) {
|
|
170
|
+
resolve();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
driftClient.accountSubscriber.eventEmitter.on(
|
|
176
|
+
'userAccountUpdate',
|
|
177
|
+
(data) => {
|
|
178
|
+
console.log('User account update:', decodeName(data.name));
|
|
179
|
+
userAccountUpdateCount++;
|
|
180
|
+
if (
|
|
181
|
+
perpMarketUpdateCount >= 10 &&
|
|
182
|
+
spotMarketUpdateCount >= 10 &&
|
|
183
|
+
oraclePriceUpdateCount >= 10 &&
|
|
184
|
+
userAccountUpdateCount >= 2
|
|
185
|
+
) {
|
|
186
|
+
resolve();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
await driftClient.subscribe();
|
|
193
|
+
console.log('DriftClient initialized and listening for updates.');
|
|
194
|
+
|
|
195
|
+
for (const marketIndex of config.perpMarketIndexes) {
|
|
196
|
+
const oracle = driftClient.getOracleDataForPerpMarket(marketIndex);
|
|
197
|
+
const mmOracle = driftClient.getMMOracleDataForPerpMarket(marketIndex);
|
|
198
|
+
console.log('Initial perp oracle price:', oracle.price.toString());
|
|
199
|
+
console.log('Initial perp MM oracle price:', mmOracle.price.toString());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (const marketIndex of config.spotMarketIndexes) {
|
|
203
|
+
const oracle = driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
204
|
+
console.log('Initial spot oracle price:', oracle.price.toString());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const stateAccount = driftClient.getStateAccount();
|
|
208
|
+
console.log('Initial state account:', stateAccount.toString());
|
|
209
|
+
|
|
210
|
+
await updatePromise;
|
|
211
|
+
console.log('Received required number of updates.');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
initializeGrpcDriftClientV2().catch(console.error);
|
|
@@ -39,13 +39,16 @@ export class grpcAccountSubscriber<T> extends WebSocketAccountSubscriber<T> {
|
|
|
39
39
|
program: Program,
|
|
40
40
|
accountPublicKey: PublicKey,
|
|
41
41
|
decodeBuffer?: (buffer: Buffer) => U,
|
|
42
|
-
resubOpts?: ResubOpts
|
|
42
|
+
resubOpts?: ResubOpts,
|
|
43
|
+
clientProp?: Client
|
|
43
44
|
): Promise<grpcAccountSubscriber<U>> {
|
|
44
|
-
const client =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const client = clientProp
|
|
46
|
+
? clientProp
|
|
47
|
+
: await createClient(
|
|
48
|
+
grpcConfigs.endpoint,
|
|
49
|
+
grpcConfigs.token,
|
|
50
|
+
grpcConfigs.channelOptions ?? {}
|
|
51
|
+
);
|
|
49
52
|
const commitmentLevel =
|
|
50
53
|
// @ts-ignore :: isomorphic exported enum fails typescript but will work at runtime
|
|
51
54
|
grpcConfigs.commitmentLevel ?? CommitmentLevel.CONFIRMED;
|
|
@@ -12,7 +12,7 @@ import { grpcAccountSubscriber } from './grpcAccountSubscriber';
|
|
|
12
12
|
import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
|
|
13
13
|
import { getOracleId } from '../oracles/oracleId';
|
|
14
14
|
|
|
15
|
-
export class
|
|
15
|
+
export class grpcDriftClientAccountSubscriber extends WebSocketDriftClientAccountSubscriber {
|
|
16
16
|
private grpcConfigs: GrpcConfigs;
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber';
|
|
2
|
+
import { OracleInfo, OraclePriceData } from '../oracles/types';
|
|
3
|
+
import { Program } from '@coral-xyz/anchor';
|
|
4
|
+
import { PublicKey } from '@solana/web3.js';
|
|
5
|
+
import { findAllMarketAndOracles } from '../config';
|
|
6
|
+
import {
|
|
7
|
+
getDriftStateAccountPublicKey,
|
|
8
|
+
getPerpMarketPublicKey,
|
|
9
|
+
getSpotMarketPublicKey,
|
|
10
|
+
} from '../addresses/pda';
|
|
11
|
+
import {
|
|
12
|
+
DataAndSlot,
|
|
13
|
+
DelistedMarketSetting,
|
|
14
|
+
GrpcConfigs,
|
|
15
|
+
ResubOpts,
|
|
16
|
+
} from './types';
|
|
17
|
+
import { grpcAccountSubscriber } from './grpcAccountSubscriber';
|
|
18
|
+
import { grpcMultiAccountSubscriber } from './grpcMultiAccountSubscriber';
|
|
19
|
+
import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
|
|
20
|
+
import {
|
|
21
|
+
getOracleId,
|
|
22
|
+
getPublicKeyAndSourceFromOracleId,
|
|
23
|
+
} from '../oracles/oracleId';
|
|
24
|
+
|
|
25
|
+
export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAccountSubscriber {
|
|
26
|
+
private grpcConfigs: GrpcConfigs;
|
|
27
|
+
private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
|
|
28
|
+
private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
|
|
29
|
+
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<OraclePriceData>;
|
|
30
|
+
private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
31
|
+
private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
grpcConfigs: GrpcConfigs,
|
|
35
|
+
program: Program,
|
|
36
|
+
perpMarketIndexes: number[],
|
|
37
|
+
spotMarketIndexes: number[],
|
|
38
|
+
oracleInfos: OracleInfo[],
|
|
39
|
+
shouldFindAllMarketsAndOracles: boolean,
|
|
40
|
+
delistedMarketSetting: DelistedMarketSetting,
|
|
41
|
+
resubOpts?: ResubOpts
|
|
42
|
+
) {
|
|
43
|
+
super(
|
|
44
|
+
program,
|
|
45
|
+
perpMarketIndexes,
|
|
46
|
+
spotMarketIndexes,
|
|
47
|
+
oracleInfos,
|
|
48
|
+
shouldFindAllMarketsAndOracles,
|
|
49
|
+
delistedMarketSetting,
|
|
50
|
+
resubOpts
|
|
51
|
+
);
|
|
52
|
+
this.grpcConfigs = grpcConfigs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async subscribe(): Promise<boolean> {
|
|
56
|
+
if (this.isSubscribed) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.isSubscribing) {
|
|
61
|
+
return await this.subscriptionPromise;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.isSubscribing = true;
|
|
65
|
+
|
|
66
|
+
this.subscriptionPromise = new Promise((res) => {
|
|
67
|
+
this.subscriptionPromiseResolver = res;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (this.shouldFindAllMarketsAndOracles) {
|
|
71
|
+
const {
|
|
72
|
+
perpMarketIndexes,
|
|
73
|
+
perpMarketAccounts,
|
|
74
|
+
spotMarketIndexes,
|
|
75
|
+
spotMarketAccounts,
|
|
76
|
+
oracleInfos,
|
|
77
|
+
} = await findAllMarketAndOracles(this.program);
|
|
78
|
+
this.perpMarketIndexes = perpMarketIndexes;
|
|
79
|
+
this.spotMarketIndexes = spotMarketIndexes;
|
|
80
|
+
this.oracleInfos = oracleInfos;
|
|
81
|
+
// front run and set the initial data here to save extra gma call in set initial data
|
|
82
|
+
this.initialPerpMarketAccountData = new Map(
|
|
83
|
+
perpMarketAccounts.map((market) => [market.marketIndex, market])
|
|
84
|
+
);
|
|
85
|
+
this.initialSpotMarketAccountData = new Map(
|
|
86
|
+
spotMarketAccounts.map((market) => [market.marketIndex, market])
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const statePublicKey = await getDriftStateAccountPublicKey(
|
|
91
|
+
this.program.programId
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// create and activate main state account subscription
|
|
95
|
+
this.stateAccountSubscriber =
|
|
96
|
+
await grpcAccountSubscriber.create<StateAccount>(
|
|
97
|
+
this.grpcConfigs,
|
|
98
|
+
'state',
|
|
99
|
+
this.program,
|
|
100
|
+
statePublicKey,
|
|
101
|
+
undefined,
|
|
102
|
+
undefined
|
|
103
|
+
);
|
|
104
|
+
await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
|
|
105
|
+
this.eventEmitter.emit('stateAccountUpdate', data);
|
|
106
|
+
this.eventEmitter.emit('update');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// set initial data to avoid spamming getAccountInfo calls in webSocketAccountSubscriber
|
|
110
|
+
await this.setInitialData();
|
|
111
|
+
|
|
112
|
+
// subscribe to perp + spot markets (separate) and oracles
|
|
113
|
+
await Promise.all([
|
|
114
|
+
this.subscribeToPerpMarketAccounts(),
|
|
115
|
+
this.subscribeToSpotMarketAccounts(),
|
|
116
|
+
this.subscribeToOracles(),
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
this.eventEmitter.emit('update');
|
|
120
|
+
|
|
121
|
+
await this.handleDelistedMarkets();
|
|
122
|
+
|
|
123
|
+
await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
|
|
124
|
+
|
|
125
|
+
this.subscriptionPromiseResolver(true);
|
|
126
|
+
|
|
127
|
+
this.isSubscribing = false;
|
|
128
|
+
this.isSubscribed = true;
|
|
129
|
+
|
|
130
|
+
// delete initial data
|
|
131
|
+
this.removeInitialData();
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
override getMarketAccountAndSlot(
|
|
137
|
+
marketIndex: number
|
|
138
|
+
): DataAndSlot<PerpMarketAccount> | undefined {
|
|
139
|
+
return this.perpMarketsSubscriber?.getAccountData(
|
|
140
|
+
this.perpMarketIndexToAccountPubkeyMap.get(marketIndex)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
override getSpotMarketAccountAndSlot(
|
|
145
|
+
marketIndex: number
|
|
146
|
+
): DataAndSlot<SpotMarketAccount> | undefined {
|
|
147
|
+
return this.spotMarketsSubscriber?.getAccountData(
|
|
148
|
+
this.spotMarketIndexToAccountPubkeyMap.get(marketIndex)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
override async setPerpOracleMap() {
|
|
153
|
+
const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
154
|
+
const perpMarkets = Array.from(perpMarketsMap.values());
|
|
155
|
+
const addOraclePromises = [];
|
|
156
|
+
for (const perpMarket of perpMarkets) {
|
|
157
|
+
if (!perpMarket || !perpMarket.data) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const perpMarketAccount = perpMarket.data;
|
|
161
|
+
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
162
|
+
const oracle = perpMarketAccount.amm.oracle;
|
|
163
|
+
const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
|
|
164
|
+
if (!this.oracleSubscribers.has(oracleId)) {
|
|
165
|
+
addOraclePromises.push(
|
|
166
|
+
this.addOracle({
|
|
167
|
+
publicKey: oracle,
|
|
168
|
+
source: perpMarket.data.amm.oracleSource,
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
this.perpOracleMap.set(perpMarketIndex, oracle);
|
|
173
|
+
this.perpOracleStringMap.set(perpMarketIndex, oracleId);
|
|
174
|
+
}
|
|
175
|
+
await Promise.all(addOraclePromises);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
override async setSpotOracleMap() {
|
|
179
|
+
const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
180
|
+
const spotMarkets = Array.from(spotMarketsMap.values());
|
|
181
|
+
const addOraclePromises = [];
|
|
182
|
+
for (const spotMarket of spotMarkets) {
|
|
183
|
+
if (!spotMarket || !spotMarket.data) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const spotMarketAccount = spotMarket.data;
|
|
187
|
+
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
188
|
+
const oracle = spotMarketAccount.oracle;
|
|
189
|
+
const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
|
|
190
|
+
if (!this.oracleSubscribers.has(oracleId)) {
|
|
191
|
+
addOraclePromises.push(
|
|
192
|
+
this.addOracle({
|
|
193
|
+
publicKey: oracle,
|
|
194
|
+
source: spotMarketAccount.oracleSource,
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
this.spotOracleMap.set(spotMarketIndex, oracle);
|
|
199
|
+
this.spotOracleStringMap.set(spotMarketIndex, oracleId);
|
|
200
|
+
}
|
|
201
|
+
await Promise.all(addOraclePromises);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
override async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
205
|
+
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
206
|
+
await Promise.all(
|
|
207
|
+
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
208
|
+
marketIndex,
|
|
209
|
+
await getPerpMarketPublicKey(this.program.programId, marketIndex),
|
|
210
|
+
])
|
|
211
|
+
);
|
|
212
|
+
for (const [
|
|
213
|
+
marketIndex,
|
|
214
|
+
accountPubkey,
|
|
215
|
+
] of perpMarketIndexToAccountPubkeys) {
|
|
216
|
+
this.perpMarketIndexToAccountPubkeyMap.set(
|
|
217
|
+
marketIndex,
|
|
218
|
+
accountPubkey.toBase58()
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const perpMarketPubkeys = perpMarketIndexToAccountPubkeys.map(
|
|
223
|
+
([_, accountPubkey]) => accountPubkey
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
this.perpMarketsSubscriber =
|
|
227
|
+
await grpcMultiAccountSubscriber.create<PerpMarketAccount>(
|
|
228
|
+
this.grpcConfigs,
|
|
229
|
+
'perpMarket',
|
|
230
|
+
this.program,
|
|
231
|
+
undefined,
|
|
232
|
+
this.resubOpts,
|
|
233
|
+
undefined,
|
|
234
|
+
async () => {
|
|
235
|
+
try {
|
|
236
|
+
if (this.resubOpts?.logResubMessages) {
|
|
237
|
+
console.log(
|
|
238
|
+
'[grpcDriftClientAccountSubscriberV2] perp markets subscriber unsubscribed; resubscribing'
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
await this.subscribeToPerpMarketAccounts();
|
|
242
|
+
} catch (e) {
|
|
243
|
+
console.error('Perp markets resubscribe failed:', e);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
for (const data of this.initialPerpMarketAccountData.values()) {
|
|
249
|
+
this.perpMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
await this.perpMarketsSubscriber.subscribe(
|
|
253
|
+
perpMarketPubkeys,
|
|
254
|
+
(_accountId, data) => {
|
|
255
|
+
this.eventEmitter.emit(
|
|
256
|
+
'perpMarketAccountUpdate',
|
|
257
|
+
data as PerpMarketAccount
|
|
258
|
+
);
|
|
259
|
+
this.eventEmitter.emit('update');
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
override async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
267
|
+
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
268
|
+
await Promise.all(
|
|
269
|
+
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
270
|
+
marketIndex,
|
|
271
|
+
await getSpotMarketPublicKey(this.program.programId, marketIndex),
|
|
272
|
+
])
|
|
273
|
+
);
|
|
274
|
+
for (const [
|
|
275
|
+
marketIndex,
|
|
276
|
+
accountPubkey,
|
|
277
|
+
] of spotMarketIndexToAccountPubkeys) {
|
|
278
|
+
this.spotMarketIndexToAccountPubkeyMap.set(
|
|
279
|
+
marketIndex,
|
|
280
|
+
accountPubkey.toBase58()
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const spotMarketPubkeys = spotMarketIndexToAccountPubkeys.map(
|
|
285
|
+
([_, accountPubkey]) => accountPubkey
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
this.spotMarketsSubscriber =
|
|
289
|
+
await grpcMultiAccountSubscriber.create<SpotMarketAccount>(
|
|
290
|
+
this.grpcConfigs,
|
|
291
|
+
'spotMarket',
|
|
292
|
+
this.program,
|
|
293
|
+
undefined,
|
|
294
|
+
this.resubOpts,
|
|
295
|
+
undefined,
|
|
296
|
+
async () => {
|
|
297
|
+
try {
|
|
298
|
+
if (this.resubOpts?.logResubMessages) {
|
|
299
|
+
console.log(
|
|
300
|
+
'[grpcDriftClientAccountSubscriberV2] spot markets subscriber unsubscribed; resubscribing'
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
await this.subscribeToSpotMarketAccounts();
|
|
304
|
+
} catch (e) {
|
|
305
|
+
console.error('Spot markets resubscribe failed:', e);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
for (const data of this.initialSpotMarketAccountData.values()) {
|
|
311
|
+
this.spotMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
await this.spotMarketsSubscriber.subscribe(
|
|
315
|
+
spotMarketPubkeys,
|
|
316
|
+
(_accountId, data) => {
|
|
317
|
+
this.eventEmitter.emit(
|
|
318
|
+
'spotMarketAccountUpdate',
|
|
319
|
+
data as SpotMarketAccount
|
|
320
|
+
);
|
|
321
|
+
this.eventEmitter.emit('update');
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
override async subscribeToOracles(): Promise<boolean> {
|
|
329
|
+
const pubkeyToSources = new Map<string, Set<OracleInfo['source']>>();
|
|
330
|
+
for (const info of this.oracleInfos) {
|
|
331
|
+
if (info.publicKey.equals((PublicKey as any).default)) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const key = info.publicKey.toBase58();
|
|
335
|
+
let sources = pubkeyToSources.get(key);
|
|
336
|
+
if (!sources) {
|
|
337
|
+
sources = new Set<OracleInfo['source']>();
|
|
338
|
+
pubkeyToSources.set(key, sources);
|
|
339
|
+
}
|
|
340
|
+
sources.add(info.source);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const oraclePubkeys = Array.from(pubkeyToSources.keys()).map(
|
|
344
|
+
(k) => new PublicKey(k)
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
this.oracleMultiSubscriber =
|
|
348
|
+
await grpcMultiAccountSubscriber.create<OraclePriceData>(
|
|
349
|
+
this.grpcConfigs,
|
|
350
|
+
'oracle',
|
|
351
|
+
this.program,
|
|
352
|
+
(buffer: Buffer, pubkey?: string) => {
|
|
353
|
+
if (!pubkey) {
|
|
354
|
+
throw new Error('Oracle pubkey missing in decode');
|
|
355
|
+
}
|
|
356
|
+
const sources = pubkeyToSources.get(pubkey);
|
|
357
|
+
if (!sources || sources.size === 0) {
|
|
358
|
+
throw new Error('Oracle sources missing for pubkey in decode');
|
|
359
|
+
}
|
|
360
|
+
const primarySource = sources.values().next().value;
|
|
361
|
+
const client = this.oracleClientCache.get(
|
|
362
|
+
primarySource,
|
|
363
|
+
this.program.provider.connection,
|
|
364
|
+
this.program
|
|
365
|
+
);
|
|
366
|
+
return client.getOraclePriceDataFromBuffer(buffer);
|
|
367
|
+
},
|
|
368
|
+
this.resubOpts,
|
|
369
|
+
undefined,
|
|
370
|
+
async () => {
|
|
371
|
+
try {
|
|
372
|
+
if (this.resubOpts?.logResubMessages) {
|
|
373
|
+
console.log(
|
|
374
|
+
'[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
await this.subscribeToOracles();
|
|
378
|
+
} catch (e) {
|
|
379
|
+
console.error('Oracle resubscribe failed:', e);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
for (const data of this.initialOraclePriceData.entries()) {
|
|
385
|
+
const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
|
|
386
|
+
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
await this.oracleMultiSubscriber.subscribe(
|
|
390
|
+
oraclePubkeys,
|
|
391
|
+
(accountId, data) => {
|
|
392
|
+
const sources = pubkeyToSources.get(accountId.toBase58());
|
|
393
|
+
if (sources) {
|
|
394
|
+
for (const source of sources.values()) {
|
|
395
|
+
this.eventEmitter.emit(
|
|
396
|
+
'oraclePriceUpdate',
|
|
397
|
+
accountId,
|
|
398
|
+
source,
|
|
399
|
+
data
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
this.eventEmitter.emit('update');
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async unsubscribeFromOracles(): Promise<void> {
|
|
411
|
+
if (this.oracleMultiSubscriber) {
|
|
412
|
+
await this.oracleMultiSubscriber.unsubscribe();
|
|
413
|
+
this.oracleMultiSubscriber = undefined;
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
await super.unsubscribeFromOracles();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
override async unsubscribe(): Promise<void> {
|
|
420
|
+
if (this.isSubscribed) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
await this.stateAccountSubscriber.unsubscribe();
|
|
425
|
+
}
|
|
426
|
+
}
|