@drift-labs/sdk 2.142.0-beta.18 → 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 CHANGED
@@ -1 +1 @@
1
- 2.142.0-beta.18
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,11 +102,23 @@ class grpcMultiAccountSubscriber {
101
102
  const accountId = chunk[i];
102
103
  const accountInfo = rpcResponse[i];
103
104
  if (accountInfo) {
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);
105
+ const prev = this.bufferMap.get(accountId);
106
+ const newBuffer = accountInfo.data;
107
+ if (prev && currentSlot < prev.slot) {
108
+ continue;
108
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);
109
122
  }
110
123
  }
111
124
  }));
@@ -153,7 +166,7 @@ class grpcMultiAccountSubscriber {
153
166
  transactionsStatus: {},
154
167
  };
155
168
  this.stream.on('data', (chunk) => {
156
- var _a;
169
+ var _a, _b;
157
170
  if (!chunk.account) {
158
171
  return;
159
172
  }
@@ -163,9 +176,15 @@ class grpcMultiAccountSubscriber {
163
176
  if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
164
177
  return;
165
178
  }
166
- // Skip processing if we already have data for this account at an equal or newer slot
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
167
186
  const existing = this.dataMap.get(accountPubkey);
168
- if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot >= slot) {
187
+ if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot > slot) {
169
188
  return;
170
189
  }
171
190
  const accountInfo = {
@@ -177,23 +196,26 @@ class grpcMultiAccountSubscriber {
177
196
  };
178
197
  const context = { slot };
179
198
  const buffer = accountInfo.data;
180
- const accountProps = (_a = this.accountPropsMap) === null || _a === void 0 ? void 0 : _a.get(accountPubkey);
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);
181
212
  const handleDataBuffer = (context, buffer, accountProps) => {
182
- var _a;
183
213
  const data = this.decodeBufferFn
184
214
  ? this.decodeBufferFn(buffer, accountPubkey, accountProps)
185
215
  : this.program.account[this.accountName].coder.accounts.decode(this.capitalize(this.accountName), buffer);
186
216
  const handler = this.onChangeMap.get(accountPubkey);
187
217
  if (handler) {
188
- if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
189
- this.receivingData = true;
190
- clearTimeout(this.timeoutId);
191
- handler(data, context, buffer, accountProps);
192
- this.setTimeout();
193
- }
194
- else {
195
- handler(data, context, buffer, accountProps);
196
- }
218
+ handler(data, context, buffer, accountProps);
197
219
  }
198
220
  };
199
221
  if (Array.isArray(accountProps)) {
@@ -212,7 +234,6 @@ class grpcMultiAccountSubscriber {
212
234
  this.listenerId = 1;
213
235
  if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
214
236
  this.receivingData = true;
215
- this.setTimeout();
216
237
  }
217
238
  resolve();
218
239
  }
@@ -262,6 +283,8 @@ class grpcMultiAccountSubscriber {
262
283
  const k = pk.toBase58();
263
284
  this.subscribedAccounts.delete(k);
264
285
  this.onChangeMap.delete(k);
286
+ this.dataMap.delete(k);
287
+ this.bufferMap.delete(k);
265
288
  }
266
289
  const request = {
267
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,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"}
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,11 +102,23 @@ class grpcMultiAccountSubscriber {
101
102
  const accountId = chunk[i];
102
103
  const accountInfo = rpcResponse[i];
103
104
  if (accountInfo) {
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);
105
+ const prev = this.bufferMap.get(accountId);
106
+ const newBuffer = accountInfo.data;
107
+ if (prev && currentSlot < prev.slot) {
108
+ continue;
108
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);
109
122
  }
110
123
  }
111
124
  }));
@@ -153,7 +166,7 @@ class grpcMultiAccountSubscriber {
153
166
  transactionsStatus: {},
154
167
  };
155
168
  this.stream.on('data', (chunk) => {
156
- var _a;
169
+ var _a, _b;
157
170
  if (!chunk.account) {
158
171
  return;
159
172
  }
@@ -163,9 +176,15 @@ class grpcMultiAccountSubscriber {
163
176
  if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
164
177
  return;
165
178
  }
166
- // Skip processing if we already have data for this account at an equal or newer slot
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
167
186
  const existing = this.dataMap.get(accountPubkey);
168
- if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot >= slot) {
187
+ if ((existing === null || existing === void 0 ? void 0 : existing.slot) !== undefined && existing.slot > slot) {
169
188
  return;
170
189
  }
171
190
  const accountInfo = {
@@ -177,23 +196,26 @@ class grpcMultiAccountSubscriber {
177
196
  };
178
197
  const context = { slot };
179
198
  const buffer = accountInfo.data;
180
- const accountProps = (_a = this.accountPropsMap) === null || _a === void 0 ? void 0 : _a.get(accountPubkey);
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);
181
212
  const handleDataBuffer = (context, buffer, accountProps) => {
182
- var _a;
183
213
  const data = this.decodeBufferFn
184
214
  ? this.decodeBufferFn(buffer, accountPubkey, accountProps)
185
215
  : this.program.account[this.accountName].coder.accounts.decode(this.capitalize(this.accountName), buffer);
186
216
  const handler = this.onChangeMap.get(accountPubkey);
187
217
  if (handler) {
188
- if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
189
- this.receivingData = true;
190
- clearTimeout(this.timeoutId);
191
- handler(data, context, buffer, accountProps);
192
- this.setTimeout();
193
- }
194
- else {
195
- handler(data, context, buffer, accountProps);
196
- }
218
+ handler(data, context, buffer, accountProps);
197
219
  }
198
220
  };
199
221
  if (Array.isArray(accountProps)) {
@@ -212,7 +234,6 @@ class grpcMultiAccountSubscriber {
212
234
  this.listenerId = 1;
213
235
  if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
214
236
  this.receivingData = true;
215
- this.setTimeout();
216
237
  }
217
238
  resolve();
218
239
  }
@@ -262,6 +283,8 @@ class grpcMultiAccountSubscriber {
262
283
  const k = pk.toBase58();
263
284
  this.subscribedAccounts.delete(k);
264
285
  this.onChangeMap.delete(k);
286
+ this.dataMap.delete(k);
287
+ this.bufferMap.delete(k);
265
288
  }
266
289
  const request = {
267
290
  slots: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.142.0-beta.18",
3
+ "version": "2.142.0-beta.19",
4
4
  "main": "lib/node/index.js",
5
5
  "types": "lib/node/index.d.ts",
6
6
  "module": "./lib/browser/index.js",
@@ -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,14 +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 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);
164
+ const prev = this.bufferMap.get(accountId);
165
+ const newBuffer = accountInfo.data as Buffer;
166
+ if (prev && currentSlot < prev.slot) {
167
+ continue;
170
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
185
+ );
186
+ this.setAccountData(accountId, accountDecoded, currentSlot);
171
187
  }
172
188
  }
173
189
  })
@@ -242,9 +258,16 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
242
258
  return;
243
259
  }
244
260
 
245
- // Skip processing if we already have data for this account at an equal or newer slot
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
246
269
  const existing = this.dataMap.get(accountPubkey);
247
- if (existing?.slot !== undefined && existing.slot >= slot) {
270
+ if (existing?.slot !== undefined && existing.slot > slot) {
248
271
  return;
249
272
  }
250
273
  const accountInfo: AccountInfoLike = {
@@ -257,6 +280,21 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
257
280
 
258
281
  const context = { slot } as Context;
259
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 });
260
298
  const accountProps = this.accountPropsMap?.get(accountPubkey);
261
299
 
262
300
  const handleDataBuffer = (
@@ -272,14 +310,7 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
272
310
  );
273
311
  const handler = this.onChangeMap.get(accountPubkey);
274
312
  if (handler) {
275
- if (this.resubOpts?.resubTimeoutMs) {
276
- this.receivingData = true;
277
- clearTimeout(this.timeoutId);
278
- handler(data, context, buffer, accountProps);
279
- this.setTimeout();
280
- } else {
281
- handler(data, context, buffer, accountProps);
282
- }
313
+ handler(data, context, buffer, accountProps);
283
314
  }
284
315
  };
285
316
 
@@ -298,7 +329,6 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
298
329
  this.listenerId = 1;
299
330
  if (this.resubOpts?.resubTimeoutMs) {
300
331
  this.receivingData = true;
301
- this.setTimeout();
302
332
  }
303
333
  resolve();
304
334
  } else {
@@ -349,6 +379,8 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
349
379
  const k = pk.toBase58();
350
380
  this.subscribedAccounts.delete(k);
351
381
  this.onChangeMap.delete(k);
382
+ this.dataMap.delete(k);
383
+ this.bufferMap.delete(k);
352
384
  }
353
385
  const request: SubscribeRequest = {
354
386
  slots: {},