@drift-labs/sdk 2.131.0-beta.6 → 2.131.0-beta.8
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/webSocketProgramAccountSubscriberV2.d.ts +53 -0
- package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +453 -0
- package/lib/browser/idl/drift.json +36 -5
- package/lib/browser/orderSubscriber/WebsocketSubscription.js +2 -2
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +54 -0
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +1 -0
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +453 -0
- package/lib/node/idl/drift.json +36 -5
- package/lib/node/orderSubscriber/WebsocketSubscription.d.ts.map +1 -1
- package/lib/node/orderSubscriber/WebsocketSubscription.js +2 -2
- package/package.json +1 -1
- package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +135 -0
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +596 -0
- package/src/idl/drift.json +37 -6
- package/src/orderSubscriber/WebsocketSubscription.ts +3 -3
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.131.0-beta.
|
|
1
|
+
2.131.0-beta.8
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types';
|
|
4
|
+
import { Program } from '@coral-xyz/anchor';
|
|
5
|
+
import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js';
|
|
6
|
+
import { AccountInfoBase, AccountInfoWithBase58EncodedData, AccountInfoWithBase64EncodedData } from 'gill';
|
|
7
|
+
export declare class WebSocketProgramAccountSubscriberV2<T> implements ProgramAccountSubscriber<T> {
|
|
8
|
+
subscriptionName: string;
|
|
9
|
+
accountDiscriminator: string;
|
|
10
|
+
bufferAndSlot?: BufferAndSlot;
|
|
11
|
+
bufferAndSlotMap: Map<string, BufferAndSlot>;
|
|
12
|
+
program: Program;
|
|
13
|
+
decodeBuffer: (accountName: string, ix: Buffer) => T;
|
|
14
|
+
onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void;
|
|
15
|
+
listenerId?: number;
|
|
16
|
+
resubOpts?: ResubOpts;
|
|
17
|
+
isUnsubscribing: boolean;
|
|
18
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
19
|
+
options: {
|
|
20
|
+
filters: MemcmpFilter[];
|
|
21
|
+
commitment?: Commitment;
|
|
22
|
+
};
|
|
23
|
+
receivingData: boolean;
|
|
24
|
+
private rpc;
|
|
25
|
+
private rpcSubscriptions;
|
|
26
|
+
private abortController?;
|
|
27
|
+
private accountsToMonitor;
|
|
28
|
+
private pollingIntervalMs;
|
|
29
|
+
private pollingTimeouts;
|
|
30
|
+
private lastWsNotificationTime;
|
|
31
|
+
private accountsCurrentlyPolling;
|
|
32
|
+
private batchPollingTimeout?;
|
|
33
|
+
constructor(subscriptionName: string, accountDiscriminator: string, program: Program, decodeBufferFn: (accountName: string, ix: Buffer) => T, options?: {
|
|
34
|
+
filters: MemcmpFilter[];
|
|
35
|
+
commitment?: Commitment;
|
|
36
|
+
}, resubOpts?: ResubOpts, accountsToMonitor?: PublicKey[]);
|
|
37
|
+
subscribe(onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void): Promise<void>;
|
|
38
|
+
protected setTimeout(): void;
|
|
39
|
+
handleRpcResponse(context: {
|
|
40
|
+
slot: bigint;
|
|
41
|
+
}, accountInfo?: AccountInfoBase & (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData)): void;
|
|
42
|
+
private startMonitoringForAccounts;
|
|
43
|
+
private startMonitoringForAccount;
|
|
44
|
+
private startPollingForAccount;
|
|
45
|
+
private startBatchPolling;
|
|
46
|
+
private pollAllAccounts;
|
|
47
|
+
private pollAccount;
|
|
48
|
+
private clearPollingTimeouts;
|
|
49
|
+
unsubscribe(onResub?: boolean): Promise<void>;
|
|
50
|
+
addAccountToMonitor(accountId: PublicKey): void;
|
|
51
|
+
removeAccountFromMonitor(accountId: PublicKey): void;
|
|
52
|
+
setPollingInterval(intervalMs: number): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WebSocketProgramAccountSubscriberV2 = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const gill_1 = require("gill");
|
|
9
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
10
|
+
class WebSocketProgramAccountSubscriberV2 {
|
|
11
|
+
constructor(subscriptionName, accountDiscriminator, program, decodeBufferFn, options = {
|
|
12
|
+
filters: [],
|
|
13
|
+
}, resubOpts, accountsToMonitor // Optional list of accounts to poll
|
|
14
|
+
) {
|
|
15
|
+
var _a;
|
|
16
|
+
this.bufferAndSlotMap = new Map();
|
|
17
|
+
this.isUnsubscribing = false;
|
|
18
|
+
this.receivingData = false;
|
|
19
|
+
// Polling logic for specific accounts
|
|
20
|
+
this.accountsToMonitor = new Set();
|
|
21
|
+
this.pollingIntervalMs = 30000; // 30 seconds
|
|
22
|
+
this.pollingTimeouts = new Map();
|
|
23
|
+
this.lastWsNotificationTime = new Map(); // Track last WS notification time per account
|
|
24
|
+
this.accountsCurrentlyPolling = new Set(); // Track which accounts are being polled
|
|
25
|
+
this.subscriptionName = subscriptionName;
|
|
26
|
+
this.accountDiscriminator = accountDiscriminator;
|
|
27
|
+
this.program = program;
|
|
28
|
+
this.decodeBuffer = decodeBufferFn;
|
|
29
|
+
this.resubOpts = resubOpts;
|
|
30
|
+
if (((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) < 1000) {
|
|
31
|
+
console.log('resubTimeoutMs should be at least 1000ms to avoid spamming resub');
|
|
32
|
+
}
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.receivingData = false;
|
|
35
|
+
// Initialize accounts to monitor
|
|
36
|
+
if (accountsToMonitor) {
|
|
37
|
+
accountsToMonitor.forEach((account) => {
|
|
38
|
+
this.accountsToMonitor.add(account.toBase58());
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Initialize gill client using the same RPC URL as the program provider
|
|
42
|
+
const rpcUrl = this.program.provider.connection
|
|
43
|
+
.rpcEndpoint;
|
|
44
|
+
const { rpc, rpcSubscriptions } = (0, gill_1.createSolanaClient)({
|
|
45
|
+
urlOrMoniker: rpcUrl,
|
|
46
|
+
});
|
|
47
|
+
this.rpc = rpc;
|
|
48
|
+
this.rpcSubscriptions = rpcSubscriptions;
|
|
49
|
+
}
|
|
50
|
+
async subscribe(onChange) {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
if (this.listenerId != null || this.isUnsubscribing) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.onChange = onChange;
|
|
56
|
+
// Create abort controller for proper cleanup
|
|
57
|
+
const abortController = new AbortController();
|
|
58
|
+
this.abortController = abortController;
|
|
59
|
+
// Subscribe to program account changes using gill's rpcSubscriptions
|
|
60
|
+
const programId = this.program.programId.toBase58();
|
|
61
|
+
if ((0, gill_1.isAddress)(programId)) {
|
|
62
|
+
const subscription = await this.rpcSubscriptions
|
|
63
|
+
.programNotifications(programId, {
|
|
64
|
+
commitment: this.options.commitment,
|
|
65
|
+
encoding: 'base64',
|
|
66
|
+
filters: this.options.filters.map((filter) => ({
|
|
67
|
+
memcmp: {
|
|
68
|
+
offset: BigInt(filter.memcmp.offset),
|
|
69
|
+
bytes: filter.memcmp.bytes,
|
|
70
|
+
encoding: 'base64',
|
|
71
|
+
},
|
|
72
|
+
})),
|
|
73
|
+
})
|
|
74
|
+
.subscribe({
|
|
75
|
+
abortSignal: abortController.signal,
|
|
76
|
+
});
|
|
77
|
+
for await (const notification of subscription) {
|
|
78
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs) {
|
|
79
|
+
this.receivingData = true;
|
|
80
|
+
clearTimeout(this.timeoutId);
|
|
81
|
+
this.handleRpcResponse(notification.context, notification.value.account);
|
|
82
|
+
this.setTimeout();
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this.handleRpcResponse(notification.context, notification.value.account);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.listenerId = Math.random(); // Unique ID for logging purposes
|
|
90
|
+
if ((_b = this.resubOpts) === null || _b === void 0 ? void 0 : _b.resubTimeoutMs) {
|
|
91
|
+
this.receivingData = true;
|
|
92
|
+
this.setTimeout();
|
|
93
|
+
}
|
|
94
|
+
// Start monitoring for accounts that may need polling if no WS event is received
|
|
95
|
+
this.startMonitoringForAccounts();
|
|
96
|
+
}
|
|
97
|
+
setTimeout() {
|
|
98
|
+
var _a;
|
|
99
|
+
if (!this.onChange) {
|
|
100
|
+
throw new Error('onChange callback function must be set');
|
|
101
|
+
}
|
|
102
|
+
this.timeoutId = setTimeout(async () => {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
if (this.isUnsubscribing) {
|
|
105
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (this.receivingData) {
|
|
109
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
110
|
+
console.log(`No ws data from ${this.subscriptionName} in ${(_b = this.resubOpts) === null || _b === void 0 ? void 0 : _b.resubTimeoutMs}ms, resubscribing`);
|
|
111
|
+
}
|
|
112
|
+
await this.unsubscribe(true);
|
|
113
|
+
this.receivingData = false;
|
|
114
|
+
await this.subscribe(this.onChange);
|
|
115
|
+
}
|
|
116
|
+
}, (_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs);
|
|
117
|
+
}
|
|
118
|
+
handleRpcResponse(context, accountInfo) {
|
|
119
|
+
const newSlot = Number(context.slot);
|
|
120
|
+
let newBuffer = undefined;
|
|
121
|
+
if (accountInfo) {
|
|
122
|
+
// Extract data from gill response
|
|
123
|
+
if (accountInfo.data) {
|
|
124
|
+
// Handle different data formats from gill
|
|
125
|
+
if (Array.isArray(accountInfo.data)) {
|
|
126
|
+
// If it's a tuple [data, encoding]
|
|
127
|
+
const [data, encoding] = accountInfo.data;
|
|
128
|
+
if (encoding === 'base58') {
|
|
129
|
+
// Convert base58 to buffer using bs58
|
|
130
|
+
newBuffer = Buffer.from(bs58_1.default.decode(data));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
newBuffer = Buffer.from(data, 'base64');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Convert gill's account key to PublicKey
|
|
139
|
+
// Note: accountInfo doesn't have a key property, we need to get it from the notification
|
|
140
|
+
// For now, we'll use a placeholder - this needs to be fixed based on the actual gill API
|
|
141
|
+
const accountId = new web3_js_1.PublicKey('11111111111111111111111111111111'); // Placeholder
|
|
142
|
+
const accountIdString = accountId.toBase58();
|
|
143
|
+
const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString);
|
|
144
|
+
// Track WebSocket notification time for this account
|
|
145
|
+
this.lastWsNotificationTime.set(accountIdString, Date.now());
|
|
146
|
+
// If this account was being polled, stop polling it
|
|
147
|
+
if (this.accountsCurrentlyPolling.has(accountIdString)) {
|
|
148
|
+
this.accountsCurrentlyPolling.delete(accountIdString);
|
|
149
|
+
// If no more accounts are being polled, stop batch polling
|
|
150
|
+
if (this.accountsCurrentlyPolling.size === 0 &&
|
|
151
|
+
this.batchPollingTimeout) {
|
|
152
|
+
clearTimeout(this.batchPollingTimeout);
|
|
153
|
+
this.batchPollingTimeout = undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (!existingBufferAndSlot) {
|
|
157
|
+
if (newBuffer) {
|
|
158
|
+
this.bufferAndSlotMap.set(accountIdString, {
|
|
159
|
+
buffer: newBuffer,
|
|
160
|
+
slot: newSlot,
|
|
161
|
+
});
|
|
162
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
163
|
+
this.onChange(accountId, account, { slot: newSlot }, newBuffer);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (newSlot < existingBufferAndSlot.slot) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const oldBuffer = existingBufferAndSlot.buffer;
|
|
171
|
+
if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
|
|
172
|
+
this.bufferAndSlotMap.set(accountIdString, {
|
|
173
|
+
buffer: newBuffer,
|
|
174
|
+
slot: newSlot,
|
|
175
|
+
});
|
|
176
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
177
|
+
this.onChange(accountId, account, { slot: newSlot }, newBuffer);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
startMonitoringForAccounts() {
|
|
181
|
+
// Clear any existing polling timeouts
|
|
182
|
+
this.clearPollingTimeouts();
|
|
183
|
+
// Start monitoring for each account in the accountsToMonitor set
|
|
184
|
+
this.accountsToMonitor.forEach((accountIdString) => {
|
|
185
|
+
this.startMonitoringForAccount(accountIdString);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
startMonitoringForAccount(accountIdString) {
|
|
189
|
+
// Clear existing timeout for this account
|
|
190
|
+
const existingTimeout = this.pollingTimeouts.get(accountIdString);
|
|
191
|
+
if (existingTimeout) {
|
|
192
|
+
clearTimeout(existingTimeout);
|
|
193
|
+
}
|
|
194
|
+
// Set up monitoring timeout - only start polling if no WS notification in 30s
|
|
195
|
+
const timeoutId = setTimeout(async () => {
|
|
196
|
+
// Check if we've received a WS notification for this account recently
|
|
197
|
+
const lastNotificationTime = this.lastWsNotificationTime.get(accountIdString);
|
|
198
|
+
const currentTime = Date.now();
|
|
199
|
+
if (!lastNotificationTime ||
|
|
200
|
+
currentTime - lastNotificationTime >= this.pollingIntervalMs) {
|
|
201
|
+
// No recent WS notification, start polling
|
|
202
|
+
await this.pollAccount(accountIdString);
|
|
203
|
+
// Schedule next poll
|
|
204
|
+
this.startPollingForAccount(accountIdString);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// We received a WS notification recently, continue monitoring
|
|
208
|
+
this.startMonitoringForAccount(accountIdString);
|
|
209
|
+
}
|
|
210
|
+
}, this.pollingIntervalMs);
|
|
211
|
+
this.pollingTimeouts.set(accountIdString, timeoutId);
|
|
212
|
+
}
|
|
213
|
+
startPollingForAccount(accountIdString) {
|
|
214
|
+
// Add account to polling set
|
|
215
|
+
this.accountsCurrentlyPolling.add(accountIdString);
|
|
216
|
+
// If this is the first account being polled, start batch polling
|
|
217
|
+
if (this.accountsCurrentlyPolling.size === 1) {
|
|
218
|
+
this.startBatchPolling();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
startBatchPolling() {
|
|
222
|
+
// Clear existing batch polling timeout
|
|
223
|
+
if (this.batchPollingTimeout) {
|
|
224
|
+
clearTimeout(this.batchPollingTimeout);
|
|
225
|
+
}
|
|
226
|
+
// Set up batch polling interval
|
|
227
|
+
this.batchPollingTimeout = setTimeout(async () => {
|
|
228
|
+
await this.pollAllAccounts();
|
|
229
|
+
// Schedule next batch poll
|
|
230
|
+
this.startBatchPolling();
|
|
231
|
+
}, this.pollingIntervalMs);
|
|
232
|
+
}
|
|
233
|
+
async pollAllAccounts() {
|
|
234
|
+
var _a, _b;
|
|
235
|
+
try {
|
|
236
|
+
// Get all accounts currently being polled
|
|
237
|
+
const accountsToPoll = Array.from(this.accountsCurrentlyPolling);
|
|
238
|
+
if (accountsToPoll.length === 0) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
// Fetch all accounts in a single batch request
|
|
242
|
+
const accountAddresses = accountsToPoll.map((accountId) => accountId);
|
|
243
|
+
const rpcResponse = await this.rpc
|
|
244
|
+
.getMultipleAccounts(accountAddresses, {
|
|
245
|
+
commitment: this.options.commitment,
|
|
246
|
+
encoding: 'base64',
|
|
247
|
+
})
|
|
248
|
+
.send();
|
|
249
|
+
const currentSlot = Number(rpcResponse.context.slot);
|
|
250
|
+
// Process each account response
|
|
251
|
+
for (let i = 0; i < accountsToPoll.length; i++) {
|
|
252
|
+
const accountIdString = accountsToPoll[i];
|
|
253
|
+
const accountInfo = rpcResponse.value[i];
|
|
254
|
+
if (!accountInfo) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString);
|
|
258
|
+
if (!existingBufferAndSlot) {
|
|
259
|
+
// Account not in our map yet, add it
|
|
260
|
+
let newBuffer = undefined;
|
|
261
|
+
if (accountInfo.data) {
|
|
262
|
+
if (Array.isArray(accountInfo.data)) {
|
|
263
|
+
const [data, encoding] = accountInfo.data;
|
|
264
|
+
newBuffer = Buffer.from(data, encoding);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (newBuffer) {
|
|
268
|
+
this.bufferAndSlotMap.set(accountIdString, {
|
|
269
|
+
buffer: newBuffer,
|
|
270
|
+
slot: currentSlot,
|
|
271
|
+
});
|
|
272
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
273
|
+
const accountId = new web3_js_1.PublicKey(accountIdString);
|
|
274
|
+
this.onChange(accountId, account, { slot: currentSlot }, newBuffer);
|
|
275
|
+
}
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// Check if we missed an update
|
|
279
|
+
if (currentSlot > existingBufferAndSlot.slot) {
|
|
280
|
+
let newBuffer = undefined;
|
|
281
|
+
if (accountInfo.data) {
|
|
282
|
+
if (Array.isArray(accountInfo.data)) {
|
|
283
|
+
const [data, encoding] = accountInfo.data;
|
|
284
|
+
if (encoding === 'base58') {
|
|
285
|
+
newBuffer = Buffer.from(bs58_1.default.decode(data));
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
newBuffer = Buffer.from(data, 'base64');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Check if buffer has changed
|
|
293
|
+
if (newBuffer &&
|
|
294
|
+
(!existingBufferAndSlot.buffer ||
|
|
295
|
+
!newBuffer.equals(existingBufferAndSlot.buffer))) {
|
|
296
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
297
|
+
console.log(`[${this.subscriptionName}] Batch polling detected missed update for account ${accountIdString}, resubscribing`);
|
|
298
|
+
}
|
|
299
|
+
// We missed an update, resubscribe
|
|
300
|
+
await this.unsubscribe(true);
|
|
301
|
+
this.receivingData = false;
|
|
302
|
+
await this.subscribe(this.onChange);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
if ((_b = this.resubOpts) === null || _b === void 0 ? void 0 : _b.logResubMessages) {
|
|
310
|
+
console.log(`[${this.subscriptionName}] Error batch polling accounts:`, error);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async pollAccount(accountIdString) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
try {
|
|
317
|
+
// Fetch current account data using gill's rpc
|
|
318
|
+
const accountAddress = accountIdString;
|
|
319
|
+
const rpcResponse = await this.rpc
|
|
320
|
+
.getAccountInfo(accountAddress, {
|
|
321
|
+
commitment: this.options.commitment,
|
|
322
|
+
encoding: 'base64',
|
|
323
|
+
})
|
|
324
|
+
.send();
|
|
325
|
+
const currentSlot = Number(rpcResponse.context.slot);
|
|
326
|
+
const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString);
|
|
327
|
+
if (!existingBufferAndSlot) {
|
|
328
|
+
// Account not in our map yet, add it
|
|
329
|
+
if (rpcResponse.value) {
|
|
330
|
+
let newBuffer = undefined;
|
|
331
|
+
if (rpcResponse.value.data) {
|
|
332
|
+
if (Array.isArray(rpcResponse.value.data)) {
|
|
333
|
+
const [data, encoding] = rpcResponse.value.data;
|
|
334
|
+
newBuffer = Buffer.from(data, encoding);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (newBuffer) {
|
|
338
|
+
this.bufferAndSlotMap.set(accountIdString, {
|
|
339
|
+
buffer: newBuffer,
|
|
340
|
+
slot: currentSlot,
|
|
341
|
+
});
|
|
342
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
343
|
+
const accountId = new web3_js_1.PublicKey(accountIdString);
|
|
344
|
+
this.onChange(accountId, account, { slot: currentSlot }, newBuffer);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Check if we missed an update
|
|
350
|
+
if (currentSlot > existingBufferAndSlot.slot) {
|
|
351
|
+
let newBuffer = undefined;
|
|
352
|
+
if (rpcResponse.value) {
|
|
353
|
+
if (rpcResponse.value.data) {
|
|
354
|
+
if (Array.isArray(rpcResponse.value.data)) {
|
|
355
|
+
const [data, encoding] = rpcResponse.value.data;
|
|
356
|
+
if (encoding === 'base58') {
|
|
357
|
+
newBuffer = Buffer.from(bs58_1.default.decode(data));
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
newBuffer = Buffer.from(data, 'base64');
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Check if buffer has changed
|
|
366
|
+
if (newBuffer &&
|
|
367
|
+
(!existingBufferAndSlot.buffer ||
|
|
368
|
+
!newBuffer.equals(existingBufferAndSlot.buffer))) {
|
|
369
|
+
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
|
|
370
|
+
console.log(`[${this.subscriptionName}] Polling detected missed update for account ${accountIdString}, resubscribing`);
|
|
371
|
+
}
|
|
372
|
+
// We missed an update, resubscribe
|
|
373
|
+
await this.unsubscribe(true);
|
|
374
|
+
this.receivingData = false;
|
|
375
|
+
await this.subscribe(this.onChange);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
if ((_b = this.resubOpts) === null || _b === void 0 ? void 0 : _b.logResubMessages) {
|
|
382
|
+
console.log(`[${this.subscriptionName}] Error polling account ${accountIdString}:`, error);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
clearPollingTimeouts() {
|
|
387
|
+
this.pollingTimeouts.forEach((timeoutId) => {
|
|
388
|
+
clearTimeout(timeoutId);
|
|
389
|
+
});
|
|
390
|
+
this.pollingTimeouts.clear();
|
|
391
|
+
// Clear batch polling timeout
|
|
392
|
+
if (this.batchPollingTimeout) {
|
|
393
|
+
clearTimeout(this.batchPollingTimeout);
|
|
394
|
+
this.batchPollingTimeout = undefined;
|
|
395
|
+
}
|
|
396
|
+
// Clear accounts currently polling
|
|
397
|
+
this.accountsCurrentlyPolling.clear();
|
|
398
|
+
}
|
|
399
|
+
unsubscribe(onResub = false) {
|
|
400
|
+
if (!onResub) {
|
|
401
|
+
this.resubOpts.resubTimeoutMs = undefined;
|
|
402
|
+
}
|
|
403
|
+
this.isUnsubscribing = true;
|
|
404
|
+
clearTimeout(this.timeoutId);
|
|
405
|
+
this.timeoutId = undefined;
|
|
406
|
+
// Clear polling timeouts
|
|
407
|
+
this.clearPollingTimeouts();
|
|
408
|
+
// Abort the WebSocket subscription
|
|
409
|
+
if (this.abortController) {
|
|
410
|
+
this.abortController.abort('unsubscribing');
|
|
411
|
+
this.abortController = undefined;
|
|
412
|
+
}
|
|
413
|
+
this.listenerId = undefined;
|
|
414
|
+
this.isUnsubscribing = false;
|
|
415
|
+
return Promise.resolve();
|
|
416
|
+
}
|
|
417
|
+
// Method to add accounts to the polling list
|
|
418
|
+
addAccountToMonitor(accountId) {
|
|
419
|
+
const accountIdString = accountId.toBase58();
|
|
420
|
+
this.accountsToMonitor.add(accountIdString);
|
|
421
|
+
// If already subscribed, start monitoring for this account
|
|
422
|
+
if (this.listenerId != null && !this.isUnsubscribing) {
|
|
423
|
+
this.startMonitoringForAccount(accountIdString);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Method to remove accounts from the polling list
|
|
427
|
+
removeAccountFromMonitor(accountId) {
|
|
428
|
+
const accountIdString = accountId.toBase58();
|
|
429
|
+
this.accountsToMonitor.delete(accountIdString);
|
|
430
|
+
// Clear monitoring timeout for this account
|
|
431
|
+
const timeoutId = this.pollingTimeouts.get(accountIdString);
|
|
432
|
+
if (timeoutId) {
|
|
433
|
+
clearTimeout(timeoutId);
|
|
434
|
+
this.pollingTimeouts.delete(accountIdString);
|
|
435
|
+
}
|
|
436
|
+
// Remove from currently polling set if it was being polled
|
|
437
|
+
this.accountsCurrentlyPolling.delete(accountIdString);
|
|
438
|
+
// If no more accounts are being polled, stop batch polling
|
|
439
|
+
if (this.accountsCurrentlyPolling.size === 0 && this.batchPollingTimeout) {
|
|
440
|
+
clearTimeout(this.batchPollingTimeout);
|
|
441
|
+
this.batchPollingTimeout = undefined;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// Method to set polling interval
|
|
445
|
+
setPollingInterval(intervalMs) {
|
|
446
|
+
this.pollingIntervalMs = intervalMs;
|
|
447
|
+
// Restart monitoring with new interval if already subscribed
|
|
448
|
+
if (this.listenerId != null && !this.isUnsubscribing) {
|
|
449
|
+
this.startMonitoringForAccounts();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
exports.WebSocketProgramAccountSubscriberV2 = WebSocketProgramAccountSubscriberV2;
|
|
@@ -7576,6 +7576,27 @@
|
|
|
7576
7576
|
}
|
|
7577
7577
|
],
|
|
7578
7578
|
"args": []
|
|
7579
|
+
},
|
|
7580
|
+
{
|
|
7581
|
+
"name": "updateFeatureBitFlagsMedianTriggerPrice",
|
|
7582
|
+
"accounts": [
|
|
7583
|
+
{
|
|
7584
|
+
"name": "admin",
|
|
7585
|
+
"isMut": false,
|
|
7586
|
+
"isSigner": true
|
|
7587
|
+
},
|
|
7588
|
+
{
|
|
7589
|
+
"name": "state",
|
|
7590
|
+
"isMut": true,
|
|
7591
|
+
"isSigner": false
|
|
7592
|
+
}
|
|
7593
|
+
],
|
|
7594
|
+
"args": [
|
|
7595
|
+
{
|
|
7596
|
+
"name": "enable",
|
|
7597
|
+
"type": "bool"
|
|
7598
|
+
}
|
|
7599
|
+
]
|
|
7579
7600
|
}
|
|
7580
7601
|
],
|
|
7581
7602
|
"accounts": [
|
|
@@ -11434,7 +11455,7 @@
|
|
|
11434
11455
|
{
|
|
11435
11456
|
"name": "openBids",
|
|
11436
11457
|
"docs": [
|
|
11437
|
-
"How many spot
|
|
11458
|
+
"How many spot non reduce only trigger orders the user has open",
|
|
11438
11459
|
"precision: token mint precision"
|
|
11439
11460
|
],
|
|
11440
11461
|
"type": "i64"
|
|
@@ -11442,7 +11463,7 @@
|
|
|
11442
11463
|
{
|
|
11443
11464
|
"name": "openAsks",
|
|
11444
11465
|
"docs": [
|
|
11445
|
-
"How many spot
|
|
11466
|
+
"How many spot non reduce only trigger orders the user has open",
|
|
11446
11467
|
"precision: token mint precision"
|
|
11447
11468
|
],
|
|
11448
11469
|
"type": "i64"
|
|
@@ -11541,7 +11562,7 @@
|
|
|
11541
11562
|
{
|
|
11542
11563
|
"name": "openBids",
|
|
11543
11564
|
"docs": [
|
|
11544
|
-
"The amount of
|
|
11565
|
+
"The amount of non reduce only trigger orders the user has open",
|
|
11545
11566
|
"precision: BASE_PRECISION"
|
|
11546
11567
|
],
|
|
11547
11568
|
"type": "i64"
|
|
@@ -11549,7 +11570,7 @@
|
|
|
11549
11570
|
{
|
|
11550
11571
|
"name": "openAsks",
|
|
11551
11572
|
"docs": [
|
|
11552
|
-
"The amount of
|
|
11573
|
+
"The amount of non reduce only trigger orders the user has open",
|
|
11553
11574
|
"precision: BASE_PRECISION"
|
|
11554
11575
|
],
|
|
11555
11576
|
"type": "i64"
|
|
@@ -12810,7 +12831,7 @@
|
|
|
12810
12831
|
"name": "MmOracleUpdate"
|
|
12811
12832
|
},
|
|
12812
12833
|
{
|
|
12813
|
-
"name": "
|
|
12834
|
+
"name": "MedianTriggerPrice"
|
|
12814
12835
|
}
|
|
12815
12836
|
]
|
|
12816
12837
|
}
|
|
@@ -12942,6 +12963,9 @@
|
|
|
12942
12963
|
},
|
|
12943
12964
|
{
|
|
12944
12965
|
"name": "SafeTriggerOrder"
|
|
12966
|
+
},
|
|
12967
|
+
{
|
|
12968
|
+
"name": "NewTriggerReduceOnly"
|
|
12945
12969
|
}
|
|
12946
12970
|
]
|
|
12947
12971
|
}
|
|
@@ -13738,6 +13762,13 @@
|
|
|
13738
13762
|
"option": "u64"
|
|
13739
13763
|
},
|
|
13740
13764
|
"index": false
|
|
13765
|
+
},
|
|
13766
|
+
{
|
|
13767
|
+
"name": "triggerPrice",
|
|
13768
|
+
"type": {
|
|
13769
|
+
"option": "u64"
|
|
13770
|
+
},
|
|
13771
|
+
"index": false
|
|
13741
13772
|
}
|
|
13742
13773
|
]
|
|
13743
13774
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WebsocketSubscription = void 0;
|
|
4
4
|
const memcmp_1 = require("../memcmp");
|
|
5
|
-
const
|
|
5
|
+
const webSocketProgramAccountSubscriberV2_1 = require("../accounts/webSocketProgramAccountSubscriberV2");
|
|
6
6
|
class WebsocketSubscription {
|
|
7
7
|
constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubOpts, resyncIntervalMs, decoded = true, }) {
|
|
8
8
|
this.orderSubscriber = orderSubscriber;
|
|
@@ -16,7 +16,7 @@ class WebsocketSubscription {
|
|
|
16
16
|
if (this.subscriber) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
this.subscriber = new
|
|
19
|
+
this.subscriber = new webSocketProgramAccountSubscriberV2_1.WebSocketProgramAccountSubscriberV2('OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, this.orderSubscriber.decodeFn, {
|
|
20
20
|
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
|
|
21
21
|
commitment: this.commitment,
|
|
22
22
|
}, this.resubOpts);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types';
|
|
4
|
+
import { Program } from '@coral-xyz/anchor';
|
|
5
|
+
import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js';
|
|
6
|
+
import { AccountInfoBase, AccountInfoWithBase58EncodedData, AccountInfoWithBase64EncodedData } from 'gill';
|
|
7
|
+
export declare class WebSocketProgramAccountSubscriberV2<T> implements ProgramAccountSubscriber<T> {
|
|
8
|
+
subscriptionName: string;
|
|
9
|
+
accountDiscriminator: string;
|
|
10
|
+
bufferAndSlot?: BufferAndSlot;
|
|
11
|
+
bufferAndSlotMap: Map<string, BufferAndSlot>;
|
|
12
|
+
program: Program;
|
|
13
|
+
decodeBuffer: (accountName: string, ix: Buffer) => T;
|
|
14
|
+
onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void;
|
|
15
|
+
listenerId?: number;
|
|
16
|
+
resubOpts?: ResubOpts;
|
|
17
|
+
isUnsubscribing: boolean;
|
|
18
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
19
|
+
options: {
|
|
20
|
+
filters: MemcmpFilter[];
|
|
21
|
+
commitment?: Commitment;
|
|
22
|
+
};
|
|
23
|
+
receivingData: boolean;
|
|
24
|
+
private rpc;
|
|
25
|
+
private rpcSubscriptions;
|
|
26
|
+
private abortController?;
|
|
27
|
+
private accountsToMonitor;
|
|
28
|
+
private pollingIntervalMs;
|
|
29
|
+
private pollingTimeouts;
|
|
30
|
+
private lastWsNotificationTime;
|
|
31
|
+
private accountsCurrentlyPolling;
|
|
32
|
+
private batchPollingTimeout?;
|
|
33
|
+
constructor(subscriptionName: string, accountDiscriminator: string, program: Program, decodeBufferFn: (accountName: string, ix: Buffer) => T, options?: {
|
|
34
|
+
filters: MemcmpFilter[];
|
|
35
|
+
commitment?: Commitment;
|
|
36
|
+
}, resubOpts?: ResubOpts, accountsToMonitor?: PublicKey[]);
|
|
37
|
+
subscribe(onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void): Promise<void>;
|
|
38
|
+
protected setTimeout(): void;
|
|
39
|
+
handleRpcResponse(context: {
|
|
40
|
+
slot: bigint;
|
|
41
|
+
}, accountInfo?: AccountInfoBase & (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData)): void;
|
|
42
|
+
private startMonitoringForAccounts;
|
|
43
|
+
private startMonitoringForAccount;
|
|
44
|
+
private startPollingForAccount;
|
|
45
|
+
private startBatchPolling;
|
|
46
|
+
private pollAllAccounts;
|
|
47
|
+
private pollAccount;
|
|
48
|
+
private clearPollingTimeouts;
|
|
49
|
+
unsubscribe(onResub?: boolean): Promise<void>;
|
|
50
|
+
addAccountToMonitor(accountId: PublicKey): void;
|
|
51
|
+
removeAccountFromMonitor(accountId: PublicKey): void;
|
|
52
|
+
setPollingInterval(intervalMs: number): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=webSocketProgramAccountSubscriberV2.d.ts.map
|