@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
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.142.0-beta.
|
|
1
|
+
2.142.0-beta.15
|
|
@@ -1,24 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { OracleInfo, OraclePriceData } from '../oracles/types';
|
|
3
5
|
import { Program } from '@coral-xyz/anchor';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import { PublicKey } from '@solana/web3.js';
|
|
7
|
+
import { AccountSubscriber, DataAndSlot, DelistedMarketSetting, DriftClientAccountEvents, DriftClientAccountSubscriber, GrpcConfigs, ResubOpts } from './types';
|
|
8
|
+
import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
|
|
9
|
+
import { OracleClientCache } from '../oracles/oracleClientCache';
|
|
10
|
+
export declare class grpcDriftClientAccountSubscriberV2 implements DriftClientAccountSubscriber {
|
|
7
11
|
private grpcConfigs;
|
|
8
12
|
private perpMarketsSubscriber?;
|
|
9
13
|
private spotMarketsSubscriber?;
|
|
10
14
|
private oracleMultiSubscriber?;
|
|
11
15
|
private perpMarketIndexToAccountPubkeyMap;
|
|
12
16
|
private spotMarketIndexToAccountPubkeyMap;
|
|
17
|
+
private delistedMarketSetting;
|
|
18
|
+
eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
|
|
19
|
+
isSubscribed: boolean;
|
|
20
|
+
isSubscribing: boolean;
|
|
21
|
+
program: Program;
|
|
22
|
+
perpMarketIndexes: number[];
|
|
23
|
+
spotMarketIndexes: number[];
|
|
24
|
+
shouldFindAllMarketsAndOracles: boolean;
|
|
25
|
+
oracleInfos: OracleInfo[];
|
|
26
|
+
initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
|
|
27
|
+
initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
|
|
28
|
+
initialOraclePriceData: Map<string, OraclePriceData>;
|
|
29
|
+
perpOracleMap: Map<number, PublicKey>;
|
|
30
|
+
perpOracleStringMap: Map<number, string>;
|
|
31
|
+
spotOracleMap: Map<number, PublicKey>;
|
|
32
|
+
spotOracleStringMap: Map<number, string>;
|
|
33
|
+
private oracleIdToOracleDataMap;
|
|
34
|
+
stateAccountSubscriber?: AccountSubscriber<StateAccount>;
|
|
35
|
+
oracleClientCache: OracleClientCache;
|
|
36
|
+
private resubOpts?;
|
|
37
|
+
private subscriptionPromise;
|
|
38
|
+
protected subscriptionPromiseResolver: (val: boolean) => void;
|
|
13
39
|
constructor(grpcConfigs: GrpcConfigs, program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean, delistedMarketSetting: DelistedMarketSetting, resubOpts?: ResubOpts);
|
|
40
|
+
chunks: <T>(array: readonly T[], size: number) => T[][];
|
|
41
|
+
setInitialData(): Promise<void>;
|
|
42
|
+
addPerpMarket(_marketIndex: number): Promise<boolean>;
|
|
43
|
+
addSpotMarket(_marketIndex: number): Promise<boolean>;
|
|
44
|
+
addOracle(oracleInfo: OracleInfo): Promise<boolean>;
|
|
14
45
|
subscribe(): Promise<boolean>;
|
|
46
|
+
fetch(): Promise<void>;
|
|
47
|
+
private assertIsSubscribed;
|
|
48
|
+
getStateAccountAndSlot(): DataAndSlot<StateAccount>;
|
|
49
|
+
getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[];
|
|
50
|
+
getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[];
|
|
15
51
|
getMarketAccountAndSlot(marketIndex: number): DataAndSlot<PerpMarketAccount> | undefined;
|
|
16
52
|
getSpotMarketAccountAndSlot(marketIndex: number): DataAndSlot<SpotMarketAccount> | undefined;
|
|
53
|
+
getOraclePriceDataAndSlot(oracleId: string): DataAndSlot<OraclePriceData> | undefined;
|
|
54
|
+
getOraclePriceDataAndSlotForPerpMarket(marketIndex: number): DataAndSlot<OraclePriceData> | undefined;
|
|
55
|
+
getOraclePriceDataAndSlotForSpotMarket(marketIndex: number): DataAndSlot<OraclePriceData> | undefined;
|
|
17
56
|
setPerpOracleMap(): Promise<void>;
|
|
18
57
|
setSpotOracleMap(): Promise<void>;
|
|
19
58
|
subscribeToPerpMarketAccounts(): Promise<boolean>;
|
|
20
59
|
subscribeToSpotMarketAccounts(): Promise<boolean>;
|
|
21
60
|
subscribeToOracles(): Promise<boolean>;
|
|
61
|
+
handleDelistedMarkets(): Promise<void>;
|
|
62
|
+
removeInitialData(): void;
|
|
22
63
|
unsubscribeFromOracles(): Promise<void>;
|
|
23
64
|
unsubscribe(): Promise<void>;
|
|
24
65
|
}
|
|
@@ -1,19 +1,115 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.grpcDriftClientAccountSubscriberV2 = void 0;
|
|
4
|
-
const
|
|
4
|
+
const events_1 = require("events");
|
|
5
5
|
const web3_js_1 = require("@solana/web3.js");
|
|
6
6
|
const config_1 = require("../config");
|
|
7
7
|
const pda_1 = require("../addresses/pda");
|
|
8
|
+
const types_1 = require("./types");
|
|
8
9
|
const grpcAccountSubscriber_1 = require("./grpcAccountSubscriber");
|
|
9
10
|
const grpcMultiAccountSubscriber_1 = require("./grpcMultiAccountSubscriber");
|
|
10
11
|
const oracleId_1 = require("../oracles/oracleId");
|
|
11
|
-
|
|
12
|
+
const oracleClientCache_1 = require("../oracles/oracleClientCache");
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
|
+
class grpcDriftClientAccountSubscriberV2 {
|
|
12
15
|
constructor(grpcConfigs, program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts) {
|
|
13
|
-
super(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts);
|
|
14
16
|
this.perpMarketIndexToAccountPubkeyMap = new Map();
|
|
15
17
|
this.spotMarketIndexToAccountPubkeyMap = new Map();
|
|
18
|
+
this.perpOracleMap = new Map();
|
|
19
|
+
this.perpOracleStringMap = new Map();
|
|
20
|
+
this.spotOracleMap = new Map();
|
|
21
|
+
this.spotOracleStringMap = new Map();
|
|
22
|
+
this.oracleIdToOracleDataMap = new Map();
|
|
23
|
+
this.oracleClientCache = new oracleClientCache_1.OracleClientCache();
|
|
24
|
+
this.chunks = (array, size) => {
|
|
25
|
+
return new Array(Math.ceil(array.length / size))
|
|
26
|
+
.fill(null)
|
|
27
|
+
.map((_, index) => index * size)
|
|
28
|
+
.map((begin) => array.slice(begin, begin + size));
|
|
29
|
+
};
|
|
30
|
+
this.eventEmitter = new events_1.EventEmitter();
|
|
31
|
+
this.isSubscribed = false;
|
|
32
|
+
this.isSubscribing = false;
|
|
33
|
+
this.program = program;
|
|
34
|
+
this.perpMarketIndexes = perpMarketIndexes;
|
|
35
|
+
this.spotMarketIndexes = spotMarketIndexes;
|
|
36
|
+
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
37
|
+
this.oracleInfos = oracleInfos;
|
|
38
|
+
this.initialPerpMarketAccountData = new Map();
|
|
39
|
+
this.initialSpotMarketAccountData = new Map();
|
|
40
|
+
this.initialOraclePriceData = new Map();
|
|
41
|
+
this.perpOracleMap = new Map();
|
|
42
|
+
this.perpOracleStringMap = new Map();
|
|
43
|
+
this.spotOracleMap = new Map();
|
|
44
|
+
this.spotOracleStringMap = new Map();
|
|
16
45
|
this.grpcConfigs = grpcConfigs;
|
|
46
|
+
this.resubOpts = resubOpts;
|
|
47
|
+
this.delistedMarketSetting = delistedMarketSetting;
|
|
48
|
+
}
|
|
49
|
+
async setInitialData() {
|
|
50
|
+
const connection = this.program.provider.connection;
|
|
51
|
+
if (!this.initialPerpMarketAccountData ||
|
|
52
|
+
this.initialPerpMarketAccountData.size === 0) {
|
|
53
|
+
const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) => (0, pda_1.getPerpMarketPublicKeySync)(this.program.programId, marketIndex));
|
|
54
|
+
const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
|
|
55
|
+
const perpMarketAccountInfos = (await Promise.all(perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) => connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)))).flat();
|
|
56
|
+
this.initialPerpMarketAccountData = new Map(perpMarketAccountInfos
|
|
57
|
+
.filter((accountInfo) => !!accountInfo)
|
|
58
|
+
.map((accountInfo) => {
|
|
59
|
+
const perpMarket = this.program.coder.accounts.decode('PerpMarket', accountInfo.data);
|
|
60
|
+
return [perpMarket.marketIndex, perpMarket];
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (!this.initialSpotMarketAccountData ||
|
|
64
|
+
this.initialSpotMarketAccountData.size === 0) {
|
|
65
|
+
const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) => (0, pda_1.getSpotMarketPublicKeySync)(this.program.programId, marketIndex));
|
|
66
|
+
const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
|
|
67
|
+
const spotMarketAccountInfos = (await Promise.all(spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) => connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)))).flat();
|
|
68
|
+
this.initialSpotMarketAccountData = new Map(spotMarketAccountInfos
|
|
69
|
+
.filter((accountInfo) => !!accountInfo)
|
|
70
|
+
.map((accountInfo) => {
|
|
71
|
+
const spotMarket = this.program.coder.accounts.decode('SpotMarket', accountInfo.data);
|
|
72
|
+
return [spotMarket.marketIndex, spotMarket];
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
const oracleAccountPubkeyChunks = this.chunks(this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey), 75);
|
|
76
|
+
const oracleAccountInfos = (await Promise.all(oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) => connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)))).flat();
|
|
77
|
+
this.initialOraclePriceData = new Map(this.oracleInfos.reduce((result, oracleInfo, i) => {
|
|
78
|
+
if (!oracleAccountInfos[i]) {
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
const oracleClient = this.oracleClientCache.get(oracleInfo.source, connection, this.program);
|
|
82
|
+
const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(oracleAccountInfos[i].data);
|
|
83
|
+
result.push([
|
|
84
|
+
(0, oracleId_1.getOracleId)(oracleInfo.publicKey, oracleInfo.source),
|
|
85
|
+
oraclePriceData,
|
|
86
|
+
]);
|
|
87
|
+
return result;
|
|
88
|
+
}, []));
|
|
89
|
+
}
|
|
90
|
+
async addPerpMarket(_marketIndex) {
|
|
91
|
+
if (!this.perpMarketIndexes.includes(_marketIndex)) {
|
|
92
|
+
this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
async addSpotMarket(_marketIndex) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
async addOracle(oracleInfo) {
|
|
100
|
+
if (oracleInfo.publicKey.equals(web3_js_1.PublicKey.default)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
|
|
104
|
+
o.publicKey.equals(oracleInfo.publicKey));
|
|
105
|
+
if (!exists) {
|
|
106
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
107
|
+
}
|
|
108
|
+
if (this.oracleMultiSubscriber) {
|
|
109
|
+
await this.unsubscribeFromOracles();
|
|
110
|
+
await this.subscribeToOracles();
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
17
113
|
}
|
|
18
114
|
async subscribe() {
|
|
19
115
|
if (this.isSubscribed) {
|
|
@@ -61,6 +157,32 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
61
157
|
this.removeInitialData();
|
|
62
158
|
return true;
|
|
63
159
|
}
|
|
160
|
+
async fetch() {
|
|
161
|
+
var _a, _c, _d, _e;
|
|
162
|
+
await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.fetch());
|
|
163
|
+
await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.fetch());
|
|
164
|
+
await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.fetch());
|
|
165
|
+
await ((_e = this.oracleMultiSubscriber) === null || _e === void 0 ? void 0 : _e.fetch());
|
|
166
|
+
}
|
|
167
|
+
assertIsSubscribed() {
|
|
168
|
+
if (!this.isSubscribed) {
|
|
169
|
+
throw new types_1.NotSubscribedError('You must call `subscribe` before using this function');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
getStateAccountAndSlot() {
|
|
173
|
+
this.assertIsSubscribed();
|
|
174
|
+
return this.stateAccountSubscriber.dataAndSlot;
|
|
175
|
+
}
|
|
176
|
+
getMarketAccountsAndSlots() {
|
|
177
|
+
var _a, _c;
|
|
178
|
+
const map = (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
|
|
179
|
+
return Array.from((_c = map === null || map === void 0 ? void 0 : map.values()) !== null && _c !== void 0 ? _c : []);
|
|
180
|
+
}
|
|
181
|
+
getSpotMarketAccountsAndSlots() {
|
|
182
|
+
var _a, _c;
|
|
183
|
+
const map = (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
|
|
184
|
+
return Array.from((_c = map === null || map === void 0 ? void 0 : map.values()) !== null && _c !== void 0 ? _c : []);
|
|
185
|
+
}
|
|
64
186
|
getMarketAccountAndSlot(marketIndex) {
|
|
65
187
|
var _a;
|
|
66
188
|
return (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.perpMarketIndexToAccountPubkeyMap.get(marketIndex));
|
|
@@ -69,8 +191,40 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
69
191
|
var _a;
|
|
70
192
|
return (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.spotMarketIndexToAccountPubkeyMap.get(marketIndex));
|
|
71
193
|
}
|
|
194
|
+
getOraclePriceDataAndSlot(oracleId) {
|
|
195
|
+
this.assertIsSubscribed();
|
|
196
|
+
// we need to rely on a map we store in this class because the grpcMultiAccountSubscriber does not track a mapping or oracle ID.
|
|
197
|
+
// 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).
|
|
198
|
+
return this.oracleIdToOracleDataMap.get(oracleId);
|
|
199
|
+
}
|
|
200
|
+
getOraclePriceDataAndSlotForPerpMarket(marketIndex) {
|
|
201
|
+
const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
|
|
202
|
+
const oracle = this.perpOracleMap.get(marketIndex);
|
|
203
|
+
const oracleId = this.perpOracleStringMap.get(marketIndex);
|
|
204
|
+
if (!perpMarketAccount || !oracleId) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
|
|
208
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
209
|
+
this.setPerpOracleMap();
|
|
210
|
+
}
|
|
211
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
212
|
+
}
|
|
213
|
+
getOraclePriceDataAndSlotForSpotMarket(marketIndex) {
|
|
214
|
+
const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
|
|
215
|
+
const oracle = this.spotOracleMap.get(marketIndex);
|
|
216
|
+
const oracleId = this.spotOracleStringMap.get(marketIndex);
|
|
217
|
+
if (!spotMarketAccount || !oracleId) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (!spotMarketAccount.data.oracle.equals(oracle)) {
|
|
221
|
+
// If the oracle has changed, we need to update the oracle map in background
|
|
222
|
+
this.setSpotOracleMap();
|
|
223
|
+
}
|
|
224
|
+
return this.getOraclePriceDataAndSlot(oracleId);
|
|
225
|
+
}
|
|
72
226
|
async setPerpOracleMap() {
|
|
73
|
-
var _a;
|
|
227
|
+
var _a, _c;
|
|
74
228
|
const perpMarketsMap = (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
|
|
75
229
|
const perpMarkets = Array.from(perpMarketsMap.values());
|
|
76
230
|
const addOraclePromises = [];
|
|
@@ -82,7 +236,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
82
236
|
const perpMarketIndex = perpMarketAccount.marketIndex;
|
|
83
237
|
const oracle = perpMarketAccount.amm.oracle;
|
|
84
238
|
const oracleId = (0, oracleId_1.getOracleId)(oracle, perpMarket.data.amm.oracleSource);
|
|
85
|
-
if (!this.
|
|
239
|
+
if (!((_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().has(oracleId))) {
|
|
86
240
|
addOraclePromises.push(this.addOracle({
|
|
87
241
|
publicKey: oracle,
|
|
88
242
|
source: perpMarket.data.amm.oracleSource,
|
|
@@ -94,7 +248,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
94
248
|
await Promise.all(addOraclePromises);
|
|
95
249
|
}
|
|
96
250
|
async setSpotOracleMap() {
|
|
97
|
-
var _a;
|
|
251
|
+
var _a, _c;
|
|
98
252
|
const spotMarketsMap = (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
|
|
99
253
|
const spotMarkets = Array.from(spotMarketsMap.values());
|
|
100
254
|
const addOraclePromises = [];
|
|
@@ -106,7 +260,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
106
260
|
const spotMarketIndex = spotMarketAccount.marketIndex;
|
|
107
261
|
const oracle = spotMarketAccount.oracle;
|
|
108
262
|
const oracleId = (0, oracleId_1.getOracleId)(oracle, spotMarketAccount.oracleSource);
|
|
109
|
-
if (!this.
|
|
263
|
+
if (!((_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().has(oracleId))) {
|
|
110
264
|
addOraclePromises.push(this.addOracle({
|
|
111
265
|
publicKey: oracle,
|
|
112
266
|
source: spotMarketAccount.oracleSource,
|
|
@@ -180,72 +334,92 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
|
|
|
180
334
|
return true;
|
|
181
335
|
}
|
|
182
336
|
async subscribeToOracles() {
|
|
183
|
-
const
|
|
337
|
+
const oraclePubkeyToInfosMap = new Map();
|
|
184
338
|
for (const info of this.oracleInfos) {
|
|
185
|
-
|
|
186
|
-
|
|
339
|
+
const pubkey = info.publicKey.toBase58();
|
|
340
|
+
if (!oraclePubkeyToInfosMap.has(pubkey)) {
|
|
341
|
+
oraclePubkeyToInfosMap.set(pubkey, []);
|
|
187
342
|
}
|
|
188
|
-
|
|
189
|
-
let sources = pubkeyToSources.get(key);
|
|
190
|
-
if (!sources) {
|
|
191
|
-
sources = new Set();
|
|
192
|
-
pubkeyToSources.set(key, sources);
|
|
193
|
-
}
|
|
194
|
-
sources.add(info.source);
|
|
343
|
+
oraclePubkeyToInfosMap.get(pubkey).push(info);
|
|
195
344
|
}
|
|
196
|
-
const oraclePubkeys = Array.from(
|
|
197
|
-
this.oracleMultiSubscriber =
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}, this.resubOpts, undefined, async () => {
|
|
210
|
-
var _a;
|
|
211
|
-
try {
|
|
212
|
-
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
213
|
-
console.log('[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing');
|
|
214
|
-
}
|
|
215
|
-
await this.subscribeToOracles();
|
|
216
|
-
}
|
|
217
|
-
catch (e) {
|
|
218
|
-
console.error('Oracle resubscribe failed:', e);
|
|
345
|
+
const oraclePubkeys = Array.from(new Set(this.oracleInfos.map((info) => info.publicKey)));
|
|
346
|
+
this.oracleMultiSubscriber = await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'oracle', this.program, (buffer, pubkey, accountProps) => {
|
|
347
|
+
if (!pubkey) {
|
|
348
|
+
throw new Error('Oracle pubkey missing in decode');
|
|
349
|
+
}
|
|
350
|
+
const client = this.oracleClientCache.get(accountProps.source, this.program.provider.connection, this.program);
|
|
351
|
+
const price = client.getOraclePriceDataFromBuffer(buffer);
|
|
352
|
+
return price;
|
|
353
|
+
}, this.resubOpts, undefined, async () => {
|
|
354
|
+
var _a;
|
|
355
|
+
try {
|
|
356
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
357
|
+
console.log('[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing');
|
|
219
358
|
}
|
|
220
|
-
|
|
359
|
+
await this.subscribeToOracles();
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
console.error('Oracle resubscribe failed:', e);
|
|
363
|
+
}
|
|
364
|
+
}, oraclePubkeyToInfosMap);
|
|
221
365
|
for (const data of this.initialOraclePriceData.entries()) {
|
|
222
366
|
const { publicKey } = (0, oracleId_1.getPublicKeyAndSourceFromOracleId)(data[0]);
|
|
223
367
|
this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
|
|
368
|
+
this.oracleIdToOracleDataMap.set(data[0], {
|
|
369
|
+
data: data[1],
|
|
370
|
+
slot: 0,
|
|
371
|
+
});
|
|
224
372
|
}
|
|
225
|
-
await this.oracleMultiSubscriber.subscribe(oraclePubkeys, (accountId, data) => {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
373
|
+
await this.oracleMultiSubscriber.subscribe(oraclePubkeys, (accountId, data, context, _b, accountProps) => {
|
|
374
|
+
const oracleId = (0, oracleId_1.getOracleId)(accountId, accountProps.source);
|
|
375
|
+
this.oracleIdToOracleDataMap.set(oracleId, {
|
|
376
|
+
data,
|
|
377
|
+
slot: context.slot,
|
|
378
|
+
});
|
|
379
|
+
this.eventEmitter.emit('oraclePriceUpdate', accountId, accountProps.source, data);
|
|
232
380
|
this.eventEmitter.emit('update');
|
|
233
381
|
});
|
|
234
382
|
return true;
|
|
235
383
|
}
|
|
384
|
+
async handleDelistedMarkets() {
|
|
385
|
+
var _a, _c;
|
|
386
|
+
if (this.delistedMarketSetting === types_1.DelistedMarketSetting.Subscribe) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const { perpMarketIndexes, oracles } = (0, utils_1.findDelistedPerpMarketsAndOracles)(Array.from(((_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap().values()) || []), Array.from(((_c = this.spotMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().values()) || []));
|
|
390
|
+
for (const perpMarketIndex of perpMarketIndexes) {
|
|
391
|
+
await this.perpMarketsSubscriber.removeAccounts([
|
|
392
|
+
new web3_js_1.PublicKey(this.perpMarketIndexToAccountPubkeyMap.get(perpMarketIndex) || ''),
|
|
393
|
+
]);
|
|
394
|
+
if (this.delistedMarketSetting === types_1.DelistedMarketSetting.Discard) {
|
|
395
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(perpMarketIndex);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const oracle of oracles) {
|
|
399
|
+
await this.oracleMultiSubscriber.removeAccounts([oracle.publicKey]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
removeInitialData() {
|
|
403
|
+
this.initialPerpMarketAccountData = new Map();
|
|
404
|
+
this.initialSpotMarketAccountData = new Map();
|
|
405
|
+
this.initialOraclePriceData = new Map();
|
|
406
|
+
}
|
|
236
407
|
async unsubscribeFromOracles() {
|
|
237
408
|
if (this.oracleMultiSubscriber) {
|
|
238
409
|
await this.oracleMultiSubscriber.unsubscribe();
|
|
239
410
|
this.oracleMultiSubscriber = undefined;
|
|
240
411
|
return;
|
|
241
412
|
}
|
|
242
|
-
await super.unsubscribeFromOracles();
|
|
243
413
|
}
|
|
244
414
|
async unsubscribe() {
|
|
415
|
+
var _a, _c;
|
|
245
416
|
if (this.isSubscribed) {
|
|
246
417
|
return;
|
|
247
418
|
}
|
|
248
419
|
await this.stateAccountSubscriber.unsubscribe();
|
|
420
|
+
await this.unsubscribeFromOracles();
|
|
421
|
+
await ((_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
422
|
+
await ((_c = this.spotMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
|
|
249
423
|
}
|
|
250
424
|
}
|
|
251
425
|
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
|
|
@@ -4,7 +4,7 @@ import { Program } from '@coral-xyz/anchor';
|
|
|
4
4
|
import { Context, PublicKey } from '@solana/web3.js';
|
|
5
5
|
import { Client } from '../isomorphic/grpc';
|
|
6
6
|
import { DataAndSlot, GrpcConfigs, ResubOpts } from './types';
|
|
7
|
-
export declare class grpcMultiAccountSubscriber<T> {
|
|
7
|
+
export declare class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
8
8
|
private client;
|
|
9
9
|
private stream;
|
|
10
10
|
private commitmentLevel;
|
|
@@ -20,12 +20,14 @@ export declare class grpcMultiAccountSubscriber<T> {
|
|
|
20
20
|
private subscribedAccounts;
|
|
21
21
|
private onChangeMap;
|
|
22
22
|
private dataMap;
|
|
23
|
+
private accountPropsMap;
|
|
23
24
|
private constructor();
|
|
24
|
-
static create<U>(grpcConfigs: GrpcConfigs, accountName: string, program: Program, decodeBuffer?: (buffer: Buffer, pubkey?: string) =>
|
|
25
|
+
static create<T, U = undefined>(grpcConfigs: GrpcConfigs, accountName: string, program: Program, decodeBuffer?: (buffer: Buffer, pubkey?: string, accountProps?: U) => T, resubOpts?: ResubOpts, clientProp?: Client, onUnsubscribe?: () => Promise<void>, accountPropsMap?: Map<string, U | Array<U>>): Promise<grpcMultiAccountSubscriber<T, U>>;
|
|
25
26
|
setAccountData(accountPubkey: string, data: T, slot?: number): void;
|
|
26
27
|
getAccountData(accountPubkey: string): DataAndSlot<T> | undefined;
|
|
27
28
|
getAccountDataMap(): Map<string, DataAndSlot<T>>;
|
|
28
|
-
|
|
29
|
+
fetch(): Promise<void>;
|
|
30
|
+
subscribe(accounts: PublicKey[], onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer, accountProps: U) => void): Promise<void>;
|
|
29
31
|
addAccounts(accounts: PublicKey[]): Promise<void>;
|
|
30
32
|
removeAccounts(accounts: PublicKey[]): Promise<void>;
|
|
31
33
|
unsubscribe(): Promise<void>;
|
|
@@ -31,13 +31,26 @@ const web3_js_1 = require("@solana/web3.js");
|
|
|
31
31
|
const Buffer = __importStar(require("buffer"));
|
|
32
32
|
const bs58_1 = __importDefault(require("bs58"));
|
|
33
33
|
const grpc_1 = require("../isomorphic/grpc");
|
|
34
|
+
function commitmentLevelToCommitment(commitmentLevel) {
|
|
35
|
+
switch (commitmentLevel) {
|
|
36
|
+
case grpc_1.CommitmentLevel.PROCESSED:
|
|
37
|
+
return 'processed';
|
|
38
|
+
case grpc_1.CommitmentLevel.CONFIRMED:
|
|
39
|
+
return 'confirmed';
|
|
40
|
+
case grpc_1.CommitmentLevel.FINALIZED:
|
|
41
|
+
return 'finalized';
|
|
42
|
+
default:
|
|
43
|
+
return 'confirmed';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
34
46
|
class grpcMultiAccountSubscriber {
|
|
35
|
-
constructor(client, commitmentLevel, accountName, program, decodeBuffer, resubOpts, onUnsubscribe) {
|
|
47
|
+
constructor(client, commitmentLevel, accountName, program, decodeBuffer, resubOpts, onUnsubscribe, accountPropsMap) {
|
|
36
48
|
this.isUnsubscribing = false;
|
|
37
49
|
this.receivingData = false;
|
|
38
50
|
this.subscribedAccounts = new Set();
|
|
39
51
|
this.onChangeMap = new Map();
|
|
40
52
|
this.dataMap = new Map();
|
|
53
|
+
this.accountPropsMap = new Map();
|
|
41
54
|
this.client = client;
|
|
42
55
|
this.commitmentLevel = commitmentLevel;
|
|
43
56
|
this.accountName = accountName;
|
|
@@ -45,8 +58,9 @@ class grpcMultiAccountSubscriber {
|
|
|
45
58
|
this.decodeBufferFn = decodeBuffer;
|
|
46
59
|
this.resubOpts = resubOpts;
|
|
47
60
|
this.onUnsubscribe = onUnsubscribe;
|
|
61
|
+
this.accountPropsMap = accountPropsMap;
|
|
48
62
|
}
|
|
49
|
-
static async create(grpcConfigs, accountName, program, decodeBuffer, resubOpts, clientProp, onUnsubscribe) {
|
|
63
|
+
static async create(grpcConfigs, accountName, program, decodeBuffer, resubOpts, clientProp, onUnsubscribe, accountPropsMap) {
|
|
50
64
|
var _a, _b;
|
|
51
65
|
const client = clientProp
|
|
52
66
|
? clientProp
|
|
@@ -54,7 +68,7 @@ class grpcMultiAccountSubscriber {
|
|
|
54
68
|
const commitmentLevel =
|
|
55
69
|
// @ts-ignore :: isomorphic exported enum fails typescript but will work at runtime
|
|
56
70
|
(_b = grpcConfigs.commitmentLevel) !== null && _b !== void 0 ? _b : grpc_1.CommitmentLevel.CONFIRMED;
|
|
57
|
-
return new grpcMultiAccountSubscriber(client, commitmentLevel, accountName, program, decodeBuffer, resubOpts, onUnsubscribe);
|
|
71
|
+
return new grpcMultiAccountSubscriber(client, commitmentLevel, accountName, program, decodeBuffer, resubOpts, onUnsubscribe, accountPropsMap);
|
|
58
72
|
}
|
|
59
73
|
setAccountData(accountPubkey, data, slot) {
|
|
60
74
|
this.dataMap.set(accountPubkey, { data, slot });
|
|
@@ -65,6 +79,40 @@ class grpcMultiAccountSubscriber {
|
|
|
65
79
|
getAccountDataMap() {
|
|
66
80
|
return this.dataMap;
|
|
67
81
|
}
|
|
82
|
+
async fetch() {
|
|
83
|
+
var _a;
|
|
84
|
+
try {
|
|
85
|
+
// Chunk account IDs into groups of 100 (getMultipleAccounts limit)
|
|
86
|
+
const chunkSize = 100;
|
|
87
|
+
const chunks = [];
|
|
88
|
+
const accountIds = Array.from(this.subscribedAccounts.values());
|
|
89
|
+
for (let i = 0; i < accountIds.length; i += chunkSize) {
|
|
90
|
+
chunks.push(accountIds.slice(i, i + chunkSize));
|
|
91
|
+
}
|
|
92
|
+
// Process all chunks concurrently
|
|
93
|
+
await Promise.all(chunks.map(async (chunk) => {
|
|
94
|
+
const accountAddresses = chunk.map((accountId) => new web3_js_1.PublicKey(accountId));
|
|
95
|
+
const rpcResponseAndContext = await this.program.provider.connection.getMultipleAccountsInfoAndContext(accountAddresses, {
|
|
96
|
+
commitment: commitmentLevelToCommitment(this.commitmentLevel),
|
|
97
|
+
});
|
|
98
|
+
const rpcResponse = rpcResponseAndContext.value;
|
|
99
|
+
const currentSlot = rpcResponseAndContext.context.slot;
|
|
100
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
101
|
+
const accountId = chunk[i];
|
|
102
|
+
const accountInfo = rpcResponse[i];
|
|
103
|
+
if (accountInfo) {
|
|
104
|
+
const perpMarket = this.program.coder.accounts.decode('PerpMarket', accountInfo.data);
|
|
105
|
+
this.setAccountData(accountId, perpMarket, currentSlot);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
112
|
+
console.log(`[${this.accountName}] grpcMultiAccountSubscriber error fetching accounts:`, error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
68
116
|
async subscribe(accounts, onChange) {
|
|
69
117
|
if (this.listenerId != null || this.isUnsubscribing) {
|
|
70
118
|
return;
|
|
@@ -73,9 +121,9 @@ class grpcMultiAccountSubscriber {
|
|
|
73
121
|
for (const pk of accounts) {
|
|
74
122
|
const key = pk.toBase58();
|
|
75
123
|
this.subscribedAccounts.add(key);
|
|
76
|
-
this.onChangeMap.set(key, (data, ctx, buffer) => {
|
|
124
|
+
this.onChangeMap.set(key, (data, ctx, buffer, accountProps) => {
|
|
77
125
|
this.setAccountData(key, data, ctx.slot);
|
|
78
|
-
onChange(new web3_js_1.PublicKey(key), data, ctx, buffer);
|
|
126
|
+
onChange(new web3_js_1.PublicKey(key), data, ctx, buffer, accountProps);
|
|
79
127
|
});
|
|
80
128
|
}
|
|
81
129
|
this.stream =
|
|
@@ -117,21 +165,33 @@ class grpcMultiAccountSubscriber {
|
|
|
117
165
|
};
|
|
118
166
|
const context = { slot };
|
|
119
167
|
const buffer = accountInfo.data;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
168
|
+
const accountProps = (_a = this.accountPropsMap) === null || _a === void 0 ? void 0 : _a.get(accountPubkey);
|
|
169
|
+
const handleDataBuffer = (context, buffer, accountProps) => {
|
|
170
|
+
var _a;
|
|
171
|
+
const data = this.decodeBufferFn
|
|
172
|
+
? this.decodeBufferFn(buffer, accountPubkey, accountProps)
|
|
173
|
+
: this.program.account[this.accountName].coder.accounts.decode(this.capitalize(this.accountName), buffer);
|
|
174
|
+
const handler = this.onChangeMap.get(accountPubkey);
|
|
175
|
+
if (handler) {
|
|
176
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
177
|
+
this.receivingData = true;
|
|
178
|
+
clearTimeout(this.timeoutId);
|
|
179
|
+
handler(data, context, buffer, accountProps);
|
|
180
|
+
this.setTimeout();
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
handler(data, context, buffer, accountProps);
|
|
184
|
+
}
|
|
130
185
|
}
|
|
131
|
-
|
|
132
|
-
|
|
186
|
+
};
|
|
187
|
+
if (Array.isArray(accountProps)) {
|
|
188
|
+
for (const props of accountProps) {
|
|
189
|
+
handleDataBuffer(context, buffer, props);
|
|
133
190
|
}
|
|
134
191
|
}
|
|
192
|
+
else {
|
|
193
|
+
handleDataBuffer(context, buffer, accountProps);
|
|
194
|
+
}
|
|
135
195
|
});
|
|
136
196
|
return new Promise((resolve, reject) => {
|
|
137
197
|
this.stream.write(request, (err) => {
|