@drift-labs/sdk 2.142.0-beta.13 → 2.142.0-beta.15
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 +46 -5
- package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +223 -49
- package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +5 -3
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +77 -17
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +46 -5
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +223 -49
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +5 -3
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcMultiAccountSubscriber.js +77 -17
- package/package.json +1 -1
- package/scripts/client-test.ts +123 -162
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +376 -81
- package/src/accounts/grpcMultiAccountSubscriber.ts +119 -29
|
@@ -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,11 +7,17 @@ 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
14
|
import {
|
|
15
|
+
AccountSubscriber,
|
|
12
16
|
DataAndSlot,
|
|
13
17
|
DelistedMarketSetting,
|
|
18
|
+
DriftClientAccountEvents,
|
|
19
|
+
DriftClientAccountSubscriber,
|
|
20
|
+
NotSubscribedError,
|
|
14
21
|
GrpcConfigs,
|
|
15
22
|
ResubOpts,
|
|
16
23
|
} from './types';
|
|
@@ -21,14 +28,51 @@ import {
|
|
|
21
28
|
getOracleId,
|
|
22
29
|
getPublicKeyAndSourceFromOracleId,
|
|
23
30
|
} from '../oracles/oracleId';
|
|
31
|
+
import { OracleClientCache } from '../oracles/oracleClientCache';
|
|
32
|
+
import { findDelistedPerpMarketsAndOracles } from './utils';
|
|
24
33
|
|
|
25
|
-
export class grpcDriftClientAccountSubscriberV2
|
|
34
|
+
export class grpcDriftClientAccountSubscriberV2
|
|
35
|
+
implements DriftClientAccountSubscriber
|
|
36
|
+
{
|
|
26
37
|
private grpcConfigs: GrpcConfigs;
|
|
27
38
|
private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
|
|
28
39
|
private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
|
|
29
|
-
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
|
|
40
|
+
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
|
|
41
|
+
OraclePriceData,
|
|
42
|
+
OracleInfo
|
|
43
|
+
>;
|
|
30
44
|
private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
31
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;
|
|
32
76
|
|
|
33
77
|
constructor(
|
|
34
78
|
grpcConfigs: GrpcConfigs,
|
|
@@ -40,16 +84,156 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
40
84
|
delistedMarketSetting: DelistedMarketSetting,
|
|
41
85
|
resubOpts?: ResubOpts
|
|
42
86
|
) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
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();
|
|
52
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 (oracleInfo.publicKey.equals(PublicKey.default)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const exists = this.oracleInfos.some(
|
|
223
|
+
(o) =>
|
|
224
|
+
o.source === oracleInfo.source &&
|
|
225
|
+
o.publicKey.equals(oracleInfo.publicKey)
|
|
226
|
+
);
|
|
227
|
+
if (!exists) {
|
|
228
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (this.oracleMultiSubscriber) {
|
|
232
|
+
await this.unsubscribeFromOracles();
|
|
233
|
+
await this.subscribeToOracles();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return true;
|
|
53
237
|
}
|
|
54
238
|
|
|
55
239
|
public async subscribe(): Promise<boolean> {
|
|
@@ -133,7 +317,37 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
133
317
|
return true;
|
|
134
318
|
}
|
|
135
319
|
|
|
136
|
-
|
|
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(
|
|
137
351
|
marketIndex: number
|
|
138
352
|
): DataAndSlot<PerpMarketAccount> | undefined {
|
|
139
353
|
return this.perpMarketsSubscriber?.getAccountData(
|
|
@@ -141,7 +355,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
141
355
|
);
|
|
142
356
|
}
|
|
143
357
|
|
|
144
|
-
|
|
358
|
+
getSpotMarketAccountAndSlot(
|
|
145
359
|
marketIndex: number
|
|
146
360
|
): DataAndSlot<SpotMarketAccount> | undefined {
|
|
147
361
|
return this.spotMarketsSubscriber?.getAccountData(
|
|
@@ -149,7 +363,52 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
149
363
|
);
|
|
150
364
|
}
|
|
151
365
|
|
|
152
|
-
|
|
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() {
|
|
153
412
|
const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
154
413
|
const perpMarkets = Array.from(perpMarketsMap.values());
|
|
155
414
|
const addOraclePromises = [];
|
|
@@ -161,7 +420,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
161
420
|
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
162
421
|
const oracle = perpMarketAccount.amm.oracle;
|
|
163
422
|
const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
|
|
164
|
-
if (!this.
|
|
423
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
165
424
|
addOraclePromises.push(
|
|
166
425
|
this.addOracle({
|
|
167
426
|
publicKey: oracle,
|
|
@@ -175,7 +434,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
175
434
|
await Promise.all(addOraclePromises);
|
|
176
435
|
}
|
|
177
436
|
|
|
178
|
-
|
|
437
|
+
async setSpotOracleMap() {
|
|
179
438
|
const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
180
439
|
const spotMarkets = Array.from(spotMarketsMap.values());
|
|
181
440
|
const addOraclePromises = [];
|
|
@@ -187,7 +446,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
187
446
|
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
188
447
|
const oracle = spotMarketAccount.oracle;
|
|
189
448
|
const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
|
|
190
|
-
if (!this.
|
|
449
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
191
450
|
addOraclePromises.push(
|
|
192
451
|
this.addOracle({
|
|
193
452
|
publicKey: oracle,
|
|
@@ -201,7 +460,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
201
460
|
await Promise.all(addOraclePromises);
|
|
202
461
|
}
|
|
203
462
|
|
|
204
|
-
|
|
463
|
+
async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
205
464
|
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
206
465
|
await Promise.all(
|
|
207
466
|
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
@@ -263,7 +522,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
263
522
|
return true;
|
|
264
523
|
}
|
|
265
524
|
|
|
266
|
-
|
|
525
|
+
async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
267
526
|
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
268
527
|
await Promise.all(
|
|
269
528
|
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
@@ -325,81 +584,81 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
325
584
|
return true;
|
|
326
585
|
}
|
|
327
586
|
|
|
328
|
-
|
|
329
|
-
const
|
|
587
|
+
async subscribeToOracles(): Promise<boolean> {
|
|
588
|
+
const oraclePubkeyToInfosMap = new Map<string, OracleInfo[]>();
|
|
330
589
|
for (const info of this.oracleInfos) {
|
|
331
|
-
|
|
332
|
-
|
|
590
|
+
const pubkey = info.publicKey.toBase58();
|
|
591
|
+
if (!oraclePubkeyToInfosMap.has(pubkey)) {
|
|
592
|
+
oraclePubkeyToInfosMap.set(pubkey, []);
|
|
333
593
|
}
|
|
334
|
-
|
|
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);
|
|
594
|
+
oraclePubkeyToInfosMap.get(pubkey).push(info);
|
|
341
595
|
}
|
|
342
596
|
|
|
343
|
-
const oraclePubkeys = Array.from(
|
|
344
|
-
(
|
|
597
|
+
const oraclePubkeys = Array.from(
|
|
598
|
+
new Set(this.oracleInfos.map((info) => info.publicKey))
|
|
345
599
|
);
|
|
346
600
|
|
|
347
|
-
this.oracleMultiSubscriber =
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
await this.subscribeToOracles();
|
|
378
|
-
} catch (e) {
|
|
379
|
-
console.error('Oracle resubscribe failed:', e);
|
|
601
|
+
this.oracleMultiSubscriber = await grpcMultiAccountSubscriber.create<
|
|
602
|
+
OraclePriceData,
|
|
603
|
+
OracleInfo
|
|
604
|
+
>(
|
|
605
|
+
this.grpcConfigs,
|
|
606
|
+
'oracle',
|
|
607
|
+
this.program,
|
|
608
|
+
(buffer: Buffer, pubkey?: string, accountProps?: OracleInfo) => {
|
|
609
|
+
if (!pubkey) {
|
|
610
|
+
throw new Error('Oracle pubkey missing in decode');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const client = this.oracleClientCache.get(
|
|
614
|
+
accountProps.source,
|
|
615
|
+
this.program.provider.connection,
|
|
616
|
+
this.program
|
|
617
|
+
);
|
|
618
|
+
const price = client.getOraclePriceDataFromBuffer(buffer);
|
|
619
|
+
return price;
|
|
620
|
+
},
|
|
621
|
+
this.resubOpts,
|
|
622
|
+
undefined,
|
|
623
|
+
async () => {
|
|
624
|
+
try {
|
|
625
|
+
if (this.resubOpts?.logResubMessages) {
|
|
626
|
+
console.log(
|
|
627
|
+
'[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
|
|
628
|
+
);
|
|
380
629
|
}
|
|
630
|
+
await this.subscribeToOracles();
|
|
631
|
+
} catch (e) {
|
|
632
|
+
console.error('Oracle resubscribe failed:', e);
|
|
381
633
|
}
|
|
382
|
-
|
|
634
|
+
},
|
|
635
|
+
oraclePubkeyToInfosMap
|
|
636
|
+
);
|
|
383
637
|
|
|
384
638
|
for (const data of this.initialOraclePriceData.entries()) {
|
|
385
639
|
const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
|
|
386
640
|
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
641
|
+
this.oracleIdToOracleDataMap.set(data[0], {
|
|
642
|
+
data: data[1],
|
|
643
|
+
slot: 0,
|
|
644
|
+
});
|
|
387
645
|
}
|
|
388
646
|
|
|
389
647
|
await this.oracleMultiSubscriber.subscribe(
|
|
390
648
|
oraclePubkeys,
|
|
391
|
-
(accountId, data) => {
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
649
|
+
(accountId, data, context, _b, accountProps) => {
|
|
650
|
+
const oracleId = getOracleId(accountId, accountProps.source);
|
|
651
|
+
this.oracleIdToOracleDataMap.set(oracleId, {
|
|
652
|
+
data,
|
|
653
|
+
slot: context.slot,
|
|
654
|
+
});
|
|
655
|
+
this.eventEmitter.emit(
|
|
656
|
+
'oraclePriceUpdate',
|
|
657
|
+
accountId,
|
|
658
|
+
accountProps.source,
|
|
659
|
+
data
|
|
660
|
+
);
|
|
661
|
+
|
|
403
662
|
this.eventEmitter.emit('update');
|
|
404
663
|
}
|
|
405
664
|
);
|
|
@@ -407,20 +666,56 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
407
666
|
return true;
|
|
408
667
|
}
|
|
409
668
|
|
|
669
|
+
async handleDelistedMarkets(): Promise<void> {
|
|
670
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
|
|
675
|
+
Array.from(
|
|
676
|
+
this.perpMarketsSubscriber?.getAccountDataMap().values() || []
|
|
677
|
+
),
|
|
678
|
+
Array.from(this.spotMarketsSubscriber?.getAccountDataMap().values() || [])
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
for (const perpMarketIndex of perpMarketIndexes) {
|
|
682
|
+
await this.perpMarketsSubscriber.removeAccounts([
|
|
683
|
+
new PublicKey(
|
|
684
|
+
this.perpMarketIndexToAccountPubkeyMap.get(perpMarketIndex) || ''
|
|
685
|
+
),
|
|
686
|
+
]);
|
|
687
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
|
|
688
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(perpMarketIndex);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
for (const oracle of oracles) {
|
|
693
|
+
await this.oracleMultiSubscriber.removeAccounts([oracle.publicKey]);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
removeInitialData() {
|
|
698
|
+
this.initialPerpMarketAccountData = new Map();
|
|
699
|
+
this.initialSpotMarketAccountData = new Map();
|
|
700
|
+
this.initialOraclePriceData = new Map();
|
|
701
|
+
}
|
|
702
|
+
|
|
410
703
|
async unsubscribeFromOracles(): Promise<void> {
|
|
411
704
|
if (this.oracleMultiSubscriber) {
|
|
412
705
|
await this.oracleMultiSubscriber.unsubscribe();
|
|
413
706
|
this.oracleMultiSubscriber = undefined;
|
|
414
707
|
return;
|
|
415
708
|
}
|
|
416
|
-
await super.unsubscribeFromOracles();
|
|
417
709
|
}
|
|
418
710
|
|
|
419
|
-
|
|
711
|
+
async unsubscribe(): Promise<void> {
|
|
420
712
|
if (this.isSubscribed) {
|
|
421
713
|
return;
|
|
422
714
|
}
|
|
423
715
|
|
|
424
716
|
await this.stateAccountSubscriber.unsubscribe();
|
|
717
|
+
await this.unsubscribeFromOracles();
|
|
718
|
+
await this.perpMarketsSubscriber?.unsubscribe();
|
|
719
|
+
await this.spotMarketsSubscriber?.unsubscribe();
|
|
425
720
|
}
|
|
426
721
|
}
|