@drift-labs/sdk 2.142.0-beta.13 → 2.142.0-beta.14
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 +45 -5
- package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +187 -8
- package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +1 -0
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +46 -0
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +45 -5
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +187 -8
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +1 -0
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcMultiAccountSubscriber.js +46 -0
- package/package.json +1 -1
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +309 -22
- package/src/accounts/grpcMultiAccountSubscriber.ts +66 -1
|
@@ -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,44 @@ 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
40
|
private oracleMultiSubscriber?: grpcMultiAccountSubscriber<OraclePriceData>;
|
|
30
41
|
private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
31
42
|
private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
|
|
43
|
+
private delistedMarketSetting: DelistedMarketSetting;
|
|
44
|
+
|
|
45
|
+
public eventEmitter: StrictEventEmitter<
|
|
46
|
+
EventEmitter,
|
|
47
|
+
DriftClientAccountEvents
|
|
48
|
+
>;
|
|
49
|
+
public isSubscribed: boolean;
|
|
50
|
+
public isSubscribing: boolean;
|
|
51
|
+
public program: Program;
|
|
52
|
+
public perpMarketIndexes: number[];
|
|
53
|
+
public spotMarketIndexes: number[];
|
|
54
|
+
public shouldFindAllMarketsAndOracles: boolean;
|
|
55
|
+
public oracleInfos: OracleInfo[];
|
|
56
|
+
public initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
|
|
57
|
+
public initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
|
|
58
|
+
public initialOraclePriceData: Map<string, OraclePriceData>;
|
|
59
|
+
public perpOracleMap = new Map<number, PublicKey>();
|
|
60
|
+
public perpOracleStringMap = new Map<number, string>();
|
|
61
|
+
public spotOracleMap = new Map<number, PublicKey>();
|
|
62
|
+
public spotOracleStringMap = new Map<number, string>();
|
|
63
|
+
public stateAccountSubscriber?: AccountSubscriber<StateAccount>;
|
|
64
|
+
oracleClientCache = new OracleClientCache();
|
|
65
|
+
private resubOpts?: ResubOpts;
|
|
66
|
+
|
|
67
|
+
private subscriptionPromise: Promise<boolean>;
|
|
68
|
+
protected subscriptionPromiseResolver: (val: boolean) => void;
|
|
32
69
|
|
|
33
70
|
constructor(
|
|
34
71
|
grpcConfigs: GrpcConfigs,
|
|
@@ -40,16 +77,156 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
40
77
|
delistedMarketSetting: DelistedMarketSetting,
|
|
41
78
|
resubOpts?: ResubOpts
|
|
42
79
|
) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
80
|
+
this.eventEmitter = new EventEmitter();
|
|
81
|
+
this.isSubscribed = false;
|
|
82
|
+
this.isSubscribing = false;
|
|
83
|
+
this.program = program;
|
|
84
|
+
this.perpMarketIndexes = perpMarketIndexes;
|
|
85
|
+
this.spotMarketIndexes = spotMarketIndexes;
|
|
86
|
+
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
87
|
+
this.oracleInfos = oracleInfos;
|
|
88
|
+
this.initialPerpMarketAccountData = new Map();
|
|
89
|
+
this.initialSpotMarketAccountData = new Map();
|
|
90
|
+
this.initialOraclePriceData = new Map();
|
|
91
|
+
this.perpOracleMap = new Map();
|
|
92
|
+
this.perpOracleStringMap = new Map();
|
|
93
|
+
this.spotOracleMap = new Map();
|
|
94
|
+
this.spotOracleStringMap = new Map();
|
|
52
95
|
this.grpcConfigs = grpcConfigs;
|
|
96
|
+
this.resubOpts = resubOpts;
|
|
97
|
+
this.delistedMarketSetting = delistedMarketSetting;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
chunks = <T>(array: readonly T[], size: number): T[][] => {
|
|
101
|
+
return new Array(Math.ceil(array.length / size))
|
|
102
|
+
.fill(null)
|
|
103
|
+
.map((_, index) => index * size)
|
|
104
|
+
.map((begin) => array.slice(begin, begin + size));
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
async setInitialData(): Promise<void> {
|
|
108
|
+
const connection = this.program.provider.connection;
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
!this.initialPerpMarketAccountData ||
|
|
112
|
+
this.initialPerpMarketAccountData.size === 0
|
|
113
|
+
) {
|
|
114
|
+
const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) =>
|
|
115
|
+
getPerpMarketPublicKeySync(this.program.programId, marketIndex)
|
|
116
|
+
);
|
|
117
|
+
const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
|
|
118
|
+
const perpMarketAccountInfos = (
|
|
119
|
+
await Promise.all(
|
|
120
|
+
perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) =>
|
|
121
|
+
connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
).flat();
|
|
125
|
+
this.initialPerpMarketAccountData = new Map(
|
|
126
|
+
perpMarketAccountInfos
|
|
127
|
+
.filter((accountInfo) => !!accountInfo)
|
|
128
|
+
.map((accountInfo) => {
|
|
129
|
+
const perpMarket = this.program.coder.accounts.decode(
|
|
130
|
+
'PerpMarket',
|
|
131
|
+
accountInfo.data
|
|
132
|
+
);
|
|
133
|
+
return [perpMarket.marketIndex, perpMarket];
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (
|
|
139
|
+
!this.initialSpotMarketAccountData ||
|
|
140
|
+
this.initialSpotMarketAccountData.size === 0
|
|
141
|
+
) {
|
|
142
|
+
const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) =>
|
|
143
|
+
getSpotMarketPublicKeySync(this.program.programId, marketIndex)
|
|
144
|
+
);
|
|
145
|
+
const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
|
|
146
|
+
const spotMarketAccountInfos = (
|
|
147
|
+
await Promise.all(
|
|
148
|
+
spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) =>
|
|
149
|
+
connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
).flat();
|
|
153
|
+
this.initialSpotMarketAccountData = new Map(
|
|
154
|
+
spotMarketAccountInfos
|
|
155
|
+
.filter((accountInfo) => !!accountInfo)
|
|
156
|
+
.map((accountInfo) => {
|
|
157
|
+
const spotMarket = this.program.coder.accounts.decode(
|
|
158
|
+
'SpotMarket',
|
|
159
|
+
accountInfo.data
|
|
160
|
+
);
|
|
161
|
+
return [spotMarket.marketIndex, spotMarket];
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const oracleAccountPubkeyChunks = this.chunks(
|
|
167
|
+
this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey),
|
|
168
|
+
75
|
|
169
|
+
);
|
|
170
|
+
const oracleAccountInfos = (
|
|
171
|
+
await Promise.all(
|
|
172
|
+
oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) =>
|
|
173
|
+
connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
).flat();
|
|
177
|
+
this.initialOraclePriceData = new Map(
|
|
178
|
+
this.oracleInfos.reduce((result, oracleInfo, i) => {
|
|
179
|
+
if (!oracleAccountInfos[i]) {
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
const oracleClient = this.oracleClientCache.get(
|
|
183
|
+
oracleInfo.source,
|
|
184
|
+
connection,
|
|
185
|
+
this.program
|
|
186
|
+
);
|
|
187
|
+
const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(
|
|
188
|
+
oracleAccountInfos[i].data
|
|
189
|
+
);
|
|
190
|
+
result.push([
|
|
191
|
+
getOracleId(oracleInfo.publicKey, oracleInfo.source),
|
|
192
|
+
oraclePriceData,
|
|
193
|
+
]);
|
|
194
|
+
return result;
|
|
195
|
+
}, [])
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async addPerpMarket(_marketIndex: number): Promise<boolean> {
|
|
200
|
+
if (!this.perpMarketIndexes.includes(_marketIndex)) {
|
|
201
|
+
this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async addSpotMarket(_marketIndex: number): Promise<boolean> {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
|
|
211
|
+
if (oracleInfo.publicKey.equals(PublicKey.default)) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const exists = this.oracleInfos.some(
|
|
216
|
+
(o) =>
|
|
217
|
+
o.source === oracleInfo.source &&
|
|
218
|
+
o.publicKey.equals(oracleInfo.publicKey)
|
|
219
|
+
);
|
|
220
|
+
if (!exists) {
|
|
221
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (this.oracleMultiSubscriber) {
|
|
225
|
+
await this.unsubscribeFromOracles();
|
|
226
|
+
await this.subscribeToOracles();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return true;
|
|
53
230
|
}
|
|
54
231
|
|
|
55
232
|
public async subscribe(): Promise<boolean> {
|
|
@@ -133,7 +310,37 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
133
310
|
return true;
|
|
134
311
|
}
|
|
135
312
|
|
|
136
|
-
|
|
313
|
+
public async fetch(): Promise<void> {
|
|
314
|
+
await this.stateAccountSubscriber?.fetch();
|
|
315
|
+
await this.perpMarketsSubscriber?.fetch();
|
|
316
|
+
await this.spotMarketsSubscriber?.fetch();
|
|
317
|
+
await this.oracleMultiSubscriber?.fetch();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private assertIsSubscribed(): void {
|
|
321
|
+
if (!this.isSubscribed) {
|
|
322
|
+
throw new NotSubscribedError(
|
|
323
|
+
'You must call `subscribe` before using this function'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public getStateAccountAndSlot(): DataAndSlot<StateAccount> {
|
|
329
|
+
this.assertIsSubscribed();
|
|
330
|
+
return this.stateAccountSubscriber.dataAndSlot;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[] {
|
|
334
|
+
const map = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
335
|
+
return Array.from(map?.values() ?? []);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
public getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[] {
|
|
339
|
+
const map = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
340
|
+
return Array.from(map?.values() ?? []);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
getMarketAccountAndSlot(
|
|
137
344
|
marketIndex: number
|
|
138
345
|
): DataAndSlot<PerpMarketAccount> | undefined {
|
|
139
346
|
return this.perpMarketsSubscriber?.getAccountData(
|
|
@@ -141,7 +348,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
141
348
|
);
|
|
142
349
|
}
|
|
143
350
|
|
|
144
|
-
|
|
351
|
+
getSpotMarketAccountAndSlot(
|
|
145
352
|
marketIndex: number
|
|
146
353
|
): DataAndSlot<SpotMarketAccount> | undefined {
|
|
147
354
|
return this.spotMarketsSubscriber?.getAccountData(
|
|
@@ -149,7 +356,51 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
149
356
|
);
|
|
150
357
|
}
|
|
151
358
|
|
|
152
|
-
|
|
359
|
+
public getOraclePriceDataAndSlot(
|
|
360
|
+
oracleId: string
|
|
361
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
362
|
+
this.assertIsSubscribed();
|
|
363
|
+
const { publicKey } = getPublicKeyAndSourceFromOracleId(oracleId);
|
|
364
|
+
return this.oracleMultiSubscriber?.getAccountData(publicKey.toBase58());
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
public getOraclePriceDataAndSlotForPerpMarket(
|
|
368
|
+
marketIndex: number
|
|
369
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
370
|
+
const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
|
|
371
|
+
const oracle = this.perpOracleMap.get(marketIndex);
|
|
372
|
+
const oracleId = this.perpOracleStringMap.get(marketIndex);
|
|
373
|
+
if (!perpMarketAccount || !oracleId) {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
|
|
378
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
379
|
+
this.setPerpOracleMap();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public getOraclePriceDataAndSlotForSpotMarket(
|
|
386
|
+
marketIndex: number
|
|
387
|
+
): DataAndSlot<OraclePriceData> | undefined {
|
|
388
|
+
const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
|
|
389
|
+
const oracle = this.spotOracleMap.get(marketIndex);
|
|
390
|
+
const oracleId = this.spotOracleStringMap.get(marketIndex);
|
|
391
|
+
if (!spotMarketAccount || !oracleId) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!spotMarketAccount.data.oracle.equals(oracle)) {
|
|
396
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
397
|
+
this.setSpotOracleMap();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async setPerpOracleMap() {
|
|
153
404
|
const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
|
|
154
405
|
const perpMarkets = Array.from(perpMarketsMap.values());
|
|
155
406
|
const addOraclePromises = [];
|
|
@@ -161,7 +412,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
161
412
|
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
162
413
|
const oracle = perpMarketAccount.amm.oracle;
|
|
163
414
|
const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
|
|
164
|
-
if (!this.
|
|
415
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
165
416
|
addOraclePromises.push(
|
|
166
417
|
this.addOracle({
|
|
167
418
|
publicKey: oracle,
|
|
@@ -175,7 +426,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
175
426
|
await Promise.all(addOraclePromises);
|
|
176
427
|
}
|
|
177
428
|
|
|
178
|
-
|
|
429
|
+
async setSpotOracleMap() {
|
|
179
430
|
const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
|
|
180
431
|
const spotMarkets = Array.from(spotMarketsMap.values());
|
|
181
432
|
const addOraclePromises = [];
|
|
@@ -187,7 +438,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
187
438
|
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
188
439
|
const oracle = spotMarketAccount.oracle;
|
|
189
440
|
const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
|
|
190
|
-
if (!this.
|
|
441
|
+
if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
|
|
191
442
|
addOraclePromises.push(
|
|
192
443
|
this.addOracle({
|
|
193
444
|
publicKey: oracle,
|
|
@@ -201,7 +452,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
201
452
|
await Promise.all(addOraclePromises);
|
|
202
453
|
}
|
|
203
454
|
|
|
204
|
-
|
|
455
|
+
async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
205
456
|
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
206
457
|
await Promise.all(
|
|
207
458
|
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
@@ -263,7 +514,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
263
514
|
return true;
|
|
264
515
|
}
|
|
265
516
|
|
|
266
|
-
|
|
517
|
+
async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
267
518
|
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
268
519
|
await Promise.all(
|
|
269
520
|
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
@@ -325,7 +576,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
325
576
|
return true;
|
|
326
577
|
}
|
|
327
578
|
|
|
328
|
-
|
|
579
|
+
async subscribeToOracles(): Promise<boolean> {
|
|
329
580
|
const pubkeyToSources = new Map<string, Set<OracleInfo['source']>>();
|
|
330
581
|
for (const info of this.oracleInfos) {
|
|
331
582
|
if (info.publicKey.equals((PublicKey as any).default)) {
|
|
@@ -407,20 +658,56 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
407
658
|
return true;
|
|
408
659
|
}
|
|
409
660
|
|
|
661
|
+
async handleDelistedMarkets(): Promise<void> {
|
|
662
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
|
|
667
|
+
Array.from(
|
|
668
|
+
this.perpMarketsSubscriber?.getAccountDataMap().values() || []
|
|
669
|
+
),
|
|
670
|
+
Array.from(this.spotMarketsSubscriber?.getAccountDataMap().values() || [])
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
for (const perpMarketIndex of perpMarketIndexes) {
|
|
674
|
+
await this.perpMarketsSubscriber.removeAccounts([
|
|
675
|
+
new PublicKey(
|
|
676
|
+
this.perpMarketIndexToAccountPubkeyMap.get(perpMarketIndex) || ''
|
|
677
|
+
),
|
|
678
|
+
]);
|
|
679
|
+
if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
|
|
680
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(perpMarketIndex);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
for (const oracle of oracles) {
|
|
685
|
+
await this.oracleMultiSubscriber.removeAccounts([oracle.publicKey]);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
removeInitialData() {
|
|
690
|
+
this.initialPerpMarketAccountData = new Map();
|
|
691
|
+
this.initialSpotMarketAccountData = new Map();
|
|
692
|
+
this.initialOraclePriceData = new Map();
|
|
693
|
+
}
|
|
694
|
+
|
|
410
695
|
async unsubscribeFromOracles(): Promise<void> {
|
|
411
696
|
if (this.oracleMultiSubscriber) {
|
|
412
697
|
await this.oracleMultiSubscriber.unsubscribe();
|
|
413
698
|
this.oracleMultiSubscriber = undefined;
|
|
414
699
|
return;
|
|
415
700
|
}
|
|
416
|
-
await super.unsubscribeFromOracles();
|
|
417
701
|
}
|
|
418
702
|
|
|
419
|
-
|
|
703
|
+
async unsubscribe(): Promise<void> {
|
|
420
704
|
if (this.isSubscribed) {
|
|
421
705
|
return;
|
|
422
706
|
}
|
|
423
707
|
|
|
424
708
|
await this.stateAccountSubscriber.unsubscribe();
|
|
709
|
+
await this.unsubscribeFromOracles();
|
|
710
|
+
await this.perpMarketsSubscriber?.unsubscribe();
|
|
711
|
+
await this.spotMarketsSubscriber?.unsubscribe();
|
|
425
712
|
}
|
|
426
713
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Program } from '@coral-xyz/anchor';
|
|
2
|
-
import { Context, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { Commitment, Context, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import * as Buffer from 'buffer';
|
|
4
4
|
import bs58 from 'bs58';
|
|
5
5
|
|
|
@@ -21,6 +21,21 @@ interface AccountInfoLike {
|
|
|
21
21
|
rentEpoch: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function commitmentLevelToCommitment(
|
|
25
|
+
commitmentLevel: CommitmentLevel
|
|
26
|
+
): Commitment {
|
|
27
|
+
switch (commitmentLevel) {
|
|
28
|
+
case CommitmentLevel.PROCESSED:
|
|
29
|
+
return 'processed';
|
|
30
|
+
case CommitmentLevel.CONFIRMED:
|
|
31
|
+
return 'confirmed';
|
|
32
|
+
case CommitmentLevel.FINALIZED:
|
|
33
|
+
return 'finalized';
|
|
34
|
+
default:
|
|
35
|
+
return 'confirmed';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
export class grpcMultiAccountSubscriber<T> {
|
|
25
40
|
private client: Client;
|
|
26
41
|
private stream: ClientDuplexStream<SubscribeRequest, SubscribeUpdate>;
|
|
@@ -105,6 +120,56 @@ export class grpcMultiAccountSubscriber<T> {
|
|
|
105
120
|
return this.dataMap;
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
async fetch(): Promise<void> {
|
|
124
|
+
try {
|
|
125
|
+
// Chunk account IDs into groups of 100 (getMultipleAccounts limit)
|
|
126
|
+
const chunkSize = 100;
|
|
127
|
+
const chunks: string[][] = [];
|
|
128
|
+
const accountIds = Array.from(this.subscribedAccounts.values());
|
|
129
|
+
for (let i = 0; i < accountIds.length; i += chunkSize) {
|
|
130
|
+
chunks.push(accountIds.slice(i, i + chunkSize));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Process all chunks concurrently
|
|
134
|
+
await Promise.all(
|
|
135
|
+
chunks.map(async (chunk) => {
|
|
136
|
+
const accountAddresses = chunk.map(
|
|
137
|
+
(accountId) => new PublicKey(accountId)
|
|
138
|
+
);
|
|
139
|
+
const rpcResponseAndContext =
|
|
140
|
+
await this.program.provider.connection.getMultipleAccountsInfoAndContext(
|
|
141
|
+
accountAddresses,
|
|
142
|
+
{
|
|
143
|
+
commitment: commitmentLevelToCommitment(this.commitmentLevel),
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const rpcResponse = rpcResponseAndContext.value;
|
|
148
|
+
const currentSlot = rpcResponseAndContext.context.slot;
|
|
149
|
+
|
|
150
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
151
|
+
const accountId = chunk[i];
|
|
152
|
+
const accountInfo = rpcResponse[i];
|
|
153
|
+
if (accountInfo) {
|
|
154
|
+
const perpMarket = this.program.coder.accounts.decode(
|
|
155
|
+
'PerpMarket',
|
|
156
|
+
accountInfo.data
|
|
157
|
+
);
|
|
158
|
+
this.setAccountData(accountId, perpMarket, currentSlot);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (this.resubOpts?.logResubMessages) {
|
|
165
|
+
console.log(
|
|
166
|
+
`[${this.accountName}] grpcMultiAccountSubscriber error fetching accounts:`,
|
|
167
|
+
error
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
108
173
|
async subscribe(
|
|
109
174
|
accounts: PublicKey[],
|
|
110
175
|
onChange: (
|