Class: WsTransport
Defined in: src/channel/transport/ws.ts:88
Carries JSON-RPC envelopes over a single WebSocket.
It owns the socket and nothing else: it does not know what starknet_getBlockWithTxs means,
and it does not know that subscriptions exist. That is what lets one socket serve several
versioned channels at once, and lets it survive the probe channel that RpcProvider.create()
builds and throws away.
This transport connects once. When the connection goes away it settles everything waiting on
it and reports 'closed'; it does not reconnect.
Batches depend on the node. Pathfinder accepts an array and answers with a single array
frame (measured 2026-08-10 with a raw socket), and so does starknet-devnet from 0.9.2 —
wsTransport.ws.test.ts exercises it against a real node. Devnet before 0.9.2 refuses it on
its /ws endpoint with -32700 Parse error.
A refusal is answered with id: null, which belongs to no request, so every call in the batch
runs out its requestTimeout instead of failing fast — worth checking on an unfamiliar node.
Prefer ReconnectingWsTransport for anything long-lived: a shared gateway drops idle connections, and this one does not come back.
Example
const transport = new WsTransport({ nodeUrl: 'wss://your-starknet-node/rpc/v0_10' });
const myProvider = new WebSocketProvider({ transport });
// ... later
transport.close();
Extended by
Implements
Constructors
Constructor
new WsTransport(
options):WsTransport
Defined in: src/channel/transport/ws.ts:146
Parameters
options
Returns
WsTransport
Properties
nodeUrl
readonlynodeUrl:string
Defined in: src/channel/transport/ws.ts:90
The URL of the WebSocket RPC node.
socket
socket:
WebSocket
Defined in: src/channel/transport/ws.ts:98
The underlying socket. Owned by this transport — close it through close().
Not readonly because a subclass may replace it on a reconnection. This class never does:
one WsTransport is one socket, which is what its own tests rely on.
WsImplementation
protectedreadonlyWsImplementation:WebSocketModule
Defined in: src/channel/transport/ws.ts:101
Kept so a subclass can build a replacement socket the same way this one was built.
Methods
swapSocket()
protectedswapSocket(next):void
Defined in: src/channel/transport/ws.ts:180
Adopts a replacement socket.
The readiness latch is discarded with the old socket: it records that that connection came up, and reusing it would let a request be written to a socket still handshaking.
Requests already on the wire are not settled here — handleClose has done that for the
socket that went away, since nothing on a dead socket can still be answered.
Parameters
next
WebSocket
Returns
void
onOpened()
protectedonOpened():void
Defined in: src/channel/transport/ws.ts:203
What happens once a socket is open. Overridden by a reconnecting subclass, which also has a retry counter to reset and a queue to flush.
Returns
void
onClosed()
protectedonClosed(_event):void
Defined in: src/channel/transport/ws.ts:211
What happens once a socket is gone. This class stays closed; a reconnecting subclass starts an attempt instead.
Parameters
_event
Event
Returns
void
onError()
protectedonError(_event):void
Defined in: src/channel/transport/ws.ts:282
What happens on a socket error. This class only logs; a reconnecting subclass treats it as a
failed attempt, because a refused connection may produce an error with no close behind it.
Parameters
_event
Event
Returns
void
setState()
protectedsetState(next):void
Defined in: src/channel/transport/ws.ts:284
Parameters
next
Returns
void
getState()
getState():
WsTransportState
Defined in: src/channel/transport/ws.ts:295
The current connection state. The getSnapshot half of the useSyncExternalStore pair.
Returns
isConnected()
isConnected():
boolean
Defined in: src/channel/transport/ws.ts:300
Whether the socket is open right now.
Returns
boolean
allocateRequestId()
allocateRequestId():
number
Defined in: src/channel/transport/ws.ts:312
Allocates the next wire id, starting at 0.
Exposed because a caller may write to the socket itself rather than through request —
WebSocketChannel.send() is fire-and-forget and correlates nothing. Drawing from this same
sequence is what stops such a write from colliding with a request this transport is
tracking, which would hand one caller the other's reply.
Returns
number
on()
on<
K>(event,listener): () =>void
Defined in: src/channel/transport/ws.ts:327
Subscribes to one of the transport's two streams.
'statechange' fires on every connection state transition; 'notification' fires for
every frame the node pushes on its own.
Type Parameters
K
K extends keyof WsTransportEvents
Parameters
event
K
listener
(payload) => void
Returns
the unsubscribe function, which is what useSyncExternalStore requires of its
subscribe argument.
() => void
request()
Call Signature
request(
body):Promise<ResponseBody>
Defined in: src/channel/transport/ws.ts:405
Sends the envelope over the socket and resolves with the node's answer. See RpcTransport.request for the contract every transport honours.
Waits for the socket to be usable when it is not yet, and rejects with a TimeoutError after
requestTimeout, or with a WebSocketNotConnectedError if the connection is lost meanwhile.
Parameters
body
A request envelope, or an array of them for a batch — read the note on batch portability in the class description first.
Returns
Promise<ResponseBody>
The response envelope, or an array of them.
Implementation of
Call Signature
request(
body):Promise<ResponseBody[]>
Defined in: src/channel/transport/ws.ts:406
Sends the envelope over the socket and resolves with the node's answer. See RpcTransport.request for the contract every transport honours.
Waits for the socket to be usable when it is not yet, and rejects with a TimeoutError after
requestTimeout, or with a WebSocketNotConnectedError if the connection is lost meanwhile.
Parameters
body
A request envelope, or an array of them for a batch — read the note on batch portability in the class description first.
Returns
Promise<ResponseBody[]>
The response envelope, or an array of them.
Implementation of
rejectPending()
protectedrejectPending(reason):void
Defined in: src/channel/transport/ws.ts:482
Fails every request still on the wire.
Their only other exit is the requestTimeout timer, so without this the caller waits the
whole timeout — 60s by default — for a reply that can no longer arrive, and that pending
timer keeps the Node event loop alive for just as long.
Parameters
reason
string
Returns
void
close()
close(
code?,reason?):void
Defined in: src/channel/transport/ws.ts:504
Closes the socket.
One object owns the socket and one object closes it. Dropping every reference to this transport does not close anything: an open socket is a libuv handle the Node event loop holds onto, so it must be closed explicitly.
Parameters
code?
number
reason?
string
Returns
void