@validators-dao/solana-stream-sdk 1.0.1 → 1.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.
Files changed (2) hide show
  1. package/README.md +63 -167
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -27,10 +27,11 @@ Solana Stream SDK by Validators DAO - A TypeScript SDK for streaming Solana bloc
27
27
  <img src="https://storage.slv.dev/PoweredBySolana.svg" alt="Powered By Solana" width="200px" height="95px">
28
28
  </a>
29
29
 
30
- ## What's New in v1.0.1
30
+ ## What's New in v1.1.0
31
31
 
32
+ - Refreshed starter layout and docs to highlight trading hooks
32
33
  - Yellowstone Geyser gRPC connection upgraded to an NAPI-RS-powered client for better backpressure
33
- - NAPI-powered Shreds client/decoder so TypeScript can tap Rust-grade throughput
34
+ - NAPI-powered Shreds client/decoder so TypeScript can tap near-native throughput
34
35
  - Improved backpressure handling and up to 4x streaming efficiency (400% improvement)
35
36
  - Faster real-time Geyser streams for TypeScript clients with lower overhead
36
37
 
@@ -39,7 +40,7 @@ Solana Stream SDK by Validators DAO - A TypeScript SDK for streaming Solana bloc
39
40
  - Ping/Pong handling to keep Yellowstone gRPC streams alive
40
41
  - Exponential reconnect backoff plus `fromSlot` gap recovery
41
42
  - Bounded in-memory queue with drop logging for backpressure safety
42
- - Hot-swappable subscriptions via a JSON file (no reconnect)
43
+ - Update subscriptions by writing a new request to the stream (no reconnect)
43
44
  - Optional runtime metrics logging (rates, queue size, drops)
44
45
  - Default filters drop vote/failed transactions to reduce traffic
45
46
 
@@ -49,9 +50,7 @@ duplicates are expected.
49
50
  ## Performance Highlights
50
51
 
51
52
  - NAPI-powered Geyser gRPC and Shreds client/decoder for high-throughput streaming
52
- - TypeScript ergonomics with Rust-grade performance under the hood
53
- - For the absolute fastest signal path, see Rust UDP Shreds in the repo:
54
- https://github.com/ValidatorsDAO/solana-stream#shreds-udp-pumpfun-watcher-rust
53
+ - TypeScript ergonomics with native performance under the hood
55
54
 
56
55
  ## Installation
57
56
 
@@ -67,69 +66,36 @@ pnpm add @validators-dao/solana-stream-sdk
67
66
 
68
67
  ## Usage
69
68
 
70
- Example of using the GeyserClient to subscribe to Solana Pump Fun transactions and accounts:
69
+ ### Geyser Client (TypeScript)
70
+
71
+ For a production-ready runner (reconnects, backpressure handling, metrics),
72
+ see https://github.com/ValidatorsDAO/solana-stream/tree/main/client/geyser-ts.
73
+
74
+ Minimal SDK usage (filters defined in code):
71
75
 
72
76
  ```typescript
73
77
  import {
74
- GeyserClient,
75
- bs58,
76
78
  CommitmentLevel,
77
- SubscribeRequestAccountsDataSlice,
78
- SubscribeRequestFilterAccounts,
79
- SubscribeRequestFilterBlocks,
80
- SubscribeRequestFilterBlocksMeta,
81
- SubscribeRequestFilterEntry,
82
- SubscribeRequestFilterSlots,
83
79
  SubscribeRequestFilterTransactions,
80
+ GeyserClient,
81
+ bs58,
84
82
  } from '@validators-dao/solana-stream-sdk'
85
83
  import 'dotenv/config'
86
84
 
87
- interface SubscribeRequest {
88
- accounts: {
89
- [key: string]: SubscribeRequestFilterAccounts
90
- }
91
- slots: {
92
- [key: string]: SubscribeRequestFilterSlots
93
- }
94
- transactions: {
95
- [key: string]: SubscribeRequestFilterTransactions
96
- }
97
- transactionsStatus: {
98
- [key: string]: SubscribeRequestFilterTransactions
99
- }
100
- blocks: {
101
- [key: string]: SubscribeRequestFilterBlocks
102
- }
103
- blocksMeta: {
104
- [key: string]: SubscribeRequestFilterBlocksMeta
105
- }
106
- entry: {
107
- [key: string]: SubscribeRequestFilterEntry
108
- }
109
- commitment?: CommitmentLevel | undefined
110
- accountsDataSlice: SubscribeRequestAccountsDataSlice[]
111
- ping?: any
112
- }
113
-
114
- // const PUMP_FUN_MINT_AUTHORITY = 'TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM'
115
85
  const PUMP_FUN_PROGRAM_ID = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'
116
86
 
117
- const tran: SubscribeRequestFilterTransactions = {
87
+ const pumpfun: SubscribeRequestFilterTransactions = {
88
+ vote: false,
89
+ failed: false,
118
90
  accountInclude: [PUMP_FUN_PROGRAM_ID],
119
91
  accountExclude: [],
120
92
  accountRequired: [],
121
93
  }
122
94
 
123
- const request: SubscribeRequest = {
124
- accounts: {
125
- pumpfun: {
126
- account: [],
127
- owner: [],
128
- filters: [],
129
- },
130
- },
95
+ const request = {
96
+ accounts: {},
131
97
  slots: {},
132
- transactions: { elsol: tran },
98
+ transactions: { pumpfun },
133
99
  transactionsStatus: {},
134
100
  blocks: {},
135
101
  blocksMeta: {},
@@ -138,101 +104,55 @@ const request: SubscribeRequest = {
138
104
  commitment: CommitmentLevel.PROCESSED,
139
105
  }
140
106
 
141
- const geyser = async () => {
142
- console.log('Starting geyser client...')
143
- const maxRetries = 2000000
144
-
145
- const createClient = () => {
146
- const token = process.env.X_TOKEN?.trim()
147
- if (!token) {
148
- console.warn('X_TOKEN not set. Connecting without auth.')
149
- }
150
- const endpoint = `https://grpc-ams-3.erpc.global`
151
- console.log('Connecting to', endpoint)
107
+ const main = async () => {
108
+ const endpoint = process.env.GEYSER_ENDPOINT || 'http://localhost:10000'
109
+ const token = process.env.X_TOKEN?.trim()
110
+ const client = new GeyserClient(endpoint, token || undefined, undefined)
152
111
 
153
- // @ts-ignore ignore
154
- return new GeyserClient(endpoint, token || undefined, undefined)
155
- }
112
+ await client.connect()
113
+ const stream = await client.subscribe()
156
114
 
157
- const connect = async (retries: number = 0): Promise<void> => {
158
- if (retries > maxRetries) {
159
- throw new Error('Max retries reached')
115
+ stream.on('data', (data: any) => {
116
+ if (data?.ping != undefined) {
117
+ stream.write({ ping: { id: 1 } }, () => undefined)
118
+ return
160
119
  }
161
-
162
- try {
163
- const client = createClient()
164
- await client.connect()
165
- const version = await client.getVersion()
166
- console.log('version: ', version)
167
- const stream = await client.subscribe()
168
- stream.on('data', async (data: any) => {
169
- if (data.transaction !== undefined) {
170
- const transaction = data.transaction
171
- const txnSignature = transaction.transaction.signature
172
- const tx = bs58.encode(new Uint8Array(txnSignature))
173
- console.log('tx:', tx)
174
- return
175
- }
176
- if (data.account === undefined) {
177
- return
178
- }
179
- // console.log('data:', JSON.stringify(data, null, 2))
180
-
181
- const accounts = data.account
182
- const rawPubkey = accounts.account.pubkey
183
- const rawTxnSignature = accounts.account.txnSignature
184
- const pubkey = bs58.encode(new Uint8Array(rawPubkey))
185
- const txnSignature = bs58.encode(new Uint8Array(rawTxnSignature))
186
- console.log('pubkey:', pubkey)
187
- console.log('txnSignature:', txnSignature)
188
- })
189
-
190
- stream.on('error', async (e: any) => {
191
- console.error('Stream error:', e)
192
- console.log(`Reconnecting ...`)
193
- await connect(retries + 1)
194
- })
195
-
196
- await new Promise<void>((resolve, reject) => {
197
- stream.write(request, (err: any) => {
198
- if (!err) {
199
- resolve()
200
- } else {
201
- console.error('Request error:', err)
202
- reject(err)
203
- }
204
- })
205
- }).catch((reason) => {
206
- console.error(reason)
207
- throw reason
208
- })
209
- } catch (error) {
210
- console.error(`Connection failed. Retrying ...`, error)
211
- await connect(retries + 1)
120
+ if (data?.pong != undefined) {
121
+ return
212
122
  }
213
- }
214
-
215
- await connect()
216
- }
123
+ if (data?.transaction != undefined) {
124
+ const signature = data.transaction.transaction.signature
125
+ const txSignature = bs58.encode(new Uint8Array(signature))
217
126
 
218
- const main = async () => {
219
- try {
220
- await geyser()
221
- } catch (error) {
222
- console.log(error)
223
- }
127
+ // TODO: Add your trade logic here.
128
+ console.log('tx:', txSignature)
129
+ }
130
+ })
131
+
132
+ await new Promise<void>((resolve, reject) => {
133
+ stream.write(request, (err: any) => {
134
+ if (!err) {
135
+ resolve()
136
+ } else {
137
+ console.error('Request error:', err)
138
+ reject(err)
139
+ }
140
+ })
141
+ })
224
142
  }
225
143
 
226
- main()
144
+ void main()
227
145
  ```
228
146
 
229
147
  If your endpoint requires authentication, set the `X_TOKEN` environment variable with your gRPC token.
230
-
231
- Please note that the url endpoint in the example is for demonstration purposes. You should replace it with the actual endpoint you are using.
148
+ Please note that the endpoint in the example is for demonstration purposes. Replace it with your actual endpoint.
232
149
 
233
150
  ### Shreds Client
234
151
 
235
- Here's how to use the SDK to subscribe to Solana Shreds and decode entries:
152
+ For a working starter that includes latency checks, see
153
+ https://github.com/ValidatorsDAO/solana-stream/tree/main/client/shreds-ts.
154
+
155
+ Here's a minimal example:
236
156
 
237
157
  ```typescript
238
158
  import {
@@ -241,9 +161,6 @@ import {
241
161
  // decodeSolanaEntries,
242
162
  } from '@validators-dao/solana-stream-sdk'
243
163
  import 'dotenv/config'
244
- // import { logDecodedEntries } from '@/utils/logDecodedEntries'
245
-
246
- import { receivedSlots, startLatencyCheck } from '@/utils/checkLatency'
247
164
 
248
165
  const endpoint = process.env.SHREDS_ENDPOINT!
249
166
 
@@ -263,29 +180,20 @@ const connect = () => {
263
180
  client.subscribeEntries(
264
181
  JSON.stringify(request),
265
182
  (_error: any, buffer: any) => {
266
- const receivedAt = new Date()
267
- if (buffer) {
268
- const {
269
- slot,
270
- // entries
271
- } = JSON.parse(buffer)
272
-
273
- // You can decode entries as needed
274
- // const decodedEntries = decodeSolanaEntries(new Uint8Array(entries))
275
- // logDecodedEntries(decodedEntries)
276
-
277
- if (!receivedSlots.has(slot)) {
278
- receivedSlots.set(slot, [{ receivedAt }])
279
- } else {
280
- receivedSlots.get(slot)!.push({ receivedAt })
281
- }
183
+ if (!buffer) {
184
+ return
282
185
  }
186
+ const { slot } = JSON.parse(buffer)
187
+
188
+ // You can decode entries as needed
189
+ // const decodedEntries = decodeSolanaEntries(new Uint8Array(entries))
190
+
191
+ console.log('slot:', slot)
283
192
  },
284
193
  )
285
194
  }
286
195
 
287
196
  connect()
288
- startLatencyCheck()
289
197
  ```
290
198
 
291
199
  Ensure the environment variable `SHREDS_ENDPOINT` is set correctly.
@@ -342,18 +250,6 @@ Other reports and suggestions are also highly appreciated.
342
250
  You can also join discussions or share feedback on Validators DAO's Discord community:
343
251
  https://discord.gg/C7ZQSrCkYR
344
252
 
345
- ## Shreds UDP configuration (Rust client)
346
-
347
- - Load base config via `SHREDS_UDP_CONFIG=/path/to/config.{json,toml}`; env vars then override it.
348
- - Core envs:
349
- - `SOLANA_RPC_ENDPOINT`
350
- - Logs: `SHREDS_UDP_LOG_RAW`, `SHREDS_UDP_LOG_SHREDS`, `SHREDS_UDP_LOG_ENTRIES`, `SHREDS_UDP_LOG_WATCH_HITS`, `SHREDS_UDP_LOG_DEFER`
351
- - Watch lists: `SHREDS_UDP_WATCH_PROGRAM_IDS`, `SHREDS_UDP_WATCH_AUTHORITIES` (defaults to pump.fun program/authority)
352
- - Hardening: `SHREDS_UDP_REQUIRE_CODE_MATCH`, `SHREDS_UDP_STRICT_FEC`, `SHREDS_UDP_STRICT_NUM_DATA`, `SHREDS_UDP_STRICT_NUM_CODING`
353
- - Slot window: `SHREDS_UDP_ROOT_SLOT`, `SHREDS_UDP_MAX_FUTURE` (default 512)
354
- - TTLs: `SHREDS_UDP_COMPLETED_TTL_MS` (default 30s), `SHREDS_UDP_EVICT_COOLDOWN_MS` (default 300ms), `SHREDS_UDP_WARN_ONCE` (default true)
355
- - Pump.fun: program and mint-authority pubkeys are fixed; token mint addresses are discovered per transaction at runtime (not hardcoded).
356
-
357
253
  ## Repository
358
254
 
359
255
  This package is part of the [Solana Stream](https://github.com/ValidatorsDAO/solana-stream) monorepo.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@validators-dao/solana-stream-sdk",
3
3
  "description": "Solana Stream SDK by Validators DAO",
4
- "version": "1.0.1",
4
+ "version": "1.1.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",