A JSON-RPC websocket proxy for the Yellowstone gRPC Geyser plugin. It subscribes to Solana whole-block updates over gRPC and re-publishes them to websocket clients as JSON-RPC subscription notifications.
At SQD it is used as a workaround for ingesting Solana data into a JavaScript stack: the proxy moves the gRPC handling and the binary-to-JSON conversion into Rust, so JS clients receive ready-to-use JSON.
- Yellowstone gRPC delivers account ids and other keys in binary form, but most consumers want base58 encodings, which are expensive to produce on the JS side.
- The Yellowstone whole-block subscription cannot exclude vote transactions. This proxy removes them by default.
- The JavaScript gRPC stack is heavy.
A single subscription kind is supported. It delivers whole-block data at the processed commitment level.
geyser_blockSubscribesubscription method (no parameters)geyser_blockNotificationdata messagegeyser_blockUnsubscribesubscription cancellation
The data message has the shape {"slot": <number>, "block": <GetBlock>}, where GetBlock follows the Solana getBlock result, with two differences:
- Each transaction carries an extra
_indexproperty recording its original position within the block (positions are otherwise lost once vote transactions are excluded). - Integer fields that can exceed the JavaScript safe integer range are emitted as number strings.
The upstream gRPC subscription is lazy: when no websocket clients are connected the proxy pauses it, and it resumes on the next subscriber.
Usage: sqd-yellowstone-geyser-proxy [OPTIONS] --geyser-url <URL>
Options:
--geyser-url <URL> Yellowstone gRPC service
--geyser-x-token <TOKEN> add X-Token header to gRPC requests
--geyser-x-access-token <TOKEN> add X-Access-Token header to gRPC requests
--with-votes disable removal of vote transactions
--port <PORT> [default: 3000]
--mapping-threads <N> number of threads to use for data transformation
Block-to-JSON conversion runs on a thread pool. Without --mapping-threads it defaults to the number of available cores, capped at 4. Log verbosity is controlled by the RUST_LOG environment variable (default info); logs are emitted as JSON when stdout is not a terminal.
With Cargo (requires protoc):
cargo build --release
./target/release/sqd-yellowstone-geyser-proxy --geyser-url <URL>
With Docker:
docker build --target proxy -t yellowstone-geyser-proxy .
docker run -p 3000:3000 yellowstone-geyser-proxy --geyser-url <URL>
SQD documentation: https://docs.sqd.dev