@validators-dao/solana-stream-sdk 0.8.0 → 0.10.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 CHANGED
@@ -210,94 +210,56 @@ Here's how to use the SDK to subscribe to Solana Shreds and decode entries:
210
210
 
211
211
  ```typescript
212
212
  import {
213
- ShredstreamProxyClient,
214
- credentials,
215
- ShredsCommitmentLevel,
216
- ShredsSubscribeEntriesRequestFns,
217
- decodeSolanaEntries,
218
- bs58,
213
+ ShredsClient,
214
+ ShredsClientCommitmentLevel,
215
+ // decodeSolanaEntries,
219
216
  } from '@validators-dao/solana-stream-sdk'
220
217
  import 'dotenv/config'
218
+ // import { logDecodedEntries } from '@/utils/logDecodedEntries'
221
219
 
222
- const endpoint = process.env.SHREDS_ENDPOINT!.replace(/^https?:\/\//, '')
220
+ import { receivedSlots, startLatencyCheck } from '@/utils/checkLatency'
223
221
 
224
- const client = new ShredstreamProxyClient(endpoint, credentials.createSsl())
222
+ const endpoint = process.env.SHREDS_ENDPOINT!
225
223
 
226
- const request = ShredsSubscribeEntriesRequestFns.create({
227
- accounts: {
228
- pumpfun: {
229
- account: [],
230
- owner: [],
231
- filters: [],
232
- },
233
- },
224
+ const client = new ShredsClient(endpoint)
225
+
226
+ // The filter is experimental
227
+ const request = {
228
+ accounts: {},
234
229
  transactions: {},
235
230
  slots: {},
236
- commitment: ShredsCommitmentLevel.PROCESSED,
237
- })
231
+ commitment: ShredsClientCommitmentLevel.Processed,
232
+ }
238
233
 
239
- const connect = async () => {
234
+ const connect = () => {
240
235
  console.log('Connecting to:', endpoint)
241
236
 
242
- const stream = client.subscribeEntries(request)
243
-
244
- stream.on('data', (data) => {
245
- console.log(`\n🟢 Received slot: ${data.slot}`)
246
-
247
- const decodedEntries = decodeSolanaEntries(data.entries)
248
-
249
- if (!Array.isArray(decodedEntries)) {
250
- console.warn('āš ļø decodedEntries is not an array:', decodedEntries)
251
- return
252
- }
253
-
254
- decodedEntries.forEach((entry, entryIdx) => {
255
- console.log(`\nāœ… Entry #${entryIdx + 1}`)
256
- console.log(
257
- ` - Hash: ${entry.hash ? bs58.encode(Buffer.from(entry.hash)) : 'N/A'}`,
258
- )
259
- console.log(` - Num Hashes: ${entry.num_hashes ?? 'N/A'}`)
260
-
261
- entry.transactions.forEach((tx, txIdx) => {
262
- console.log(`\nšŸ“„ Transaction #${txIdx + 1}`)
263
- const signaturesBase58 = tx.signatures
264
- .slice(1)
265
- .map((sig) => bs58.encode(Buffer.from(sig)))
266
- console.log(` - Signatures:`, signaturesBase58)
267
-
268
- const message = tx.message[0]
269
- if (message) {
270
- message.accountKeys.forEach((key, idx) => {
271
- console.log(` [${idx}] ${bs58.encode(Buffer.from(key))}`)
272
- })
273
-
274
- message.instructions.forEach((inst, instIdx) => {
275
- console.log(` [${instIdx}]`)
276
- console.log(` - Program ID Index: ${inst.programIdIndex}`)
277
- console.log(` - Accounts: ${inst.accounts.join(', ')}`)
278
- console.log(` - Data: ${bs58.encode(Buffer.from(inst.data))}`)
279
- })
280
-
281
- console.log(
282
- ` šŸ“Œ Recent Blockhash: ${bs58.encode(Buffer.from(message.recentBlockhash))}`,
283
- )
237
+ client.subscribeEntries(
238
+ JSON.stringify(request),
239
+ (_error: any, buffer: any) => {
240
+ const receivedAt = new Date()
241
+ if (buffer) {
242
+ const {
243
+ slot,
244
+ // entries
245
+ } = JSON.parse(buffer)
246
+
247
+ // You can decode entries as needed
248
+ // const decodedEntries = decodeSolanaEntries(new Uint8Array(entries))
249
+ // logDecodedEntries(decodedEntries)
250
+
251
+ if (!receivedSlots.has(slot)) {
252
+ receivedSlots.set(slot, [{ receivedAt }])
253
+ } else {
254
+ receivedSlots.get(slot)!.push({ receivedAt })
284
255
  }
285
- })
286
- })
287
- })
288
-
289
- stream.on('error', (err) => {
290
- console.error('🚨 Stream error:', err)
291
- setTimeout(connect, 5000)
292
- })
293
-
294
- stream.on('end', () => {
295
- console.log('šŸ”š Stream ended, reconnecting...')
296
- setTimeout(connect, 5000)
297
- })
256
+ }
257
+ },
258
+ )
298
259
  }
299
260
 
300
261
  connect()
262
+ startLatencyCheck()
301
263
  ```
302
264
 
303
265
  Ensure the environment variable `SHREDS_ENDPOINT` is set correctly.
@@ -325,33 +287,21 @@ Ensure the environment variable `SHREDS_ENDPOINT` is set correctly.
325
287
  - `SubscribeRequestAccountsDataSlice`: Data slice configuration for account subscriptions.
326
288
  - `bs58`: Base58 encoding/decoding utilities for Solana addresses and data.
327
289
 
328
- ### Shredstream Client Types
329
-
330
- - `ShredstreamProxyClient`: Client for streaming Solana shreds through proxy endpoints.
331
- - `ShredstreamClient`: Direct client for streaming Solana shreds.
332
- - `ShredsCommitmentLevel`: Commitment levels specifically for Shredstream data.
333
- - `ShredsSubscribeEntriesRequestFns`: Functions to construct entry subscription requests.
334
- - `ShredsEntryFns`: Utilities and functions for handling shred entries.
335
-
336
- ### Shredstream Exported Type Definitions
290
+ ### Shredstream Client
337
291
 
338
- - `ShredsSubscribeEntriesRequest`: Request type definition for subscribing to entries.
339
- - `ShredsSubscribeRequestFilterAccounts`: Account filter type for shred subscriptions.
340
- - `ShredsSubscribeRequestFilterTransactions`: Transaction filter type for shred subscriptions.
341
- - `ShredsSubscribeRequestFilterSlots`: Slot filter type for shred subscriptions.
342
- - `ShredsEntry`: Entry type definition representing Solana shred entries.
292
+ - `ShredsClient`: Client for streaming Solana shreds through shreds endpoints.
293
+ - `ShredsClientCommitmentLevel`: Solana commitment levels (e.g., processed, confirmed, finalized).
343
294
 
344
295
  ### Utility Exports
345
296
 
346
297
  - `decodeSolanaEntries`: Function to decode raw Solana shred entry data into structured, human-readable formats.
347
- - `credentials`, `Metadata`: gRPC credentials and metadata utilities.
348
298
 
349
299
  ## Dependencies
350
300
 
351
301
  - `@triton-one/yellowstone-grpc`: For gRPC streaming capabilities
352
302
  - `bs58`: For base58 encoding/decoding
353
- - `@grpc/grpc-js`
354
303
  - `@validators-dao/solana-entry-decoder`: Utility for decoding Solana shred entries.
304
+ - `@validators-dao/solana-shreds-client`: Solana Shreds Client for Scale. (NAPI-RS)
355
305
 
356
306
  ## āš ļø Experimental Filtering Feature Notice
357
307
 
package/dist/index.d.ts CHANGED
@@ -1,179 +1,5 @@
1
1
  export { CommitmentLevel, default as GeyserClient, SubscribeRequestAccountsDataSlice, SubscribeRequestFilterAccounts, SubscribeRequestFilterBlocks, SubscribeRequestFilterBlocksMeta, SubscribeRequestFilterEntry, SubscribeRequestFilterSlots, SubscribeRequestFilterTransactions } from '@triton-one/yellowstone-grpc';
2
2
  export { default as bs58 } from 'bs58';
3
- import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
4
- import { Client, CallOptions, ClientReadableStream, Metadata, ChannelCredentials, ClientOptions, ServiceError, ClientUnaryCall } from '@grpc/grpc-js';
5
- export { Metadata, credentials } from '@grpc/grpc-js';
6
-
7
- interface Socket {
8
- ip: string;
9
- port: number;
10
- }
11
- declare const Socket: MessageFns$1<Socket>;
12
- type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
13
- type DeepPartial$1<T> = T extends Builtin$1 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$1<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$1<U>> : T extends {} ? {
14
- [K in keyof T]?: DeepPartial$1<T[K]>;
15
- } : Partial<T>;
16
- type KeysOfUnion$1<T> = T extends T ? keyof T : never;
17
- type Exact$1<P, I extends P> = P extends Builtin$1 ? P : P & {
18
- [K in keyof P]: Exact$1<P[K], I[K]>;
19
- } & {
20
- [K in Exclude<keyof I, KeysOfUnion$1<P>>]: never;
21
- };
22
- interface MessageFns$1<T> {
23
- encode(message: T, writer?: BinaryWriter): BinaryWriter;
24
- decode(input: BinaryReader | Uint8Array, length?: number): T;
25
- fromJSON(object: any): T;
26
- toJSON(message: T): unknown;
27
- create<I extends Exact$1<DeepPartial$1<T>, I>>(base?: I): T;
28
- fromPartial<I extends Exact$1<DeepPartial$1<T>, I>>(object: I): T;
29
- }
30
-
31
- declare enum CommitmentLevel {
32
- PROCESSED = 0,
33
- CONFIRMED = 1,
34
- FINALIZED = 2,
35
- UNRECOGNIZED = -1
36
- }
37
- interface Heartbeat {
38
- /**
39
- * don't trust IP:PORT from tcp header since it can be tampered over the wire
40
- * `socket.ip` must match incoming packet's ip. this prevents spamming an unwitting destination
41
- */
42
- socket: Socket | undefined;
43
- /**
44
- * regions for shredstream proxy to receive shreds from
45
- * list of valid regions: https://docs.jito.wtf/lowlatencytxnsend/#api
46
- */
47
- regions: string[];
48
- }
49
- declare const Heartbeat: MessageFns<Heartbeat>;
50
- interface HeartbeatResponse {
51
- /** client must respond within `ttl_ms` to keep stream alive */
52
- ttlMs: number;
53
- }
54
- declare const HeartbeatResponse: MessageFns<HeartbeatResponse>;
55
- interface SubscribeEntriesRequest {
56
- accounts: {
57
- [key: string]: SubscribeRequestFilterAccounts;
58
- };
59
- transactions: {
60
- [key: string]: SubscribeRequestFilterTransactions;
61
- };
62
- slots: {
63
- [key: string]: SubscribeRequestFilterSlots;
64
- };
65
- commitment?: CommitmentLevel | undefined;
66
- }
67
- declare const SubscribeEntriesRequest: MessageFns<SubscribeEntriesRequest>;
68
- interface SubscribeRequestFilterAccounts {
69
- account: string[];
70
- owner: string[];
71
- filters: SubscribeRequestFilterAccountsFilter[];
72
- nonemptyTxnSignature?: boolean | undefined;
73
- }
74
- declare const SubscribeRequestFilterAccounts: MessageFns<SubscribeRequestFilterAccounts>;
75
- interface SubscribeRequestFilterAccountsFilter {
76
- memcmp?: SubscribeRequestFilterAccountsFilterMemcmp | undefined;
77
- datasize?: number | undefined;
78
- tokenAccountState?: boolean | undefined;
79
- lamports?: SubscribeRequestFilterAccountsFilterLamports | undefined;
80
- }
81
- declare const SubscribeRequestFilterAccountsFilter: MessageFns<SubscribeRequestFilterAccountsFilter>;
82
- interface SubscribeRequestFilterAccountsFilterMemcmp {
83
- offset: number;
84
- bytes?: Uint8Array | undefined;
85
- base58?: string | undefined;
86
- base64?: string | undefined;
87
- }
88
- declare const SubscribeRequestFilterAccountsFilterMemcmp: MessageFns<SubscribeRequestFilterAccountsFilterMemcmp>;
89
- interface SubscribeRequestFilterAccountsFilterLamports {
90
- eq?: number | undefined;
91
- ne?: number | undefined;
92
- lt?: number | undefined;
93
- gt?: number | undefined;
94
- }
95
- declare const SubscribeRequestFilterAccountsFilterLamports: MessageFns<SubscribeRequestFilterAccountsFilterLamports>;
96
- interface SubscribeRequestFilterSlots {
97
- filterByCommitment?: boolean | undefined;
98
- interslotUpdates?: boolean | undefined;
99
- }
100
- declare const SubscribeRequestFilterSlots: MessageFns<SubscribeRequestFilterSlots>;
101
- interface SubscribeRequestFilterTransactions {
102
- accountInclude: string[];
103
- accountExclude: string[];
104
- accountRequired: string[];
105
- }
106
- declare const SubscribeRequestFilterTransactions: MessageFns<SubscribeRequestFilterTransactions>;
107
- interface Entry {
108
- /** the slot that the entry is from */
109
- slot: number;
110
- /** Serialized bytes of Vec<Entry>: https://docs.rs/solana-entry/latest/solana_entry/entry/struct.Entry.html */
111
- entries: Uint8Array;
112
- }
113
- declare const Entry: MessageFns<Entry>;
114
- type ShredstreamService = typeof ShredstreamService;
115
- declare const ShredstreamService: {
116
- /** RPC endpoint to send heartbeats to keep shreds flowing */
117
- readonly sendHeartbeat: {
118
- readonly path: "/shredstream.Shredstream/SendHeartbeat";
119
- readonly requestStream: false;
120
- readonly responseStream: false;
121
- readonly requestSerialize: (value: Heartbeat) => Buffer<ArrayBuffer>;
122
- readonly requestDeserialize: (value: Buffer) => Heartbeat;
123
- readonly responseSerialize: (value: HeartbeatResponse) => Buffer<ArrayBuffer>;
124
- readonly responseDeserialize: (value: Buffer) => HeartbeatResponse;
125
- };
126
- };
127
- interface ShredstreamClient extends Client {
128
- /** RPC endpoint to send heartbeats to keep shreds flowing */
129
- sendHeartbeat(request: Heartbeat, callback: (error: ServiceError | null, response: HeartbeatResponse) => void): ClientUnaryCall;
130
- sendHeartbeat(request: Heartbeat, metadata: Metadata, callback: (error: ServiceError | null, response: HeartbeatResponse) => void): ClientUnaryCall;
131
- sendHeartbeat(request: Heartbeat, metadata: Metadata, options: Partial<CallOptions>, callback: (error: ServiceError | null, response: HeartbeatResponse) => void): ClientUnaryCall;
132
- }
133
- declare const ShredstreamClient: {
134
- new (address: string, credentials: ChannelCredentials, options?: Partial<ClientOptions>): ShredstreamClient;
135
- service: typeof ShredstreamService;
136
- serviceName: string;
137
- };
138
- type ShredstreamProxyService = typeof ShredstreamProxyService;
139
- declare const ShredstreamProxyService: {
140
- readonly subscribeEntries: {
141
- readonly path: "/shredstream.ShredstreamProxy/SubscribeEntries";
142
- readonly requestStream: false;
143
- readonly responseStream: true;
144
- readonly requestSerialize: (value: SubscribeEntriesRequest) => Buffer<ArrayBuffer>;
145
- readonly requestDeserialize: (value: Buffer) => SubscribeEntriesRequest;
146
- readonly responseSerialize: (value: Entry) => Buffer<ArrayBuffer>;
147
- readonly responseDeserialize: (value: Buffer) => Entry;
148
- };
149
- };
150
- interface ShredstreamProxyClient extends Client {
151
- subscribeEntries(request: SubscribeEntriesRequest, options?: Partial<CallOptions>): ClientReadableStream<Entry>;
152
- subscribeEntries(request: SubscribeEntriesRequest, metadata?: Metadata, options?: Partial<CallOptions>): ClientReadableStream<Entry>;
153
- }
154
- declare const ShredstreamProxyClient: {
155
- new (address: string, credentials: ChannelCredentials, options?: Partial<ClientOptions>): ShredstreamProxyClient;
156
- service: typeof ShredstreamProxyService;
157
- serviceName: string;
158
- };
159
- type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
160
- type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
161
- [K in keyof T]?: DeepPartial<T[K]>;
162
- } : Partial<T>;
163
- type KeysOfUnion<T> = T extends T ? keyof T : never;
164
- type Exact<P, I extends P> = P extends Builtin ? P : P & {
165
- [K in keyof P]: Exact<P[K], I[K]>;
166
- } & {
167
- [K in Exclude<keyof I, KeysOfUnion<P>>]: never;
168
- };
169
- interface MessageFns<T> {
170
- encode(message: T, writer?: BinaryWriter): BinaryWriter;
171
- decode(input: BinaryReader | Uint8Array, length?: number): T;
172
- fromJSON(object: any): T;
173
- toJSON(message: T): unknown;
174
- create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
175
- fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
176
- }
177
3
 
178
4
  declare const decodeSolanaEntries: any;
179
5
  declare const ShredsClient: any;
@@ -183,4 +9,4 @@ declare enum ShredsClientCommitmentLevel {
183
9
  Confirmed = "Confirmed"
184
10
  }
185
11
 
186
- export { ShredsClient, ShredsClientCommitmentLevel, CommitmentLevel as ShredsCommitmentLevel, Entry as ShredsEntry, Entry as ShredsEntryFns, SubscribeEntriesRequest as ShredsSubscribeEntriesRequest, SubscribeEntriesRequest as ShredsSubscribeEntriesRequestFns, SubscribeRequestFilterAccounts as ShredsSubscribeRequestFilterAccounts, SubscribeRequestFilterSlots as ShredsSubscribeRequestFilterSlots, SubscribeRequestFilterTransactions as ShredsSubscribeRequestFilterTransactions, ShredstreamClient, ShredstreamProxyClient, decodeSolanaEntries };
12
+ export { ShredsClient, ShredsClientCommitmentLevel, decodeSolanaEntries };