@drift-labs/sdk 2.142.0-beta.17 → 2.142.0-beta.18

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 CHANGED
@@ -1 +1 @@
1
- 2.142.0-beta.17
1
+ 2.142.0-beta.18
@@ -101,8 +101,11 @@ class grpcMultiAccountSubscriber {
101
101
  const accountId = chunk[i];
102
102
  const accountInfo = rpcResponse[i];
103
103
  if (accountInfo) {
104
- const perpMarket = this.program.coder.accounts.decode('PerpMarket', accountInfo.data);
105
- this.setAccountData(accountId, perpMarket, currentSlot);
104
+ const existingData = this.getAccountData(accountId);
105
+ if (!existingData || currentSlot > existingData.slot) {
106
+ const accountDecoded = this.program.coder.accounts.decode(this.capitalize(this.accountName), accountInfo.data);
107
+ this.setAccountData(accountId, accountDecoded, currentSlot);
108
+ }
106
109
  }
107
110
  }
108
111
  }));
@@ -160,6 +163,11 @@ class grpcMultiAccountSubscriber {
160
163
  if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
161
164
  return;
162
165
  }
166
+ // Skip processing if we already have data for this account at an equal or newer slot
167
+ const existing = this.dataMap.get(accountPubkey);
168
+ if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot >= slot) {
169
+ return;
170
+ }
163
171
  const accountInfo = {
164
172
  owner: new web3_js_1.PublicKey(chunk.account.account.owner),
165
173
  lamports: Number(chunk.account.account.lamports),
@@ -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,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAyB9D,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;IAE1D,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;IAkDtB,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;IAkHV,WAAW,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCjD,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCpD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA4ClC,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;CAIlB"}
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,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAyB9D,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;IAE1D,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;IAqDtB,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;IAwHV,WAAW,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCjD,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCpD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA4ClC,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;CAIlB"}
@@ -101,8 +101,11 @@ class grpcMultiAccountSubscriber {
101
101
  const accountId = chunk[i];
102
102
  const accountInfo = rpcResponse[i];
103
103
  if (accountInfo) {
104
- const perpMarket = this.program.coder.accounts.decode('PerpMarket', accountInfo.data);
105
- this.setAccountData(accountId, perpMarket, currentSlot);
104
+ const existingData = this.getAccountData(accountId);
105
+ if (!existingData || currentSlot > existingData.slot) {
106
+ const accountDecoded = this.program.coder.accounts.decode(this.capitalize(this.accountName), accountInfo.data);
107
+ this.setAccountData(accountId, accountDecoded, currentSlot);
108
+ }
106
109
  }
107
110
  }
108
111
  }));
@@ -160,6 +163,11 @@ class grpcMultiAccountSubscriber {
160
163
  if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
161
164
  return;
162
165
  }
166
+ // Skip processing if we already have data for this account at an equal or newer slot
167
+ const existing = this.dataMap.get(accountPubkey);
168
+ if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot >= slot) {
169
+ return;
170
+ }
163
171
  const accountInfo = {
164
172
  owner: new web3_js_1.PublicKey(chunk.account.account.owner),
165
173
  lamports: Number(chunk.account.account.lamports),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.142.0-beta.17",
3
+ "version": "2.142.0-beta.18",
4
4
  "main": "lib/node/index.js",
5
5
  "types": "lib/node/index.d.ts",
6
6
  "module": "./lib/browser/index.js",
@@ -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('https://api.mainnet-beta.solana.com');
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: 'confirmed',
39
+ commitment: 'processed',
38
40
  }
39
41
  );
40
42
 
41
43
  const program = new Program(driftIDL as Idl, programId, provider);
42
44
 
43
- const perpMarketIndexes = [4];
44
- const spotMarketIndexes = [32];
45
-
46
- const perpMarketProgramAccounts = (
47
- await program.account.perpMarket.all()
48
- ).filter((a) =>
49
- perpMarketIndexes.includes(a.account.marketIndex as number)
50
- ) as ProgramAccount<PerpMarketAccount>[];
51
- const spotMarketProgramAccounts = (
52
- await program.account.spotMarket.all()
53
- ).filter((a) =>
54
- spotMarketIndexes.includes(a.account.marketIndex as number)
55
- ) as ProgramAccount<SpotMarketAccount>[];
56
-
57
- // const perpMarketIndexes = perpMarketProgramAccounts.map(
58
- // (a) => a.account.marketIndex
59
- // );
60
- // const spotMarketIndexes = spotMarketProgramAccounts.map(
61
- // (a) => a.account.marketIndex
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()}-${Object.keys(
78
- acct.account.amm.oracleSource ?? {}
79
- )?.[0]}`;
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()}-${Object.keys(
90
- acct.account.oracleSource ?? {}
91
- )?.[0]}`;
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: 'confirmed' as unknown as 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
- for (const idx of perpMarketIndexes) {
157
- const p1 = clientV1.getOracleDataForPerpMarket(idx).price;
158
- const p2 = clientV2.getOracleDataForPerpMarket(idx).price;
159
- console.log(
160
- `perp mkt ${idx} | v1 ${p1.toString()} | v2 ${p2.toString()}`
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
- for (const idx of spotMarketIndexes) {
164
- const s1 = clientV1.getOracleDataForSpotMarket(idx).price;
165
- const s2 = clientV2.getOracleDataForSpotMarket(idx).price;
166
- console.log(
167
- `spot mkt ${idx} | v1 ${s1.toString()} | v2 ${s2.toString()}`
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
 
@@ -160,11 +160,14 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
160
160
  const accountId = chunk[i];
161
161
  const accountInfo = rpcResponse[i];
162
162
  if (accountInfo) {
163
- const perpMarket = this.program.coder.accounts.decode(
164
- 'PerpMarket',
165
- accountInfo.data
166
- );
167
- this.setAccountData(accountId, perpMarket, currentSlot);
163
+ const existingData = this.getAccountData(accountId);
164
+ if (!existingData || currentSlot > existingData.slot) {
165
+ const accountDecoded = this.program.coder.accounts.decode(
166
+ this.capitalize(this.accountName),
167
+ accountInfo.data
168
+ );
169
+ this.setAccountData(accountId, accountDecoded, currentSlot);
170
+ }
168
171
  }
169
172
  }
170
173
  })
@@ -238,6 +241,12 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
238
241
  if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
239
242
  return;
240
243
  }
244
+
245
+ // Skip processing if we already have data for this account at an equal or newer slot
246
+ const existing = this.dataMap.get(accountPubkey);
247
+ if (existing?.slot !== undefined && existing.slot >= slot) {
248
+ return;
249
+ }
241
250
  const accountInfo: AccountInfoLike = {
242
251
  owner: new PublicKey(chunk.account.account.owner),
243
252
  lamports: Number(chunk.account.account.lamports),