Skip to Content

Getting Started with Pulse

Connect to Raiden Pulse gRPC binary stream in minutes

1. Reserve Access

→ dashboard 

2. Get Your Endpoint

RegionEndpoint
FRAhttp://fra.pulse.raiden.wtf:16000/
NYChttp://nyc.pulse.raiden.wtf:16000/
AMShttp://ams.pulse.raiden.wtf:16000/

Each region requires a separate subscription.

3. Protocol

Raiden Pulse is protocol-compatible with the open shreder-rust-example reference client and its shreder_binary.proto. If your stack already integrates that proto, you can point it at Raiden Pulse endpoints with no code changes.

Proto summary

  • Service: ShrederBinaryService
  • RPC: SubscribeBinaryTransactions — bidirectional streaming
  • Request: SubscribeBinaryTransactionsRequest with named filter map
  • Filter: SubscribeRequestFilterBinaryTransactions — account_include, account_exclude, account_required
  • Payload: raw bytes deserialized via bincode into Solana VersionedTransaction

4. Connect

Rust (tonic)

pub mod shreder_binary { tonic::include_proto!("shreder_binary"); } use futures::{channel::mpsc::unbounded, sink::SinkExt}; use maplit::hashmap; use shreder_binary::{ shreder_binary_service_client::ShrederBinaryServiceClient, SubscribeBinaryTransactionsRequest, SubscribeRequestFilterBinaryTransactions, }; use solana_transaction::versioned::VersionedTransaction; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let entrypoint = "http://fra.pulse.raiden.wtf:9991"; let mut client = ShrederBinaryServiceClient::connect(entrypoint).await?; let (mut tx_req, rx_req) = unbounded(); let request = SubscribeBinaryTransactionsRequest { transactions: hashmap! { "pumpfun".to_owned() => SubscribeRequestFilterBinaryTransactions { account_exclude: vec![], account_include: vec![], account_required: vec![ "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P".to_owned(), ], } }, }; tx_req.send(request).await?; let mut stream = client .subscribe_binary_transactions(rx_req) .await? .into_inner(); while let Some(response) = stream.message().await? { let Some(update) = response.transaction else { continue }; let Some(tx) = update.transaction else { continue }; let versioned_tx: VersionedTransaction = match bincode::deserialize(&tx.binary_transaction) { Ok(vt) => vt, Err(_) => continue, }; let instruction_count = versioned_tx.message.instructions().len(); println!( "slot={} sig={} ixs={}", tx.slot, tx.signature, instruction_count, ); } Ok(()) }

Enable tonic TLS features if your endpoint uses HTTPS:

tonic = { version = "0.10", features = ["tls", "tls-roots", "tls-webpki-roots"] }

Python (grpcio + protobuf)

Generate Python bindings from shreder_binary.proto:

python -m grpc_tools.protoc -Iproto \ --python_out=. --grpc_python_out=. \ proto/shreder_binary.proto

Then connect:

import grpc from shreder_binary_pb2 import ( SubscribeBinaryTransactionsRequest, SubscribeRequestFilterBinaryTransactions, ) from shreder_binary_pb2_grpc import ShrederBinaryServiceStub channel = grpc.insecure_channel("fra.pulse.raiden.wtf:9991") stub = ShrederBinaryServiceStub(channel) def make_requests(): yield SubscribeBinaryTransactionsRequest( transactions={ "pumpfun": SubscribeRequestFilterBinaryTransactions( account_required=["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"], ), }, ) for response in stub.SubscribeBinaryTransactions(make_requests()): tx = response.transaction.transaction # tx.binary_transaction is raw bincode-encoded VersionedTransaction print(f"slot {tx.slot} sig {tx.signature}")

TypeScript (@grpc/grpc-js)

Generate TS bindings from the proto, then:

import { credentials } from '@grpc/grpc-js'; import { ShrederBinaryServiceClient } from './generated/shreder_binary_grpc_pb'; import { SubscribeBinaryTransactionsRequest, SubscribeRequestFilterBinaryTransactions, } from './generated/shreder_binary_pb'; const client = new ShrederBinaryServiceClient( 'fra.pulse.raiden.wtf:9991', credentials.createInsecure(), ); const stream = client.subscribeBinaryTransactions(); const filter = new SubscribeRequestFilterBinaryTransactions(); filter.setAccountRequiredList(['6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P']); const req = new SubscribeBinaryTransactionsRequest(); req.getTransactionsMap().set('pumpfun', filter); stream.write(req); stream.on('data', (response) => { const tx = response.getTransaction().getTransaction(); console.log(`slot ${tx.getSlot()} sig ${tx.getSignature()}`); });

5. Filtering

Every subscription requires at least one filter entry in the transactions map. Each entry name is client-chosen (e.g. "pumpfun", "jupiter-swaps") and carries:

FieldPurpose
account_requiredTransactions must touch every listed account
account_includeTransactions may touch any listed account
account_excludeTransactions must not touch any listed account

Server-side filtering — no wasted bandwidth.

6. Decoding the Payload

Each tx.binary_transaction is a bincode-encoded Solana VersionedTransaction. Deserialize client-side with your language’s bincode / serde equivalent:

  • Rust: bincode::deserialize::<VersionedTransaction>(&bytes)
  • Python: solders → VersionedTransaction.from_bytes(bytes)
  • TS: @solana/web3.js → VersionedTransaction.deserialize(bytes)

7. Limits

LimitValue
Concurrent connections10
Named filters35 per connection
Account filters5,000 per named filter

A named filter carrying more than 5,000 accounts is rejected. If you need to watch more, split them across filters, and across connections once you pass 35.

8. Pulse V2: raiden_binary.proto and resolved ALTs

Pulse V2 moves off the third-party shreder_binary proto onto Raiden’s own, raiden_binary. Both are served during the transition, so nothing breaks on the day it lands, but new integrations should expect to move.

The rename is not the point. V2 resolves address lookup tables server-side, which is what makes the migration worth doing rather than merely necessary.

What changes

shreder_binary (today)raiden_binary (Pulse V2)
Packageshreder_binaryraiden_binary
ServiceShrederBinaryServiceRaidenBinaryService
RPCSubscribeBinaryTransactionsunchanged
Filtersaccount_include / _exclude / _requiredunchanged
Matched filter namesnot reportedfilters on every response
Server timestampnot reportedcreated_at
ALT resolutionclient-side, your problemserver-side, in loaded_writable_addresses and loaded_readonly_addresses
Signaturessingle fieldsignatures, repeated

Resolved ALTs

A V0 transaction names most of its accounts indirectly, through an address lookup table. To know which accounts it actually touches you have to resolve those tables yourself, which means fetching them, caching them, and keeping the cache correct as tables change.

Pulse V2 does that resolution server-side and ships the result alongside the transaction. The full account set is then the direct keys inside binary_transaction plus loaded_writable_addresses and loaded_readonly_addresses. Both are empty for legacy transactions, which carry no lookup tables.

If you are filtering or routing on the accounts a transaction touches, this removes an entire subsystem from your client.

The proto

syntax = "proto3"; package raiden_binary; import "google/protobuf/timestamp.proto"; service RaidenBinaryService { rpc SubscribeBinaryTransactions(stream SubscribeBinaryTransactionsRequest) returns (stream SubscribeBinaryTransactionsResponse); } message SubscribeBinaryTransactionsRequest { map<string, SubscribeRequestFilterBinaryTransactions> transactions = 3; } message SubscribeBinaryTransactionsResponse { repeated string filters = 1; SubscribeUpdateBinaryTransaction transaction = 4; google.protobuf.Timestamp created_at = 11; } message SubscribeUpdateBinaryTransaction { BinaryTransaction transaction = 1; uint64 slot = 2; // ALT-resolved accounts. Empty for legacy transactions. repeated bytes loaded_writable_addresses = 3; repeated bytes loaded_readonly_addresses = 4; } message SubscribeRequestFilterBinaryTransactions { repeated string account_include = 3; repeated string account_exclude = 4; repeated string account_required = 6; } message BinaryTransaction { repeated bytes signatures = 1; // Serialized VersionedTransaction bytes binary_transaction = 3; }

Migrating

The subscription shape is unchanged, so a filter map written for shreder_binary transfers as-is. What moves is where fields live:

  • Regenerate bindings from raiden_binary.proto and swap ShrederBinaryServiceClient for RaidenBinaryServiceClient.
  • slot is on the update, not on the transaction.
  • signatures is a repeated field of raw bytes rather than a single string, so take the first element and encode it yourself if you were printing it.
  • binary_transaction is unchanged: still a bincode-encoded VersionedTransaction, decoded exactly as before.

Pricing

See Pricing.

Payment in USDC on Solana via the dashboard.

Last updated on