@drift-labs/sdk 2.142.0-beta.2 → 2.142.0-beta.21
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/browser/accounts/grpcDriftClientAccountSubscriberV2.d.ts +52 -4
- package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +315 -38
- package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +11 -4
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +124 -18
- package/lib/browser/adminClient.d.ts +2 -0
- package/lib/browser/adminClient.js +17 -0
- package/lib/browser/constants/spotMarkets.js +4 -4
- package/lib/browser/driftClient.d.ts +25 -2
- package/lib/browser/driftClient.js +27 -4
- package/lib/browser/events/types.d.ts +3 -3
- package/lib/browser/idl/drift.json +72 -0
- package/lib/browser/types.d.ts +22 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +52 -4
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +315 -38
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +11 -4
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcMultiAccountSubscriber.js +124 -18
- package/lib/node/adminClient.d.ts +2 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +17 -0
- package/lib/node/constants/spotMarkets.js +4 -4
- package/lib/node/driftClient.d.ts +25 -2
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +27 -4
- package/lib/node/events/types.d.ts +3 -3
- package/lib/node/events/types.d.ts.map +1 -1
- package/lib/node/idl/drift.json +72 -0
- package/lib/node/types.d.ts +22 -1
- package/lib/node/types.d.ts.map +1 -1
- package/package.json +8 -2
- package/scripts/client-test.ts +361 -75
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +517 -77
- package/src/accounts/grpcMultiAccountSubscriber.ts +179 -32
- package/src/adminClient.ts +34 -0
- package/src/constants/spotMarkets.ts +4 -4
- package/src/driftClient.ts +32 -4
- package/src/events/types.ts +4 -2
- package/src/idl/drift.json +72 -0
- package/src/types.ts +25 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
2
3
|
import { OracleInfo, OraclePriceData } from '../oracles/types';
|
|
3
4
|
import { Program } from '@coral-xyz/anchor';
|
|
4
5
|
import { PublicKey } from '@solana/web3.js';
|
|
@@ -6,19 +7,72 @@ import { findAllMarketAndOracles } from '../config';
|
|
|
6
7
|
import {
|
|
7
8
|
getDriftStateAccountPublicKey,
|
|
8
9
|
getPerpMarketPublicKey,
|
|
10
|
+
getPerpMarketPublicKeySync,
|
|
9
11
|
getSpotMarketPublicKey,
|
|
12
|
+
getSpotMarketPublicKeySync,
|
|
10
13
|
} from '../addresses/pda';
|
|
11
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
AccountSubscriber,
|
|
16
|
+
DataAndSlot,
|
|
17
|
+
DelistedMarketSetting,
|
|
18
|
+
DriftClientAccountEvents,
|
|
19
|
+
DriftClientAccountSubscriber,
|
|
20
|
+
NotSubscribedError,
|
|
21
|
+
GrpcConfigs,
|
|
22
|
+
ResubOpts,
|
|
23
|
+
} from './types';
|
|
12
24
|
import { grpcAccountSubscriber } from './grpcAccountSubscriber';
|
|
13
25
|
import { grpcMultiAccountSubscriber } from './grpcMultiAccountSubscriber';
|
|
14
26
|
import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
import {
|
|
28
|
+
getOracleId,
|
|
29
|
+
getPublicKeyAndSourceFromOracleId,
|
|
30
|
+
} from '../oracles/oracleId';
|
|
31
|
+
import { OracleClientCache } from '../oracles/oracleClientCache';
|
|
32
|
+
import { findDelistedPerpMarketsAndOracles } from './utils';
|
|
33
|
+
|
|
34
|
+
export class grpcDriftClientAccountSubscriberV2
|
|
35
|
+
implements DriftClientAccountSubscriber
|
|
36
|
+
{
|
|
18
37
|
private grpcConfigs: GrpcConfigs;
|
|
19
38
|
private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
|
|
20
39
|
private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
|
|
21
|
-
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
|
|
40
|
+
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
|
|
41
|
+
OraclePriceData,
|
|
42
|
+
OracleInfo
|
|
43
|
+
>;
|
|
44
|
+
private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
45
|
+
private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
46
|
+
private delistedMarketSetting: DelistedMarketSetting;
|
|
47
|
+
|
|
48
|
+
public eventEmitter: StrictEventEmitter<
|
|
49
|
+
EventEmitter,
|
|
50
|
+
DriftClientAccountEvents
|
|
51
|
+
>;
|
|
52
|
+
public isSubscribed: boolean;
|
|
53
|
+
public isSubscribing: boolean;
|
|
54
|
+
public program: Program;
|
|
55
|
+
public perpMarketIndexes: number[];
|
|
56
|
+
public spotMarketIndexes: number[];
|
|
57
|
+
public shouldFindAllMarketsAndOracles: boolean;
|
|
58
|
+
public oracleInfos: OracleInfo[];
|
|
59
|
+
public initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
|
|
60
|
+
public initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
|
|
61
|
+
public initialOraclePriceData: Map<string, OraclePriceData>;
|
|
62
|
+
public perpOracleMap = new Map<number, PublicKey>();
|
|
63
|
+
public perpOracleStringMap = new Map<number, string>();
|
|
64
|
+
public spotOracleMap = new Map<number, PublicKey>();
|
|
65
|
+
public spotOracleStringMap = new Map<number, string>();
|
|
66
|
+
private oracleIdToOracleDataMap = new Map<
|
|
67
|
+
string,
|
|
68
|
+
DataAndSlot<OraclePriceData>
|
|
69
|
+
>();
|
|
70
|
+
public stateAccountSubscriber?: AccountSubscriber<StateAccount>;
|
|
71
|
+
oracleClientCache = new OracleClientCache();
|
|
72
|
+
private resubOpts?: ResubOpts;
|
|
73
|
+
|
|
74
|
+
private subscriptionPromise: Promise<boolean>;
|
|
75
|
+
protected subscriptionPromiseResolver: (val: boolean) => void;
|
|
22
76
|
|
|
23
77
|
constructor(
|
|
24
78
|
grpcConfigs: GrpcConfigs,
|
|
@@ -30,16 +84,156 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
30
84
|
delistedMarketSetting: DelistedMarketSetting,
|
|
31
85
|
resubOpts?: ResubOpts
|
|
32
86
|
) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
87
|
+
this.eventEmitter = new EventEmitter();
|
|
88
|
+
this.isSubscribed = false;
|
|
89
|
+
this.isSubscribing = false;
|
|
90
|
+
this.program = program;
|
|
91
|
+
this.perpMarketIndexes = perpMarketIndexes;
|
|
92
|
+
this.spotMarketIndexes = spotMarketIndexes;
|
|
93
|
+
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
94
|
+
this.oracleInfos = oracleInfos;
|
|
95
|
+
this.initialPerpMarketAccountData = new Map();
|
|
96
|
+
this.initialSpotMarketAccountData = new Map();
|
|
97
|
+
this.initialOraclePriceData = new Map();
|
|
98
|
+
this.perpOracleMap = new Map();
|
|
99
|
+
this.perpOracleStringMap = new Map();
|
|
100
|
+
this.spotOracleMap = new Map();
|
|
101
|
+
this.spotOracleStringMap = new Map();
|
|
42
102
|
this.grpcConfigs = grpcConfigs;
|
|
103
|
+
this.resubOpts = resubOpts;
|
|
104
|
+
this.delistedMarketSetting = delistedMarketSetting;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
chunks = <T>(array: readonly T[], size: number): T[][] => {
|
|
108
|
+
return new Array(Math.ceil(array.length / size))
|
|
109
|
+
.fill(null)
|
|
110
|
+
.map((_, index) => index * size)
|
|
111
|
+
.map((begin) => array.slice(begin, begin + size));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
async setInitialData(): Promise<void> {
|
|
115
|
+
const connection = this.program.provider.connection;
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
!this.initialPerpMarketAccountData ||
|
|
119
|
+
this.initialPerpMarketAccountData.size === 0
|
|
120
|
+
) {
|
|
121
|
+
const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) =>
|
|
122
|
+
getPerpMarketPublicKeySync(this.program.programId, marketIndex)
|
|
123
|
+
);
|
|
124
|
+
const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
|
|
125
|
+
const perpMarketAccountInfos = (
|
|
126
|
+
await Promise.all(
|
|
127
|
+
perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) =>
|
|
128
|
+
connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
).flat();
|
|
132
|
+
this.initialPerpMarketAccountData = new Map(
|
|
133
|
+
perpMarketAccountInfos
|
|
134
|
+
.filter((accountInfo) => !!accountInfo)
|
|
135
|
+
.map((accountInfo) => {
|
|
136
|
+
const perpMarket = this.program.coder.accounts.decode(
|
|
137
|
+
'PerpMarket',
|
|
138
|
+
accountInfo.data
|
|
139
|
+
);
|
|
140
|
+
return [perpMarket.marketIndex, perpMarket];
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
!this.initialSpotMarketAccountData ||
|
|
147
|
+
this.initialSpotMarketAccountData.size === 0
|
|
148
|
+
) {
|
|
149
|
+
const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) =>
|
|
150
|
+
getSpotMarketPublicKeySync(this.program.programId, marketIndex)
|
|
151
|
+
);
|
|
152
|
+
const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
|
|
153
|
+
const spotMarketAccountInfos = (
|
|
154
|
+
await Promise.all(
|
|
155
|
+
spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) =>
|
|
156
|
+
connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
).flat();
|
|
160
|
+
this.initialSpotMarketAccountData = new Map(
|
|
161
|
+
spotMarketAccountInfos
|
|
162
|
+
.filter((accountInfo) => !!accountInfo)
|
|
163
|
+
.map((accountInfo) => {
|
|
164
|
+
const spotMarket = this.program.coder.accounts.decode(
|
|
165
|
+
'SpotMarket',
|
|
166
|
+
accountInfo.data
|
|
167
|
+
);
|
|
168
|
+
return [spotMarket.marketIndex, spotMarket];
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const oracleAccountPubkeyChunks = this.chunks(
|
|
174
|
+
this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey),
|
|
175
|
+
75
|
|
176
|
+
);
|
|
177
|
+
const oracleAccountInfos = (
|
|
178
|
+
await Promise.all(
|
|
179
|
+
oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) =>
|
|
180
|
+
connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
).flat();
|
|
184
|
+
this.initialOraclePriceData = new Map(
|
|
185
|
+
this.oracleInfos.reduce((result, oracleInfo, i) => {
|
|
186
|
+
if (!oracleAccountInfos[i]) {
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
const oracleClient = this.oracleClientCache.get(
|
|
190
|
+
oracleInfo.source,
|
|
191
|
+
connection,
|
|
192
|
+
this.program
|
|
193
|
+
);
|
|
194
|
+
const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(
|
|
195
|
+
oracleAccountInfos[i].data
|
|
196
|
+
);
|
|
197
|
+
result.push([
|
|
198
|
+
getOracleId(oracleInfo.publicKey, oracleInfo.source),
|
|
199
|
+
oraclePriceData,
|
|
200
|
+
]);
|
|
201
|
+
return result;
|
|
202
|
+
}, [])
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async addPerpMarket(_marketIndex: number): Promise<boolean> {
|
|
207
|
+
if (!this.perpMarketIndexes.includes(_marketIndex)) {
|
|
208
|
+
this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async addSpotMarket(_marketIndex: number): Promise<boolean> {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
|
|
218
|
+
if (this.resubOpts?.logResubMessages) {
|
|
219
|
+
console.log('[grpcDriftClientAccountSubscriberV2] addOracle');
|
|
220
|
+
}
|
|
221
|
+
if (oracleInfo.publicKey.equals(PublicKey.default)) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const exists = this.oracleInfos.some(
|
|
226
|
+
(o) =>
|
|
227
|
+
o.source === oracleInfo.source &&
|
|
228
|
+
o.publicKey.equals(oracleInfo.publicKey)
|
|
229
|
+
);
|
|
230
|
+
if (!exists) {
|
|
231
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.oracleMultiSubscriber?.addAccounts([oracleInfo.publicKey]);
|
|
235
|
+
|
|
236
|
+
return true;
|
|
43
237
|
}
|
|
44
238
|
|
|
45
239
|
public async subscribe(): Promise<boolean> {
|
|
@@ -123,11 +317,174 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
123
317
|
return true;
|
|
124
318
|
}
|
|
125
319
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
320
|
+
public async fetch(): Promise<void> {
|
|
321
|
+
await this.stateAccountSubscriber?.fetch();
|
|
322
|
+
await this.perpMarketsSubscriber?.fetch();
|
|
323
|
+
await this.spotMarketsSubscriber?.fetch();
|
|
324
|
+
await this.oracleMultiSubscriber?.fetch();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private assertIsSubscribed(): void {
|
|
328
|
+
if (!this.isSubscribed) {
|
|
329
|
+
throw new NotSubscribedError(
|
|
330
|
+
'You must call `subscribe` before using this function'
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
public getStateAccountAndSlot(): DataAndSlot<StateAccount> {
|
|
336
|
+
this.assertIsSubscribed();
|
|
337
|
+
return this.stateAccountSubscriber.dataAndSlot;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
public getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[] {
|
|
341
|
+
const map = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
342
|
+
return Array.from(map?.values() ?? []);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
public getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[] {
|
|
346
|
+
const map = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
347
|
+
return Array.from(map?.values() ?? []);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getMarketAccountAndSlot(
|
|
351
|
+
marketIndex: number
|
|
352
|
+
): DataAndSlot<PerpMarketAccount> | undefined {
|
|
353
|
+
return this.perpMarketsSubscriber?.getAccountData(
|
|
354
|
+
this.perpMarketIndexToAccountPubkeyMap.get(marketIndex)
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
getSpotMarketAccountAndSlot(
|
|
359
|
+
marketIndex: number
|
|
360
|
+
): DataAndSlot<SpotMarketAccount> | undefined {
|
|
361
|
+
return this.spotMarketsSubscriber?.getAccountData(
|
|
362
|
+
this.spotMarketIndexToAccountPubkeyMap.get(marketIndex)
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
public getOraclePriceDataAndSlot(
|
|
367
|
+
oracleId: string
|
|
368
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
369
|
+
this.assertIsSubscribed();
|
|
370
|
+
// we need to rely on a map we store in this class because the grpcMultiAccountSubscriber does not track a mapping or oracle ID.
|
|
371
|
+
// DO NOT call getAccountData on the oracleMultiSubscriber, it will not return the correct data in certain cases(BONK spot and perp market subscribed too at once).
|
|
372
|
+
return this.oracleIdToOracleDataMap.get(oracleId);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
public getOraclePriceDataAndSlotForPerpMarket(
|
|
376
|
+
marketIndex: number
|
|
377
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
378
|
+
const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
|
|
379
|
+
const oracle = this.perpOracleMap.get(marketIndex);
|
|
380
|
+
const oracleId = this.perpOracleStringMap.get(marketIndex);
|
|
381
|
+
if (!perpMarketAccount || !oracleId) {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
|
|
386
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
387
|
+
this.setPerpOracleMap();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
public getOraclePriceDataAndSlotForSpotMarket(
|
|
394
|
+
marketIndex: number
|
|
395
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
396
|
+
const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
|
|
397
|
+
const oracle = this.spotOracleMap.get(marketIndex);
|
|
398
|
+
const oracleId = this.spotOracleStringMap.get(marketIndex);
|
|
399
|
+
if (!spotMarketAccount || !oracleId) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!spotMarketAccount.data.oracle.equals(oracle)) {
|
|
404
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
405
|
+
this.setSpotOracleMap();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async setPerpOracleMap() {
|
|
412
|
+
const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
413
|
+
const perpMarkets = Array.from(perpMarketsMap.values());
|
|
414
|
+
const addOraclePromises = [];
|
|
415
|
+
for (const perpMarket of perpMarkets) {
|
|
416
|
+
if (!perpMarket || !perpMarket.data) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const perpMarketAccount = perpMarket.data;
|
|
420
|
+
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
421
|
+
const oracle = perpMarketAccount.amm.oracle;
|
|
422
|
+
const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
|
|
423
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
424
|
+
addOraclePromises.push(
|
|
425
|
+
this.addOracle({
|
|
426
|
+
publicKey: oracle,
|
|
427
|
+
source: perpMarket.data.amm.oracleSource,
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
this.perpOracleMap.set(perpMarketIndex, oracle);
|
|
432
|
+
this.perpOracleStringMap.set(perpMarketIndex, oracleId);
|
|
433
|
+
}
|
|
434
|
+
await Promise.all(addOraclePromises);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async setSpotOracleMap() {
|
|
438
|
+
const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
439
|
+
const spotMarkets = Array.from(spotMarketsMap.values());
|
|
440
|
+
const addOraclePromises = [];
|
|
441
|
+
for (const spotMarket of spotMarkets) {
|
|
442
|
+
if (!spotMarket || !spotMarket.data) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const spotMarketAccount = spotMarket.data;
|
|
446
|
+
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
447
|
+
const oracle = spotMarketAccount.oracle;
|
|
448
|
+
const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
|
|
449
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
450
|
+
addOraclePromises.push(
|
|
451
|
+
this.addOracle({
|
|
452
|
+
publicKey: oracle,
|
|
453
|
+
source: spotMarketAccount.oracleSource,
|
|
454
|
+
})
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
this.spotOracleMap.set(spotMarketIndex, oracle);
|
|
458
|
+
this.spotOracleStringMap.set(spotMarketIndex, oracleId);
|
|
459
|
+
}
|
|
460
|
+
await Promise.all(addOraclePromises);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
464
|
+
if (this.resubOpts?.logResubMessages) {
|
|
465
|
+
console.log(
|
|
466
|
+
'[grpcDriftClientAccountSubscriberV2] subscribeToPerpMarketAccounts'
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
470
|
+
await Promise.all(
|
|
471
|
+
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
472
|
+
marketIndex,
|
|
473
|
+
await getPerpMarketPublicKey(this.program.programId, marketIndex),
|
|
474
|
+
])
|
|
475
|
+
);
|
|
476
|
+
for (const [
|
|
477
|
+
marketIndex,
|
|
478
|
+
accountPubkey,
|
|
479
|
+
] of perpMarketIndexToAccountPubkeys) {
|
|
480
|
+
this.perpMarketIndexToAccountPubkeyMap.set(
|
|
481
|
+
marketIndex,
|
|
482
|
+
accountPubkey.toBase58()
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const perpMarketPubkeys = perpMarketIndexToAccountPubkeys.map(
|
|
487
|
+
([_, accountPubkey]) => accountPubkey
|
|
131
488
|
);
|
|
132
489
|
|
|
133
490
|
this.perpMarketsSubscriber =
|
|
@@ -151,6 +508,11 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
151
508
|
}
|
|
152
509
|
}
|
|
153
510
|
);
|
|
511
|
+
|
|
512
|
+
for (const data of this.initialPerpMarketAccountData.values()) {
|
|
513
|
+
this.perpMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
514
|
+
}
|
|
515
|
+
|
|
154
516
|
await this.perpMarketsSubscriber.subscribe(
|
|
155
517
|
perpMarketPubkeys,
|
|
156
518
|
(_accountId, data) => {
|
|
@@ -165,11 +527,31 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
165
527
|
return true;
|
|
166
528
|
}
|
|
167
529
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
)
|
|
530
|
+
async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
531
|
+
if (this.resubOpts?.logResubMessages) {
|
|
532
|
+
console.log(
|
|
533
|
+
'[grpcDriftClientAccountSubscriberV2] subscribeToSpotMarketAccounts'
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
537
|
+
await Promise.all(
|
|
538
|
+
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
539
|
+
marketIndex,
|
|
540
|
+
await getSpotMarketPublicKey(this.program.programId, marketIndex),
|
|
541
|
+
])
|
|
542
|
+
);
|
|
543
|
+
for (const [
|
|
544
|
+
marketIndex,
|
|
545
|
+
accountPubkey,
|
|
546
|
+
] of spotMarketIndexToAccountPubkeys) {
|
|
547
|
+
this.spotMarketIndexToAccountPubkeyMap.set(
|
|
548
|
+
marketIndex,
|
|
549
|
+
accountPubkey.toBase58()
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const spotMarketPubkeys = spotMarketIndexToAccountPubkeys.map(
|
|
554
|
+
([_, accountPubkey]) => accountPubkey
|
|
173
555
|
);
|
|
174
556
|
|
|
175
557
|
this.spotMarketsSubscriber =
|
|
@@ -193,6 +575,11 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
193
575
|
}
|
|
194
576
|
}
|
|
195
577
|
);
|
|
578
|
+
|
|
579
|
+
for (const data of this.initialSpotMarketAccountData.values()) {
|
|
580
|
+
this.spotMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
|
|
581
|
+
}
|
|
582
|
+
|
|
196
583
|
await this.spotMarketsSubscriber.subscribe(
|
|
197
584
|
spotMarketPubkeys,
|
|
198
585
|
(_accountId, data) => {
|
|
@@ -207,67 +594,84 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
207
594
|
return true;
|
|
208
595
|
}
|
|
209
596
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
597
|
+
async subscribeToOracles(): Promise<boolean> {
|
|
598
|
+
if (this.resubOpts?.logResubMessages) {
|
|
599
|
+
console.log('grpcDriftClientAccountSubscriberV2 subscribeToOracles');
|
|
600
|
+
}
|
|
601
|
+
const oraclePubkeyToInfosMap = new Map<string, OracleInfo[]>();
|
|
213
602
|
for (const info of this.oracleInfos) {
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
!info.publicKey.equals((PublicKey as any).default)
|
|
218
|
-
) {
|
|
219
|
-
uniqueOraclePubkeys.set(id, info);
|
|
603
|
+
const pubkey = info.publicKey.toBase58();
|
|
604
|
+
if (!oraclePubkeyToInfosMap.has(pubkey)) {
|
|
605
|
+
oraclePubkeyToInfosMap.set(pubkey, []);
|
|
220
606
|
}
|
|
607
|
+
oraclePubkeyToInfosMap.get(pubkey).push(info);
|
|
221
608
|
}
|
|
222
609
|
|
|
223
|
-
const oraclePubkeys = Array.from(
|
|
224
|
-
(
|
|
225
|
-
);
|
|
226
|
-
const pubkeyToSource = new Map<string, OracleInfo['source']>(
|
|
227
|
-
Array.from(uniqueOraclePubkeys.values()).map((i) => [
|
|
228
|
-
i.publicKey.toBase58(),
|
|
229
|
-
i.source,
|
|
230
|
-
])
|
|
610
|
+
const oraclePubkeys = Array.from(
|
|
611
|
+
new Set(this.oracleInfos.map((info) => info.publicKey))
|
|
231
612
|
);
|
|
232
613
|
|
|
233
|
-
this.oracleMultiSubscriber =
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
console.error('Oracle resubscribe failed:', e);
|
|
614
|
+
this.oracleMultiSubscriber = await grpcMultiAccountSubscriber.create<
|
|
615
|
+
OraclePriceData,
|
|
616
|
+
OracleInfo
|
|
617
|
+
>(
|
|
618
|
+
this.grpcConfigs,
|
|
619
|
+
'oracle',
|
|
620
|
+
this.program,
|
|
621
|
+
(buffer: Buffer, pubkey?: string, accountProps?: OracleInfo) => {
|
|
622
|
+
if (!pubkey) {
|
|
623
|
+
throw new Error('Oracle pubkey missing in decode');
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const client = this.oracleClientCache.get(
|
|
627
|
+
accountProps.source,
|
|
628
|
+
this.program.provider.connection,
|
|
629
|
+
this.program
|
|
630
|
+
);
|
|
631
|
+
const price = client.getOraclePriceDataFromBuffer(buffer);
|
|
632
|
+
return price;
|
|
633
|
+
},
|
|
634
|
+
this.resubOpts,
|
|
635
|
+
undefined,
|
|
636
|
+
async () => {
|
|
637
|
+
try {
|
|
638
|
+
if (this.resubOpts?.logResubMessages) {
|
|
639
|
+
console.log(
|
|
640
|
+
'[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
|
|
641
|
+
);
|
|
262
642
|
}
|
|
643
|
+
await this.subscribeToOracles();
|
|
644
|
+
} catch (e) {
|
|
645
|
+
console.error('Oracle resubscribe failed:', e);
|
|
263
646
|
}
|
|
264
|
-
|
|
647
|
+
},
|
|
648
|
+
oraclePubkeyToInfosMap
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
for (const data of this.initialOraclePriceData.entries()) {
|
|
652
|
+
const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
|
|
653
|
+
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
654
|
+
this.oracleIdToOracleDataMap.set(data[0], {
|
|
655
|
+
data: data[1],
|
|
656
|
+
slot: 0,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
265
659
|
|
|
266
660
|
await this.oracleMultiSubscriber.subscribe(
|
|
267
661
|
oraclePubkeys,
|
|
268
|
-
(accountId, data) => {
|
|
269
|
-
const
|
|
270
|
-
this.
|
|
662
|
+
(accountId, data, context, _b, accountProps) => {
|
|
663
|
+
const oracleId = getOracleId(accountId, accountProps.source);
|
|
664
|
+
this.oracleIdToOracleDataMap.set(oracleId, {
|
|
665
|
+
data,
|
|
666
|
+
slot: context.slot,
|
|
667
|
+
});
|
|
668
|
+
this.eventEmitter.emit(
|
|
669
|
+
'oraclePriceUpdate',
|
|
670
|
+
accountId,
|
|
671
|
+
accountProps.source,
|
|
672
|
+
data
|
|
673
|
+
);
|
|
674
|
+
|
|
271
675
|
this.eventEmitter.emit('update');
|
|
272
676
|
}
|
|
273
677
|
);
|
|
@@ -275,20 +679,56 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
275
679
|
return true;
|
|
276
680
|
}
|
|
277
681
|
|
|
682
|
+
async handleDelistedMarkets(): Promise<void> {
|
|
683
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
|
|
688
|
+
Array.from(
|
|
689
|
+
this.perpMarketsSubscriber?.getAccountDataMap().values() || []
|
|
690
|
+
),
|
|
691
|
+
Array.from(this.spotMarketsSubscriber?.getAccountDataMap().values() || [])
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
for (const perpMarketIndex of perpMarketIndexes) {
|
|
695
|
+
await this.perpMarketsSubscriber.removeAccounts([
|
|
696
|
+
new PublicKey(
|
|
697
|
+
this.perpMarketIndexToAccountPubkeyMap.get(perpMarketIndex) || ''
|
|
698
|
+
),
|
|
699
|
+
]);
|
|
700
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
|
|
701
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(perpMarketIndex);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
for (const oracle of oracles) {
|
|
706
|
+
await this.oracleMultiSubscriber.removeAccounts([oracle.publicKey]);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
removeInitialData() {
|
|
711
|
+
this.initialPerpMarketAccountData = new Map();
|
|
712
|
+
this.initialSpotMarketAccountData = new Map();
|
|
713
|
+
this.initialOraclePriceData = new Map();
|
|
714
|
+
}
|
|
715
|
+
|
|
278
716
|
async unsubscribeFromOracles(): Promise<void> {
|
|
279
717
|
if (this.oracleMultiSubscriber) {
|
|
280
718
|
await this.oracleMultiSubscriber.unsubscribe();
|
|
281
719
|
this.oracleMultiSubscriber = undefined;
|
|
282
720
|
return;
|
|
283
721
|
}
|
|
284
|
-
await super.unsubscribeFromOracles();
|
|
285
722
|
}
|
|
286
723
|
|
|
287
|
-
|
|
724
|
+
async unsubscribe(): Promise<void> {
|
|
288
725
|
if (this.isSubscribed) {
|
|
289
726
|
return;
|
|
290
727
|
}
|
|
291
728
|
|
|
292
729
|
await this.stateAccountSubscriber.unsubscribe();
|
|
730
|
+
await this.unsubscribeFromOracles();
|
|
731
|
+
await this.perpMarketsSubscriber?.unsubscribe();
|
|
732
|
+
await this.spotMarketsSubscriber?.unsubscribe();
|
|
293
733
|
}
|
|
294
734
|
}
|