@drift-labs/sdk 2.142.0 ā 2.143.0-beta.1
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.js +39 -7
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +39 -7
- package/package.json +1 -1
- package/scripts/single-grpc-client-test.ts +226 -0
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +43 -4
- /package/scripts/{client-test.ts ā grpc-client-test-comparison.ts} +0 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.143.0-beta.1
|
|
@@ -106,9 +106,10 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
106
106
|
}
|
|
107
107
|
const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
|
|
108
108
|
o.publicKey.equals(oracleInfo.publicKey));
|
|
109
|
-
if (
|
|
110
|
-
|
|
109
|
+
if (exists) {
|
|
110
|
+
return true; // Already exists, don't add duplicate
|
|
111
111
|
}
|
|
112
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
112
113
|
(_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.addAccounts([oracleInfo.publicKey]);
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
@@ -412,10 +413,31 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
412
413
|
// Remove accounts in batches - perp markets
|
|
413
414
|
if (perpMarketPubkeysToRemove.length > 0) {
|
|
414
415
|
await this.perpMarketsSubscriber.removeAccounts(perpMarketPubkeysToRemove);
|
|
416
|
+
// Clean up the mapping for removed perp markets
|
|
417
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
418
|
+
const pubkeyString = pubkey.toBase58();
|
|
419
|
+
for (const [marketIndex, accountPubkey,] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
420
|
+
if (accountPubkey === pubkeyString) {
|
|
421
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
422
|
+
this.perpOracleMap.delete(marketIndex);
|
|
423
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
415
428
|
}
|
|
416
429
|
// Remove accounts in batches - oracles
|
|
417
430
|
if (oraclePubkeysToRemove.length > 0) {
|
|
418
431
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
432
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
433
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
434
|
+
// Find the oracle source by checking oracleInfos
|
|
435
|
+
const oracleInfo = this.oracleInfos.find((info) => info.publicKey.equals(pubkey));
|
|
436
|
+
if (oracleInfo) {
|
|
437
|
+
const oracleId = (0, oracleId_1.getOracleId)(pubkey, oracleInfo.source);
|
|
438
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
removeInitialData() {
|
|
@@ -431,14 +453,24 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
455
|
async unsubscribe() {
|
|
434
|
-
var _a, _c;
|
|
435
|
-
if (this.isSubscribed) {
|
|
456
|
+
var _a, _c, _d;
|
|
457
|
+
if (!this.isSubscribed) {
|
|
436
458
|
return;
|
|
437
459
|
}
|
|
438
|
-
|
|
460
|
+
this.isSubscribed = false;
|
|
461
|
+
this.isSubscribing = false;
|
|
462
|
+
await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
439
463
|
await this.unsubscribeFromOracles();
|
|
440
|
-
await ((
|
|
441
|
-
await ((
|
|
464
|
+
await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
|
|
465
|
+
await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.unsubscribe());
|
|
466
|
+
// Clean up all maps to prevent memory leaks
|
|
467
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
468
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
469
|
+
this.oracleIdToOracleDataMap.clear();
|
|
470
|
+
this.perpOracleMap.clear();
|
|
471
|
+
this.perpOracleStringMap.clear();
|
|
472
|
+
this.spotOracleMap.clear();
|
|
473
|
+
this.spotOracleStringMap.clear();
|
|
442
474
|
}
|
|
443
475
|
}
|
|
444
476
|
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grpcDriftClientAccountSubscriberV2.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcDriftClientAccountSubscriberV2.ts"],"names":[],"mappings":";AAAA,OAAO,kBAAkB,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAS5C,OAAO,EACN,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,wBAAwB,EACxB,4BAA4B,EAE5B,WAAW,EACX,SAAS,EACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAK9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,qBAAa,kCACZ,YAAW,4BAA4B;IAEvC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,qBAAqB,CAAC,CAAgD;IAC9E,OAAO,CAAC,qBAAqB,CAAC,CAAgD;IAC9E,OAAO,CAAC,qBAAqB,CAAC,CAG5B;IACF,OAAO,CAAC,iCAAiC,CAA6B;IACtE,OAAO,CAAC,iCAAiC,CAA6B;IACtE,OAAO,CAAC,qBAAqB,CAAwB;IAE9C,YAAY,EAAE,kBAAkB,CACtC,YAAY,EACZ,wBAAwB,CACxB,CAAC;IACK,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,8BAA8B,EAAE,OAAO,CAAC;IACxC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,4BAA4B,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC7D,4BAA4B,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC7D,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,aAAa,yBAAgC;IAC7C,mBAAmB,sBAA6B;IAChD,aAAa,yBAAgC;IAC7C,mBAAmB,sBAA6B;IACvD,OAAO,CAAC,uBAAuB,CAG3B;IACG,sBAAsB,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAChE,iBAAiB,oBAA2B;IAC5C,OAAO,CAAC,SAAS,CAAC,CAAY;IAE9B,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,SAAS,CAAC,2BAA2B,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;gBAG7D,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,iBAAiB,EAAE,MAAM,EAAE,EAC3B,iBAAiB,EAAE,MAAM,EAAE,EAC3B,WAAW,EAAE,UAAU,EAAE,EACzB,8BAA8B,EAAE,OAAO,EACvC,qBAAqB,EAAE,qBAAqB,EAC5C,SAAS,CAAC,EAAE,SAAS;IAsBtB,MAAM,aAAc,SAAS,CAAC,EAAE,QAAQ,MAAM,KAAG,CAAC,EAAE,EAAE,CAKpD;IAEI,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA4F/B,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOrD,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"grpcDriftClientAccountSubscriberV2.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcDriftClientAccountSubscriberV2.ts"],"names":[],"mappings":"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"}
|
|
@@ -106,9 +106,10 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
106
106
|
}
|
|
107
107
|
const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
|
|
108
108
|
o.publicKey.equals(oracleInfo.publicKey));
|
|
109
|
-
if (
|
|
110
|
-
|
|
109
|
+
if (exists) {
|
|
110
|
+
return true; // Already exists, don't add duplicate
|
|
111
111
|
}
|
|
112
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
112
113
|
(_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.addAccounts([oracleInfo.publicKey]);
|
|
113
114
|
return true;
|
|
114
115
|
}
|
|
@@ -412,10 +413,31 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
412
413
|
// Remove accounts in batches - perp markets
|
|
413
414
|
if (perpMarketPubkeysToRemove.length > 0) {
|
|
414
415
|
await this.perpMarketsSubscriber.removeAccounts(perpMarketPubkeysToRemove);
|
|
416
|
+
// Clean up the mapping for removed perp markets
|
|
417
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
418
|
+
const pubkeyString = pubkey.toBase58();
|
|
419
|
+
for (const [marketIndex, accountPubkey,] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
420
|
+
if (accountPubkey === pubkeyString) {
|
|
421
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
422
|
+
this.perpOracleMap.delete(marketIndex);
|
|
423
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
415
428
|
}
|
|
416
429
|
// Remove accounts in batches - oracles
|
|
417
430
|
if (oraclePubkeysToRemove.length > 0) {
|
|
418
431
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
432
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
433
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
434
|
+
// Find the oracle source by checking oracleInfos
|
|
435
|
+
const oracleInfo = this.oracleInfos.find((info) => info.publicKey.equals(pubkey));
|
|
436
|
+
if (oracleInfo) {
|
|
437
|
+
const oracleId = (0, oracleId_1.getOracleId)(pubkey, oracleInfo.source);
|
|
438
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
removeInitialData() {
|
|
@@ -431,14 +453,24 @@ class grpcDriftClientAccountSubscriberV2 {
|
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
455
|
async unsubscribe() {
|
|
434
|
-
var _a, _c;
|
|
435
|
-
if (this.isSubscribed) {
|
|
456
|
+
var _a, _c, _d;
|
|
457
|
+
if (!this.isSubscribed) {
|
|
436
458
|
return;
|
|
437
459
|
}
|
|
438
|
-
|
|
460
|
+
this.isSubscribed = false;
|
|
461
|
+
this.isSubscribing = false;
|
|
462
|
+
await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
439
463
|
await this.unsubscribeFromOracles();
|
|
440
|
-
await ((
|
|
441
|
-
await ((
|
|
464
|
+
await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
|
|
465
|
+
await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.unsubscribe());
|
|
466
|
+
// Clean up all maps to prevent memory leaks
|
|
467
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
468
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
469
|
+
this.oracleIdToOracleDataMap.clear();
|
|
470
|
+
this.perpOracleMap.clear();
|
|
471
|
+
this.perpOracleStringMap.clear();
|
|
472
|
+
this.spotOracleMap.clear();
|
|
473
|
+
this.spotOracleStringMap.clear();
|
|
442
474
|
}
|
|
443
475
|
}
|
|
444
476
|
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
|
package/package.json
CHANGED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { DriftClient } from '../src/driftClient';
|
|
2
|
+
import { grpcDriftClientAccountSubscriberV2 } from '../src/accounts/grpcDriftClientAccountSubscriberV2';
|
|
3
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { DriftClientConfig } from '../src/driftClientConfig';
|
|
5
|
+
import {
|
|
6
|
+
DRIFT_PROGRAM_ID,
|
|
7
|
+
PerpMarketAccount,
|
|
8
|
+
SpotMarketAccount,
|
|
9
|
+
Wallet,
|
|
10
|
+
OracleInfo,
|
|
11
|
+
decodeName,
|
|
12
|
+
} from '../src';
|
|
13
|
+
import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
|
|
14
|
+
import dotenv from 'dotenv';
|
|
15
|
+
import {
|
|
16
|
+
AnchorProvider,
|
|
17
|
+
Idl,
|
|
18
|
+
Program,
|
|
19
|
+
ProgramAccount,
|
|
20
|
+
} from '@coral-xyz/anchor';
|
|
21
|
+
import driftIDL from '../src/idl/drift.json';
|
|
22
|
+
|
|
23
|
+
const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
|
|
24
|
+
const TOKEN = process.env.TOKEN;
|
|
25
|
+
const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
|
|
26
|
+
|
|
27
|
+
async function initializeSingleGrpcClient() {
|
|
28
|
+
console.log('š Initializing single gRPC Drift Client...');
|
|
29
|
+
|
|
30
|
+
const connection = new Connection(RPC_ENDPOINT);
|
|
31
|
+
const wallet = new Wallet(new Keypair());
|
|
32
|
+
dotenv.config({ path: '../' });
|
|
33
|
+
|
|
34
|
+
const programId = new PublicKey(DRIFT_PROGRAM_ID);
|
|
35
|
+
const provider = new AnchorProvider(
|
|
36
|
+
connection,
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
wallet,
|
|
39
|
+
{
|
|
40
|
+
commitment: 'processed',
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const program = new Program(driftIDL as Idl, programId, provider);
|
|
45
|
+
|
|
46
|
+
// Get perp market accounts
|
|
47
|
+
const allPerpMarketProgramAccounts =
|
|
48
|
+
(await program.account.perpMarket.all()) as ProgramAccount<PerpMarketAccount>[];
|
|
49
|
+
const perpMarketProgramAccounts = allPerpMarketProgramAccounts.filter((val) =>
|
|
50
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].includes(
|
|
51
|
+
val.account.marketIndex
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
const perpMarketIndexes = perpMarketProgramAccounts.map(
|
|
55
|
+
(val) => val.account.marketIndex
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Get spot market accounts
|
|
59
|
+
const allSpotMarketProgramAccounts =
|
|
60
|
+
(await program.account.spotMarket.all()) as ProgramAccount<SpotMarketAccount>[];
|
|
61
|
+
const spotMarketProgramAccounts = allSpotMarketProgramAccounts.filter((val) =>
|
|
62
|
+
[0, 1, 2, 3, 4, 5].includes(val.account.marketIndex)
|
|
63
|
+
);
|
|
64
|
+
const spotMarketIndexes = spotMarketProgramAccounts.map(
|
|
65
|
+
(val) => val.account.marketIndex
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Get oracle infos
|
|
69
|
+
const seen = new Set<string>();
|
|
70
|
+
const oracleInfos: OracleInfo[] = [];
|
|
71
|
+
for (const acct of perpMarketProgramAccounts) {
|
|
72
|
+
const key = `${acct.account.amm.oracle.toBase58()}-${
|
|
73
|
+
Object.keys(acct.account.amm.oracleSource)[0]
|
|
74
|
+
}`;
|
|
75
|
+
if (!seen.has(key)) {
|
|
76
|
+
seen.add(key);
|
|
77
|
+
oracleInfos.push({
|
|
78
|
+
publicKey: acct.account.amm.oracle,
|
|
79
|
+
source: acct.account.amm.oracleSource,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const acct of spotMarketProgramAccounts) {
|
|
84
|
+
const key = `${acct.account.oracle.toBase58()}-${
|
|
85
|
+
Object.keys(acct.account.oracleSource)[0]
|
|
86
|
+
}`;
|
|
87
|
+
if (!seen.has(key)) {
|
|
88
|
+
seen.add(key);
|
|
89
|
+
oracleInfos.push({
|
|
90
|
+
publicKey: acct.account.oracle,
|
|
91
|
+
source: acct.account.oracleSource,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`š Markets: ${perpMarketIndexes.length} perp, ${spotMarketIndexes.length} spot`);
|
|
97
|
+
console.log(`š® Oracles: ${oracleInfos.length}`);
|
|
98
|
+
|
|
99
|
+
const baseAccountSubscription = {
|
|
100
|
+
type: 'grpc' as const,
|
|
101
|
+
grpcConfigs: {
|
|
102
|
+
endpoint: GRPC_ENDPOINT,
|
|
103
|
+
token: TOKEN,
|
|
104
|
+
commitmentLevel: CommitmentLevel.PROCESSED,
|
|
105
|
+
channelOptions: {
|
|
106
|
+
'grpc.keepalive_time_ms': 10_000,
|
|
107
|
+
'grpc.keepalive_timeout_ms': 1_000,
|
|
108
|
+
'grpc.keepalive_permit_without_calls': 1,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const config: DriftClientConfig = {
|
|
114
|
+
connection,
|
|
115
|
+
wallet,
|
|
116
|
+
programID: new PublicKey(DRIFT_PROGRAM_ID),
|
|
117
|
+
accountSubscription: {
|
|
118
|
+
...baseAccountSubscription,
|
|
119
|
+
driftClientAccountSubscriber: grpcDriftClientAccountSubscriberV2,
|
|
120
|
+
},
|
|
121
|
+
perpMarketIndexes,
|
|
122
|
+
spotMarketIndexes,
|
|
123
|
+
oracleInfos,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const client = new DriftClient(config);
|
|
127
|
+
|
|
128
|
+
// Set up event listeners
|
|
129
|
+
const eventCounts = {
|
|
130
|
+
stateAccountUpdate: 0,
|
|
131
|
+
perpMarketAccountUpdate: 0,
|
|
132
|
+
spotMarketAccountUpdate: 0,
|
|
133
|
+
oraclePriceUpdate: 0,
|
|
134
|
+
update: 0,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
console.log('š§ Setting up event listeners...');
|
|
138
|
+
|
|
139
|
+
client.eventEmitter.on('stateAccountUpdate', (_data) => {
|
|
140
|
+
eventCounts.stateAccountUpdate++;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
client.eventEmitter.on('perpMarketAccountUpdate', (_data) => {
|
|
144
|
+
eventCounts.perpMarketAccountUpdate++;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
client.eventEmitter.on('spotMarketAccountUpdate', (_data) => {
|
|
148
|
+
eventCounts.spotMarketAccountUpdate++;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
client.eventEmitter.on('oraclePriceUpdate', (_publicKey, _source, _data) => {
|
|
152
|
+
eventCounts.oraclePriceUpdate++;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
client.accountSubscriber.eventEmitter.on('update', () => {
|
|
156
|
+
eventCounts.update++;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Subscribe
|
|
160
|
+
console.log('š Subscribing to accounts...');
|
|
161
|
+
await client.subscribe();
|
|
162
|
+
|
|
163
|
+
console.log('ā
Client subscribed successfully!');
|
|
164
|
+
console.log('š Starting high-load testing (50 reads/sec per perp market)...');
|
|
165
|
+
|
|
166
|
+
// High-frequency load testing - 50 reads per second per perp market
|
|
167
|
+
const loadTestInterval = setInterval(async () => {
|
|
168
|
+
try {
|
|
169
|
+
// Test getPerpMarketAccount for each perp market (50 times per second per market)
|
|
170
|
+
for (const marketIndex of perpMarketIndexes) {
|
|
171
|
+
const perpMarketAccount = client.getPerpMarketAccount(marketIndex);
|
|
172
|
+
console.log("perpMarketAccount name: ", decodeName(perpMarketAccount.name));
|
|
173
|
+
console.log("perpMarketAccount data: ", JSON.stringify({
|
|
174
|
+
marketIndex: perpMarketAccount.marketIndex,
|
|
175
|
+
name: decodeName(perpMarketAccount.name),
|
|
176
|
+
baseAssetReserve: perpMarketAccount.amm.baseAssetReserve.toString(),
|
|
177
|
+
quoteAssetReserve: perpMarketAccount.amm.quoteAssetReserve.toString()
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Test getMMOracleDataForPerpMarket for each perp market (50 times per second per market)
|
|
182
|
+
for (const marketIndex of perpMarketIndexes) {
|
|
183
|
+
try {
|
|
184
|
+
const oracleData = client.getMMOracleDataForPerpMarket(marketIndex);
|
|
185
|
+
console.log("oracleData price: ", oracleData.price.toString());
|
|
186
|
+
console.log("oracleData: ", JSON.stringify({
|
|
187
|
+
price: oracleData.price.toString(),
|
|
188
|
+
confidence: oracleData.confidence?.toString(),
|
|
189
|
+
slot: oracleData.slot?.toString()
|
|
190
|
+
}));
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// Ignore errors for load testing
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error('Load test error:', error);
|
|
197
|
+
}
|
|
198
|
+
}, 20); // 50 times per second = 1000ms / 50 = 20ms interval
|
|
199
|
+
|
|
200
|
+
// Log periodic stats
|
|
201
|
+
const statsInterval = setInterval(() => {
|
|
202
|
+
console.log('\nš Event Counts:', eventCounts);
|
|
203
|
+
console.log(`ā±ļø Client subscribed: ${client.isSubscribed}`);
|
|
204
|
+
console.log(`š Account subscriber subscribed: ${client.accountSubscriber.isSubscribed}`);
|
|
205
|
+
console.log(`š„ Load: ${perpMarketIndexes.length * 50 * 2} reads/sec (${perpMarketIndexes.length} markets Ć 50 getPerpMarketAccount + 50 getMMOracleDataForPerpMarket)`);
|
|
206
|
+
}, 5000);
|
|
207
|
+
|
|
208
|
+
// Handle shutdown signals - just exit without cleanup since they never unsubscribe
|
|
209
|
+
process.on('SIGINT', () => {
|
|
210
|
+
console.log('\nš Shutting down...');
|
|
211
|
+
clearInterval(loadTestInterval);
|
|
212
|
+
clearInterval(statsInterval);
|
|
213
|
+
process.exit(0);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
process.on('SIGTERM', () => {
|
|
217
|
+
console.log('\nš Shutting down...');
|
|
218
|
+
clearInterval(loadTestInterval);
|
|
219
|
+
clearInterval(statsInterval);
|
|
220
|
+
process.exit(0);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return client;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
initializeSingleGrpcClient().catch(console.error);
|
|
@@ -227,10 +227,11 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
227
227
|
o.source === oracleInfo.source &&
|
|
228
228
|
o.publicKey.equals(oracleInfo.publicKey)
|
|
229
229
|
);
|
|
230
|
-
if (
|
|
231
|
-
|
|
230
|
+
if (exists) {
|
|
231
|
+
return true; // Already exists, don't add duplicate
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
this.oracleInfos = this.oracleInfos.concat(oracleInfo);
|
|
234
235
|
this.oracleMultiSubscriber?.addAccounts([oracleInfo.publicKey]);
|
|
235
236
|
|
|
236
237
|
return true;
|
|
@@ -708,11 +709,37 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
708
709
|
await this.perpMarketsSubscriber.removeAccounts(
|
|
709
710
|
perpMarketPubkeysToRemove
|
|
710
711
|
);
|
|
712
|
+
// Clean up the mapping for removed perp markets
|
|
713
|
+
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
714
|
+
const pubkeyString = pubkey.toBase58();
|
|
715
|
+
for (const [
|
|
716
|
+
marketIndex,
|
|
717
|
+
accountPubkey,
|
|
718
|
+
] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
719
|
+
if (accountPubkey === pubkeyString) {
|
|
720
|
+
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
721
|
+
this.perpOracleMap.delete(marketIndex);
|
|
722
|
+
this.perpOracleStringMap.delete(marketIndex);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
711
727
|
}
|
|
712
728
|
|
|
713
729
|
// Remove accounts in batches - oracles
|
|
714
730
|
if (oraclePubkeysToRemove.length > 0) {
|
|
715
731
|
await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
|
|
732
|
+
// Clean up oracle data for removed oracles by finding their sources
|
|
733
|
+
for (const pubkey of oraclePubkeysToRemove) {
|
|
734
|
+
// Find the oracle source by checking oracleInfos
|
|
735
|
+
const oracleInfo = this.oracleInfos.find((info) =>
|
|
736
|
+
info.publicKey.equals(pubkey)
|
|
737
|
+
);
|
|
738
|
+
if (oracleInfo) {
|
|
739
|
+
const oracleId = getOracleId(pubkey, oracleInfo.source);
|
|
740
|
+
this.oracleIdToOracleDataMap.delete(oracleId);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
716
743
|
}
|
|
717
744
|
}
|
|
718
745
|
|
|
@@ -731,13 +758,25 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
731
758
|
}
|
|
732
759
|
|
|
733
760
|
async unsubscribe(): Promise<void> {
|
|
734
|
-
if (this.isSubscribed) {
|
|
761
|
+
if (!this.isSubscribed) {
|
|
735
762
|
return;
|
|
736
763
|
}
|
|
737
764
|
|
|
738
|
-
|
|
765
|
+
this.isSubscribed = false;
|
|
766
|
+
this.isSubscribing = false;
|
|
767
|
+
|
|
768
|
+
await this.stateAccountSubscriber?.unsubscribe();
|
|
739
769
|
await this.unsubscribeFromOracles();
|
|
740
770
|
await this.perpMarketsSubscriber?.unsubscribe();
|
|
741
771
|
await this.spotMarketsSubscriber?.unsubscribe();
|
|
772
|
+
|
|
773
|
+
// Clean up all maps to prevent memory leaks
|
|
774
|
+
this.perpMarketIndexToAccountPubkeyMap.clear();
|
|
775
|
+
this.spotMarketIndexToAccountPubkeyMap.clear();
|
|
776
|
+
this.oracleIdToOracleDataMap.clear();
|
|
777
|
+
this.perpOracleMap.clear();
|
|
778
|
+
this.perpOracleStringMap.clear();
|
|
779
|
+
this.spotOracleMap.clear();
|
|
780
|
+
this.spotOracleStringMap.clear();
|
|
742
781
|
}
|
|
743
782
|
}
|
|
File without changes
|