@drift-labs/sdk 2.136.0-beta.0 → 2.136.0-beta.2
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/types.d.ts +2 -0
- package/lib/browser/accounts/webSocketAccountSubscriberV2.d.ts +76 -3
- package/lib/browser/accounts/webSocketAccountSubscriberV2.js +211 -39
- package/lib/browser/accounts/webSocketDriftClientAccountSubscriberV2.d.ts +87 -0
- package/lib/browser/accounts/webSocketDriftClientAccountSubscriberV2.js +444 -0
- package/lib/browser/accounts/webSocketProgramAccountsSubscriberV2.d.ts +145 -0
- package/lib/browser/accounts/webSocketProgramAccountsSubscriberV2.js +744 -0
- package/lib/browser/accounts/websocketProgramUserAccountSubscriber.d.ts +22 -0
- package/lib/browser/accounts/websocketProgramUserAccountSubscriber.js +54 -0
- package/lib/browser/driftClient.js +22 -18
- package/lib/browser/driftClientConfig.d.ts +7 -2
- package/lib/browser/factory/bigNum.d.ts +2 -2
- package/lib/browser/factory/bigNum.js +20 -5
- package/lib/browser/index.d.ts +4 -0
- package/lib/browser/index.js +9 -1
- package/lib/browser/memcmp.d.ts +2 -0
- package/lib/browser/memcmp.js +19 -1
- package/lib/browser/oracles/oracleId.d.ts +5 -0
- package/lib/browser/oracles/oracleId.js +46 -1
- package/lib/browser/user.js +12 -5
- package/lib/browser/userConfig.d.ts +3 -0
- package/lib/node/accounts/types.d.ts +2 -0
- package/lib/node/accounts/types.d.ts.map +1 -1
- package/lib/node/accounts/webSocketAccountSubscriberV2.d.ts +76 -3
- package/lib/node/accounts/webSocketAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/webSocketAccountSubscriberV2.js +211 -39
- package/lib/node/accounts/webSocketDriftClientAccountSubscriberV2.d.ts +88 -0
- package/lib/node/accounts/webSocketDriftClientAccountSubscriberV2.d.ts.map +1 -0
- package/lib/node/accounts/webSocketDriftClientAccountSubscriberV2.js +444 -0
- package/lib/node/accounts/webSocketProgramAccountsSubscriberV2.d.ts +146 -0
- package/lib/node/accounts/webSocketProgramAccountsSubscriberV2.d.ts.map +1 -0
- package/lib/node/accounts/webSocketProgramAccountsSubscriberV2.js +744 -0
- package/lib/node/accounts/websocketProgramUserAccountSubscriber.d.ts +23 -0
- package/lib/node/accounts/websocketProgramUserAccountSubscriber.d.ts.map +1 -0
- package/lib/node/accounts/websocketProgramUserAccountSubscriber.js +54 -0
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +22 -18
- package/lib/node/driftClientConfig.d.ts +7 -2
- package/lib/node/driftClientConfig.d.ts.map +1 -1
- package/lib/node/factory/bigNum.d.ts +2 -2
- package/lib/node/factory/bigNum.d.ts.map +1 -1
- package/lib/node/factory/bigNum.js +20 -5
- package/lib/node/index.d.ts +4 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +9 -1
- package/lib/node/memcmp.d.ts +2 -0
- package/lib/node/memcmp.d.ts.map +1 -1
- package/lib/node/memcmp.js +19 -1
- package/lib/node/oracles/oracleId.d.ts +5 -0
- package/lib/node/oracles/oracleId.d.ts.map +1 -1
- package/lib/node/oracles/oracleId.js +46 -1
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +12 -5
- package/lib/node/userConfig.d.ts +3 -0
- package/lib/node/userConfig.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/accounts/README_WebSocketAccountSubscriberV2.md +41 -0
- package/src/accounts/types.ts +3 -0
- package/src/accounts/webSocketAccountSubscriberV2.ts +243 -42
- package/src/accounts/webSocketDriftClientAccountSubscriberV2.ts +745 -0
- package/src/accounts/webSocketProgramAccountsSubscriberV2.ts +995 -0
- package/src/accounts/websocketProgramUserAccountSubscriber.ts +94 -0
- package/src/driftClient.ts +13 -7
- package/src/driftClientConfig.ts +15 -8
- package/src/factory/bigNum.ts +22 -5
- package/src/index.ts +4 -0
- package/src/memcmp.ts +17 -0
- package/src/oracles/oracleId.ts +34 -0
- package/src/user.ts +21 -9
- package/src/userConfig.ts +3 -0
- package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.d.ts +0 -53
- package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +0 -453
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +0 -54
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +0 -1
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +0 -453
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +0 -596
|
@@ -8,16 +8,67 @@ import { AnchorProvider, Program } from '@coral-xyz/anchor';
|
|
|
8
8
|
import { capitalize } from './utils';
|
|
9
9
|
import {
|
|
10
10
|
AccountInfoBase,
|
|
11
|
-
AccountInfoWithBase58EncodedData,
|
|
12
11
|
AccountInfoWithBase64EncodedData,
|
|
12
|
+
AccountInfoWithBase58EncodedData,
|
|
13
13
|
createSolanaClient,
|
|
14
14
|
isAddress,
|
|
15
|
+
Rpc,
|
|
16
|
+
RpcSubscriptions,
|
|
17
|
+
SolanaRpcSubscriptionsApi,
|
|
15
18
|
type Address,
|
|
16
19
|
type Commitment,
|
|
17
20
|
} from 'gill';
|
|
18
21
|
import { PublicKey } from '@solana/web3.js';
|
|
19
22
|
import bs58 from 'bs58';
|
|
20
23
|
|
|
24
|
+
/**
|
|
25
|
+
* WebSocketAccountSubscriberV2
|
|
26
|
+
*
|
|
27
|
+
* High-level overview
|
|
28
|
+
* - WebSocket-first subscriber for a single Solana account with optional
|
|
29
|
+
* polling safeguards when the WS feed goes quiet.
|
|
30
|
+
* - Emits decoded updates via `onChange` and maintains the latest
|
|
31
|
+
* `{buffer, slot}` and decoded `{data, slot}` internally.
|
|
32
|
+
*
|
|
33
|
+
* Why polling if this is a WebSocket subscriber?
|
|
34
|
+
* - Under real-world conditions, WS notifications can stall or get dropped.
|
|
35
|
+
* - When `resubOpts.resubTimeoutMs` elapses without WS data, you can either:
|
|
36
|
+
* - resubscribe to the WS stream (default), or
|
|
37
|
+
* - enable `resubOpts.usePollingInsteadOfResub` to start polling this single
|
|
38
|
+
* account via RPC to check for missed changes.
|
|
39
|
+
* - Polling compares the fetched buffer to the last known buffer. If different
|
|
40
|
+
* at an equal-or-later slot, it indicates a missed update and we resubscribe
|
|
41
|
+
* to WS to restore a clean stream.
|
|
42
|
+
*
|
|
43
|
+
* Initial fetch (on subscribe)
|
|
44
|
+
* - On `subscribe()`, we do a one-time RPC `fetch()` to seed internal state and
|
|
45
|
+
* emit the latest account state, ensuring consumers start from ground truth
|
|
46
|
+
* even before WS events arrive.
|
|
47
|
+
*
|
|
48
|
+
* Continuous polling (opt-in)
|
|
49
|
+
* - If `usePollingInsteadOfResub` is set, the inactivity timeout triggers a
|
|
50
|
+
* polling loop that periodically `fetch()`es the account and checks for
|
|
51
|
+
* changes. On change, polling stops and we resubscribe to WS.
|
|
52
|
+
* - If not set (default), the inactivity timeout immediately triggers a WS
|
|
53
|
+
* resubscription (no polling loop).
|
|
54
|
+
*
|
|
55
|
+
* Account focus
|
|
56
|
+
* - This class tracks exactly one account — the one passed to the constructor —
|
|
57
|
+
* which is by definition the account the consumer cares about. The extra
|
|
58
|
+
* logic is narrowly scoped to this account to minimize overhead.
|
|
59
|
+
*
|
|
60
|
+
* Tuning knobs
|
|
61
|
+
* - `resubOpts.resubTimeoutMs`: WS inactivity threshold before fallback.
|
|
62
|
+
* - `resubOpts.usePollingInsteadOfResub`: toggle polling vs immediate resub.
|
|
63
|
+
* - `resubOpts.pollingIntervalMs`: polling cadence (default 30s).
|
|
64
|
+
* - `resubOpts.logResubMessages`: verbose logs for diagnostics.
|
|
65
|
+
* - `commitment`: WS/RPC commitment used for reads and notifications.
|
|
66
|
+
* - `decodeBufferFn`: optional custom decode; defaults to Anchor coder.
|
|
67
|
+
*
|
|
68
|
+
* Implementation notes
|
|
69
|
+
* - Uses `gill` for both WS (`rpcSubscriptions`) and RPC (`rpc`) to match the
|
|
70
|
+
* program provider’s RPC endpoint. Handles base58/base64 encoded data.
|
|
71
|
+
*/
|
|
21
72
|
export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
22
73
|
dataAndSlot?: DataAndSlot<T>;
|
|
23
74
|
bufferAndSlot?: BufferAndSlot;
|
|
@@ -29,12 +80,13 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
29
80
|
onChange: (data: T) => void;
|
|
30
81
|
listenerId?: number;
|
|
31
82
|
|
|
32
|
-
resubOpts
|
|
83
|
+
resubOpts: ResubOpts;
|
|
33
84
|
|
|
34
85
|
commitment?: Commitment;
|
|
35
86
|
isUnsubscribing = false;
|
|
36
87
|
|
|
37
88
|
timeoutId?: ReturnType<typeof setTimeout>;
|
|
89
|
+
pollingTimeoutId?: ReturnType<typeof setTimeout>;
|
|
38
90
|
|
|
39
91
|
receivingData: boolean;
|
|
40
92
|
|
|
@@ -45,21 +97,40 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
45
97
|
>['rpcSubscriptions'];
|
|
46
98
|
private abortController?: AbortController;
|
|
47
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Create a single-account WebSocket subscriber with optional polling fallback.
|
|
102
|
+
*
|
|
103
|
+
* @param accountName Name of the Anchor account type (used for default decode).
|
|
104
|
+
* @param program Anchor `Program` used for decoding and provider access.
|
|
105
|
+
* @param accountPublicKey Public key of the account to track.
|
|
106
|
+
* @param decodeBuffer Optional custom decode function; if omitted, uses
|
|
107
|
+
* program coder to decode `accountName`.
|
|
108
|
+
* @param resubOpts Resubscription/polling options. See class docs.
|
|
109
|
+
* @param commitment Commitment for WS and RPC operations.
|
|
110
|
+
* @param rpcSubscriptions Optional override/injection for testing.
|
|
111
|
+
* @param rpc Optional override/injection for testing.
|
|
112
|
+
*/
|
|
48
113
|
public constructor(
|
|
49
114
|
accountName: string,
|
|
50
115
|
program: Program,
|
|
51
116
|
accountPublicKey: PublicKey,
|
|
52
117
|
decodeBuffer?: (buffer: Buffer) => T,
|
|
53
118
|
resubOpts?: ResubOpts,
|
|
54
|
-
commitment?: Commitment
|
|
119
|
+
commitment?: Commitment,
|
|
120
|
+
rpcSubscriptions?: RpcSubscriptions<SolanaRpcSubscriptionsApi> & string,
|
|
121
|
+
rpc?: Rpc<any>
|
|
55
122
|
) {
|
|
56
123
|
this.accountName = accountName;
|
|
57
124
|
this.logAccountName = `${accountName}-${accountPublicKey.toBase58()}-ws-acct-subscriber-v2`;
|
|
58
125
|
this.program = program;
|
|
59
126
|
this.accountPublicKey = accountPublicKey;
|
|
60
127
|
this.decodeBufferFn = decodeBuffer;
|
|
61
|
-
this.resubOpts = resubOpts
|
|
62
|
-
|
|
128
|
+
this.resubOpts = resubOpts ?? {
|
|
129
|
+
resubTimeoutMs: 30000,
|
|
130
|
+
usePollingInsteadOfResub: true,
|
|
131
|
+
logResubMessages: false,
|
|
132
|
+
};
|
|
133
|
+
if (this.resubOpts.resubTimeoutMs < 1000) {
|
|
63
134
|
console.log(
|
|
64
135
|
`resubTimeoutMs should be at least 1000ms to avoid spamming resub ${this.logAccountName}`
|
|
65
136
|
);
|
|
@@ -81,31 +152,67 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
81
152
|
((this.program.provider as AnchorProvider).opts.commitment as Commitment);
|
|
82
153
|
|
|
83
154
|
// Initialize gill client using the same RPC URL as the program provider
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
155
|
+
|
|
156
|
+
this.rpc = rpc
|
|
157
|
+
? rpc
|
|
158
|
+
: (() => {
|
|
159
|
+
const rpcUrl = (this.program.provider as AnchorProvider).connection
|
|
160
|
+
.rpcEndpoint;
|
|
161
|
+
const { rpc } = createSolanaClient({
|
|
162
|
+
urlOrMoniker: rpcUrl,
|
|
163
|
+
});
|
|
164
|
+
return rpc;
|
|
165
|
+
})();
|
|
166
|
+
this.rpcSubscriptions = rpcSubscriptions
|
|
167
|
+
? rpcSubscriptions
|
|
168
|
+
: (() => {
|
|
169
|
+
const rpcUrl = (this.program.provider as AnchorProvider).connection
|
|
170
|
+
.rpcEndpoint;
|
|
171
|
+
const { rpcSubscriptions } = createSolanaClient({
|
|
172
|
+
urlOrMoniker: rpcUrl,
|
|
173
|
+
});
|
|
174
|
+
return rpcSubscriptions;
|
|
175
|
+
})();
|
|
91
176
|
}
|
|
92
177
|
|
|
93
|
-
private async handleNotificationLoop(
|
|
178
|
+
private async handleNotificationLoop(
|
|
179
|
+
subscriptionPromise: Promise<AsyncIterable<any>>
|
|
180
|
+
) {
|
|
181
|
+
const subscription = await subscriptionPromise;
|
|
94
182
|
for await (const notification of subscription) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
183
|
+
// If we're currently polling and receive a WebSocket event, stop polling
|
|
184
|
+
if (this.pollingTimeoutId) {
|
|
185
|
+
if (this.resubOpts.logResubMessages) {
|
|
186
|
+
console.log(
|
|
187
|
+
`[${this.logAccountName}] Received WebSocket event while polling, stopping polling`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
this.stopPolling();
|
|
102
191
|
}
|
|
192
|
+
|
|
193
|
+
this.receivingData = true;
|
|
194
|
+
clearTimeout(this.timeoutId);
|
|
195
|
+
this.handleRpcResponse(notification.context, notification.value);
|
|
196
|
+
this.setTimeout();
|
|
103
197
|
}
|
|
104
198
|
}
|
|
105
199
|
|
|
106
200
|
async subscribe(onChange: (data: T) => void): Promise<void> {
|
|
201
|
+
/**
|
|
202
|
+
* Start the WebSocket subscription and (optionally) setup inactivity
|
|
203
|
+
* fallback.
|
|
204
|
+
*
|
|
205
|
+
* Flow
|
|
206
|
+
* - If we do not have initial state, perform a one-time `fetch()` to seed
|
|
207
|
+
* internal buffers and emit current data.
|
|
208
|
+
* - Subscribe to account notifications via WS.
|
|
209
|
+
* - If `resubOpts.resubTimeoutMs` is set, schedule an inactivity timeout.
|
|
210
|
+
* When it fires:
|
|
211
|
+
* - if `usePollingInsteadOfResub` is true, start polling loop;
|
|
212
|
+
* - otherwise, resubscribe to WS immediately.
|
|
213
|
+
*/
|
|
107
214
|
if (this.listenerId != null || this.isUnsubscribing) {
|
|
108
|
-
if (this.resubOpts
|
|
215
|
+
if (this.resubOpts.logResubMessages) {
|
|
109
216
|
console.log(
|
|
110
217
|
`[${this.logAccountName}] Subscribe returning early - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
|
|
111
218
|
);
|
|
@@ -124,7 +231,7 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
124
231
|
|
|
125
232
|
this.listenerId = Math.random(); // Unique ID for logging purposes
|
|
126
233
|
|
|
127
|
-
if (this.resubOpts
|
|
234
|
+
if (this.resubOpts.resubTimeoutMs) {
|
|
128
235
|
this.receivingData = true;
|
|
129
236
|
this.setTimeout();
|
|
130
237
|
}
|
|
@@ -132,7 +239,7 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
132
239
|
// Subscribe to account changes using gill's rpcSubscriptions
|
|
133
240
|
const pubkey = this.accountPublicKey.toBase58();
|
|
134
241
|
if (isAddress(pubkey)) {
|
|
135
|
-
const
|
|
242
|
+
const subscriptionPromise = this.rpcSubscriptions
|
|
136
243
|
.accountNotifications(pubkey, {
|
|
137
244
|
commitment: this.commitment,
|
|
138
245
|
encoding: 'base64',
|
|
@@ -141,8 +248,10 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
141
248
|
abortSignal: abortController.signal,
|
|
142
249
|
});
|
|
143
250
|
|
|
144
|
-
// Start notification loop
|
|
145
|
-
this.handleNotificationLoop(
|
|
251
|
+
// Start notification loop with the subscription promise
|
|
252
|
+
this.handleNotificationLoop(subscriptionPromise);
|
|
253
|
+
} else {
|
|
254
|
+
throw new Error('Invalid account public key');
|
|
146
255
|
}
|
|
147
256
|
}
|
|
148
257
|
|
|
@@ -159,23 +268,37 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
159
268
|
}
|
|
160
269
|
|
|
161
270
|
protected setTimeout(): void {
|
|
271
|
+
/**
|
|
272
|
+
* Schedule inactivity handling. If WS is quiet for
|
|
273
|
+
* `resubOpts.resubTimeoutMs` and `receivingData` is true, trigger either
|
|
274
|
+
* a polling loop or a resubscribe depending on options.
|
|
275
|
+
*/
|
|
162
276
|
if (!this.onChange) {
|
|
163
277
|
throw new Error('onChange callback function must be set');
|
|
164
278
|
}
|
|
165
|
-
this.timeoutId = setTimeout(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
279
|
+
this.timeoutId = setTimeout(async () => {
|
|
280
|
+
if (this.isUnsubscribing) {
|
|
281
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
282
|
+
if (this.resubOpts.logResubMessages) {
|
|
283
|
+
console.log(
|
|
284
|
+
`[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.receivingData) {
|
|
291
|
+
if (this.resubOpts.usePollingInsteadOfResub) {
|
|
292
|
+
// Use polling instead of resubscribing
|
|
293
|
+
if (this.resubOpts.logResubMessages) {
|
|
170
294
|
console.log(
|
|
171
|
-
`[${this.logAccountName}]
|
|
295
|
+
`[${this.logAccountName}] No ws data in ${this.resubOpts.resubTimeoutMs}ms, starting polling - listenerId=${this.listenerId}`
|
|
172
296
|
);
|
|
173
297
|
}
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (this.resubOpts?.logResubMessages) {
|
|
298
|
+
this.startPolling();
|
|
299
|
+
} else {
|
|
300
|
+
// Original resubscribe behavior
|
|
301
|
+
if (this.resubOpts.logResubMessages) {
|
|
179
302
|
console.log(
|
|
180
303
|
`No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
|
|
181
304
|
);
|
|
@@ -183,23 +306,93 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
183
306
|
await this.unsubscribe(true);
|
|
184
307
|
this.receivingData = false;
|
|
185
308
|
await this.subscribe(this.onChange);
|
|
186
|
-
if (this.resubOpts
|
|
309
|
+
if (this.resubOpts.logResubMessages) {
|
|
187
310
|
console.log(
|
|
188
311
|
`[${this.logAccountName}] Resubscribe completed - receivingData=${this.receivingData}, listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
|
|
189
312
|
);
|
|
190
313
|
}
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
if (this.resubOpts.logResubMessages) {
|
|
317
|
+
console.log(
|
|
318
|
+
`[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}, this.resubOpts.resubTimeoutMs);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Start the polling loop (single-account).
|
|
327
|
+
* - Periodically calls `fetch()` and compares buffers to detect changes.
|
|
328
|
+
* - On detected change, stops polling and resubscribes to WS.
|
|
329
|
+
*/
|
|
330
|
+
private startPolling(): void {
|
|
331
|
+
const pollingInterval = this.resubOpts.pollingIntervalMs || 30000; // Default to 30s
|
|
332
|
+
|
|
333
|
+
const poll = async () => {
|
|
334
|
+
if (this.isUnsubscribing) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// Store current data and buffer before polling
|
|
340
|
+
const currentBuffer = this.bufferAndSlot?.buffer;
|
|
341
|
+
|
|
342
|
+
// Fetch latest account data
|
|
343
|
+
await this.fetch();
|
|
344
|
+
|
|
345
|
+
// Check if we got new data by comparing buffers
|
|
346
|
+
const newBuffer = this.bufferAndSlot?.buffer;
|
|
347
|
+
const hasNewData =
|
|
348
|
+
newBuffer && (!currentBuffer || !newBuffer.equals(currentBuffer));
|
|
349
|
+
|
|
350
|
+
if (hasNewData) {
|
|
351
|
+
// New data received, stop polling and resubscribe to websocket
|
|
352
|
+
if (this.resubOpts.logResubMessages) {
|
|
353
|
+
console.log(
|
|
354
|
+
`[${this.logAccountName}] Polling detected account data change, resubscribing to websocket`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
await this.unsubscribe(true);
|
|
358
|
+
this.receivingData = false;
|
|
359
|
+
await this.subscribe(this.onChange);
|
|
191
360
|
} else {
|
|
192
|
-
|
|
361
|
+
// No new data, continue polling
|
|
362
|
+
if (this.resubOpts.logResubMessages) {
|
|
193
363
|
console.log(
|
|
194
|
-
`[${this.logAccountName}]
|
|
364
|
+
`[${this.logAccountName}] Polling found no account changes, continuing to poll every ${pollingInterval}ms`
|
|
195
365
|
);
|
|
196
366
|
}
|
|
367
|
+
this.pollingTimeoutId = setTimeout(poll, pollingInterval);
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (this.resubOpts.logResubMessages) {
|
|
371
|
+
console.error(
|
|
372
|
+
`[${this.logAccountName}] Error during polling:`,
|
|
373
|
+
error
|
|
374
|
+
);
|
|
197
375
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
376
|
+
// On error, continue polling
|
|
377
|
+
this.pollingTimeoutId = setTimeout(poll, pollingInterval);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// Start polling immediately
|
|
382
|
+
poll();
|
|
201
383
|
}
|
|
202
384
|
|
|
385
|
+
private stopPolling(): void {
|
|
386
|
+
if (this.pollingTimeoutId) {
|
|
387
|
+
clearTimeout(this.pollingTimeoutId);
|
|
388
|
+
this.pollingTimeoutId = undefined;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Fetch the current account state via RPC and process it through the same
|
|
394
|
+
* decoding and update pathway as WS notifications.
|
|
395
|
+
*/
|
|
203
396
|
async fetch(): Promise<void> {
|
|
204
397
|
// Use gill's rpc for fetching account info
|
|
205
398
|
const accountAddress = this.accountPublicKey.toBase58() as Address;
|
|
@@ -294,6 +487,11 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
294
487
|
}
|
|
295
488
|
|
|
296
489
|
unsubscribe(onResub = false): Promise<void> {
|
|
490
|
+
/**
|
|
491
|
+
* Stop timers, polling, and WS subscription.
|
|
492
|
+
* - When called during a resubscribe (`onResub=true`), we preserve
|
|
493
|
+
* `resubOpts.resubTimeoutMs` for the restarted subscription.
|
|
494
|
+
*/
|
|
297
495
|
if (!onResub && this.resubOpts) {
|
|
298
496
|
this.resubOpts.resubTimeoutMs = undefined;
|
|
299
497
|
}
|
|
@@ -301,6 +499,9 @@ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
|
|
|
301
499
|
clearTimeout(this.timeoutId);
|
|
302
500
|
this.timeoutId = undefined;
|
|
303
501
|
|
|
502
|
+
// Stop polling if active
|
|
503
|
+
this.stopPolling();
|
|
504
|
+
|
|
304
505
|
// Abort the WebSocket subscription
|
|
305
506
|
if (this.abortController) {
|
|
306
507
|
this.abortController.abort('unsubscribing');
|