wsbridge-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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Digital Resistance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,405 @@
1
+ # wsbridge-sdk
2
+
3
+ TypeScript SDK for the [TON WebSocket JSON-RPC bridge](https://github.com/TONresistor/Tonutils-Proxy). 61 typed methods across 12 namespaces with real-time subscriptions, ADNL peer messaging, and overlay networks.
4
+
5
+ Connects to [Tonutils-Proxy](https://github.com/TONresistor/Tonutils-Proxy) running locally or on a remote server. No centralized API, no rate limits. Direct access to the TON P2P network.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install wsbridge-sdk
11
+ ```
12
+
13
+ `ws` is needed for Node.js environments. It is listed as an optional peer dependency:
14
+
15
+ ```bash
16
+ npm install ws
17
+ ```
18
+
19
+ The SDK uses the native `WebSocket` API.
20
+
21
+ ## Prerequisites
22
+
23
+ The SDK connects to the WebSocket bridge exposed by [Tonutils-Proxy](https://github.com/TONresistor/Tonutils-Proxy). The proxy must be running before using the SDK.
24
+
25
+ ```bash
26
+ # Build and start the proxy
27
+ git clone https://github.com/TONresistor/Tonutils-Proxy.git
28
+ cd Tonutils-Proxy
29
+ go build -o proxy ./cmd/proxy-cli/
30
+ ./proxy --ws-addr 127.0.0.1:8081
31
+ ```
32
+
33
+ Default endpoint: `ws://127.0.0.1:8081`. See the [proxy README](https://github.com/TONresistor/Tonutils-Proxy) for configuration options.
34
+
35
+ ## Quickstart
36
+
37
+ ### Query the blockchain
38
+
39
+ ```typescript
40
+ import { Web3SocketsClient } from 'wsbridge-sdk'
41
+
42
+ const client = new Web3SocketsClient()
43
+ await client.connect()
44
+
45
+ const info = await client.lite.getMasterchainInfo()
46
+ console.log('Masterchain seqno:', info.seqno)
47
+
48
+ const state = await client.lite.getAccountState({
49
+ address: 'EQD...your-address',
50
+ })
51
+ console.log('Balance:', state.balance, 'nanotons')
52
+
53
+ client.destroy()
54
+ ```
55
+
56
+ ### Subscribe to transactions
57
+
58
+ ```typescript
59
+ const sub = await client.subscribe.transactions({
60
+ address: 'EQD...your-address',
61
+ })
62
+
63
+ sub.on('data', (tx) => {
64
+ console.log('New tx:', tx.hash, 'LT:', tx.lt)
65
+ })
66
+
67
+ // Clean up when done
68
+ await sub.unsubscribe()
69
+ ```
70
+
71
+ ### Resolve a .ton domain
72
+
73
+ ```typescript
74
+ const result = await client.dns.resolve({ domain: 'foundation.ton' })
75
+ console.log('Wallet:', result.wallet)
76
+ console.log('Site ADNL:', result.site_adnl)
77
+ console.log('Text records:', result.text_records)
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ All options are optional. Pass them to the `Web3SocketsClient` constructor:
83
+
84
+ ```typescript
85
+ const client = new Web3SocketsClient({
86
+ url: 'ws://127.0.0.1:8081', // Bridge WebSocket endpoint
87
+ autoReconnect: true, // Reconnect on disconnect (default: true)
88
+ reconnectDelay: 1000, // Initial reconnect delay in ms (default: 1000)
89
+ reconnectMaxDelay: 30000, // Maximum reconnect delay in ms (default: 30000)
90
+ reconnectMaxRetries: Infinity, // Max reconnect attempts (default: Infinity)
91
+ keepaliveInterval: 30000, // Ping interval in ms, Node.js only (default: 30000)
92
+ requestTimeout: 30000, // RPC request timeout in ms (default: 30000)
93
+ logger: { // Optional structured logger
94
+ debug: console.debug,
95
+ warn: console.warn,
96
+ error: console.error,
97
+ },
98
+ })
99
+ ```
100
+
101
+ | Option | Type | Default | Description |
102
+ |---|---|---|---|
103
+ | `url` | `string` | `ws://127.0.0.1:8081` | Bridge WebSocket endpoint |
104
+ | `autoReconnect` | `boolean` | `true` | Automatically reconnect on unexpected disconnection |
105
+ | `reconnectDelay` | `number` | `1000` | Initial delay before first reconnect attempt (ms) |
106
+ | `reconnectMaxDelay` | `number` | `30000` | Maximum delay between reconnect attempts (ms, exponential backoff) |
107
+ | `reconnectMaxRetries` | `number` | `Infinity` | Maximum number of reconnect attempts |
108
+ | `keepaliveInterval` | `number` | `30000` | WebSocket ping interval (ms). Node.js only. Set to `0` to disable |
109
+ | `requestTimeout` | `number` | `30000` | Timeout for individual RPC requests (ms) |
110
+ | `logger` | `Logger` | `undefined` | Object with `debug`, `warn`, `error` methods |
111
+
112
+ ## Connection lifecycle
113
+
114
+ ```typescript
115
+ const client = new Web3SocketsClient()
116
+
117
+ await client.connect() // Open the WebSocket connection
118
+ client.isConnected // true when connected
119
+ client.state // Current TransportState
120
+
121
+ client.disconnect() // Close gracefully (no auto-reconnect)
122
+ client.destroy() // Permanent teardown, releases all resources
123
+ ```
124
+
125
+ `TransportState` enum values: `DISCONNECTED`, `CONNECTING`, `CONNECTED`, `RECONNECTING`, `DESTROYED`.
126
+
127
+ ## Events
128
+
129
+ The client emits four events:
130
+
131
+ | Event | Callback signature | Description |
132
+ |---|---|---|
133
+ | `connected` | `() => void` | WebSocket connection established |
134
+ | `disconnected` | `(code: number, reason: string) => void` | Connection closed |
135
+ | `error` | `(err: Error) => void` | Transport-level error |
136
+ | `stateChange` | `(newState: TransportState, oldState: TransportState) => void` | Any state transition |
137
+
138
+ ```typescript
139
+ import { Web3SocketsClient, TransportState } from 'wsbridge-sdk'
140
+
141
+ const client = new Web3SocketsClient()
142
+
143
+ client.on('connected', () => {
144
+ console.log('Connected to bridge')
145
+ })
146
+
147
+ client.on('disconnected', (code, reason) => {
148
+ console.log(`Disconnected: ${code} ${reason}`)
149
+ })
150
+
151
+ client.on('error', (err) => {
152
+ console.error('Transport error:', err.message)
153
+ })
154
+
155
+ client.on('stateChange', (newState, oldState) => {
156
+ console.log(`${oldState} -> ${newState}`)
157
+ })
158
+
159
+ await client.connect()
160
+ ```
161
+
162
+ Use `client.off(event, listener)` to remove a listener.
163
+
164
+ ## Subscriptions
165
+
166
+ Subscription methods return a `Subscription<T>` object. Two consumption patterns are supported: callbacks and async iteration.
167
+
168
+ ### Callback pattern
169
+
170
+ ```typescript
171
+ const sub = await client.subscribe.transactions({
172
+ address: 'EQD...your-address',
173
+ })
174
+
175
+ sub.on('data', (tx) => {
176
+ console.log('Transaction:', tx.hash)
177
+ })
178
+
179
+ sub.on('error', (err) => {
180
+ console.error('Subscription error:', err.message)
181
+ })
182
+
183
+ // Stop receiving events
184
+ await sub.unsubscribe()
185
+ ```
186
+
187
+ ### Async iterator pattern
188
+
189
+ ```typescript
190
+ const sub = await client.subscribe.blocks()
191
+
192
+ for await (const block of sub) {
193
+ console.log('Block seqno:', block.seqno)
194
+ if (block.seqno > 50000000) break // break auto-unsubscribes
195
+ }
196
+ ```
197
+
198
+ ### Subscription API
199
+
200
+ | Method / Property | Description |
201
+ |---|---|
202
+ | `on('data', cb)` | Register a data listener |
203
+ | `on('error', cb)` | Register an error listener |
204
+ | `off('data', cb)` | Remove a data listener |
205
+ | `off('error', cb)` | Remove an error listener |
206
+ | `unsubscribe()` | Stop the subscription and release resources |
207
+ | `active` | `boolean`, true while the subscription is active |
208
+ | `[Symbol.asyncIterator]()` | Consume events as an async iterable |
209
+
210
+ The async iterator respects backpressure via a `highWaterMark` option (default: 1000). When the buffer exceeds this limit, the oldest events are dropped:
211
+
212
+ ```typescript
213
+ const sub = await client.subscribe.newTransactions()
214
+ // The highWaterMark is set internally on the Subscription.
215
+ // Default is 1000 buffered events before oldest are dropped.
216
+ ```
217
+
218
+ ## Error handling
219
+
220
+ All SDK errors extend `Web3SocketsError`, which carries a `.code` string:
221
+
222
+ ```
223
+ Web3SocketsError (base, .code: string)
224
+ ConnectionError (.url: string)
225
+ ConnectionClosedError (.closeCode: number)
226
+ RpcError (.rpcCode: number | undefined)
227
+ TimeoutError
228
+ ValidationError
229
+ ```
230
+
231
+ Use `instanceof` to distinguish error types:
232
+
233
+ ```typescript
234
+ import {
235
+ Web3SocketsError,
236
+ RpcError,
237
+ ConnectionError,
238
+ TimeoutError,
239
+ } from 'wsbridge-sdk'
240
+
241
+ try {
242
+ await client.lite.getAccountState({ address: 'invalid' })
243
+ } catch (err) {
244
+ if (err instanceof RpcError) {
245
+ console.error(`RPC error [${err.rpcCode}]: ${err.message}`)
246
+ } else if (err instanceof ConnectionError) {
247
+ console.error(`Connection failed to ${err.url}: ${err.message}`)
248
+ } else if (err instanceof TimeoutError) {
249
+ console.error(`Request timed out: ${err.message}`)
250
+ } else if (err instanceof Web3SocketsError) {
251
+ console.error(`SDK error [${err.code}]: ${err.message}`)
252
+ }
253
+ }
254
+ ```
255
+
256
+ ## AbortSignal
257
+
258
+ Every RPC method accepts an optional `AbortSignal` to cancel in-flight requests:
259
+
260
+ ```typescript
261
+ const controller = new AbortController()
262
+
263
+ setTimeout(() => controller.abort(), 5000)
264
+
265
+ try {
266
+ const txs = await client.lite.getTransactions(
267
+ { address: 'EQD...', limit: 100 },
268
+ { signal: controller.signal },
269
+ )
270
+ } catch (err) {
271
+ if (err instanceof TimeoutError) {
272
+ console.log('Request was cancelled')
273
+ }
274
+ }
275
+ ```
276
+
277
+ ## API reference
278
+
279
+ All 61 bridge methods organized by namespace. Each method is called as `client.<namespace>.<method>(params?, options?)`.
280
+
281
+ | Namespace | Methods |
282
+ |---|---|
283
+ | `dht` | `findAddresses`, `findOverlayNodes`, `findTunnelNodes`, `findValue` |
284
+ | `lite` | `getMasterchainInfo`, `getAccountState`, `runMethod`, `sendMessage`, `sendMessageWait`, `getTransactions`, `getTransaction`, `findTxByInMsgHash`, `findTxByOutMsgHash`, `getTime`, `lookupBlock`, `getBlockTransactions`, `getShards`, `getBlockchainConfig`, `sendAndWatch`, `getBlockData`, `getBlockHeader`, `getLibraries` |
285
+ | `subscribe` | `transactions`, `blocks`, `accountState`, `newTransactions`, `configChanges`, `multiAccount`, `trace` |
286
+ | `jetton` | `getData`, `getWalletAddress`, `getBalance` |
287
+ | `nft` | `getData`, `getCollectionData`, `getAddressByIndex`, `getRoyaltyParams`, `getContent` |
288
+ | `dns` | `resolve` |
289
+ | `adnl` | `connect`, `connectByADNL`, `sendMessage`, `ping`, `disconnect`, `peers`, `query`, `setQueryHandler`, `answer` |
290
+ | `network` | `info` |
291
+ | `overlay` | `join`, `leave`, `getPeers`, `sendMessage`, `query`, `setQueryHandler`, `answer` |
292
+ | `wallet` | `getSeqno`, `getPublicKey` |
293
+ | `sbt` | `getAuthorityAddress`, `getRevokedTime` |
294
+ | `payment` | `getChannelState` |
295
+
296
+ `subscribe.unsubscribe` is handled internally by the SDK when you call `sub.unsubscribe()`.
297
+
298
+ ## Advanced examples
299
+
300
+ ### ADNL peer messaging
301
+
302
+ Connect to a peer by ADNL address, send messages, and listen for incoming events:
303
+
304
+ ```typescript
305
+ const client = new Web3SocketsClient()
306
+ await client.connect()
307
+
308
+ // Connect to a peer via DHT resolution
309
+ const peer = await client.adnl.connectByADNL({
310
+ adnl_id: 'base64-encoded-adnl-id',
311
+ })
312
+ console.log('Connected, peer:', peer.peer_id)
313
+
314
+ // Listen for incoming messages
315
+ client.adnl.on('message', (event) => {
316
+ console.log('Message from', event.peer_id, ':', event.data)
317
+ })
318
+
319
+ // Listen for peer disconnections
320
+ client.adnl.on('disconnected', (event) => {
321
+ console.log('Peer disconnected:', event.peer_id)
322
+ })
323
+
324
+ // Send a message
325
+ await client.adnl.sendMessage({
326
+ peer_id: peer.peer_id,
327
+ data: 'base64-encoded-payload',
328
+ })
329
+
330
+ // Query-response pattern
331
+ const response = await client.adnl.query({
332
+ peer_id: peer.peer_id,
333
+ data: 'base64-encoded-tl-query',
334
+ })
335
+ console.log('Response:', response.data)
336
+
337
+ // Disconnect from peer
338
+ await client.adnl.disconnect({ peer_id: peer.peer_id })
339
+ ```
340
+
341
+ ### Jetton data with TEP-64 content
342
+
343
+ Query jetton master data including the full TEP-64 content object:
344
+
345
+ ```typescript
346
+ const jetton = await client.jetton.getData({
347
+ address: 'EQB...jetton-master',
348
+ })
349
+
350
+ console.log('Total supply:', jetton.total_supply)
351
+ console.log('Admin:', jetton.admin)
352
+
353
+ // content is a TEP-64 object with name, description, image, etc.
354
+ if (jetton.content) {
355
+ console.log('Name:', jetton.content.name)
356
+ console.log('Symbol:', jetton.content.symbol)
357
+ console.log('Decimals:', jetton.content.decimals)
358
+ console.log('Image:', jetton.content.image)
359
+ }
360
+ ```
361
+
362
+ ### Transaction tracing
363
+
364
+ Follow the full internal message chain of a transaction:
365
+
366
+ ```typescript
367
+ const sub = await client.subscribe.trace({
368
+ address: 'EQD...your-address',
369
+ max_depth: 3,
370
+ msg_timeout_sec: 10,
371
+ })
372
+
373
+ sub.on('data', (event) => {
374
+ // Events are a union of TraceStartedEvent | TraceTxEvent | TraceCompleteEvent | TraceTimeoutEvent
375
+ if ('root_tx' in event) {
376
+ console.log('Trace started:', event.trace_id)
377
+ } else if ('transaction' in event) {
378
+ console.log('Internal tx at depth', event.depth)
379
+ } else if ('total_txs' in event) {
380
+ console.log('Trace complete:', event.total_txs, 'transactions')
381
+ sub.unsubscribe()
382
+ }
383
+ })
384
+ ```
385
+
386
+ ## Environment
387
+
388
+ | Runtime | WebSocket | Notes |
389
+ |---|---|---|
390
+ | **Browser** | Native `WebSocket` | No extra dependencies |
391
+ | **Node.js** (>=18) | `ws` package | Install as peer dependency |
392
+ | **Electron** | Auto-detects `IpcTransport` via `window.ton.adnl` | Falls back to `ws` |
393
+
394
+ The package ships dual ESM/CJS output:
395
+ - ESM: `dist/index.mjs`
396
+ - CJS: `dist/index.cjs`
397
+ - Types: `dist/index.d.ts` / `dist/index.d.mts`
398
+
399
+ ## Protocol reference
400
+
401
+ See [WSBRIDGE.md](https://github.com/TONresistor/Tonutils-Proxy/blob/master/WSBRIDGE.md) for the full WebSocket JSON-RPC protocol specification.
402
+
403
+ ## License
404
+
405
+ MIT