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 +21 -0
- package/README.md +405 -0
- package/dist/index.cjs +1207 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1764 -0
- package/dist/index.d.ts +1764 -0
- package/dist/index.js +1181 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
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
|