sol-trade-sdk 0.1.0
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/README.md +390 -0
- package/dist/chunk-MMQAMIKR.mjs +3735 -0
- package/dist/chunk-NEZDFAYA.mjs +7744 -0
- package/dist/clients-VITWK7B6.mjs +1370 -0
- package/dist/index-1BK_FXsW.d.mts +2327 -0
- package/dist/index-1BK_FXsW.d.ts +2327 -0
- package/dist/index.d.mts +2659 -0
- package/dist/index.d.ts +2659 -0
- package/dist/index.js +13265 -0
- package/dist/index.mjs +562 -0
- package/dist/perf/index.d.mts +2 -0
- package/dist/perf/index.d.ts +2 -0
- package/dist/perf/index.js +3742 -0
- package/dist/perf/index.mjs +214 -0
- package/package.json +101 -0
- package/src/__tests__/complete_sdk.test.ts +354 -0
- package/src/__tests__/hotpath.test.ts +486 -0
- package/src/__tests__/nonce.test.ts +45 -0
- package/src/__tests__/sdk.test.ts +425 -0
- package/src/address-lookup/index.ts +197 -0
- package/src/cache/cache.ts +308 -0
- package/src/calc/index.ts +1058 -0
- package/src/calc/pumpfun.ts +124 -0
- package/src/common/bonding_curve.ts +272 -0
- package/src/common/compute-budget.ts +148 -0
- package/src/common/confirm-any-signature.ts +184 -0
- package/src/common/fast-timing.ts +481 -0
- package/src/common/fast_fn.ts +150 -0
- package/src/common/gas-fee-strategy.ts +253 -0
- package/src/common/map-pool.ts +23 -0
- package/src/common/nonce.ts +40 -0
- package/src/common/sdk-log.ts +460 -0
- package/src/common/seed.ts +381 -0
- package/src/common/spl-token.ts +578 -0
- package/src/common/subscription-handle.ts +644 -0
- package/src/common/trading-utils.ts +239 -0
- package/src/common/wsol-manager.ts +325 -0
- package/src/compute/compute_budget_manager.ts +187 -0
- package/src/compute/index.ts +21 -0
- package/src/constants/index.ts +96 -0
- package/src/execution/execution.ts +532 -0
- package/src/execution/index.ts +42 -0
- package/src/hotpath/executor.ts +464 -0
- package/src/hotpath/index.ts +64 -0
- package/src/hotpath/state.ts +435 -0
- package/src/index.ts +2117 -0
- package/src/instruction/bonk_builder.ts +730 -0
- package/src/instruction/index.ts +24 -0
- package/src/instruction/meteora_damm_v2_builder.ts +509 -0
- package/src/instruction/pumpfun_builder.ts +1183 -0
- package/src/instruction/pumpswap.ts +1123 -0
- package/src/instruction/raydium_amm_v4_builder.ts +692 -0
- package/src/instruction/raydium_cpmm_builder.ts +795 -0
- package/src/middleware/traits.ts +407 -0
- package/src/params/index.ts +483 -0
- package/src/perf/compiler-optimization.ts +529 -0
- package/src/perf/hardware.ts +631 -0
- package/src/perf/index.ts +9 -0
- package/src/perf/kernel-bypass.ts +656 -0
- package/src/perf/protocol.ts +682 -0
- package/src/perf/realtime.ts +592 -0
- package/src/perf/simd.ts +668 -0
- package/src/perf/syscall-bypass.ts +331 -0
- package/src/perf/ultra-low-latency.ts +505 -0
- package/src/perf/zero-copy.ts +589 -0
- package/src/pool/pool.ts +294 -0
- package/src/rpc/client.ts +345 -0
- package/src/sdk-errors.ts +13 -0
- package/src/security/index.ts +26 -0
- package/src/security/secure-key.ts +303 -0
- package/src/security/validators.ts +281 -0
- package/src/seed/pda.ts +262 -0
- package/src/serialization/index.ts +28 -0
- package/src/serialization/serialization.ts +288 -0
- package/src/swqos/clients.ts +1754 -0
- package/src/swqos/index.ts +50 -0
- package/src/swqos/providers.ts +1707 -0
- package/src/trading/core/async-executor.ts +702 -0
- package/src/trading/core/confirmation-monitor.ts +711 -0
- package/src/trading/core/index.ts +82 -0
- package/src/trading/core/retry-handler.ts +683 -0
- package/src/trading/core/transaction-pool.ts +780 -0
- package/src/trading/executor.ts +385 -0
- package/src/trading/factory.ts +282 -0
- package/src/trading/index.ts +30 -0
- package/src/types.ts +8 -0
- package/src/utils/index.ts +155 -0
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription handle utilities for Sol Trade SDK
|
|
3
|
+
* Provides subscription management for accounts, programs, and signatures.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PublicKey, Connection, Commitment } from '@solana/web3.js';
|
|
7
|
+
import type { GetProgramAccountsFilter } from '@solana/web3.js';
|
|
8
|
+
|
|
9
|
+
// ===== Subscription State =====
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Subscription state enum
|
|
13
|
+
*/
|
|
14
|
+
export enum SubscriptionState {
|
|
15
|
+
Pending = 'pending',
|
|
16
|
+
Active = 'active',
|
|
17
|
+
Paused = 'paused',
|
|
18
|
+
Error = 'error',
|
|
19
|
+
Closed = 'closed',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ===== Subscription Configuration =====
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Base subscription configuration
|
|
26
|
+
*/
|
|
27
|
+
export interface SubscriptionConfig {
|
|
28
|
+
commitment?: Commitment;
|
|
29
|
+
encoding?: 'base58' | 'base64' | 'base64+zstd' | 'jsonParsed';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Account subscription configuration
|
|
34
|
+
*/
|
|
35
|
+
export interface AccountSubscriptionConfig extends SubscriptionConfig {
|
|
36
|
+
publicKey: PublicKey;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Program subscription configuration
|
|
41
|
+
*/
|
|
42
|
+
export interface ProgramSubscriptionConfig extends SubscriptionConfig {
|
|
43
|
+
programId: PublicKey;
|
|
44
|
+
filters?: ProgramAccountFilter[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Program account filter
|
|
49
|
+
*/
|
|
50
|
+
export interface ProgramAccountFilter {
|
|
51
|
+
memcmp?: {
|
|
52
|
+
offset: number;
|
|
53
|
+
bytes: string;
|
|
54
|
+
};
|
|
55
|
+
dataSize?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Signature subscription configuration
|
|
60
|
+
*/
|
|
61
|
+
export interface SignatureSubscriptionConfig extends SubscriptionConfig {
|
|
62
|
+
signature: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ===== Subscription Handle =====
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Subscription handle for managing a single subscription
|
|
69
|
+
*/
|
|
70
|
+
export class SubscriptionHandle {
|
|
71
|
+
private state: SubscriptionState = SubscriptionState.Pending;
|
|
72
|
+
private subscriptionId?: number;
|
|
73
|
+
private error?: Error;
|
|
74
|
+
private lastUpdate?: Date;
|
|
75
|
+
private updateCount: number = 0;
|
|
76
|
+
|
|
77
|
+
private _unsubscribeFn?: () => Promise<void>;
|
|
78
|
+
|
|
79
|
+
constructor(
|
|
80
|
+
private type: 'account' | 'program' | 'signature',
|
|
81
|
+
private config: AccountSubscriptionConfig | ProgramSubscriptionConfig | SignatureSubscriptionConfig,
|
|
82
|
+
unsubscribeFn?: () => Promise<void>
|
|
83
|
+
) {
|
|
84
|
+
this._unsubscribeFn = unsubscribeFn;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get subscription type
|
|
89
|
+
*/
|
|
90
|
+
getType(): 'account' | 'program' | 'signature' {
|
|
91
|
+
return this.type;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get subscription configuration
|
|
96
|
+
*/
|
|
97
|
+
getConfig(): AccountSubscriptionConfig | ProgramSubscriptionConfig | SignatureSubscriptionConfig {
|
|
98
|
+
return this.config;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get current subscription state
|
|
103
|
+
*/
|
|
104
|
+
getState(): SubscriptionState {
|
|
105
|
+
return this.state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Set subscription state
|
|
110
|
+
*/
|
|
111
|
+
setState(state: SubscriptionState): void {
|
|
112
|
+
this.state = state;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get subscription ID from connection
|
|
117
|
+
*/
|
|
118
|
+
getSubscriptionId(): number | undefined {
|
|
119
|
+
return this.subscriptionId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Set subscription ID
|
|
124
|
+
*/
|
|
125
|
+
setSubscriptionId(id: number): void {
|
|
126
|
+
this.subscriptionId = id;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get last error
|
|
131
|
+
*/
|
|
132
|
+
getError(): Error | undefined {
|
|
133
|
+
return this.error;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set error state
|
|
138
|
+
*/
|
|
139
|
+
setError(error: Error): void {
|
|
140
|
+
this.error = error;
|
|
141
|
+
this.state = SubscriptionState.Error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Record an update
|
|
146
|
+
*/
|
|
147
|
+
recordUpdate(): void {
|
|
148
|
+
this.lastUpdate = new Date();
|
|
149
|
+
this.updateCount++;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get last update timestamp
|
|
154
|
+
*/
|
|
155
|
+
getLastUpdate(): Date | undefined {
|
|
156
|
+
return this.lastUpdate;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get total update count
|
|
161
|
+
*/
|
|
162
|
+
getUpdateCount(): number {
|
|
163
|
+
return this.updateCount;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if subscription is active
|
|
168
|
+
*/
|
|
169
|
+
isActive(): boolean {
|
|
170
|
+
return this.state === SubscriptionState.Active;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if subscription is closed
|
|
175
|
+
*/
|
|
176
|
+
isClosed(): boolean {
|
|
177
|
+
return this.state === SubscriptionState.Closed;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Set the unsubscribe function
|
|
182
|
+
*/
|
|
183
|
+
setUnsubscribeFn(fn: () => Promise<void>): void {
|
|
184
|
+
this._unsubscribeFn = fn;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Unsubscribe from this subscription
|
|
189
|
+
*/
|
|
190
|
+
async unsubscribe(): Promise<void> {
|
|
191
|
+
if (this.state === SubscriptionState.Closed) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
if (this._unsubscribeFn) {
|
|
197
|
+
await this._unsubscribeFn();
|
|
198
|
+
}
|
|
199
|
+
this.state = SubscriptionState.Closed;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
this.setError(error as Error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ===== Subscription Manager =====
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Subscription manager for handling multiple subscriptions
|
|
211
|
+
*/
|
|
212
|
+
export class SubscriptionManager {
|
|
213
|
+
private subscriptions: Map<string, SubscriptionHandle> = new Map();
|
|
214
|
+
private connection: Connection;
|
|
215
|
+
|
|
216
|
+
constructor(connection: Connection) {
|
|
217
|
+
this.connection = connection;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate a unique subscription key
|
|
222
|
+
*/
|
|
223
|
+
private generateKey(type: string, identifier: string): string {
|
|
224
|
+
return `${type}:${identifier}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Subscribe to account changes
|
|
229
|
+
*/
|
|
230
|
+
async subscribeAccount(
|
|
231
|
+
config: AccountSubscriptionConfig,
|
|
232
|
+
callback: (accountInfo: any, context: any) => void
|
|
233
|
+
): Promise<SubscriptionHandle> {
|
|
234
|
+
const key = this.generateKey('account', config.publicKey.toBase58());
|
|
235
|
+
|
|
236
|
+
// Unsubscribe existing if any
|
|
237
|
+
if (this.subscriptions.has(key)) {
|
|
238
|
+
await this.subscriptions.get(key)!.unsubscribe();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const handle = new SubscriptionHandle('account', config);
|
|
242
|
+
handle.setState(SubscriptionState.Active);
|
|
243
|
+
|
|
244
|
+
const subscriptionId = this.connection.onAccountChange(
|
|
245
|
+
config.publicKey,
|
|
246
|
+
(accountInfo, context) => {
|
|
247
|
+
handle.recordUpdate();
|
|
248
|
+
callback(accountInfo, context);
|
|
249
|
+
},
|
|
250
|
+
config.commitment ?? 'confirmed'
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
handle.setSubscriptionId(subscriptionId);
|
|
254
|
+
handle.setUnsubscribeFn(async () => {
|
|
255
|
+
await this.connection.removeAccountChangeListener(subscriptionId);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
this.subscriptions.set(key, handle);
|
|
259
|
+
return handle;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Subscribe to program account changes
|
|
264
|
+
*/
|
|
265
|
+
async subscribeProgram(
|
|
266
|
+
config: ProgramSubscriptionConfig,
|
|
267
|
+
callback: (keyedAccountInfo: any, context: any) => void
|
|
268
|
+
): Promise<SubscriptionHandle> {
|
|
269
|
+
const key = this.generateKey('program', config.programId.toBase58());
|
|
270
|
+
|
|
271
|
+
// Unsubscribe existing if any
|
|
272
|
+
if (this.subscriptions.has(key)) {
|
|
273
|
+
await this.subscriptions.get(key)!.unsubscribe();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const handle = new SubscriptionHandle('program', config);
|
|
277
|
+
handle.setState(SubscriptionState.Active);
|
|
278
|
+
|
|
279
|
+
const filters: GetProgramAccountsFilter[] | undefined = config.filters
|
|
280
|
+
?.map((filter): GetProgramAccountsFilter | undefined => {
|
|
281
|
+
if (filter.memcmp) {
|
|
282
|
+
return { memcmp: filter.memcmp };
|
|
283
|
+
}
|
|
284
|
+
if (filter.dataSize !== undefined) {
|
|
285
|
+
return { dataSize: filter.dataSize };
|
|
286
|
+
}
|
|
287
|
+
return undefined;
|
|
288
|
+
})
|
|
289
|
+
.filter((filter): filter is GetProgramAccountsFilter => filter !== undefined);
|
|
290
|
+
|
|
291
|
+
const subscriptionId = this.connection.onProgramAccountChange(
|
|
292
|
+
config.programId,
|
|
293
|
+
(keyedAccountInfo, context) => {
|
|
294
|
+
handle.recordUpdate();
|
|
295
|
+
callback(keyedAccountInfo, context);
|
|
296
|
+
},
|
|
297
|
+
config.commitment ?? 'confirmed',
|
|
298
|
+
filters
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
handle.setSubscriptionId(subscriptionId);
|
|
302
|
+
handle.setUnsubscribeFn(async () => {
|
|
303
|
+
await this.connection.removeProgramAccountChangeListener(subscriptionId);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
this.subscriptions.set(key, handle);
|
|
307
|
+
return handle;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Subscribe to signature status changes
|
|
312
|
+
*/
|
|
313
|
+
async subscribeSignature(
|
|
314
|
+
config: SignatureSubscriptionConfig,
|
|
315
|
+
callback: (signatureResult: any, context: any) => void
|
|
316
|
+
): Promise<SubscriptionHandle> {
|
|
317
|
+
const key = this.generateKey('signature', config.signature);
|
|
318
|
+
|
|
319
|
+
// Unsubscribe existing if any
|
|
320
|
+
if (this.subscriptions.has(key)) {
|
|
321
|
+
await this.subscriptions.get(key)!.unsubscribe();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const handle = new SubscriptionHandle('signature', config);
|
|
325
|
+
handle.setState(SubscriptionState.Active);
|
|
326
|
+
|
|
327
|
+
const subscriptionId = this.connection.onSignature(
|
|
328
|
+
config.signature,
|
|
329
|
+
(signatureResult, context) => {
|
|
330
|
+
handle.recordUpdate();
|
|
331
|
+
callback(signatureResult, context);
|
|
332
|
+
},
|
|
333
|
+
config.commitment ?? 'confirmed'
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
handle.setSubscriptionId(subscriptionId);
|
|
337
|
+
handle.setUnsubscribeFn(async () => {
|
|
338
|
+
await this.connection.removeSignatureListener(subscriptionId);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
this.subscriptions.set(key, handle);
|
|
342
|
+
return handle;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Subscribe to slot changes
|
|
347
|
+
*/
|
|
348
|
+
async subscribeSlot(callback: (slotInfo: any) => void): Promise<SubscriptionHandle> {
|
|
349
|
+
const key = this.generateKey('slot', 'global');
|
|
350
|
+
|
|
351
|
+
// Unsubscribe existing if any
|
|
352
|
+
if (this.subscriptions.has(key)) {
|
|
353
|
+
await this.subscriptions.get(key)!.unsubscribe();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const config: SubscriptionConfig = {};
|
|
357
|
+
const handle = new SubscriptionHandle('slot' as any, config as any);
|
|
358
|
+
handle.setState(SubscriptionState.Active);
|
|
359
|
+
|
|
360
|
+
const subscriptionId = this.connection.onSlotChange((slotInfo) => {
|
|
361
|
+
handle.recordUpdate();
|
|
362
|
+
callback(slotInfo);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
handle.setSubscriptionId(subscriptionId);
|
|
366
|
+
handle.setUnsubscribeFn(async () => {
|
|
367
|
+
await this.connection.removeSlotChangeListener(subscriptionId);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
this.subscriptions.set(key, handle);
|
|
371
|
+
return handle;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Subscribe to root changes
|
|
376
|
+
*/
|
|
377
|
+
async subscribeRoot(callback: (root: number) => void): Promise<SubscriptionHandle> {
|
|
378
|
+
const key = this.generateKey('root', 'global');
|
|
379
|
+
|
|
380
|
+
// Unsubscribe existing if any
|
|
381
|
+
if (this.subscriptions.has(key)) {
|
|
382
|
+
await this.subscriptions.get(key)!.unsubscribe();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const config: SubscriptionConfig = {};
|
|
386
|
+
const handle = new SubscriptionHandle('signature', config as SignatureSubscriptionConfig);
|
|
387
|
+
handle.setState(SubscriptionState.Active);
|
|
388
|
+
|
|
389
|
+
const subscriptionId = this.connection.onRootChange((root) => {
|
|
390
|
+
handle.recordUpdate();
|
|
391
|
+
callback(root);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
handle.setSubscriptionId(subscriptionId);
|
|
395
|
+
handle.setUnsubscribeFn(async () => {
|
|
396
|
+
await this.connection.removeRootChangeListener(subscriptionId);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
this.subscriptions.set(key, handle);
|
|
400
|
+
return handle;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get a subscription by key
|
|
405
|
+
*/
|
|
406
|
+
getSubscription(key: string): SubscriptionHandle | undefined {
|
|
407
|
+
return this.subscriptions.get(key);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Get all subscriptions
|
|
412
|
+
*/
|
|
413
|
+
getAllSubscriptions(): SubscriptionHandle[] {
|
|
414
|
+
return Array.from(this.subscriptions.values());
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get subscriptions by type
|
|
419
|
+
*/
|
|
420
|
+
getSubscriptionsByType(type: 'account' | 'program' | 'signature'): SubscriptionHandle[] {
|
|
421
|
+
return this.getAllSubscriptions().filter((sub) => sub.getType() === type);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Unsubscribe from a specific subscription
|
|
426
|
+
*/
|
|
427
|
+
async unsubscribe(key: string): Promise<void> {
|
|
428
|
+
const handle = this.subscriptions.get(key);
|
|
429
|
+
if (handle) {
|
|
430
|
+
await handle.unsubscribe();
|
|
431
|
+
this.subscriptions.delete(key);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Unsubscribe from all subscriptions
|
|
437
|
+
*/
|
|
438
|
+
async unsubscribeAll(): Promise<void> {
|
|
439
|
+
const promises: Promise<void>[] = [];
|
|
440
|
+
for (const [key, handle] of this.subscriptions) {
|
|
441
|
+
promises.push(
|
|
442
|
+
handle.unsubscribe().then(() => {
|
|
443
|
+
this.subscriptions.delete(key);
|
|
444
|
+
})
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
await Promise.all(promises);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Get subscription statistics
|
|
452
|
+
*/
|
|
453
|
+
getStats(): {
|
|
454
|
+
total: number;
|
|
455
|
+
active: number;
|
|
456
|
+
pending: number;
|
|
457
|
+
paused: number;
|
|
458
|
+
error: number;
|
|
459
|
+
closed: number;
|
|
460
|
+
totalUpdates: number;
|
|
461
|
+
} {
|
|
462
|
+
const subs = this.getAllSubscriptions();
|
|
463
|
+
return {
|
|
464
|
+
total: subs.length,
|
|
465
|
+
active: subs.filter((s) => s.getState() === SubscriptionState.Active).length,
|
|
466
|
+
pending: subs.filter((s) => s.getState() === SubscriptionState.Pending).length,
|
|
467
|
+
paused: subs.filter((s) => s.getState() === SubscriptionState.Paused).length,
|
|
468
|
+
error: subs.filter((s) => s.getState() === SubscriptionState.Error).length,
|
|
469
|
+
closed: subs.filter((s) => s.getState() === SubscriptionState.Closed).length,
|
|
470
|
+
totalUpdates: subs.reduce((sum, s) => sum + s.getUpdateCount(), 0),
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// ===== Convenience Classes =====
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Account subscription helper
|
|
479
|
+
*/
|
|
480
|
+
export class AccountSubscription {
|
|
481
|
+
private handle?: SubscriptionHandle;
|
|
482
|
+
|
|
483
|
+
constructor(
|
|
484
|
+
private manager: SubscriptionManager,
|
|
485
|
+
private publicKey: PublicKey
|
|
486
|
+
) {}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Subscribe to account changes
|
|
490
|
+
*/
|
|
491
|
+
async subscribe(
|
|
492
|
+
callback: (accountInfo: any, context: any) => void,
|
|
493
|
+
commitment?: Commitment
|
|
494
|
+
): Promise<SubscriptionHandle> {
|
|
495
|
+
this.handle = await this.manager.subscribeAccount(
|
|
496
|
+
{ publicKey: this.publicKey, commitment },
|
|
497
|
+
callback
|
|
498
|
+
);
|
|
499
|
+
return this.handle;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Unsubscribe from account changes
|
|
504
|
+
*/
|
|
505
|
+
async unsubscribe(): Promise<void> {
|
|
506
|
+
if (this.handle) {
|
|
507
|
+
await this.handle.unsubscribe();
|
|
508
|
+
this.handle = undefined;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Check if subscribed
|
|
514
|
+
*/
|
|
515
|
+
isSubscribed(): boolean {
|
|
516
|
+
return this.handle?.isActive() ?? false;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Program subscription helper
|
|
522
|
+
*/
|
|
523
|
+
export class ProgramSubscription {
|
|
524
|
+
private handle?: SubscriptionHandle;
|
|
525
|
+
|
|
526
|
+
constructor(
|
|
527
|
+
private manager: SubscriptionManager,
|
|
528
|
+
private programId: PublicKey
|
|
529
|
+
) {}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Subscribe to program account changes
|
|
533
|
+
*/
|
|
534
|
+
async subscribe(
|
|
535
|
+
callback: (keyedAccountInfo: any, context: any) => void,
|
|
536
|
+
filters?: ProgramAccountFilter[],
|
|
537
|
+
commitment?: Commitment
|
|
538
|
+
): Promise<SubscriptionHandle> {
|
|
539
|
+
this.handle = await this.manager.subscribeProgram(
|
|
540
|
+
{ programId: this.programId, filters, commitment },
|
|
541
|
+
callback
|
|
542
|
+
);
|
|
543
|
+
return this.handle;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Unsubscribe from program changes
|
|
548
|
+
*/
|
|
549
|
+
async unsubscribe(): Promise<void> {
|
|
550
|
+
if (this.handle) {
|
|
551
|
+
await this.handle.unsubscribe();
|
|
552
|
+
this.handle = undefined;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Check if subscribed
|
|
558
|
+
*/
|
|
559
|
+
isSubscribed(): boolean {
|
|
560
|
+
return this.handle?.isActive() ?? false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Signature subscription helper
|
|
566
|
+
*/
|
|
567
|
+
export class SignatureSubscription {
|
|
568
|
+
private handle?: SubscriptionHandle;
|
|
569
|
+
|
|
570
|
+
constructor(
|
|
571
|
+
private manager: SubscriptionManager,
|
|
572
|
+
private signature: string
|
|
573
|
+
) {}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Subscribe to signature status changes
|
|
577
|
+
*/
|
|
578
|
+
async subscribe(
|
|
579
|
+
callback: (signatureResult: any, context: any) => void,
|
|
580
|
+
commitment?: Commitment
|
|
581
|
+
): Promise<SubscriptionHandle> {
|
|
582
|
+
this.handle = await this.manager.subscribeSignature(
|
|
583
|
+
{ signature: this.signature, commitment },
|
|
584
|
+
callback
|
|
585
|
+
);
|
|
586
|
+
return this.handle;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Unsubscribe from signature changes
|
|
591
|
+
*/
|
|
592
|
+
async unsubscribe(): Promise<void> {
|
|
593
|
+
if (this.handle) {
|
|
594
|
+
await this.handle.unsubscribe();
|
|
595
|
+
this.handle = undefined;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Check if subscribed
|
|
601
|
+
*/
|
|
602
|
+
isSubscribed(): boolean {
|
|
603
|
+
return this.handle?.isActive() ?? false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ===== Factory Functions =====
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Create a subscription manager
|
|
611
|
+
*/
|
|
612
|
+
export function createSubscriptionManager(connection: Connection): SubscriptionManager {
|
|
613
|
+
return new SubscriptionManager(connection);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Create an account subscription
|
|
618
|
+
*/
|
|
619
|
+
export function createAccountSubscription(
|
|
620
|
+
manager: SubscriptionManager,
|
|
621
|
+
publicKey: PublicKey
|
|
622
|
+
): AccountSubscription {
|
|
623
|
+
return new AccountSubscription(manager, publicKey);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Create a program subscription
|
|
628
|
+
*/
|
|
629
|
+
export function createProgramSubscription(
|
|
630
|
+
manager: SubscriptionManager,
|
|
631
|
+
programId: PublicKey
|
|
632
|
+
): ProgramSubscription {
|
|
633
|
+
return new ProgramSubscription(manager, programId);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Create a signature subscription
|
|
638
|
+
*/
|
|
639
|
+
export function createSignatureSubscription(
|
|
640
|
+
manager: SubscriptionManager,
|
|
641
|
+
signature: string
|
|
642
|
+
): SignatureSubscription {
|
|
643
|
+
return new SignatureSubscription(manager, signature);
|
|
644
|
+
}
|