@drift-labs/sdk 2.142.0-beta.9 → 2.142.0
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 +242 -41
- package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +6 -3
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +112 -19
- package/lib/browser/adminClient.d.ts +4 -0
- package/lib/browser/adminClient.js +34 -0
- package/lib/browser/constants/perpMarkets.js +35 -0
- package/lib/browser/constants/spotMarkets.js +4 -4
- package/lib/browser/driftClient.d.ts +35 -5
- package/lib/browser/driftClient.js +41 -14
- package/lib/browser/events/parse.d.ts +2 -0
- package/lib/browser/events/parse.js +94 -1
- package/lib/browser/events/types.d.ts +22 -3
- package/lib/browser/idl/drift.json +105 -6
- package/lib/browser/math/amm.d.ts +1 -0
- package/lib/browser/math/amm.js +28 -4
- package/lib/browser/types.d.ts +20 -0
- 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 +242 -41
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +6 -3
- package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcMultiAccountSubscriber.js +112 -19
- package/lib/node/adminClient.d.ts +4 -0
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +34 -0
- package/lib/node/constants/perpMarkets.d.ts.map +1 -1
- package/lib/node/constants/perpMarkets.js +35 -0
- package/lib/node/constants/spotMarkets.js +4 -4
- package/lib/node/driftClient.d.ts +35 -5
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +41 -14
- package/lib/node/events/parse.d.ts +2 -0
- package/lib/node/events/parse.d.ts.map +1 -1
- package/lib/node/events/parse.js +94 -1
- package/lib/node/events/types.d.ts +22 -3
- package/lib/node/events/types.d.ts.map +1 -1
- package/lib/node/idl/drift.json +105 -6
- package/lib/node/math/amm.d.ts +1 -0
- package/lib/node/math/amm.d.ts.map +1 -1
- package/lib/node/math/amm.js +28 -4
- package/lib/node/types.d.ts +20 -0
- package/lib/node/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/scripts/client-test.ts +294 -135
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +398 -72
- package/src/accounts/grpcMultiAccountSubscriber.ts +163 -31
- package/src/adminClient.ts +74 -0
- package/src/constants/perpMarkets.ts +37 -0
- package/src/constants/spotMarkets.ts +4 -4
- package/src/driftClient.ts +65 -14
- package/src/events/parse.ts +115 -0
- package/src/events/types.ts +26 -2
- package/src/idl/drift.json +105 -6
- package/src/math/amm.ts +52 -8
- package/src/types.ts +22 -0
- package/tests/events/parseLogsForCuUsage.ts +139 -0
|
@@ -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 (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;
|
|
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,12 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
201
460
|
await Promise.all(addOraclePromises);
|
|
202
461
|
}
|
|
203
462
|
|
|
204
|
-
|
|
463
|
+
async subscribeToPerpMarketAccounts(): Promise<boolean> {
|
|
464
|
+
if (this.resubOpts?.logResubMessages) {
|
|
465
|
+
console.log(
|
|
466
|
+
'[grpcDriftClientAccountSubscriberV2] subscribeToPerpMarketAccounts'
|
|
467
|
+
);
|
|
468
|
+
}
|
|
205
469
|
const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
206
470
|
await Promise.all(
|
|
207
471
|
this.perpMarketIndexes.map(async (marketIndex) => [
|
|
@@ -263,7 +527,12 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
263
527
|
return true;
|
|
264
528
|
}
|
|
265
529
|
|
|
266
|
-
|
|
530
|
+
async subscribeToSpotMarketAccounts(): Promise<boolean> {
|
|
531
|
+
if (this.resubOpts?.logResubMessages) {
|
|
532
|
+
console.log(
|
|
533
|
+
'[grpcDriftClientAccountSubscriberV2] subscribeToSpotMarketAccounts'
|
|
534
|
+
);
|
|
535
|
+
}
|
|
267
536
|
const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
|
|
268
537
|
await Promise.all(
|
|
269
538
|
this.spotMarketIndexes.map(async (marketIndex) => [
|
|
@@ -325,72 +594,84 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
325
594
|
return true;
|
|
326
595
|
}
|
|
327
596
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
597
|
+
async subscribeToOracles(): Promise<boolean> {
|
|
598
|
+
if (this.resubOpts?.logResubMessages) {
|
|
599
|
+
console.log('grpcDriftClientAccountSubscriberV2 subscribeToOracles');
|
|
600
|
+
}
|
|
601
|
+
const oraclePubkeyToInfosMap = new Map<string, OracleInfo[]>();
|
|
331
602
|
for (const info of this.oracleInfos) {
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
!info.publicKey.equals((PublicKey as any).default)
|
|
336
|
-
) {
|
|
337
|
-
uniqueOraclePubkeys.set(id, info);
|
|
603
|
+
const pubkey = info.publicKey.toBase58();
|
|
604
|
+
if (!oraclePubkeyToInfosMap.has(pubkey)) {
|
|
605
|
+
oraclePubkeyToInfosMap.set(pubkey, []);
|
|
338
606
|
}
|
|
607
|
+
oraclePubkeyToInfosMap.get(pubkey).push(info);
|
|
339
608
|
}
|
|
340
609
|
|
|
341
|
-
const oraclePubkeys = Array.from(
|
|
342
|
-
(
|
|
343
|
-
);
|
|
344
|
-
const pubkeyToSource = new Map<string, OracleInfo['source']>(
|
|
345
|
-
Array.from(uniqueOraclePubkeys.values()).map((i) => [
|
|
346
|
-
i.publicKey.toBase58(),
|
|
347
|
-
i.source,
|
|
348
|
-
])
|
|
610
|
+
const oraclePubkeys = Array.from(
|
|
611
|
+
new Set(this.oracleInfos.map((info) => info.publicKey))
|
|
349
612
|
);
|
|
350
613
|
|
|
351
|
-
this.oracleMultiSubscriber =
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
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
|
+
);
|
|
380
642
|
}
|
|
643
|
+
await this.subscribeToOracles();
|
|
644
|
+
} catch (e) {
|
|
645
|
+
console.error('Oracle resubscribe failed:', e);
|
|
381
646
|
}
|
|
382
|
-
|
|
647
|
+
},
|
|
648
|
+
oraclePubkeyToInfosMap
|
|
649
|
+
);
|
|
383
650
|
|
|
384
651
|
for (const data of this.initialOraclePriceData.entries()) {
|
|
385
652
|
const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
|
|
386
653
|
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
654
|
+
this.oracleIdToOracleDataMap.set(data[0], {
|
|
655
|
+
data: data[1],
|
|
656
|
+
slot: 0,
|
|
657
|
+
});
|
|
387
658
|
}
|
|
388
659
|
|
|
389
660
|
await this.oracleMultiSubscriber.subscribe(
|
|
390
661
|
oraclePubkeys,
|
|
391
|
-
(accountId, data) => {
|
|
392
|
-
const
|
|
393
|
-
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
|
+
|
|
394
675
|
this.eventEmitter.emit('update');
|
|
395
676
|
}
|
|
396
677
|
);
|
|
@@ -398,20 +679,65 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
|
|
|
398
679
|
return true;
|
|
399
680
|
}
|
|
400
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
|
+
// Build array of perp market pubkeys to remove
|
|
695
|
+
const perpMarketPubkeysToRemove = perpMarketIndexes
|
|
696
|
+
.map((marketIndex) => {
|
|
697
|
+
const pubkeyString =
|
|
698
|
+
this.perpMarketIndexToAccountPubkeyMap.get(marketIndex);
|
|
699
|
+
return pubkeyString ? new PublicKey(pubkeyString) : null;
|
|
700
|
+
})
|
|
701
|
+
.filter((pubkey) => pubkey !== null) as PublicKey[];
|
|
702
|
+
|
|
703
|
+
// Build array of oracle pubkeys to remove
|
|
704
|
+
const oraclePubkeysToRemove = oracles.map((oracle) => oracle.publicKey);
|
|
705
|
+
|
|
706
|
+
// Remove accounts in batches - perp markets
|
|
707
|
+
if (perpMarketPubkeysToRemove.length > 0) {
|
|
708
|
+
await this.perpMarketsSubscriber.removeAccounts(
|
|
709
|
+
perpMarketPubkeysToRemove
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Remove accounts in batches - oracles
|
|
714
|
+
if (oraclePubkeysToRemove.length > 0) {
|
|
715
|
+
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
removeInitialData() {
|
|
720
|
+
this.initialPerpMarketAccountData = new Map();
|
|
721
|
+
this.initialSpotMarketAccountData = new Map();
|
|
722
|
+
this.initialOraclePriceData = new Map();
|
|
723
|
+
}
|
|
724
|
+
|
|
401
725
|
async unsubscribeFromOracles(): Promise<void> {
|
|
402
726
|
if (this.oracleMultiSubscriber) {
|
|
403
727
|
await this.oracleMultiSubscriber.unsubscribe();
|
|
404
728
|
this.oracleMultiSubscriber = undefined;
|
|
405
729
|
return;
|
|
406
730
|
}
|
|
407
|
-
await super.unsubscribeFromOracles();
|
|
408
731
|
}
|
|
409
732
|
|
|
410
|
-
|
|
733
|
+
async unsubscribe(): Promise<void> {
|
|
411
734
|
if (this.isSubscribed) {
|
|
412
735
|
return;
|
|
413
736
|
}
|
|
414
737
|
|
|
415
738
|
await this.stateAccountSubscriber.unsubscribe();
|
|
739
|
+
await this.unsubscribeFromOracles();
|
|
740
|
+
await this.perpMarketsSubscriber?.unsubscribe();
|
|
741
|
+
await this.spotMarketsSubscriber?.unsubscribe();
|
|
416
742
|
}
|
|
417
743
|
}
|