@drift-labs/sdk 2.142.0-beta.17 → 2.142.0-beta.19
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/grpcMultiAccountSubscriber.d.ts +1 -0
- package/lib/browser/accounts/grpcMultiAccountSubscriber.js +46 -15
- 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 -15
- package/package.json +1 -1
- package/scripts/client-test.ts +248 -60
- package/src/accounts/grpcMultiAccountSubscriber.ts +55 -14
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.142.0-beta.
|
|
1
|
+
2.142.0-beta.19
|
|
@@ -21,6 +21,7 @@ export declare class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
21
21
|
private onChangeMap;
|
|
22
22
|
private dataMap;
|
|
23
23
|
private accountPropsMap;
|
|
24
|
+
private bufferMap;
|
|
24
25
|
private constructor();
|
|
25
26
|
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>>;
|
|
26
27
|
setAccountData(accountPubkey: string, data: T, slot?: number): void;
|
|
@@ -51,6 +51,7 @@ class grpcMultiAccountSubscriber {
|
|
|
51
51
|
this.onChangeMap = new Map();
|
|
52
52
|
this.dataMap = new Map();
|
|
53
53
|
this.accountPropsMap = new Map();
|
|
54
|
+
this.bufferMap = new Map();
|
|
54
55
|
this.client = client;
|
|
55
56
|
this.commitmentLevel = commitmentLevel;
|
|
56
57
|
this.accountName = accountName;
|
|
@@ -101,8 +102,23 @@ class grpcMultiAccountSubscriber {
|
|
|
101
102
|
const accountId = chunk[i];
|
|
102
103
|
const accountInfo = rpcResponse[i];
|
|
103
104
|
if (accountInfo) {
|
|
104
|
-
const
|
|
105
|
-
|
|
105
|
+
const prev = this.bufferMap.get(accountId);
|
|
106
|
+
const newBuffer = accountInfo.data;
|
|
107
|
+
if (prev && currentSlot < prev.slot) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (prev &&
|
|
111
|
+
prev.buffer &&
|
|
112
|
+
newBuffer &&
|
|
113
|
+
newBuffer.equals(prev.buffer)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
this.bufferMap.set(accountId, {
|
|
117
|
+
buffer: newBuffer,
|
|
118
|
+
slot: currentSlot,
|
|
119
|
+
});
|
|
120
|
+
const accountDecoded = this.program.coder.accounts.decode(this.capitalize(this.accountName), newBuffer);
|
|
121
|
+
this.setAccountData(accountId, accountDecoded, currentSlot);
|
|
106
122
|
}
|
|
107
123
|
}
|
|
108
124
|
}));
|
|
@@ -150,7 +166,7 @@ class grpcMultiAccountSubscriber {
|
|
|
150
166
|
transactionsStatus: {},
|
|
151
167
|
};
|
|
152
168
|
this.stream.on('data', (chunk) => {
|
|
153
|
-
var _a;
|
|
169
|
+
var _a, _b;
|
|
154
170
|
if (!chunk.account) {
|
|
155
171
|
return;
|
|
156
172
|
}
|
|
@@ -160,6 +176,17 @@ class grpcMultiAccountSubscriber {
|
|
|
160
176
|
if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
|
|
161
177
|
return;
|
|
162
178
|
}
|
|
179
|
+
// Touch resub timer on any incoming account update for subscribed keys
|
|
180
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
181
|
+
this.receivingData = true;
|
|
182
|
+
clearTimeout(this.timeoutId);
|
|
183
|
+
this.setTimeout();
|
|
184
|
+
}
|
|
185
|
+
// Skip processing if we already have data for this account at a newer slot
|
|
186
|
+
const existing = this.dataMap.get(accountPubkey);
|
|
187
|
+
if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot > slot) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
163
190
|
const accountInfo = {
|
|
164
191
|
owner: new web3_js_1.PublicKey(chunk.account.account.owner),
|
|
165
192
|
lamports: Number(chunk.account.account.lamports),
|
|
@@ -169,23 +196,26 @@ class grpcMultiAccountSubscriber {
|
|
|
169
196
|
};
|
|
170
197
|
const context = { slot };
|
|
171
198
|
const buffer = accountInfo.data;
|
|
172
|
-
|
|
199
|
+
// Check existing buffer for this account and skip if unchanged or slot regressed
|
|
200
|
+
const prevBuffer = this.bufferMap.get(accountPubkey);
|
|
201
|
+
if (prevBuffer && slot < prevBuffer.slot) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (prevBuffer &&
|
|
205
|
+
prevBuffer.buffer &&
|
|
206
|
+
buffer &&
|
|
207
|
+
buffer.equals(prevBuffer.buffer)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.bufferMap.set(accountPubkey, { buffer, slot });
|
|
211
|
+
const accountProps = (_b = this.accountPropsMap) === null || _b === void 0 ? void 0 : _b.get(accountPubkey);
|
|
173
212
|
const handleDataBuffer = (context, buffer, accountProps) => {
|
|
174
|
-
var _a;
|
|
175
213
|
const data = this.decodeBufferFn
|
|
176
214
|
? this.decodeBufferFn(buffer, accountPubkey, accountProps)
|
|
177
215
|
: this.program.account[this.accountName].coder.accounts.decode(this.capitalize(this.accountName), buffer);
|
|
178
216
|
const handler = this.onChangeMap.get(accountPubkey);
|
|
179
217
|
if (handler) {
|
|
180
|
-
|
|
181
|
-
this.receivingData = true;
|
|
182
|
-
clearTimeout(this.timeoutId);
|
|
183
|
-
handler(data, context, buffer, accountProps);
|
|
184
|
-
this.setTimeout();
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
handler(data, context, buffer, accountProps);
|
|
188
|
-
}
|
|
218
|
+
handler(data, context, buffer, accountProps);
|
|
189
219
|
}
|
|
190
220
|
};
|
|
191
221
|
if (Array.isArray(accountProps)) {
|
|
@@ -204,7 +234,6 @@ class grpcMultiAccountSubscriber {
|
|
|
204
234
|
this.listenerId = 1;
|
|
205
235
|
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
206
236
|
this.receivingData = true;
|
|
207
|
-
this.setTimeout();
|
|
208
237
|
}
|
|
209
238
|
resolve();
|
|
210
239
|
}
|
|
@@ -254,6 +283,8 @@ class grpcMultiAccountSubscriber {
|
|
|
254
283
|
const k = pk.toBase58();
|
|
255
284
|
this.subscribedAccounts.delete(k);
|
|
256
285
|
this.onChangeMap.delete(k);
|
|
286
|
+
this.dataMap.delete(k);
|
|
287
|
+
this.bufferMap.delete(k);
|
|
257
288
|
}
|
|
258
289
|
const request = {
|
|
259
290
|
slots: {},
|
|
@@ -21,6 +21,7 @@ export declare class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
21
21
|
private onChangeMap;
|
|
22
22
|
private dataMap;
|
|
23
23
|
private accountPropsMap;
|
|
24
|
+
private bufferMap;
|
|
24
25
|
private constructor();
|
|
25
26
|
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>>;
|
|
26
27
|
setAccountData(accountPubkey: string, data: T, slot?: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grpcMultiAccountSubscriber.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcMultiAccountSubscriber.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAc,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,EACN,MAAM,EAMN,MAAM,oBAAoB,CAAC;AAC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"grpcMultiAccountSubscriber.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcMultiAccountSubscriber.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAc,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,EACN,MAAM,EAMN,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAiB,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAyB7E,qBAAa,0BAA0B,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS;IACvD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAwD;IACtE,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAC,CAIhB;IACP,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,aAAa,CAAC,CAAsB;IAErC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,UAAS;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAgC;IAClD,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,WAAW,CAGf;IAEJ,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,SAAS,CAAoC;IAErD,OAAO;WAoBa,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,EAC1C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,OAAO,EAChB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC,EACvE,SAAS,CAAC,EAAE,SAAS,EACrB,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACnC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,0BAA0B,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAwB5C,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAInE,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS;IAIjE,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAI1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoEtB,SAAS,CACd,QAAQ,EAAE,SAAS,EAAE,EACrB,QAAQ,EAAE,CACT,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,CAAC,KACX,IAAI,GACP,OAAO,CAAC,IAAI,CAAC;IAsIV,WAAW,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCjD,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCpD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA4ClC,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;CAIlB"}
|
|
@@ -51,6 +51,7 @@ class grpcMultiAccountSubscriber {
|
|
|
51
51
|
this.onChangeMap = new Map();
|
|
52
52
|
this.dataMap = new Map();
|
|
53
53
|
this.accountPropsMap = new Map();
|
|
54
|
+
this.bufferMap = new Map();
|
|
54
55
|
this.client = client;
|
|
55
56
|
this.commitmentLevel = commitmentLevel;
|
|
56
57
|
this.accountName = accountName;
|
|
@@ -101,8 +102,23 @@ class grpcMultiAccountSubscriber {
|
|
|
101
102
|
const accountId = chunk[i];
|
|
102
103
|
const accountInfo = rpcResponse[i];
|
|
103
104
|
if (accountInfo) {
|
|
104
|
-
const
|
|
105
|
-
|
|
105
|
+
const prev = this.bufferMap.get(accountId);
|
|
106
|
+
const newBuffer = accountInfo.data;
|
|
107
|
+
if (prev && currentSlot < prev.slot) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (prev &&
|
|
111
|
+
prev.buffer &&
|
|
112
|
+
newBuffer &&
|
|
113
|
+
newBuffer.equals(prev.buffer)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
this.bufferMap.set(accountId, {
|
|
117
|
+
buffer: newBuffer,
|
|
118
|
+
slot: currentSlot,
|
|
119
|
+
});
|
|
120
|
+
const accountDecoded = this.program.coder.accounts.decode(this.capitalize(this.accountName), newBuffer);
|
|
121
|
+
this.setAccountData(accountId, accountDecoded, currentSlot);
|
|
106
122
|
}
|
|
107
123
|
}
|
|
108
124
|
}));
|
|
@@ -150,7 +166,7 @@ class grpcMultiAccountSubscriber {
|
|
|
150
166
|
transactionsStatus: {},
|
|
151
167
|
};
|
|
152
168
|
this.stream.on('data', (chunk) => {
|
|
153
|
-
var _a;
|
|
169
|
+
var _a, _b;
|
|
154
170
|
if (!chunk.account) {
|
|
155
171
|
return;
|
|
156
172
|
}
|
|
@@ -160,6 +176,17 @@ class grpcMultiAccountSubscriber {
|
|
|
160
176
|
if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
|
|
161
177
|
return;
|
|
162
178
|
}
|
|
179
|
+
// Touch resub timer on any incoming account update for subscribed keys
|
|
180
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
181
|
+
this.receivingData = true;
|
|
182
|
+
clearTimeout(this.timeoutId);
|
|
183
|
+
this.setTimeout();
|
|
184
|
+
}
|
|
185
|
+
// Skip processing if we already have data for this account at a newer slot
|
|
186
|
+
const existing = this.dataMap.get(accountPubkey);
|
|
187
|
+
if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot > slot) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
163
190
|
const accountInfo = {
|
|
164
191
|
owner: new web3_js_1.PublicKey(chunk.account.account.owner),
|
|
165
192
|
lamports: Number(chunk.account.account.lamports),
|
|
@@ -169,23 +196,26 @@ class grpcMultiAccountSubscriber {
|
|
|
169
196
|
};
|
|
170
197
|
const context = { slot };
|
|
171
198
|
const buffer = accountInfo.data;
|
|
172
|
-
|
|
199
|
+
// Check existing buffer for this account and skip if unchanged or slot regressed
|
|
200
|
+
const prevBuffer = this.bufferMap.get(accountPubkey);
|
|
201
|
+
if (prevBuffer && slot < prevBuffer.slot) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (prevBuffer &&
|
|
205
|
+
prevBuffer.buffer &&
|
|
206
|
+
buffer &&
|
|
207
|
+
buffer.equals(prevBuffer.buffer)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.bufferMap.set(accountPubkey, { buffer, slot });
|
|
211
|
+
const accountProps = (_b = this.accountPropsMap) === null || _b === void 0 ? void 0 : _b.get(accountPubkey);
|
|
173
212
|
const handleDataBuffer = (context, buffer, accountProps) => {
|
|
174
|
-
var _a;
|
|
175
213
|
const data = this.decodeBufferFn
|
|
176
214
|
? this.decodeBufferFn(buffer, accountPubkey, accountProps)
|
|
177
215
|
: this.program.account[this.accountName].coder.accounts.decode(this.capitalize(this.accountName), buffer);
|
|
178
216
|
const handler = this.onChangeMap.get(accountPubkey);
|
|
179
217
|
if (handler) {
|
|
180
|
-
|
|
181
|
-
this.receivingData = true;
|
|
182
|
-
clearTimeout(this.timeoutId);
|
|
183
|
-
handler(data, context, buffer, accountProps);
|
|
184
|
-
this.setTimeout();
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
handler(data, context, buffer, accountProps);
|
|
188
|
-
}
|
|
218
|
+
handler(data, context, buffer, accountProps);
|
|
189
219
|
}
|
|
190
220
|
};
|
|
191
221
|
if (Array.isArray(accountProps)) {
|
|
@@ -204,7 +234,6 @@ class grpcMultiAccountSubscriber {
|
|
|
204
234
|
this.listenerId = 1;
|
|
205
235
|
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
206
236
|
this.receivingData = true;
|
|
207
|
-
this.setTimeout();
|
|
208
237
|
}
|
|
209
238
|
resolve();
|
|
210
239
|
}
|
|
@@ -254,6 +283,8 @@ class grpcMultiAccountSubscriber {
|
|
|
254
283
|
const k = pk.toBase58();
|
|
255
284
|
this.subscribedAccounts.delete(k);
|
|
256
285
|
this.onChangeMap.delete(k);
|
|
286
|
+
this.dataMap.delete(k);
|
|
287
|
+
this.bufferMap.delete(k);
|
|
257
288
|
}
|
|
258
289
|
const request = {
|
|
259
290
|
slots: {},
|
package/package.json
CHANGED
package/scripts/client-test.ts
CHANGED
|
@@ -19,12 +19,14 @@ import {
|
|
|
19
19
|
ProgramAccount,
|
|
20
20
|
} from '@coral-xyz/anchor';
|
|
21
21
|
import driftIDL from '../src/idl/drift.json';
|
|
22
|
+
import assert from 'assert';
|
|
22
23
|
|
|
23
24
|
const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
|
|
24
25
|
const TOKEN = process.env.TOKEN;
|
|
26
|
+
const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
|
|
25
27
|
|
|
26
28
|
async function initializeGrpcDriftClientV2VersusV1() {
|
|
27
|
-
const connection = new Connection(
|
|
29
|
+
const connection = new Connection(RPC_ENDPOINT);
|
|
28
30
|
const wallet = new Wallet(new Keypair());
|
|
29
31
|
dotenv.config({ path: '../' });
|
|
30
32
|
|
|
@@ -34,49 +36,38 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
34
36
|
// @ts-ignore
|
|
35
37
|
wallet,
|
|
36
38
|
{
|
|
37
|
-
commitment: '
|
|
39
|
+
commitment: 'processed',
|
|
38
40
|
}
|
|
39
41
|
);
|
|
40
42
|
|
|
41
43
|
const program = new Program(driftIDL as Idl, programId, provider);
|
|
42
44
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// );
|
|
63
|
-
// const oracleInfos = [
|
|
64
|
-
// {
|
|
65
|
-
// publicKey: new PublicKey('BERaNi6cpEresbq6HC1EQGaB1H1UjvEo4NGnmYSSJof4'),
|
|
66
|
-
// source: OracleSource.PYTH_LAZER,
|
|
67
|
-
// },
|
|
68
|
-
// {
|
|
69
|
-
// publicKey: new PublicKey('BERaNi6cpEresbq6HC1EQGaB1H1UjvEo4NGnmYSSJof4'),
|
|
70
|
-
// source: OracleSource.PYTH_LAZER_1M,
|
|
71
|
-
// },
|
|
72
|
-
// ];
|
|
45
|
+
const allPerpMarketProgramAccounts =
|
|
46
|
+
(await program.account.perpMarket.all()) as ProgramAccount<PerpMarketAccount>[];
|
|
47
|
+
const perpMarketProgramAccounts = allPerpMarketProgramAccounts.filter((val) =>
|
|
48
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].includes(
|
|
49
|
+
val.account.marketIndex
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
const perpMarketIndexes = perpMarketProgramAccounts.map(
|
|
53
|
+
(val) => val.account.marketIndex
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const allSpotMarketProgramAccounts =
|
|
57
|
+
(await program.account.spotMarket.all()) as ProgramAccount<SpotMarketAccount>[];
|
|
58
|
+
const spotMarketProgramAccounts = allSpotMarketProgramAccounts.filter((val) =>
|
|
59
|
+
[0, 1, 2, 3, 4, 5].includes(val.account.marketIndex)
|
|
60
|
+
);
|
|
61
|
+
const spotMarketIndexes = spotMarketProgramAccounts.map(
|
|
62
|
+
(val) => val.account.marketIndex
|
|
63
|
+
);
|
|
73
64
|
|
|
74
65
|
const seen = new Set<string>();
|
|
75
66
|
const oracleInfos: OracleInfo[] = [];
|
|
76
67
|
for (const acct of perpMarketProgramAccounts) {
|
|
77
|
-
const key = `${acct.account.amm.oracle.toBase58()}-${
|
|
78
|
-
acct.account.amm.oracleSource
|
|
79
|
-
|
|
68
|
+
const key = `${acct.account.amm.oracle.toBase58()}-${
|
|
69
|
+
Object.keys(acct.account.amm.oracleSource)[0]
|
|
70
|
+
}`;
|
|
80
71
|
if (!seen.has(key)) {
|
|
81
72
|
seen.add(key);
|
|
82
73
|
oracleInfos.push({
|
|
@@ -86,9 +77,9 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
86
77
|
}
|
|
87
78
|
}
|
|
88
79
|
for (const acct of spotMarketProgramAccounts) {
|
|
89
|
-
const key = `${acct.account.oracle.toBase58()}-${
|
|
90
|
-
acct.account.oracleSource
|
|
91
|
-
|
|
80
|
+
const key = `${acct.account.oracle.toBase58()}-${
|
|
81
|
+
Object.keys(acct.account.oracleSource)[0]
|
|
82
|
+
}`;
|
|
92
83
|
if (!seen.has(key)) {
|
|
93
84
|
seen.add(key);
|
|
94
85
|
oracleInfos.push({
|
|
@@ -103,7 +94,7 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
103
94
|
grpcConfigs: {
|
|
104
95
|
endpoint: GRPC_ENDPOINT,
|
|
105
96
|
token: TOKEN,
|
|
106
|
-
commitmentLevel:
|
|
97
|
+
commitmentLevel: CommitmentLevel.PROCESSED,
|
|
107
98
|
channelOptions: {
|
|
108
99
|
'grpc.keepalive_time_ms': 10_000,
|
|
109
100
|
'grpc.keepalive_timeout_ms': 1_000,
|
|
@@ -119,7 +110,6 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
119
110
|
accountSubscription: {
|
|
120
111
|
...baseAccountSubscription,
|
|
121
112
|
driftClientAccountSubscriber: grpcDriftClientAccountSubscriberV2,
|
|
122
|
-
logResubMessages: true,
|
|
123
113
|
},
|
|
124
114
|
perpMarketIndexes,
|
|
125
115
|
spotMarketIndexes,
|
|
@@ -133,7 +123,6 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
133
123
|
accountSubscription: {
|
|
134
124
|
...baseAccountSubscription,
|
|
135
125
|
driftClientAccountSubscriber: grpcDriftClientAccountSubscriber,
|
|
136
|
-
// logResubMessages: true,
|
|
137
126
|
},
|
|
138
127
|
perpMarketIndexes,
|
|
139
128
|
spotMarketIndexes,
|
|
@@ -144,28 +133,227 @@ async function initializeGrpcDriftClientV2VersusV1() {
|
|
|
144
133
|
const clientV1 = new DriftClient(configV1);
|
|
145
134
|
|
|
146
135
|
await Promise.all([clientV1.subscribe(), clientV2.subscribe()]);
|
|
147
|
-
|
|
148
|
-
clientV2.eventEmitter.on('oraclePriceUpdate', (pubkey, source, data) => {
|
|
149
|
-
const key = pubkey.toBase58();
|
|
150
|
-
const src = Object.keys(source ?? {})[0];
|
|
151
|
-
console.log(
|
|
152
|
-
`v2 oracle update ${key} (${src}) price ${data.price.toString()}`
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
136
|
const compare = () => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
137
|
+
try {
|
|
138
|
+
// 1. Test getStateAccountAndSlot
|
|
139
|
+
const state1 = clientV1.accountSubscriber.getStateAccountAndSlot();
|
|
140
|
+
const state2 = clientV2.accountSubscriber.getStateAccountAndSlot();
|
|
141
|
+
assert.deepStrictEqual(
|
|
142
|
+
state1.data,
|
|
143
|
+
state2.data,
|
|
144
|
+
'State accounts should match'
|
|
161
145
|
);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
146
|
+
if (
|
|
147
|
+
state1.slot !== undefined &&
|
|
148
|
+
state2.slot !== undefined &&
|
|
149
|
+
state2.slot < state1.slot
|
|
150
|
+
) {
|
|
151
|
+
console.error(
|
|
152
|
+
`State account slot regression: v2 slot ${state2.slot} < v1 slot ${state1.slot}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 2. Test getMarketAccountsAndSlots (all perp markets) - sorted comparison
|
|
157
|
+
const allPerpMarkets1 = clientV1.accountSubscriber
|
|
158
|
+
.getMarketAccountsAndSlots()
|
|
159
|
+
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
|
|
160
|
+
const allPerpMarkets2 = clientV2.accountSubscriber
|
|
161
|
+
.getMarketAccountsAndSlots()
|
|
162
|
+
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
|
|
163
|
+
assert.strictEqual(
|
|
164
|
+
allPerpMarkets1.length,
|
|
165
|
+
allPerpMarkets2.length,
|
|
166
|
+
'Number of perp markets should match'
|
|
168
167
|
);
|
|
168
|
+
|
|
169
|
+
// Compare each perp market in the sorted arrays
|
|
170
|
+
for (let i = 0; i < allPerpMarkets1.length; i++) {
|
|
171
|
+
const market1 = allPerpMarkets1[i];
|
|
172
|
+
const market2 = allPerpMarkets2[i];
|
|
173
|
+
assert.strictEqual(
|
|
174
|
+
market1.data.marketIndex,
|
|
175
|
+
market2.data.marketIndex,
|
|
176
|
+
`Perp market at position ${i} should have same marketIndex`
|
|
177
|
+
);
|
|
178
|
+
// assert.deepStrictEqual(
|
|
179
|
+
// market1.data,
|
|
180
|
+
// market2.data,
|
|
181
|
+
// `Perp market ${market1.data.marketIndex} (from getMarketAccountsAndSlots) should match`
|
|
182
|
+
// );
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 3. Test getMarketAccountAndSlot for each perp market
|
|
186
|
+
for (const idx of perpMarketIndexes) {
|
|
187
|
+
const market1 = clientV1.accountSubscriber.getMarketAccountAndSlot(idx);
|
|
188
|
+
const market2 = clientV2.accountSubscriber.getMarketAccountAndSlot(idx);
|
|
189
|
+
// assert.deepStrictEqual(
|
|
190
|
+
// market1?.data,
|
|
191
|
+
// market2?.data,
|
|
192
|
+
// `Perp market ${idx} data should match`
|
|
193
|
+
// );
|
|
194
|
+
// assert.strictEqual(
|
|
195
|
+
// market1?.slot,
|
|
196
|
+
// market2?.slot,
|
|
197
|
+
// `Perp market ${idx} slot should match`
|
|
198
|
+
// );
|
|
199
|
+
if (
|
|
200
|
+
market1?.slot !== undefined &&
|
|
201
|
+
market2?.slot !== undefined &&
|
|
202
|
+
market2.slot < market1.slot
|
|
203
|
+
) {
|
|
204
|
+
console.error(
|
|
205
|
+
`Perp market ${idx} slot regression: v2 slot ${market2.slot} < v1 slot ${market1.slot}`
|
|
206
|
+
);
|
|
207
|
+
} else if (
|
|
208
|
+
market1?.slot !== undefined &&
|
|
209
|
+
market2?.slot !== undefined &&
|
|
210
|
+
market2.slot > market1.slot
|
|
211
|
+
) {
|
|
212
|
+
console.info(
|
|
213
|
+
`Perp market ${idx} slot is FASTER! v2: ${market2.slot}, v1: ${market1.slot}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 4. Test getSpotMarketAccountsAndSlots (all spot markets) - sorted comparison
|
|
219
|
+
const allSpotMarkets1 = clientV1.accountSubscriber
|
|
220
|
+
.getSpotMarketAccountsAndSlots()
|
|
221
|
+
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
|
|
222
|
+
const allSpotMarkets2 = clientV2.accountSubscriber
|
|
223
|
+
.getSpotMarketAccountsAndSlots()
|
|
224
|
+
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
|
|
225
|
+
assert.strictEqual(
|
|
226
|
+
allSpotMarkets1.length,
|
|
227
|
+
allSpotMarkets2.length,
|
|
228
|
+
'Number of spot markets should match'
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Compare each spot market in the sorted arrays
|
|
232
|
+
for (let i = 0; i < allSpotMarkets1.length; i++) {
|
|
233
|
+
const market1 = allSpotMarkets1[i];
|
|
234
|
+
const market2 = allSpotMarkets2[i];
|
|
235
|
+
assert.strictEqual(
|
|
236
|
+
market1.data.marketIndex,
|
|
237
|
+
market2.data.marketIndex,
|
|
238
|
+
`Spot market at position ${i} should have same marketIndex`
|
|
239
|
+
);
|
|
240
|
+
// assert.deepStrictEqual(
|
|
241
|
+
// market1.data,
|
|
242
|
+
// market2.data,
|
|
243
|
+
// `Spot market ${market1.data.marketIndex} (from getSpotMarketAccountsAndSlots) should match`
|
|
244
|
+
// );
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 5. Test getSpotMarketAccountAndSlot for each spot market
|
|
248
|
+
for (const idx of spotMarketIndexes) {
|
|
249
|
+
const market1 =
|
|
250
|
+
clientV1.accountSubscriber.getSpotMarketAccountAndSlot(idx);
|
|
251
|
+
const market2 =
|
|
252
|
+
clientV2.accountSubscriber.getSpotMarketAccountAndSlot(idx);
|
|
253
|
+
// assert.deepStrictEqual(
|
|
254
|
+
// market1?.data,
|
|
255
|
+
// market2?.data,
|
|
256
|
+
// `Spot market ${idx} data should match`
|
|
257
|
+
// );
|
|
258
|
+
// assert.strictEqual(
|
|
259
|
+
// market1?.slot,
|
|
260
|
+
// market2?.slot,
|
|
261
|
+
// `Spot market ${idx} slot should match`
|
|
262
|
+
// );
|
|
263
|
+
if (
|
|
264
|
+
market1?.slot !== undefined &&
|
|
265
|
+
market2?.slot !== undefined &&
|
|
266
|
+
market2.slot < market1.slot
|
|
267
|
+
) {
|
|
268
|
+
console.error(
|
|
269
|
+
`Spot market ${idx} slot regression: v2 slot ${market2.slot} < v1 slot ${market1.slot}`
|
|
270
|
+
);
|
|
271
|
+
} else if (
|
|
272
|
+
market1?.slot !== undefined &&
|
|
273
|
+
market2?.slot !== undefined &&
|
|
274
|
+
market2.slot > market1.slot
|
|
275
|
+
) {
|
|
276
|
+
console.info(
|
|
277
|
+
`Spot market ${idx} slot is FASTER! v2: ${market2.slot}, v1: ${market1.slot}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 6. Test getOraclePriceDataAndSlotForPerpMarket
|
|
283
|
+
for (const idx of perpMarketIndexes) {
|
|
284
|
+
const oracle1 =
|
|
285
|
+
clientV1.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket(
|
|
286
|
+
idx
|
|
287
|
+
);
|
|
288
|
+
const oracle2 =
|
|
289
|
+
clientV2.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket(
|
|
290
|
+
idx
|
|
291
|
+
);
|
|
292
|
+
// assert.deepStrictEqual(
|
|
293
|
+
// oracle1?.data,
|
|
294
|
+
// oracle2?.data,
|
|
295
|
+
// `Perp market ${idx} oracle data should match`
|
|
296
|
+
// );
|
|
297
|
+
// Note: slots might differ slightly due to timing, so we can optionally skip this check or be lenient
|
|
298
|
+
// assert.strictEqual(oracle1?.slot, oracle2?.slot, `Perp market ${idx} oracle slot should match`);
|
|
299
|
+
if (
|
|
300
|
+
oracle1?.slot !== undefined &&
|
|
301
|
+
oracle2?.slot !== undefined &&
|
|
302
|
+
oracle2.slot < oracle1.slot
|
|
303
|
+
) {
|
|
304
|
+
console.error(
|
|
305
|
+
`Perp market ${idx} oracle slot regression: v2 slot ${oracle2.slot} < v1 slot ${oracle1.slot}`
|
|
306
|
+
);
|
|
307
|
+
} else if (
|
|
308
|
+
oracle1?.slot !== undefined &&
|
|
309
|
+
oracle2?.slot !== undefined &&
|
|
310
|
+
oracle2.slot > oracle1.slot
|
|
311
|
+
) {
|
|
312
|
+
console.info(
|
|
313
|
+
`Perp market ${idx} oracle slot is FASTER! v2: ${oracle2.slot}, v1: ${oracle1.slot}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 7. Test getOraclePriceDataAndSlotForSpotMarket
|
|
319
|
+
for (const idx of spotMarketIndexes) {
|
|
320
|
+
const oracle1 =
|
|
321
|
+
clientV1.accountSubscriber.getOraclePriceDataAndSlotForSpotMarket(
|
|
322
|
+
idx
|
|
323
|
+
);
|
|
324
|
+
const oracle2 =
|
|
325
|
+
clientV2.accountSubscriber.getOraclePriceDataAndSlotForSpotMarket(
|
|
326
|
+
idx
|
|
327
|
+
);
|
|
328
|
+
// assert.deepStrictEqual(
|
|
329
|
+
// oracle1?.data,
|
|
330
|
+
// oracle2?.data,
|
|
331
|
+
// `Spot market ${idx} oracle data should match`
|
|
332
|
+
// );
|
|
333
|
+
// Note: slots might differ slightly due to timing
|
|
334
|
+
// assert.strictEqual(oracle1?.slot, oracle2?.slot, `Spot market ${idx} oracle slot should match`);
|
|
335
|
+
if (
|
|
336
|
+
oracle1?.slot !== undefined &&
|
|
337
|
+
oracle2?.slot !== undefined &&
|
|
338
|
+
oracle2.slot < oracle1.slot
|
|
339
|
+
) {
|
|
340
|
+
console.error(
|
|
341
|
+
`Spot market ${idx} oracle slot regression: v2 slot ${oracle2.slot} < v1 slot ${oracle1.slot}`
|
|
342
|
+
);
|
|
343
|
+
} else if (
|
|
344
|
+
oracle1?.slot !== undefined &&
|
|
345
|
+
oracle2?.slot !== undefined &&
|
|
346
|
+
oracle2.slot > oracle1.slot
|
|
347
|
+
) {
|
|
348
|
+
console.info(
|
|
349
|
+
`Spot market ${idx} oracle slot is FASTER! v2: ${oracle2.slot}, v1: ${oracle1.slot}`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
console.log('✓ All comparisons passed');
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error('✗ Comparison failed:', error);
|
|
169
357
|
}
|
|
170
358
|
};
|
|
171
359
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
SubscribeUpdate,
|
|
12
12
|
createClient,
|
|
13
13
|
} from '../isomorphic/grpc';
|
|
14
|
-
import { DataAndSlot, GrpcConfigs, ResubOpts } from './types';
|
|
14
|
+
import { BufferAndSlot, DataAndSlot, GrpcConfigs, ResubOpts } from './types';
|
|
15
15
|
|
|
16
16
|
interface AccountInfoLike {
|
|
17
17
|
owner: PublicKey;
|
|
@@ -63,6 +63,7 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
63
63
|
|
|
64
64
|
private dataMap = new Map<string, DataAndSlot<T>>();
|
|
65
65
|
private accountPropsMap = new Map<string, U | Array<U>>();
|
|
66
|
+
private bufferMap = new Map<string, BufferAndSlot>();
|
|
66
67
|
|
|
67
68
|
private constructor(
|
|
68
69
|
client: Client,
|
|
@@ -160,11 +161,29 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
160
161
|
const accountId = chunk[i];
|
|
161
162
|
const accountInfo = rpcResponse[i];
|
|
162
163
|
if (accountInfo) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
const prev = this.bufferMap.get(accountId);
|
|
165
|
+
const newBuffer = accountInfo.data as Buffer;
|
|
166
|
+
if (prev && currentSlot < prev.slot) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
prev &&
|
|
171
|
+
prev.buffer &&
|
|
172
|
+
newBuffer &&
|
|
173
|
+
newBuffer.equals(prev.buffer)
|
|
174
|
+
) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
this.bufferMap.set(accountId, {
|
|
178
|
+
buffer: newBuffer,
|
|
179
|
+
slot: currentSlot,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const accountDecoded = this.program.coder.accounts.decode(
|
|
183
|
+
this.capitalize(this.accountName),
|
|
184
|
+
newBuffer
|
|
166
185
|
);
|
|
167
|
-
this.setAccountData(accountId,
|
|
186
|
+
this.setAccountData(accountId, accountDecoded, currentSlot);
|
|
168
187
|
}
|
|
169
188
|
}
|
|
170
189
|
})
|
|
@@ -238,6 +257,19 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
238
257
|
if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
|
|
239
258
|
return;
|
|
240
259
|
}
|
|
260
|
+
|
|
261
|
+
// Touch resub timer on any incoming account update for subscribed keys
|
|
262
|
+
if (this.resubOpts?.resubTimeoutMs) {
|
|
263
|
+
this.receivingData = true;
|
|
264
|
+
clearTimeout(this.timeoutId);
|
|
265
|
+
this.setTimeout();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Skip processing if we already have data for this account at a newer slot
|
|
269
|
+
const existing = this.dataMap.get(accountPubkey);
|
|
270
|
+
if (existing?.slot !== undefined && existing.slot > slot) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
241
273
|
const accountInfo: AccountInfoLike = {
|
|
242
274
|
owner: new PublicKey(chunk.account.account.owner),
|
|
243
275
|
lamports: Number(chunk.account.account.lamports),
|
|
@@ -248,6 +280,21 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
248
280
|
|
|
249
281
|
const context = { slot } as Context;
|
|
250
282
|
const buffer = accountInfo.data;
|
|
283
|
+
|
|
284
|
+
// Check existing buffer for this account and skip if unchanged or slot regressed
|
|
285
|
+
const prevBuffer = this.bufferMap.get(accountPubkey);
|
|
286
|
+
if (prevBuffer && slot < prevBuffer.slot) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (
|
|
290
|
+
prevBuffer &&
|
|
291
|
+
prevBuffer.buffer &&
|
|
292
|
+
buffer &&
|
|
293
|
+
buffer.equals(prevBuffer.buffer)
|
|
294
|
+
) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
this.bufferMap.set(accountPubkey, { buffer, slot });
|
|
251
298
|
const accountProps = this.accountPropsMap?.get(accountPubkey);
|
|
252
299
|
|
|
253
300
|
const handleDataBuffer = (
|
|
@@ -263,14 +310,7 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
263
310
|
);
|
|
264
311
|
const handler = this.onChangeMap.get(accountPubkey);
|
|
265
312
|
if (handler) {
|
|
266
|
-
|
|
267
|
-
this.receivingData = true;
|
|
268
|
-
clearTimeout(this.timeoutId);
|
|
269
|
-
handler(data, context, buffer, accountProps);
|
|
270
|
-
this.setTimeout();
|
|
271
|
-
} else {
|
|
272
|
-
handler(data, context, buffer, accountProps);
|
|
273
|
-
}
|
|
313
|
+
handler(data, context, buffer, accountProps);
|
|
274
314
|
}
|
|
275
315
|
};
|
|
276
316
|
|
|
@@ -289,7 +329,6 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
289
329
|
this.listenerId = 1;
|
|
290
330
|
if (this.resubOpts?.resubTimeoutMs) {
|
|
291
331
|
this.receivingData = true;
|
|
292
|
-
this.setTimeout();
|
|
293
332
|
}
|
|
294
333
|
resolve();
|
|
295
334
|
} else {
|
|
@@ -340,6 +379,8 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
|
|
|
340
379
|
const k = pk.toBase58();
|
|
341
380
|
this.subscribedAccounts.delete(k);
|
|
342
381
|
this.onChangeMap.delete(k);
|
|
382
|
+
this.dataMap.delete(k);
|
|
383
|
+
this.bufferMap.delete(k);
|
|
343
384
|
}
|
|
344
385
|
const request: SubscribeRequest = {
|
|
345
386
|
slots: {},
|