# Overview

Global low-latency infrastructure for orderflow projects, trading bots, searchers, and quant trading on BSC, Solana, Ethereum, Base, and Robinhood Chain.

BlockRazor is a research institution focused on Web3 infrastructure and DeFi trading. It focuses on solving key problems in trading scenarios and continuously transforms its research results into infrastructure products and services, creating a globally distributed, high-performance multi-chain infrastructure system for builders who pursue excellence.

Through long-term research and engineering practice, BlockRazor provides integrated capabilities covering Transaction Submission and Streams for orderflow projects, trading bots, searchers, and quantitative trading systems, helping customers obtain faster block transaction signals and better transaction execution results on BSC, Solana, Robinhood Chain, Ethereum and Base.

### Get Started

<mark style="color:$info;">Service</mark>

<table data-card-wrap="false" data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Obtain Auth Token</td><td><a href="https://blockrazor.io/#/register?redirect=onboarding">https://blockrazor.io/#/register?redirect=onboarding</a></td></tr><tr><td>Start for Free</td><td><a href="/get-started/start-for-free">Start for Free</a></td></tr><tr><td>Pricing</td><td><a href="/get-started/pricing">Pricing</a></td></tr></tbody></table>

<mark style="color:$info;">Use Case</mark>

<table data-card-wrap="false" data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Trading Bot</td><td><a href="/get-started/use-cases/trading-bot">Trading Bot</a></td></tr><tr><td>Searcher</td><td><a href="/get-started/use-cases/searcher">Searcher</a></td></tr><tr><td>Quant Trading</td><td><a href="/get-started/use-cases/quant-trading">Quant Trading</a></td></tr><tr><td>Orderflow Projects</td><td><a href="/get-started/use-cases/wallet-dex">Wallet / DEX</a></td></tr><tr><td>Individual Trader</td><td><a href="/get-started/use-cases/individual-trader">Individual Trader</a></td></tr></tbody></table>

<mark style="color:$info;">Chain</mark>

<table data-card-wrap="false" data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>BSC</td><td><a href="/get-started/supported-chains#bsc">Supported Chains</a></td></tr><tr><td>Solana</td><td><a href="/get-started/supported-chains#solana">Supported Chains</a></td></tr><tr><td>Robinhood Chain</td><td><a href="/get-started/supported-chains#robinhood-chain">Supported Chains</a></td></tr><tr><td>Ethereum</td><td><a href="/get-started/supported-chains#ethereum">Supported Chains</a></td></tr><tr><td>Base</td><td><a href="/get-started/supported-chains#base">Supported Chains</a></td></tr></tbody></table>

### Streams

<mark style="color:$info;">BlockRazor offers a variety of high-performance real-time data streaming capabilities, helping trading systems and infrastructure systems obtain signals earlier and synchronize states faster</mark>

<table data-card-wrap="false" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Public Mempool</strong></td><td><mark style="color:$info;">See BSC pending transactions earlier and gain a decisive edge in smart-money tracking, sniping, copy trading, and backruns.</mark></td><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a></td></tr><tr><td><strong>Private Mempool</strong></td><td><mark style="color:$info;">Access exclusive private order flow as an early alpha signal for faster sniping, copy-trading and backrun opportunities.</mark></td><td><a href="/streams/private-mempool">Private Mempool</a></td></tr><tr><td><strong>Block Stream</strong></td><td><mark style="color:$info;">Receive the latest BSC blocks and confirmed transactions with low latency so your strategies can react immediately.</mark></td><td><a href="/streams/block-stream">Block Stream</a></td></tr><tr><td><strong>Node Stream</strong></td><td><mark style="color:$info;">Accelerate world-state synchronization of node to keep state-dependent strategies ahead of your competitors.</mark></td><td><a href="/streams/node-stream">Node Stream</a></td></tr><tr><td><strong>Network Fee Stream</strong></td><td><mark style="color:$info;">Track gas prices and Tip in real time to optimize every transaction for speed, cost, and inclusion probability.</mark></td><td><a href="/streams/network-fee-stream">Network Fee Stream</a></td></tr></tbody></table>

### Transaction Submission

<mark style="color:$info;">BlockRazor offers multiple transaction submission modes to adapt to the different needs of various businesses in terms of mev protection, inclusion speed, and transaction costs</mark>

<table data-card-wrap="false" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>RPC</strong></td><td><mark style="color:$info;">Provides standard JSON-RPC methods, offers MEV protection, low-latency inclusion, and real-time refunds for transactions</mark></td><td><a href="/transaction-submission/rpc">RPC</a></td></tr><tr><td><strong>Block Builder</strong></td><td><mark style="color:$info;">A block building service for BSC, providing users with a high-win-rate block building commitment and low-latency access capabilities.</mark></td><td><a href="/transaction-submission/block-builder">Block Builder</a></td></tr><tr><td><strong>Transaction Sending</strong></td><td><mark style="color:$info;">Utilizes</mark> <a href="/core-technology/blockchain-edge-fabric"><mark style="color:$info;">BEF</mark></a> <mark style="color:$info;">to improve inclusion speed, suitable for users with extremely high requirements for transaction speed.</mark></td><td><a href="/transaction-submission/transaction-sending">Transaction Sending</a></td></tr><tr><td><strong>Gas Sponsor</strong></td><td><mark style="color:$info;">Provides gas sponsorship for users whose native tokens are insufficient to cover transaction fees, enhancing the user trading experience.</mark></td><td><a href="/transaction-submission/gas-sponsor">Gas Sponsor</a></td></tr></tbody></table>


# Which Chains Do We Support

From a blockchain perspective, BlockRazor offers services that currently support Solana, BSC, Ethereum, Base, and Robinhood.

### Solana

<table><thead><tr><th width="238.33984375">Service</th><th>Index</th></tr></thead><tbody><tr><td>Transaction Submission</td><td><ul><li><a href="/transaction-submission/transaction-sending/solana/send-transaction">Transaction Sending</a></li><li><a href="/transaction-submission/gas-sponsor">Gas Sponsor</a></li></ul></td></tr><tr><td>Streams</td><td><ul><li><a href="/streams/block-stream/solana/shred-stream">Shred Stream</a></li><li><a href="/streams/block-stream/solana/geyser-stream">Geyser Stream</a></li><li><a href="/streams/network-fee-stream/solana/get-transactionfee">Network Fee Stream</a></li></ul></td></tr></tbody></table>

### BSC

<table><thead><tr><th width="229.67578125">Service</th><th>Index</th></tr></thead><tbody><tr><td>Transaction Submission</td><td><ul><li><a href="/transaction-submission/rpc/bsc/integration">RPC</a></li><li><a href="/transaction-submission/block-builder">Block Builder</a></li><li><a href="/transaction-submission/transaction-sending/bsc/broadcast-tx">Transaction Sending</a></li><li><a href="/transaction-submission/gas-sponsor">Gas Sponsor</a></li></ul></td></tr><tr><td>Streams</td><td><ul><li><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a></li><li><a href="/streams/private-mempool">Private Mempool</a></li><li><a href="/streams/block-stream/bsc/newblocks">Block Stream</a></li><li><a href="/streams/node-stream/bsc/full-node-synchronization">Node Stream</a></li><li><a href="/streams/network-fee-stream/bsc/getgaspricestream">Network Fee Stream</a></li></ul></td></tr></tbody></table>

### Robinhood Chain

<table><thead><tr><th width="239">Service</th><th>Index</th></tr></thead><tbody><tr><td>Transaction Submission</td><td><ul><li><a href="/transaction-submission/transaction-sending/robinhood-chain/eth_sendrawtransaction">Transaction Sending</a></li></ul></td></tr><tr><td>Streams</td><td><ul><li><a href="/streams/node-stream/robinhood-chain/sequencer-feed">Node-required Sequencer Feed</a></li><li><a href="/streams/node-stream/robinhood-chain/direct-sequencer-feed">Direct Sequencer Feed</a></li></ul></td></tr></tbody></table>

### Ethereum

<table><thead><tr><th width="241.84765625">Service</th><th>Index</th></tr></thead><tbody><tr><td>Transaction Submission</td><td><ul><li><a href="/transaction-submission/rpc/ethereum/integration">RPC</a></li><li><a href="/transaction-submission/transaction-sending/ethereum/broadcast-tx">Transaction Sending</a></li><li><a href="/transaction-submission/gas-sponsor">Gas Sponsor</a></li></ul></td></tr><tr><td>Streams</td><td><ul><li><a href="/streams/public-mempool/ethereum/public-mempool">Public Mempool</a></li><li><a href="/streams/block-stream/ethereum/newblocks">Block Stream</a></li><li><a href="/streams/node-stream/ethereum/cl-el-client-sync">Node Stream</a></li></ul></td></tr></tbody></table>

### Base

<table><thead><tr><th width="240.02734375">Service</th><th>Index</th></tr></thead><tbody><tr><td>Transaction Submission</td><td><ul><li><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction-tip">RPC</a></li><li><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction">Transaction Sending</a></li></ul></td></tr><tr><td>Streams</td><td><ul><li><a href="/streams/block-stream/base/get-blockstream">Block Stream</a></li></ul></td></tr></tbody></table>


# Start for Free

BlockRazor offers new registered users multi-mode transaction sending modes on Solana, BSC, Robinhood Chain, Etherem, and Base for free.

### RPC

<table><thead><tr><th width="125.16015625">Chain</th><th>Methods</th><th>Limit</th></tr></thead><tbody><tr><td>BSC</td><td><ul><li><a href="/transaction-submission/rpc/bsc/eth_sendrawtransaction"><code>eth_sendRawTransaction</code></a></li><li><a href="/transaction-submission/rpc/bsc/eth_sendbundle"><code>eth_sendBundle</code></a></li><li>Other JSON RPC methods</li></ul></td><td>-</td></tr><tr><td>Ethereum</td><td><ul><li><a href="/transaction-submission/rpc/ethereum/eth_sendrawtransaction"><code>eth_sendRawTransaction</code></a></li><li><a href="/transaction-submission/rpc/ethereum/eth_sendbundle"><code>eth_sendBundle</code></a></li><li>Other JSON RPC methods</li></ul></td><td>-</td></tr></tbody></table>

### Transaction Sending

<table><thead><tr><th width="169.95703125">Chain</th><th width="306.46484375">Methods</th><th width="227.57421875">Limit</th></tr></thead><tbody><tr><td>Solana</td><td><ul><li><a href="/transaction-submission/transaction-sending/solana/send-transaction"><code>Send Transaction</code></a> </li><li><a href="/transaction-submission/transaction-sending/solana/send-bundle"><code>Send Bundle</code></a></li><li><a href="/transaction-submission/transaction-sending/solana/send-batch"><code>Send Batch</code></a></li></ul></td><td>-</td></tr><tr><td>Robinhood Chain</td><td><ul><li><a href="/transaction-submission/transaction-sending/robinhood-chain/eth_sendrawtransaction"><code>eth_sendRawTransaction</code></a></li></ul></td><td><ul><li>1 Tx / 5s</li></ul></td></tr><tr><td>BSC</td><td><ul><li><a href="/transaction-submission/transaction-sending/bsc/broadcast-tx"><code>Broadcast Tx</code></a></li></ul></td><td><ul><li>TPS：10 Txs / 5s</li><li>Daily Tx Limit：10</li></ul></td></tr><tr><td>Ethereum</td><td><ul><li><a href="/transaction-submission/transaction-sending/ethereum/broadcast-tx"><code>Broadcast Tx</code></a></li></ul></td><td><ul><li>TPS：10 Txs / 5s</li><li>Daily Tx Limit：10</li></ul></td></tr><tr><td>Base</td><td><ul><li><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction"><code>eth_sendRawTransaction</code></a></li></ul></td><td><ul><li>1 Tx / 5s</li></ul></td></tr><tr><td>Base</td><td><ul><li><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction-tip"><code>eth_sendRawTransaction(tip)</code></a></li></ul></td><td><ul><li>Default to 10 TPS</li></ul></td></tr></tbody></table>

### Block Builder

<table><thead><tr><th width="125.484375">Chain</th><th width="288.10546875">Methods</th><th>Limit</th></tr></thead><tbody><tr><td>BSC</td><td><ul><li><a href="/transaction-submission/block-builder/send-bundle"><code>eth_sendBundle</code></a></li><li><a href="/transaction-submission/block-builder/send-privatetransaction"><code>eth_sendPrivateTransaction</code></a></li></ul></td><td>-</td></tr></tbody></table>


# Pricing

Subscribe to BlockRazor services, scale on demand, and strengthen your competitive edge.

### Personalized

{% tabs %}
{% tab title="BSC" %}

<table data-search="false"><thead><tr><th width="147.91796875">Service</th><th width="236.78125">Description</th><th width="164.94921875">Price</th><th width="112.015625">Action</th></tr></thead><tbody><tr><td><a href="/transaction-submission/transaction-sending/bsc/broadcast-tx">Broadcast Tx</a></td><td>Broadcast BSC transactions through a global high-speed network to reach block builders at ultra-low latency when every millisecond matters.</td><td>$50 / day<br>$500 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fast_tx&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a></td><td>See BSC pending transactions earlier and gain a decisive edge in smart-money tracking, sniping, copy trading, and backruns.</td><td>$30 / stream / day<br>$300 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_public_mempool&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/private-mempool">Private Mempool</a></td><td>Access exclusive private order flow as an early alpha signal for faster sniping, copy-trading and backrun opportunities.</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_private_mempool&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/block-stream/bsc/newblocks">Block Stream</a></td><td>Receive the latest BSC blocks and confirmed transactions with low latency so your strategies can react immediately.</td><td>$50 / stream / day<br>$500 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_block_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/node-stream/bsc/full-node-synchronization">Node Stream</a></td><td>Accelerate world-state synchronization of your BSC full node to keep state-dependent strategies ahead of your competitors.</td><td>$80 / enode / day<br>$800 / enode / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_enode&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/transaction-submission/block-builder/trace-bundle">Bundle Tracing &#x26; Explorer</a></td><td>Trace bundle status and execution results in one place to diagnose failures faster and improve every submission.</td><td>$150 / day<br>$1500 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_bundle_tracing&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/transaction-submission/block-builder/send-backbundle">0 Gwei</a></td><td>Place 0 gas transactions at the end of BSC blocks to unlock cost-efficient, Builder-native searcher strategies.</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_0_gwei&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/transaction-submission/block-builder/fast-submit">Fast Submit</a></td><td>Reach the BSC Builder faster and more reliably through a dedicated optimized channel—without rewriting your submission flow.</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fast_submit&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/public-mempool/bsc/tx-trace">Tx Trace</a></td><td>Visualize global transaction propagation and regional latency to pinpoint bottlenecks.</td><td>$20 / day<br>$200 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_tx_trace&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/transaction-submission/block-builder/call-bundle">Call Bundle</a></td><td>Simulate bundles before submission to catch reverts and parameter issues before they become costly on-chain failures.</td><td>$20 / day<br>$200 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_call_bundle&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/network-fee-stream/bsc/getgaspricestream">Network Fee Stream</a></td><td>Track BSC gas prices in real time to optimize every transaction for speed, cost, and inclusion probability.</td><td>$30 / day<br>$300 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fee_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Robinhood Chain" %}

<table><thead><tr><th width="168.30078125">Service</th><th width="252.80859375">Description</th><th width="151.01953125">Price</th><th width="104.97265625">Action</th></tr></thead><tbody><tr><td><a href="/transaction-submission/transaction-sending/robinhood-chain">Transaction Sending</a></td><td>Reach the FCFS Sequencer faster through optimized multi-region routing</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_rpc_send_tx&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/node-stream/robinhood-chain/sequencer-feed">Node-required Sequencer Feed</a></td><td>Low-latency, sequential block data via a local node, ideal for arbitrage,orderflow projects and quant trading.</td><td>$80 / stream / day<br>$800 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/node-stream/robinhood-chain/direct-sequencer-feed">Direct Sequencer Feed</a></td><td>Low-latency access to the latest blocks without running a node, ideal for sniping and copy trading.</td><td>$80 / stream / day<br>$800 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_direct_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Solana" %}

<table><thead><tr><th width="125.3828125">Service</th><th width="231.48046875">Description</th><th width="224.19921875">Price</th><th width="101.73828125">Action</th></tr></thead><tbody><tr><td><a href="/streams/block-stream/solana/shred-stream">Shred Stream</a></td><td>Capture raw shreds before full blocks form, giving sniping, copy-trading, and latency-sensitive strategies an earlier edge.</td><td>$50 / stream / day<br>$500 / stream / month</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_shreds_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/block-stream/solana/geyser-stream">Geyser Stream</a></td><td>Stream structured accounts, transactions, slots, and blocks via gRPC for effortless access to real-time Solana data.</td><td>5 TiB - $250 / month<br>10 TiB - $500 / month<br>50 TiB - $250 / month<br>100 TiB - $4750 / month<br>150 TiB - $6750 / month<br>200 TiB - $8500 / month<br>250 TiB - $10000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_geyser_stream&#x26;billing=month" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/network-fee-stream/solana/get-transactionfee">Network Fee Stream</a></td><td>Track Priority Fees and tips in real time to land Solana transactions faster without overpaying.</td><td>$30 / day<br>$300 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_network_fee_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Ethereum" %}

<table><thead><tr><th width="134.33984375">Service</th><th width="295.6328125">Description</th><th width="146.40234375">Price</th><th width="109.1015625">Action</th></tr></thead><tbody><tr><td><a href="/transaction-submission/transaction-sending/ethereum/broadcast-tx">Broadcast Tx</a></td><td>Broadcast Ethereum transactions at high speed without an additional tip for a fast submission experience.</td><td>$50 / day<br>$500 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_fast_tx&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/public-mempool/ethereum/public-mempool">Public Mempool</a></td><td>Capture Ethereum pending transactions earlier, giving your strategy more time to calculate, manage risk, and secure better execution.</td><td>$30 / day<br>$300 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_public_mempool&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/block-stream/ethereum/newblocks">Block Stream</a></td><td>Receive the latest Ethereum blocks and confirmations with low latency to keep trading, monitoring, and data systems up to date.</td><td>$50 / day<br>$500 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_block_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/node-stream/ethereum/cl-el-client-sync">Node Stream</a></td><td>Accelerate Ethereum CL/EL synchronization so your local node receives blocks and world-state updates faster.</td><td>$80 / day<br>$800 / client / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_enode&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}

{% tab title="Base" %}

<table><thead><tr><th width="123.83203125">Service</th><th width="282.56640625">Description</th><th width="170.31640625">Price</th><th width="100.3515625">Action</th></tr></thead><tbody><tr><td><a href="/streams/block-stream/base/get-flashblockstream">FlashBlock Stream</a></td><td>Access Base pre-confirmation updates approximately every 200ms and see on-chain changes before full blocks are formed.</td><td>$25 / stream / day<br>$250 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_flashblock&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/streams/block-stream/base/get-blockstream">Block Stream</a></td><td>Stream complete Base blocks via gRPC with low latency for reliable confirmation monitoring and block-level analysis.</td><td>$30 / stream / day<br>$300 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_blockstream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction">Transaction Sending</a></td><td>Send Base transactions faster and more reliably through global regional endpoints and optimized intercontinental routes.</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_rpc_send_tx&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}

{% tab title="General" %}

<table><thead><tr><th width="112.5078125">Service</th><th width="288.58203125">Description</th><th width="145.3046875">Price</th><th width="133.0546875">Action</th></tr></thead><tbody><tr><td>Dedicated Channel</td><td>Get dedicated technical consulting and tailored support to solve complex technical challenges faster.</td><td>$100 / day<br>$1000 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=common&#x26;serviceId=common_dedicated_channel&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Package

{% hint style="info" %}
Compared to purchasing personalized services, users can complete a package purchase at a lower price through the Package, including the BSC and Ethereum service, with a total subscription price of **$1250/month**. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a>
{% endhint %}

<table data-search="false"><thead><tr><th width="242.91015625">Service</th><th width="90.55078125">Quota</th><th>Description</th></tr></thead><tbody><tr><td><a href="/transaction-submission/transaction-sending/bsc/broadcast-tx">Broadcast Tx - BSC</a></td><td>1</td><td>Broadcast BSC transactions through a global high-speed network to reach block builders at ultra-low latency when every millisecond matters.</td></tr><tr><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool - BSC</a><br><a href="/streams/public-mempool/ethereum/public-mempool">Public Mempool - Ethereum</a></td><td>2</td><td>See pending transactions earlier and gain a decisive edge in smart-money tracking, sniping, copy trading, and backruns, with subscription quota shared across chains.</td></tr><tr><td><a href="/streams/block-stream/bsc/newblocks">Block Stream - BSC</a><br><a href="/streams/block-stream/ethereum/newblocks">Block Stream - Ethereum</a></td><td>2</td><td>Receive the latest blocks and confirmed transactions with low latency so your strategies can react immediately, with subscription quota shared across chains.</td></tr><tr><td><a href="/streams/node-stream/bsc/full-node-synchronization">Node Stream - BSC</a><br><a href="/streams/node-stream/ethereum/cl-el-client-sync">Node Stream - Ethereum</a></td><td>1</td><td>Accelerate world-state synchronization of your node to keep state-dependent strategies ahead of your competitors, with cross-chain sharing of synchronization quotas.</td></tr><tr><td><a href="/transaction-submission/block-builder/send-backbundle">0 Gwei</a></td><td>1</td><td>Place 0 gas transactions at the end of BSC blocks to unlock cost-efficient, Builder-native searcher strategies.</td></tr><tr><td><a href="/transaction-submission/block-builder/call-bundle">Call Bundle</a></td><td>1</td><td>Simulate bundles before submission to catch reverts and parameter issues before they become costly on-chain failures.</td></tr><tr><td><a href="/transaction-submission/block-builder/fast-submit">Fast Submit</a></td><td>1</td><td>Reach the BSC Builder faster and more reliably through a dedicated optimized channel—without rewriting your submission flow.</td></tr><tr><td><a href="/streams/public-mempool/bsc/tx-trace">Tx Trace</a></td><td>1</td><td>Visualize global transaction propagation and regional latency to pinpoint bottlenecks.</td></tr></tbody></table>

### Discount

The relationship between discounts and subscription periods is as follows:

<table><thead><tr><th width="314.41796875">Periods</th><th width="328.90234375">Discount</th></tr></thead><tbody><tr><td>1 month</td><td>-</td></tr><tr><td>3 months</td><td>5% off</td></tr><tr><td>6 months</td><td>10% off </td></tr><tr><td>9 months</td><td>15% off</td></tr><tr><td>12 months</td><td>20% off</td></tr></tbody></table>

### FAQ

<details>

<summary>Can the Navigator Package and Personalized Services be ordered at the same time?</summary>

They can be ordered at the same time.

</details>

<details>

<summary>What does the data stream limit refer to in the optional services?</summary>

Data stream service quota refers to the number of gRPC data streams that are allowed to connect. The quota is shared across multiple regions. For example, if you purchase one Public Mempool, only one data stream connection is allowed in all regions.

</details>

<details>

<summary>What does "shared quota" mean in the Navigator Package?</summary>

In the Navigator Package, Public Mempool, Block Stream, and Node Stream share data stream quotas on BSC and Ethereum. For example, if you purchase the Navigator Package and obtain 2 Public Mempool quotas, you are allowed to subscribe to a total of 2 data streams on BSC and Ethereum. If you have already subscribed to 2 BSC Public Mempools, you cannot subscribe to Public Mempools on Ethereum.

</details>


# How to Obtain Auth Token

When integrating with BlockRazor services, if "auth" is required in the request, please follow the steps below to obtain it

{% stepper %}
{% step %}
**Create Account**

[Sign up](https://blockrazor.io/#/register?redirect=onboarding) to create an account
{% endstep %}

{% step %}
**Log in**

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2Fo9R9Dl5ai192AeTH0gMk%2Fimage.png?alt=media&amp;token=b6f3d0dd-d69d-4201-91a7-56a44e8a6193" alt=""><figcaption></figcaption></figure>

Log in and obtain the auth token in the home page
{% endstep %}
{% endstepper %}


# Who Do We Serve

This analysis examines pain points from a user perspective and explains how to use BlockRazor's services, including wallets/DEXs, trading bots, searchers, quantitative trading systems

### Wallet

* [Wallet / DEX](/get-started/use-cases/wallet-dex)

### DEX

* [Wallet / DEX](/get-started/use-cases/wallet-dex)

### Sniper

* [Trading Bot](/get-started/use-cases/trading-bot)

### Copy Trading

* [Trading Bot](/get-started/use-cases/trading-bot)

### Algorithmic Trading

* [Quant Trading](/get-started/use-cases/quant-trading)

### Individual Trader

* [Individual Trader](/get-started/use-cases/individual-trader)


# Wallet / DEX

This section introduces the pain points of wallets/dexs in transaction sending scenarios and how to solve these pain points by using Blockrazor services, including RPC and Fast mode

In the current DeFi ecosystem, wallets andvDEXs are facing increasingly severe challenges in user retention. As the market matures, users are no longer satisfied with basic transaction fulfillment; their expectations for security, convenience, and real-time execution are rising. Furthermore, there is a growing interest in emerging trading models such as copy trading and token sniping.

### Pain Point Analysis

* MEV Attacks and Value Leakage: When swap transactions are submitted to standard RPC nodes, the user's intent is fully exposed in the public Mempool. MEV bots can monitor these transactions and employ "sandwich attacks" to exploit slippage, resulting in execution prices far worse than expected.
* Barriers to Native Token-less Transactions: New users withdrawing from CEXs or multi-chain players often face the dilemma of having no native tokens to pay for Gas fees. This interrupts the transaction flow and lowers conversion rates.
* On-chain Latency: During periods of network congestion or volatility, standard RPC nodes propagate transactions slowly, causing users to miss the optimal timing for on-chain inclusion.

### Services

BlockRazor provides targeted product services for wallets and DEXs to address the aforementioned pain points.

**MEV Protection**: To combat MEV attacks and value leakage, BlockRazor offers MEV-protected RPC services. Transactions are under full-privacy protection during the submission and inclusion process, being shielded from MEV bot monitoring and malicious attacks. Additionally, RPC service allows wallets and DEXs to securely disclose transaction data to earn real-time rebates. Currently, BlockRazor provides [MEV-protected RPC](/transaction-submission/rpc) for Ethereum and BSC.

**Gas Sponsor**: To solve the issue of native token shortages, BlockRazor provides [Gas Sponsor ](/transaction-submission/gas-sponsor)service. This allows users to perform swaps without paying any native blockchain currency (e.g., ETH, BNB, SOL). For wallets and DEXs, integrating Gas Sponsor brings additional benefits:

* Attract Incremental Traffic: Gas Sponsor serves as a powerful branding and marketing tool to attract new traffic and boost overall trading activity.
* Increase Revenue: By enabling more users to complete swaps, the overall volume increases, generating higher swap fee revenue for the platform.

Currently, BlockRazor provides Gas Sponsor services for Ethereum, BSC, and Solana.

**Transaction Sending Mode**: To address slow on-chain speeds, BlockRazor offers "[Transaction Sending Mode](/transaction-submission/transaction-sending)", which utilizes a globally accelerated, high-performance network to achieve the lowest possible latency for transaction inclusion—ideal for global user bases with extreme speed requirements.


# Trading Bot

This section introduces the pain points of Trading Bots in signal listening and transaction sending scenarios, and how to use Blockrazor to extend and accelerate signal listening and transactions

In onchain trading, a Trading Bot's competitiveness depends not only on its strategy, but also on two critical dimensions of speed:

1. **Signal monitoring speed**: How early the bot can detect a new pool, market-opening event, or target-wallet transaction.
2. **Transaction submission speed**: How quickly the bot can deliver a transaction to a Leader, Builder, Validator, or Sequencer.

The complete transaction path is as follows:

> Onchain event → Signal monitoring and parsing → Strategy evaluation → Transaction construction and signing → Transaction submission → Ordering node receives the transaction → Onchain inclusion

Signal monitoring determines when a bot can act, while transaction submission determines when its transaction reaches the chain. Latency at either stage can eliminate the strategy's original advantage.

### Typical Trading Bot Scenarios

#### **Sniper**

A Sniper Bot monitors new token deployments, new liquidity pools, market-opening transactions, or other predefined events. Once a target signal appears, it immediately constructs and submits a transaction.

Its primary objective is to discover trading opportunities earlier, enter the target block or slot, and secure a more favorable ordering position.

#### **Copy Trading**

A Copy Trading Bot monitors the buys, sells, or position changes of a designated Leader wallet. It then constructs a Follower transaction according to predefined amounts, ratios, and strategy rules.

Its primary objective is to reduce the inclusion-time gap between the Leader and Follower and minimize their execution-price difference.

Sniper and Copy Trading Bots monitor different targets: the former focuses on new pools, market openings, and contract events, while the latter focuses on designated wallet activity. However, both depend on the same underlying path: receiving signals, parsing signals, constructing transactions, and submitting them quickly.

### Trading Bot Pain Points

#### **Pain Point 1: Slow Signal Monitoring**

**Waiting for complete blocks delays signals**

Standard RPC or WebSocket services often deliver transaction data only after a node has received, processed, or even reconstructed a block.

This can mean:

* A competitor has already submitted a transaction when a Sniper Bot detects a new pool.
* The market price has already changed when a Copy Trading Bot detects the Leader's transaction.
* The original opportunity decays while the bot waits for data.

**Public nodes introduce longer data paths**

Public nodes may introduce cross-region transmission, multiple proxy layers, shared-resource queues, rate limits during peak traffic, and connection jitter.

Even a difference of tens of milliseconds can directly affect the final execution position in competitive Sniper and Copy Trading scenarios.

**A single data source provides limited signal coverage**

On chains that support Pending Transactions, public and private transactions may propagate through different paths:

* Public Mempool mainly covers publicly broadcast Pending Transactions.
* Private transactions may not appear in the Public Mempool.
* Confirmed onchain signals offer greater certainty but arrive later.

A Trading Bot should select Pending data, private order flow, or block data according to its target chain and strategy.

#### **Pain Point 2: Slow Transaction Submission**

Receiving a signal first does not guarantee execution first. After strategy evaluation, a transaction may still pass through a public RPC, proxy layers, cross-region networks, and intermediate relay nodes.

These stages consume the lead gained during signal monitoring and can cause:

* A Sniper Bot to miss the target block or slot.
* A Copy Trading Bot to land several blocks behind the Leader.
* A Follower to receive a materially worse execution price than the Leader.
* A transaction to fail because the market state has changed.

The destination that must receive a transaction quickly differs by chain:

<table><thead><tr><th width="170.10546875">Chain</th><th width="211.27734375">Transaction Destination</th><th>Primary Speed Consideration</th></tr></thead><tbody><tr><td>Solana</td><td>Current and upcoming Leaders</td><td>Leader routing and SWQoS transmission</td></tr><tr><td>Ethereum</td><td>Builder / Validator</td><td>Faster entry into propagation and block-building paths</td></tr><tr><td>BSC</td><td>Builder / Validator</td><td>Reaching the target block's processing window in time</td></tr><tr><td>Base</td><td>Sequencer</td><td>Shortening the transaction path to the Sequencer</td></tr><tr><td>Robinhood Chain</td><td>Official Sequencer</td><td>Arrival time under FCFS ordering</td></tr></tbody></table>

Note: Robinhood Chain uses First-Come, First-Served ordering. Transaction order depends on arrival time at the Sequencer, and a higher fee cannot move a transaction ahead of one that arrived earlier. The submission path is therefore a direct competitive variable.

### Recommended Services

BlockRazor provides low-latency infrastructure for Sniper and Copy Trading Bots across both signal monitoring and transaction submission.

#### Signal Monitoring Services

<table><thead><tr><th width="151.9765625">Chain</th><th width="207.79296875">Recommended Services</th><th>Differences and Selection Guidance</th></tr></thead><tbody><tr><td>Solana</td><td><a href="/streams/block-stream/solana/shred-stream">Shred Stream</a><br><a href="/streams/block-stream/solana/geyser-stream">Geyser Stream</a></td><td>Shred Stream transmits shreds before complete block reconstruction; Geyser Stream provides structured transaction and account data.</td></tr><tr><td>Ethereum</td><td><a href="/streams/public-mempool/ethereum/public-mempool">Public Mempool</a><br><a href="/tc/streams/block-stream/ethereum/newblocks">Block Stream</a></td><td>Public Mempool monitors publicly broadcast Pending Transactions; Block Stream monitors confirmed onchain signals.</td></tr><tr><td>BSC</td><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a><br><a href="/streams/private-mempool">Private Mempool</a><br><a href="/streams/block-stream/bsc/newblocks">Block Stream</a></td><td>Public and Private Mempool complement each other to provide earlier Pending signals; Block Stream monitors confirmed onchain signals.</td></tr><tr><td>Base</td><td><a href="/streams/block-stream/base/get-flashblockstream">FlashBlock Stream</a><br><a href="/streams/block-stream/base/get-blockstream">Block Stream</a></td><td>FlashBlock Stream provides preconfirmation data before a complete block is formed; Block Stream provides complete block data.</td></tr><tr><td>Robinhood Chain</td><td><a href="/streams/node-stream/robinhood-chain/sequencer-feed">Sequencer Feed</a></td><td>Proximity-based access and optimized transmission paths provide faster and more stable delivery of block data pushed by the Sequencer.</td></tr></tbody></table>

> Sniper and Copy Trading Bots can use the same monitoring services. The difference lies in the signals each bot filters and parses.

#### Transaction Submission Services

<table><thead><tr><th width="149.85546875">Chain</th><th width="228.19140625">Recommended Services</th><th>Differences and Selection Guidance</th></tr></thead><tbody><tr><td>Solana</td><td><a href="/transaction-submission/transaction-sending/solana/send-transaction">Send Transaction</a></td><td>Uses a global high-performance network and SWQoS paths to deliver transactions quickly to current and upcoming Leaders.</td></tr><tr><td>Ethereum</td><td><a href="/transaction-submission/transaction-sending/ethereum/broadcast-tx">Broadcast Tx</a></td><td>Optimized for maximum broadcast speed but does not provide MEV protection. Use BlockRazor RPC when MEV protection is required.</td></tr><tr><td>BSC</td><td><p><a href="/transaction-submission/transaction-sending/bsc/broadcast-tx">Broadcast Tx</a></p><p><a href="/transaction-submission/block-builder/fast-submit">Fast Submit</a></p></td><td>Broadcast Tx provides maximum transaction broadcast speed but no MEV protection; use BlockRazor RPC when MEV protection is required. Fast Submit uses a dedicated submission entry point and optimized cross-region paths to shorten the route to the BlockRazor Builder.</td></tr><tr><td>Base</td><td><a href="/transaction-submission/transaction-sending/base/eth_sendrawtransaction">eth_sendRawTransaction</a></td><td>Provides standardized, multi-region transaction submission for quickly delivering signed transactions to the Base Sequencer.</td></tr><tr><td>Robinhood Chain</td><td><a href="/transaction-submission/transaction-sending/robinhood-chain/eth_sendrawtransaction">eth_sendRawTransaction</a></td><td>Uses multi-region entry points and optimized routing to reach the official FCFS Sequencer faster.</td></tr></tbody></table>

> Signal monitoring services determine when a bot can act. Transaction submission services determine when the transaction reaches the ordering node. Prefer a regional endpoint close to the bot's deployment location.

### Benchmark

BlockRazor has published the following relevant performance tests:

<table data-search="false"><thead><tr><th width="116.55859375">Chain</th><th width="169.1484375">Service</th><th>Benchmark</th><th>What It Measures</th></tr></thead><tbody><tr><td>Solana</td><td>Shred Stream</td><td><a href="https://blockrazor.io/blog/20250818shredbenchmark/">Solana Shred Stream Benchmark</a></td><td>Compares the first-arrival rate and arrival-time difference of BlockRazor and Jito shreds across multiple regions.</td></tr><tr><td>Solana</td><td>Send Transaction</td><td><a href="https://blockrazor.io/blog/20250801Benchmarking/">Benchmarking Solana Send Transaction Service</a></td><td>Uses consistent Tips, Priority Fees, and Durable Nonce transactions to compare transaction races over SWQoS paths.</td></tr><tr><td>BSC</td><td>Fast Submit</td><td><a href="https://blockrazor.io/blog/20260625BSC-Builder-Fast-Submit/">BSC Fast Submit Benchmark</a></td><td>Measures submission-latency improvements from fewer proxies, network hops, and optimized cross-region paths.</td></tr><tr><td>Base</td><td>RPC<br>Block Stream<br>FlashBlock Stream</td><td><a href="https://blockrazor.io/blog/20250922basebenchmark/">Base Benchmark</a></td><td>Compares transaction ordering positions and the data-arrival latency of Block Stream and FlashBlock Stream.</td></tr><tr><td>Robinhood Chain</td><td>Sequencer Feed</td><td><a href="https://docs.blockrazor.io/streams/node-stream/robinhood-chain/sequencer-feed">Sequencer Feed Benchmark</a></td><td>Compares first-arrival rates and latency distributions between BlockRazor and the official Feed across three AWS Ohio Availability Zones.</td></tr><tr><td>Robinhood Chain</td><td>Robinhood Chain RPC</td><td><a href="https://www.blockrazor.io/blog/RobinhoodChainRPC/">Robinhood Chain RPC Benchmark</a></td><td>Uses same-nonce transactions to compare the competitive inclusion rates of BlockRazor RPC and the official RPC across multiple regions.</td></tr></tbody></table>


# Searcher

This section discusses the pain points of Searcher in handling bundles and how to use Blockrazor services to enhance competitiveness.

As competition becomes increasingly fierce, Searchers can no longer win solely with Alpha strategies; they must also possess Beta competitiveness to further improve their chances of success.

What is Beta competitiveness? If we liken the competition among Searchers to a war, Alpha strategies are the strategies and tactics, while Beta competitiveness represents the infrastructure such as equipment and supplies, which are prerequisite for competition.

Beta competitiveness consists of speed, inclusion certainty, and cost, which helps to increase Searcher’s chances of winning from different aspects.

### Speed

The time interval of block production is fixed. To send the bundle to the Builder in time on the assembly line, the Searcher needs to minimize the time spent processing the bundle. The main steps for the Searcher to process a bundle are as follows:

1. Subscribe to target transactions and synchronize the latest block.
2. Based on the world state of the latest block, calculate the arbitrage opportunities for each transaction according to the Alpha strategy.
3. Filter the arbitrage opportunities and assemble the selected target transactions and strategy transactions into a bundle in a specified order.
4. Send the bundle to the Builder.

From the steps mentioned above, the time the Searcher takes to process the bundle is composed of the lantency of transaction subscription, the arbitrage calculation time, and the lantency of bundle sending to builders.

Furthermore, synchronizing the latest world state is a prerequisite for calculating arbitrage opportunities (calculating based on old state would distort the arbitrage action and lose the competitiveness of the Alpha strategy). If the state synchronization lantency is high, it will severely compress the time available for arbitrage calculation, especially during periods of network congestion, which can put immense pressure on computational performance.

In the last step of the bundle processing, the Searcher need to submit the bundle consisting of the target transaction and the arbitrage transaction to the mainstream Builders, ensuring that the bundle is included in all blocks participating in the auction, to increase the speed of the bundle inclusion.

### Cost

Cost directly affects the profit of a Searcher. Searchers that are evenly matched in terms of Alpha strategy, speed, and inclusion certainty may not differ much in capturing arbitrage opportunities and calculating arbitrage space. However, if the average cost per arbitrage opportunity varies significantly, over the long term, Searchers with higher costs will gradually become less competitive due to lower profits.

The costs for a Searcher mainly include the execution cost of the Alpha strategy, the cost of improving speed, the cost of enhancing inclusion certainty, and transaction fees. Increasing costs can improve Beta competitiveness, but it also impacts profits. Therefore, it is essential to prioritize and make appropriate trade-offs.

#### **Execution Cost of The Alpha Strategy**

The execution of Alpha strategies incurs computational costs. Since the Alpha strategy is the core competitive advantage of a Searcher, and its execution speed also directly affects the processing time of bundles, this cost cannot be reduced.

#### **Cost of Speed Improvement**

Improving speed invloves cost of development and server. To reduce transaction subscription latency, bundle sending latency, and the latency in synchronizing the latest world state, it is necessary to develop algorithms and build a high-speed backbone network. This is too costly for a Searcher, so utilizing established third-party services is a cost-effective choice.

Enhancing inclusion certainty primarily involves the cost of removing Builder rate limits. Searchers can attempt to freely integrate with RPCs that allow sending [bundles](/transaction-submission/rpc/bsc/orderflow-auction), which will forward the bundles to mainstream Builders. This approach not only saves costs but also ensures speed and inclusion certainty of transactions.

#### **Transaction Fee**

When calculating the arbitrage opportunity space, only when the arbitrage space minus the transaction fee is greater than 0 is it considered an arbitrage opportunity. If transaction fees can be reduced to 0, then even the arbitrage opportunities generated by smallest market fluctuations can be captured.

Trading at 0 gwei not only saves transaction costs but also greatly expands the range of arbitrage opportunities that the Alpha strategy can capture. Currently, Builders on BSC support receiving transactions at 0 gwei. For Searchers whose Alpha strategy focuses on expanding the range of arbitrage opportunities, they should try to send transactions at 0 gwei as much as possible to eliminate this cost.

### How does BlockRazor enhance the Beta competitiveness?

#### Speed

BlockRazor provides Searchers with [Public Mempool](/streams/public-mempool/bsc/public-mempool) and [Block Stream](/streams/block-stream/bsc/newblocks) services, enabling them to subscribe to transactions and synchronize blocks with extremely low latency.

Benchmark shows that compared to the industry-leading high-performance network provider bloXroute, BlockRazor can receive the latest transactions with lower latency. Detailed data comparisons are as follows:

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2F9XGrscx2XaO8YIbYSOTz%2Fimage.png?alt=media&amp;token=a2290ce6-a142-4eb7-b250-d239caa51694" alt="" width="563"><figcaption></figcaption></figure>

Additionally, BlockRazor also demonstrates excellent performance in terms of latency of synchronization of the latest world state, better meeting the needs of speed-sensitive users. For the complete comparison results, please refer to the [benchmark](https://medium.com/@blockrazor/to-build-the-competitive-high-performance-network-d7c0705b5171).

Searchers can directly integrate with [Bundle](/transaction-submission/rpc/bsc/orderflow-auction), constructing a bundle consisting of mempool transactions and strategy transactions and submitting it to BlockRazor RPC, which will forward it to mainstream Builders at the earliest opportunity. Additionally, based on a globally distributed network, BlockRazor RPC achieves end-to-end low-latency forwarding at the network level.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2Fk384Kehz2eLcCPP7wYb5%2Fimage.png?alt=media&amp;token=2dd43255-1e09-4a50-ad8d-49a587ff9028" alt=""><figcaption><p>The benchmark client sends transactions to BlockRazor RPC, A RPC, and B RPC, recording the difference between the latest block number at the time of sending the transaction and the block number in which the transaction is included. A smaller difference indicates a faster on-chain speed.</p></figcaption></figure>

The chart data shows that transactions submitted to Scutum have a probability of being included in the next block as high as 95%, and 100% probability being included in the next 2 blocks, which significantly surpasses that of competitors.

#### Cost

BlockRazor's subscription plans are highly competitive in terms of pricing, and Searchers are allowed to send 0 gwei transactions to the Builder with the highest block production rate on BSC. Additionally, Searchers can subscribe to the [Private Mempool](/streams/private-mempool), and by executing backrun strategies, they can further expand the range of arbitrage opportunities. The profits generated from this can be used to subsidize the costs associated with the subscription plan.

### How to use BlockRazor Service

#### **Arbitrage of Private Mempool**

1. [Register](https://www.blockrazor.io/#/register) for BlockRazor
2. [Log in](https://www.blockrazor.io/#/login) to BlockRazor, purchase Private Mempool, and go to the account module to obtain the auth token
3. Subscribe to the [Private Mempool](/streams/private-mempool)
4. Execute the arbitrage strategy, and submit bundle to BlockRazor RPC using [Bundle](/transaction-submission/rpc/bsc/orderflow-auction)

#### **Arbitrage of Public Mempool**

1. [Register](https://www.blockrazor.io/#/register) for BlockRazor
2. [Log in](https://www.blockrazor.io/#/login) to BlockRazor, purchase Public Mempool, and go to the account module to obtain the auth token
3. Integrate the [<mark style="color:blue;">Public Mempool</mark>](/streams/public-mempool/bsc/public-mempool) to subscribe to the latest transactions with low latency; synchronize blocks with low latency through [Node Stream](/streams/node-stream/bsc/full-node-synchronization) if you have local node.
4. Execute the arbitrage strategy, and submit bundle to BlockRazor RPC using [Bundle](/transaction-submission/rpc/bsc/orderflow-auction).

#### **0 Gwei Transaction at End of Block**

1. [Register](https://www.blockrazor.io/#/register) for BlockRazor
2. [Log in](https://www.blockrazor.io/#/login) to BlockRazor, purchase package, and go to the account module to obtain the auth token
3. Join [Discord](https://discord.com/invite/qqJuwRb8Nh) to contact, integrate with the Block Builder's API of 0 Gwei Transaction at End of Block

### FAQ

<details>

<summary><strong>What is the difference between submitting a Bundle to an RPC and submitting a Bundle to a Block Builder?</strong></summary>

The core difference between the two lies in the different submission paths and final destinations of the Bundle.

When submitting a Bundle to BlockRazor RPC, BlockRazor RPC forwards the Bundle to mainstream builders with low latency. This approach is more suitable as a unified access point, allowing users to submit Bundles without having to connect to different builders individually.

When you submit a Bundle to Block Builder, the Bundle is sent directly to BlockRazor Builder. This is more suitable for scenarios where you explicitly want to use BlockRazor Builder capabilities and the access path.

</details>

<details>

<summary><strong>How to understand that the average gas price of a bundle must not be less than 0.05 gwei?</strong></summary>

Assume a bundle contains three transactions: `{tx1, tx2, tx3}`. Since `tx1` comes from the mempool, BlockRazor Builder excludes it and calculates the average gas price using only `tx2` and `tx3`. If `tx3` includes an additional tip paid to the Builder, the tip amount is added to the numerator, while the denominator still includes only the gas used by `tx2` and `tx3`. The formula is:

`(tx2.gasPrice × tx2.gasUsed + tx3.gasPrice × tx3.gasUsed + tx3.tip) / (tx2.gasUsed + tx3.gasUsed)`

</details>


# Quant Trading

This section introduces the pain points of quantitative trading systems and explains how to use Blockrazor services to improve on-chain certainty.

DEX-CEX Arbitrage is a low-risk quantitative trading strategy. Due to factors such as liquidity, market mechanisms, and trading speed, certain trading pairs may have price differences at the same time on different exchanges. The arbitrage strategy involves buying assets on the exchange with lower prices and selling them on the exchange with higher prices, thereby earning the price difference.

Although DEX-CEX arbitrage trading is relatively low-risk, its stable profitability can still be influenced by several factors, with "certainty" being a crucial one.

### Certainty of Price Difference

The price difference determines the arbitrage space, and the price difference is determined by the prices of the target token on both CEX and DEX. Price difference certainty refers to ensuring that a price difference exists for the target token while the price on both CEX and DEX are changing continuously.

#### Certainty of DEX Price

Quantitative trading strategies can use DEX aggregator APIs (such as 1inch) to query prices from multiple DEXs. However, this approach may involve data query lantency, and APIs often have rate limits that require payment to lift. Additionally, they may not be compatible with emerging or niche DEXs, making it impossible to retrieve prices.

Another method is to deploy a full node oneself, synchronize the latest blocks, and directly query the token prices from DEX contracts. This approach can solve the rate limiting and compatibility issues. To reduce data query lantency, it is necessary to synchronize the latest blocks at the earliest opportunity.

#### Certainty of CEX Price

Unlike DEX prices, which are updated per block, the prices of token on CEXs are in continuous real-time flux, which poses a risk.

Suppose the chain where the DEX is located produces a block every 3 seconds. The strategy detects a price difference signal at 1000ms and completes the construction and sending of the DEX transaction at 1600ms. The remaining 1400ms is a period during which the price changes of token on the CEX are beyond the control. If the CEX price experiences significant fluctuations during this time, erasing the price difference or even causing a negative price difference, the strategy will face a loss.

To eliminate risk of price difference and enhance the certainty of CEX prices, the time period that the quantitative strategy cannot control needs to be minimized. In other words, the quantitative strategy needs to send transactions at the last possible moment of the block to ensure that the transaction can be included in the current block while still leaving room for profit.

### Certainty of Inclusion

After confirming that a price difference exists for the target token, the arbitrage strategy will place orders simultaneously on both CEX and DEX. Unlike CEXs, which use order book matching mechanisms, DEX transactions may not be included in the next block, leading to the price difference being erased. Enhancing the inclusion certainty of DEX transactions is also key to the strategy's profitability.

Improving the inclusion certainty of DEX transactions can also be understood as increasing the probability that DEX transactions will be included in the next block. For more details, see the [Analysis of Swap Transaction Elements](https://www.blockrazor.io/#/blogs/20250331swap). For quantitative trading systems that focus on strategy, it is recommended to directly integrate with a professional RPC to increase transaction inclusion rate, thus investing time and energy into the core strategy.

### How does BlockRazor enhance “Certainty”?

To enhance DEX price certainty, a quantitative trading system can use the [Node Stream](/streams/node-stream/bsc/full-node-synchronization) to synchronize the latest blocks obtain the latest DEX trading pair prices at the earliest opportunity.

For CEX price certainty, a quantitative trading system can establish a direct connection with the BlockRazor Builder to send transactions with extremely low latency at the "last moment" of a block, minimizing the risk of CEX price fluctuations. BlockRazor Builder is the top block producer on BSC. If you are interested in establishing a direct connection with BlockRazor Builder, please [contact](https://discord.com/invite/qqJuwRb8Nh) us.

To enhance inclusion certainty, a quantitative trading system can choose to submit strategy transactions to BlockRazor RPC. Based on a globally distributed network, BlockRazor RPC can receive transactions from clients with extremely low latency and then forward these transactions to geographically nearby Builders, which achieves end-to-end low-latency forwarding at the network level and improves the transaction inclusion rate.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2F9hDxSegamNMcYtiG34Gm%2Fimage.png?alt=media&amp;token=e44c41e9-2350-45fc-a1b2-222f48f90e7d" alt=""><figcaption><p>The benchmark client sends transactions to BlockRazor RPC, A RPC, and B RPC, recording the difference between the latest block number at the time of sending the transaction and the block number in which the transaction is included. A smaller difference indicates a faster on-chain speed.</p></figcaption></figure>

The chart data shows that transactions submitted to BlockRazor RPC have a probability of being included in the next block as high as 95%, and 100% probability being included in the next 2 blocks, which significantly surpasses that of competitors.

### How to Use BlockRazor

#### Node Stream

1. [Register](https://www.blockrazor.io/#/register) for BlockRazor
2. [Log in](https://www.blockrazor.io/#/login) to BlockRazor and subscribe to the Tier2 or Tier1 plan
3. Configure the full node, for details, see [here](https://blockrazor.gitbook.io/blockrazor/tc/gao-xing-neng-wang-luo-fu-wu/bsc/quan-jie-dian-tong-bu)

#### Integrate Scutum RPC

BlockRazor RPC will generate a dedicated RPC URL for quantitative trading systems registered with BlockRazor. The dedicated RPC supports same JSON RPC methods as other Public RPC. The specific steps are as follows:

1. [Register](https://www.blockrazor.io/#/register) for BlockRazor
2. [Login](https://www.blockrazor.io/#/login) to the portal of BlockRazor
3. Go to the RPC module to view your dedicated RPC (currently supporting Ethereum and BSC). You can configure RPC parameters with one click, and then copy the RPC URL.
4. Go to the project of quantitative trading system, locate the chain's RPC configuration file, and replace the default endpoint with the dedicated RPC URL.
5. Update and publish the project of quantitative trading system(optional).
6. Execute the strategy by submitting transactions via `eth_sendRawTransaction` to the RPC. If the strategy involves multiple transactions, submit them as a Bundle to the RPC.
7. Log in to the portal of BlockRazor to check the refund and transactions.

#### Direct Connection to Builder

* If you need to establish a direct connection with the BlockRazor Builder, please go to [Discord](https://discord.com/invite/qqJuwRb8Nh) to get in touch with us.


# Individual Trader

This section addresses the pain points of individual traders and explains how to add BlockRazor RPC to your wallet.

{% hint style="info" %}
Individual users can use general RPC without subscribing to a plan.
{% endhint %}

For users who frequently trade on DEXs, there is a high probability that their trades will execute at the maximum slippage limit, resulting in "invisible" financial losses. Individual traders can now [add](#how-to-add-rpc-to-my-wallet) the BlockRazor RPC to their wallets with a single click. From then on, all initiated swap transactions will benefit from the deep protection of the BlockRazor RPC, reducing slippage losses while offering the opportunity to receive real-time transaction rebates. Currently, BlockRazor supports RPC integration for individual traders on Ethereum and BSC.

Advanced users can choose to [add different modes of the RPC](#how-to-add-custom-rpc-to-wallet) according to your needs. If you have any questions, please contact us on [Discord](https://discord.com/invite/qqJuwRb8Nh).

### Add RPC to your wallet with one click

{% hint style="info" %}
Currently, only Metamask and OKX wallets support one-click addition. The following steps take adding BSC RPC to OKX wallet as an example.
{% endhint %}

{% stepper %}
{% step %}
Go to [http://blockrazor.io/rpc/?chain=bsc](http://127.0.0.1:4186/rpc/?chain=bsc)
{% endstep %}

{% step %}
Select RPC, click "Connect wallet to add", select your wallet to complete the linking.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FuF4IAZeruSNFdi7HqfF2%2Fimage.png?alt=media&amp;token=0d7ec620-7250-41fc-8361-803488d3bde9" alt="" width="563"><figcaption></figcaption></figure>
{% endstep %}

{% step %}
Click "Add this RPC" to Approve its addition.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FErfSDZi9xdMP0zJin3vn%2Fimage.png?alt=media&amp;token=89df9e62-b806-4cc3-8ab7-4a43e95786aa" alt="" width="350"><figcaption></figcaption></figure>
{% endstep %}

{% step %}
RPC added successfully

{% hint style="info" %}
For Metamask wallets, you need to manually switch networks after successfully adding them via RPC.
{% endhint %}

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FREBgbJSd1fMh5fUbCEee%2Fimage.png?alt=media&amp;token=6aa0e558-180b-45d1-b31b-ce181eecf752" alt="" width="375"><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

### Manually add RPC to wallet

{% stepper %}
{% step %}
Open OKX Wallet, open the network.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FzF9tgpqxsy3GZDhCweFV%2Fimage.png?alt=media&amp;token=ed7d0639-e631-4051-b774-ec6ad98191d7" alt="" width="351"><figcaption></figcaption></figure>

{% endstep %}

{% step %}
點擊 Add custom network，輸入[網絡信息](#tong-yong-rpc-wang-luo-xin-xi)Click "Add custom network" and enter the [network information](#network-information).

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FJcg71piCHi9n2yrEb4Wz%2Fimage.png?alt=media&amp;token=2375e29e-5219-4d4b-8dfe-c26be6f232ac" alt="" width="346"><figcaption></figcaption></figure>

{% endstep %}

{% step %}
Click Save to complete adding the network.
{% endstep %}
{% endstepper %}

### Network Information

<table><thead><tr><th width="200"></th><th>Ethereum</th><th>BSC</th></tr></thead><tbody><tr><td>Network Name</td><td>Ethereum Mainnet</td><td>BNB Smart Chain Mainnet</td></tr><tr><td>RPC URL</td><td>https://eth.blockrazor.xyz</td><td>https://bsc.blockrazor.xyz</td></tr><tr><td>Chain ID</td><td>1</td><td>56</td></tr><tr><td>Currency Symbol</td><td>ETH</td><td>BNB</td></tr><tr><td>Block Explorer URL</td><td><a href="https://etherscan.io">https://etherscan.io</a></td><td><a href="https://bscscan.com">https://bscscan.com</a></td></tr></tbody></table>

### Comparison of RPC

<table data-search="false"><thead><tr><th width="146"></th><th>default</th><th>fullprivacy</th><th>maxbackrun</th></tr></thead><tbody><tr><td>BSC</td><td>https://bsc.blockrazor.xyz</td><td>https://bsc.blockrazor.xyz/fullprivacy</td><td>https://bsc.blockrazor.xyz/maxbackrun</td></tr><tr><td>Ethereum</td><td>https://eth.blockrazor.xyz</td><td>https://eth.blockrazor.xyz/fullprivacy</td><td>https://eth.blockrazor.xyz/maxbackrun</td></tr><tr><td>MEV Protection</td><td>Protected</td><td>Protected</td><td>Protected</td></tr><tr><td>Transaction Privacy</td><td>Minimal Disclosure</td><td>Full Privacy</td><td>Maximum Disclosure</td></tr><tr><td>Refund Probability</td><td>Moderate</td><td>No refunds</td><td>High</td></tr><tr><td>Refunds</td><td>Supported</td><td>No refunds</td><td>Supported</td></tr><tr><td>Revert Protection</td><td>Not Protected</td><td>Protected</td><td>Protected</td></tr></tbody></table>

**default**

* In default mode, transactions submitted to MEV Protect RPC General for Wallet User only disclose necessary transaction data (hash, logs & state) to Searcher to win refund opportunities while protecting transaction privacy to the greatest extent. At the same time, in order to ensure the speed of transactions being included in blocks, transactions are not under revert protection  (which is consistent with the default RPC of wallet).&#x20;

**fullprivacy**

* In fullprivacy mode, transactions submitted to MEV Protect RPC General for Wallet User will not disclose any transaction data, and Scutum will directly forward the transaction to the mainstream builders. Transactions in this mode are under revert protection. In order to ensure the speed of inclusion in blocks, it is recommended to set priority fee (Ethereum) when sending transactions.

**maxbackrun**

* In the maxbackrun mode, transactions submitted to MEV Protect RPC General for Wallet User will disclose necessary transaction data (hash, to, calldata, functionSelector, logs & state) under the premise of MEV protection to maximize the possibility of obtaining refunds. Transactions are under revert protection. In order to ensure the speed of inclusion in blocks, it is recommended to set priority fee (Ethereum) when sending transactions.


# Streams Overview

Introducing BlockRazor's Streams, the capabilities Streams provide, and how to choose a Stream.

### What are Streams

Streams is a suite of high-performance, real-time data streaming services provided by BlockRazor to deliver low-latency data to trading systems, strategy systems, and infrastructure systems. Unlike Transaction Submission, Streams focuses on "how to see data earlier, how to synchronize state faster, and how to observe the network propagation process in more detail.

For Searcher, Trading Bot, Wallet, DEX, and quantitative trading systems, signal acquisition speed, state synchronization speed, and cross-region data consistency directly impact strategy effectiveness and execution quality. Streams' core value lies in helping users acquire critical on-chain data with lower latency, primarily addressing four types of issues:

* Detect unconfirmed transactions and order flow signals earlier
* Get the latest blocks and confirmed transactions faster
* Get timely references for Gas Price, Priority Fee, or Tip.
* Synchronize local nodes with the latest world state with lower latency

### What capabilities does Streams provide

#### Public Mempool

Mempool is used to obtain unconfirmed transaction or private order stream data with low latency, making it suitable for scenarios that require capturing on-chain signals as early as possible.

* [Public Mempool](/streams/public-mempool/bsc/public-mempool): Used for low-latency subscription to pending transactions, suitable for monitoring public trading signals.
* [Tx Trace](/streams/public-mempool/bsc/tx-trace): Used to observe the propagation path and cross-regional latency distribution of transactions in the Public Mempool, suitable for transaction latency investigation, multi-regional deployment evaluation, and propagation effect verification.

#### Private Mempool

[Private Mempool](/streams/private-mempool): Used for subscribing to private orderflow, suitable for scenarios such as backrun, sniping and copy trading.

#### Block Stream

Block Stream is used for low-latency retrieval of the latest blocks and confirmed transactions, making it suitable for scenarios that focus on post-confirmation signals, block events, and on-chain results. Compared to Mempool, Block Stream observes "data that has already entered the block," while Mempool focuses more on "data that has not yet been confirmed but has entered the propagation process." For systems that need to confirm transaction results, track block events, or perform block-level analysis, Block Stream is more suitable.

#### Network Fee Stream

Network Fee Stream is used to obtain real-time fee references such as Gas Price, Priority Fee, or Tip based on recent historical block data. This type of data is suitable for:

* Dynamically adjust trading parameters
* Optimize fee strategy
* Assisted transaction sending decision

#### Node Stream

[Node Stream](/streams/node-stream) is used for low-latency synchronization of the latest blocks and world state, and is suitable for systems that rely on local node state for decision-making.

Unlike subscribing to Block Stream, Node Stream doesn't simply push block data; instead, it allows users' own full nodes to synchronize faster via [BEF](/core-technology/blockchain-edge-fabric). For teams that need to obtain the latest status immediately and directly rely on local nodes to run strategies or services, Node Stream is more suitable as an underlying infrastructure capability.

### How to choose a suitable Stream

<table data-search="false"><thead><tr><th>場景</th><th>適用用戶</th><th>推薦能力</th></tr></thead><tbody><tr><td>monitor pending transactions and backrun / copy trading / sniping</td><td>Searcher, Trading Bot</td><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a></td></tr><tr><td>subscribe provite orderflow and backrun / copy trading / sniping</td><td>Searcher, Trading Bot</td><td><a href="/streams/private-mempool">Private Mempool</a></td></tr><tr><td>Dynamic optimization for Gas / Priority Fee / Tip</td><td>Searcher, Trading Bot, Quant Team, Wallets, DEX</td><td><a href="/streams/network-fee-stream">Network Fee Stream</a></td></tr><tr><td>Keep local nodes and world state up-to-date</td><td>Searcher, Trading Bot, Quant Team, Wallets, DEX</td><td><a href="/streams/node-stream">Node Stream</a></td></tr><tr><td>Observe the transaction propagation path and cross-regional latency</td><td>Searcher, Trading Bot, Quant Team</td><td><a href="/streams/public-mempool/bsc/tx-trace">Tx Trace</a></td></tr><tr><td>Subscribe Base FlashBlock Stream</td><td>Searcher, Trading Bot, Quant Team, Wallets, DEX</td><td><a href="/streams/block-stream/base/get-flashblocktransaction">FlashBlock Stream</a></td></tr><tr><td>Subscribe Solana accounts、transactions、slots and blocks</td><td>Searcher, Trading Bot, Quant Team, Wallets, DEX</td><td><a href="/streams/block-stream/solana/geyser-stream">Geyser Stream</a></td></tr><tr><td>Obtain shreds from Solana with extremely low latency.</td><td>Searcher, Trading Bot</td><td><a href="/streams/block-stream/solana/shred-stream">Shred Stream</a></td></tr></tbody></table>

### Quick Start

{% stepper %}
{% step %}
**Purchase Stream**

Go to the [Pricing](https://blockrazor.io/#/pricing) page to purchase the target stream.
{% endstep %}

{% step %}
**Apply for auth**

For details, see [Authentication](/get-started/authentication)
{% endstep %}

{% step %}
**Integrate Stream**

Based on the target stream access documentation, access the target stream and test its latency and stability performance.
{% endstep %}
{% endstepper %}

### FAQ

<details>

<summary>What is the difference between Public Mempool and Private Mempool?</summary>

Public Mempool is used to subscribe to publicly disseminated pending transactions, making it suitable for early detection of trading signals.\
Private Mempool is used to subscribe to private pending transactions, and is suitable for scenarios such as backrun.

</details>

<details>

<summary>What are the differences between Mempool and Block Stream?</summary>

Mempool focuses on transactions that are not yet confirmed but have entered the propagation process; Block Stream focuses on blocks and transactions that have entered the block and have been confirmed.

If you want to discover opportunities earlier, prioritize Mempool; if you are more concerned with result confirmation and block-level analysis, prioritize Block Stream.

</details>

<details>

<summary>What is the difference between Block Stream and Node Stream?</summary>

Block Stream is a subscription-based data stream used to obtain the latest blocks and confirmed transactions with low latency.

Node Stream, on the other hand, focuses more on infrastructure capabilities, allowing users' own full nodes to synchronize the latest blocks and world state faster.

If you only need block data, Block Stream is usually more straightforward; if you need to run the system based on the state of local nodes, Node Stream is more suitable.

</details>


# Public Mempool

Viewing specific Mempool services from a blockchain perspective, supporting BSC Public Mempool and Ethereum Public Mempool.

<table><thead><tr><th width="100.19921875">Chain</th><th width="184.76953125">Service</th><th>Description</th></tr></thead><tbody><tr><td>BSC</td><td><a href="/streams/public-mempool/bsc/public-mempool">Public Mempool</a></td><td>Low-latency subscription to pending BSC transaction data</td></tr><tr><td>BSC</td><td><a href="/streams/public-mempool/bsc/tx-trace">Tx Trace</a></td><td>Monitoring the propagation path of public transactions and cross-regional delay distribution</td></tr><tr><td>Ethereum</td><td><a href="/streams/public-mempool/ethereum/public-mempool">Public Mempool</a></td><td>Low-latency subscription to pending Ethereum transaction data</td></tr></tbody></table>


# BSC Mempool

This section introduces the BlockRazor BSC Stream service, primarily consisting of Public Mempool and Private Mempool.


# BSC Public Mempool

This section introduces the services, advantages, application scenarios, and access methods of BlockRazor BSC Public Mempool.

### What is BSC Public Mempool

Public Mempool is a high-performance pending transaction stream based on [BEF](/core-technology/blockchain-edge-fabric), used for low-latency subscription to unconfirmed transactions in public propagation.

In the EVM network, transactions typically propagate through the mempool before entering a block. The core value of the Public Mempool is to help users obtain publicly available pending transaction signals earlier and integrate these signals into their strategy systems with lower latency. It is commonly used to monitor public trading activity, track Smart Money behavior, identify new opportunities, and provide faster signal input for strategies such as backrun, copy trading, and sniping. For systems that rely on pending signals to drive trading decisions, seeing transactions earlier often means:

* Entering the strategy judgment process earlier
* More time to complete calculations and risk control
* Higher probability of obtaining a better execution position in competitive scenarios

### Scenarios of BSC Public Mempool

* Pending Transaction Monitoring: Real-time monitoring of publicly distributed pending transactions to identify active addresses, popular contracts, or unusual transaction behavior.
* Smart Money Tracking: Track transaction activity at target addresses early on, providing signals for copy trading or strategy following.
* Backrun Discovery: Identifying publicly trades that may trigger backrun opportunities, allowing more time for subsequent strategy evaluation and trade submission.
* Sniping Opportunities: Capture the first signals in the open market as early as possible when new pools launch, liquidity injections occur, or target trades emerge.
* Real-time data input for strategies: Serving as a real-time input source for the trading system, it can be used in conjunction with capabilities such as Block Stream, Node Stream, RPC, or Block Builder to build a more complete monitoring and execution project.

### Benchmark

In our transaction reception latency benchmark, we compared BlockRazor with a regular Node in four regions: Dublin, Frankfurt, Tokyo, and Virginia. The evaluation was based on the time difference for clients receiving the same transaction from BlockRazor and regular Node among comparable samples.

<table><thead><tr><th width="120.25390625">Region</th><th width="188.93359375">BlockRazor Lead Rate</th><th>Avg Lead</th><th>P50 Lead</th><th>P90 Lead</th></tr></thead><tbody><tr><td>Dublin</td><td><strong>99.7%</strong></td><td>43.7 ms</td><td>28.8 ms</td><td>89.7 ms</td></tr><tr><td>Frankfurt</td><td><strong>99.5%</strong></td><td>29.0 ms</td><td>21.5 ms</td><td>48.1 ms</td></tr><tr><td>Tokyo</td><td><strong>99.8%</strong></td><td>136.4 ms</td><td>119.5 ms</td><td>218.7 ms</td></tr><tr><td>Virginia</td><td><strong>99.8%</strong></td><td>55.1 ms</td><td>40.5 ms</td><td>107.4 ms</td></tr></tbody></table>

The results show that BlockRazor maintained a significant lead in all four regions. Its transaction reception lead rate exceeded 99% in all regions. In terms of lead magnitude, BlockRazor's average lead time in different regions was approximately 43.7ms, 29.0ms, 136.4ms, and 55.1ms; under the P90 dimension, the lead magnitudes reached 89.7ms, 48.1ms, 218.7ms, and 107.4ms, respectively.

The results above demonstrate that BlockRazor exhibits a stable priority reception capability in the transaction propagation chain, enabling it to capture transactions earlier than ordinary nodes in the vast majority of comparable samples.

### Price

<table><thead><tr><th width="167.296875">Payment Method</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$30 / stream / day<br>$300 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_public_mempool&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 /  month <br>packaged with 9 other services. </td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Endpoint

<table><thead><tr><th width="160">Region</th><th>Relay Address</th></tr></thead><tbody><tr><td>Frankfurt</td><td>64.130.47.75:50051</td></tr><tr><td>Tokyo</td><td>63.254.162.18:50051</td></tr><tr><td>Dublin</td><td>141.98.217.82:50051</td></tr><tr><td>Virginia</td><td>208.91.105.204:50051</td></tr></tbody></table>

### Request Parameters

<table><thead><tr><th width="168">Parameters</th><th width="119">Mandatory</th><th width="103">Format</th><th width="98">Example</th><th>Description</th></tr></thead><tbody><tr><td>NodeValidation</td><td>Mandatory</td><td>boolean</td><td>false</td><td>This field currently only supports being set to <code>false</code>, and the relay will push all new transactions (unchecked) with lower latency.</td></tr></tbody></table>

### Request Example

<https://github.com/BlockRazorinc/relay_example>

{% tabs %}
{% tab title="Go" %}

```go
package main

import (
	"context"
	"fmt"

	// directory of the generated code using the provided relay.proto file
	pb "github.com/BlockRazorinc/relay_example/protobuf"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/rlp"
	"google.golang.org/grpc"
)

// auth will be used to verify the credential
type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apiKey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func main() {

	// BlockRazor relay endpoint address
	blzrelayEndPoint := "ip:port"

	// auth will be used to verify the credential
	auth := Authentication{
		"your auth token",
	}

	// open gRPC connection to BlockRazor relay
	var err error
	conn, err := grpc.Dial(blzrelayEndPoint, grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth), grpc.WithWriteBufferSize(0), grpc.WithInitialConnWindowSize(128*1024))
	if err != nil {
		fmt.Println("error: ", err)
		return
	}

	// use the Gateway client connection interface
	client := pb.NewGatewayClient(conn)

	// create context and defer cancel of context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create a subscription using the stream-specific method and request
	stream, err := client.NewTxs(ctx, &pb.TxsRequest{NodeValidation: false})
	if err != nil {
		fmt.Println("failed to subscribe new tx: ", err)
		return
	}

	for {
		reply, err := stream.Recv()
		if err != nil {
			fmt.Println("stream receive error: ", err)
		}
		tx := &types.Transaction{}

		err = rlp.DecodeBytes(reply.Tx.RawTx, tx)
		if err != nil {
			continue
		}

		fmt.Println("recieve new tx, tx hash is ", tx.Hash().String())

	}
}
```

{% endtab %}
{% endtabs %}

#### Proto

The code of `relay.proto` is as follows:

```
syntax = "proto3";

package blockchain;

option go_package = "/Users/code/relay/grpcServer"; 
service Gateway {
  rpc SendTx (SendTxRequest) returns (SendTxReply) {}
  rpc SendTxs (SendTxsRequest) returns (SendTxsReply) {}
  rpc NewTxs (TxsRequest) returns (stream TxsReply){}
  rpc NewBlocks (BlocksRequest) returns (stream BlocksReply){}
}

message TxsRequest{
  bool node_validation = 1;
}

message Tx{
  bytes from = 1;
  int64 timestamp = 2;
  bytes raw_tx = 3;
}

message TxsReply{
   Tx tx = 1;
}

message BlocksRequest{
  bool node_validation = 1;
}

message BlockHeader{
  string parent_hash = 1;
  string sha3_uncles = 2;
  string miner = 3;
  string state_root = 4;
  string transactions_root = 5;
  string receipts_root = 6;
  string logs_bloom = 7;
  string difficulty = 8;
  string number = 9;
  uint64 gas_limit = 10;
  uint64 gas_used = 11;
  uint64 timestamp = 12;
  bytes extra_data = 13;
  string mix_hash = 14;
  uint64 nonce = 15;
  uint64 base_fee_per_gas = 16;
  string withdrawals_root = 17;
  uint64 blob_gas_used = 18;
  uint64 excess_blob_gas = 19;
  string parent_beacon_block_root = 20;
}

message NextValidator{
  string block_height = 1;
  string coinbase = 2;
}

message BlocksReply{
  string hash = 1;
  BlockHeader header = 2;
  repeated NextValidator nextValidator = 3;
  repeated Tx txs = 4;
}

message Transaction {
  string content = 1;
}

message Transactions {
  repeated Transaction transactions = 1;
}

message SendTxRequest {
  string transaction = 1;
}

message SendTxsRequest {
  string transactions = 1;
}

message SendTxReply {
  string tx_hash = 1;
}

message SendTxsReply {
  repeated string tx_hashs = 1;
}
```

### Response Example

**Success**

```json
{
  "tx":[
     {
        "raw_tx":"+QH0gjOthDuaygCDBrbAlKoP7P6dEOH8IzwtDAw9whDVeHKRhwFrzEHpAAC5AYTVQ9H9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmsrt4HBPm7jWohanh7XmbX9S/gRLrth87fXF3H2gC0FAAAAAAAAAAAAAAAAbsa1rd5IJ6lr43ixr1+LWmT/OhgAAAAAAAAAAAAAAABV05gyb5kFn/d1SFJGmZAnsxl5VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVkLNFR0SQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZ7qkBM09jlPtkQprbOV2bITVAfdbvzTltwBYjUJu6OIzF3aAAAAAAAAAAAAAAAAHqXLqcmW4qO1ZEAZXn2nYI/dKV1AAAAAAAAAAAAAAAAVdOYMm+ZBZ/3dUhSRpmQJ7MZeVUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASvCnY7scAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABme6pAgZOgy2LsKlIqPeeM7d520T3eAwIVk9O+vY4wT+zifYp0GGOgTY7Z5J3zs/YCj1HvVXOZF9Q2rj5x421GBG9CrKmxVGo="
     }
   ]
}
```

**Error**

```
rpc error: code = Unknown desc = data streams have exceeded its max limit [5]
```


# BSC Public Mempool Tx Trace

This section introduces the services, application scenarios, and integration methods of BlockRazor BSC Public Mempool Tx Trace.

### What is BSC Tx Trace

Tx Trace is a transaction propagation path observation tool provided by BlockRazor, used to query the propagation path, arrival time, and cross-regional latency distribution of a specified transaction in the global network.

For transaction systems, many issues cannot be determined solely from on-chain receipts. For example, although a transaction may be successfully included on-chain, the region where it first appeared and the time it took to propagate between different regions cannot be directly observed from the on-chain results alone. Based on [BEF](/core-technology/blockchain-edge-fabric), Tx Trace helps users observe transaction behavior from a perspective closer to the network propagation layer, transforming "latency issues" or "regional difference issues" that previously relied solely on experience into data issues that can be analyzed.

### Scenarios of BSC Tx Trace

**Transaction delay troubleshooting**: When transaction sending results are abnormal, performance is unstable, or the actual execution result is inconsistent with expectations, Tx Trace can be used to view the propagation path and time difference of the transaction in the global network, which can help determine whether the problem lies in the network propagation process.

Multi-region deployment evaluation: When a team deploys a bot or sending services in multiple regions, it can use Tx Trace to compare the entry time and propagation effect of transactions in different regions, and evaluate whether the current deployment truly brings better network coverage and propagation performance.

High-frequency trading path optimization: For trading systems that rely on timing and speed, Tx Trace can be used to analyze the spread of trading in different regions, providing a reference for path optimization.

### Price & Rate Limit

<table><thead><tr><th width="132.03515625">Payment Method</th><th width="197.1875">Rate Limit</th><th width="265.8828125">Price</th><th width="127.80078125">Action</th></tr></thead><tbody><tr><td>Free</td><td>20 requests / day</td><td>Free</td><td>-</td></tr><tr><td>Personalized</td><td>500 requests / day</td><td>$20 / day</td><td><a href="https://blockrazor.io/#/portal/pricing?purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_tx_trace&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>500 requests / day</td><td>$1250 / month<br>packaged with 9 other services</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Endpoint

<http://tx-trace.blockrazor.io>

### Request Example

```bash
curl -H "Authorization: <YOUR_AUTHORIZATION>" \
  http://tx-trace.blockrazor.io/txtrace/0xea4cac1749fcbfd53d798edf795d80c550aa00873d3acb1019000bf74dd18404
```

### Response Example

**Normal**

```json
{
	"txTrace": [{
		"region": "EU Germany",
		"txTime": "2026-06-03 03:18:10.481",
		"diff": "+0ms"
	},
	{
		"region": "NA US Virginia",
		"txTime": "2026-06-03 03:18:10.510",
		"diff": "+29ms" // The transaction spread from EU Germany to NA US Virginia in 29ms.
	},
	},
	{
		"region": "EU Ireland",
		"txTime": "2026-06-03 03:18:10.515",
		"diff": "+34ms" // The transaction spread from EU Germany to EU Ireland in 34ms.
	},
	{
		"region": "AS Japan",
		"txTime": "2026-06-03 03:18:10.574",
		"diff": "+93ms"
	}],
	"txHash": "0xe55b39c4dead92fe956f7ce2d640e0fcf0ce0cd969da9e3f900d493634b64a54",
	"numberOfRegions": 4
}
```

**Abnormal**

```json
{"error":"invalid token"}
```

```json
{"error":"daily limit exceeded"}
```

### FAQ

<details>

<summary>Which transactions can Tx Trace track?</summary>

Tx Trace is primarily suitable for querying the propagation status, arrival time, and latency distribution of transactions that have entered the public propagation process across different regions. If a transaction has not entered the public propagation path, or is outside the valid query window, Tx Trace cannot provide corresponding propagation results.

</details>

<details>

<summary>What is the difference between Tx Trace and Public Mempool?</summary>

The two have different positioning:

* Public Mempool is a real-time subscription service used for low-latency reception of pending transactions in public circulation, with the emphasis on "seeing transactions as early as possible".
* Tx Trace is a propagation observation tool used to query the propagation path and cross-regional latency distribution of a specified transaction within a global network, focusing on "seeing how transactions propagate."

</details>


# Ethereum Mempool

Introducing the BlockRazor Ethereum Stream service, primarily a Public Mempool.


# Ethereum Public Mempool

This section introduces the services, application scenarios, and access methods of the BlockRazor Ethereum Public Mempool.

### What is Ethereum Public Mempool

Public Mempool is a high-performance pending transaction stream based on [BEF](/core-technology/blockchain-edge-fabric), used for low-latency subscription to unconfirmed transactions in public propagation.

In the EVM network, transactions typically propagate through the mempool before entering a block. The core value of the Public Mempool is to help users obtain publicly available pending transaction signals earlier and integrate these signals into their strategy systems with lower latency. It is commonly used to monitor public trading activity, track Smart Money behavior, identify new opportunities, and provide faster signal input for strategies such as backrun, copy trading, and sniping. For systems that rely on pending signals to drive trading decisions, seeing transactions earlier often means:

* Entering the strategy judgment process earlier
* More time to complete calculations and risk control
* Higher probability of obtaining a better execution position in competitive scenarios

### Scenarios of Ethereum Public Mempool

* Pending Transaction Monitoring: Real-time monitoring of publicly distributed pending transactions to identify active addresses, popular contracts, or unusual transaction behavior.
* Smart Money Tracking: Track transaction activity at target addresses early on, providing signals for copy trading or strategy following.
* Backrun Discovery: Identifying publicly trades that may trigger backrun opportunities, allowing more time for subsequent strategy evaluation and trade submission.
* Sniping Opportunities: Capture the first signals in the open market as early as possible when new pools launch, liquidity injections occur, or target trades emerge.
* Real-time data input for strategies: Serving as a real-time input source for the trading system, it can be used in conjunction with capabilities such as Block Stream, Node Stream, RPC, or Block Builder to build a more complete monitoring and execution project.

### Price

<table><thead><tr><th width="167.296875">Payment Method</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$30 / stream / day<br>$300 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_public_mempool&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 / month<br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Endpoint

<table><thead><tr><th width="160">Region</th><th>Relay Address</th></tr></thead><tbody><tr><td>Frankfurt</td><td>64.130.47.75:50061</td></tr><tr><td>Tokyo</td><td>63.254.162.18:50061</td></tr><tr><td>Virginia</td><td>208.91.105.204:50061</td></tr></tbody></table>

### Request Example

[https://github.com/BlockRazorinc/eth\_relay\_example](https://github.com/BlockRazorinc/eth_relay_examplehttps://github.com/BlockRazorinc/eth_relay_example)

{% tabs %}
{% tab title="Go" %}

```go
package main

import (
	"context"
	"encoding/hex"
	"fmt"

	// directory of the generated code using the provided relay.proto file
	pb "github.com/BlockRazorinc/eth_relay_example/protobuf"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

// auth will be used to verify the credential
type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apikey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func main() {

	// BlockRazor relay endpoint address
	blzrelayEndPoint := "ip:port"

	// auth will be used to verify the credential
	auth := Authentication{
		"your auth token",
	}

	// open gRPC connection to BlockRazor relay
	var err error
	conn, err := grpc.NewClient(blzrelayEndPoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(&auth), grpc.WithWriteBufferSize(0), grpc.WithInitialConnWindowSize(128*1024))
	if err != nil {
		fmt.Println("error: ", err)
		return
	}
	defer conn.Close()

	// use the Relay client connection interface
	client := pb.NewRelayClient(conn)

	// create context and defer cancel of context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create a subscription using the stream-specific method and request
	stream, err := client.NewTxs(ctx, &pb.NewTxsRequest{IncludeRawTx: true})
	if err != nil {
		fmt.Println("failed to subscribe new tx: ", err)
		return
	}

	for {
		reply, err := stream.Recv()
		if err != nil {
			fmt.Println("stream receive error: ", err)
			return
		}

		fmt.Println("receive new tx, tx hash is ", reply.TxHash, " source is ", reply.Source)
		if len(reply.RawTx) > 0 {
			fmt.Println("raw tx is 0x" + hex.EncodeToString(reply.RawTx))
		}
	}
}
```

{% endtab %}
{% endtabs %}

```go

service Relay {
  rpc SendTx(SendTxRequest) returns (SendTxReply);
  rpc NewTxs(NewTxsRequest) returns (stream NewTx);
  rpc NewBlocks(NewBlocksRequest) returns (stream NewBlock);
}

message SendTxRequest {
  bytes raw_tx = 1;
}

message SendTxReply {
  string tx_hash = 1;
}

message NewTxsRequest {
  bool include_raw_tx = 1;
}

message NewTx {
  string tx_hash = 1;
  bytes raw_tx = 2;
  int64 first_seen_unix_ns = 3;
  string source = 4;
}

message NewBlocksRequest {
  bool parsed_txs = 1;
}

message NewBlock {
  string hash = 1;
  BlockHeader header = 2;
  repeated BlockTransaction transactions = 3;
  repeated Withdrawal withdrawals = 4;
}

message BlockHeader {
  string parent_hash = 1;
  string sha3_uncles = 2;
  string miner = 3;
  string state_root = 4;
  string transactions_root = 5;
  string receipts_root = 6;
  string logs_bloom = 7;
  string difficulty = 8;
  string number = 9;
  string gas_limit = 10;
  string gas_used = 11;
  string timestamp = 12;
  string extra_data = 13;
  string mix_hash = 14;
  string nonce = 15;
  string base_fee_per_gas = 16;
  string withdrawals_root = 17;
  string blob_gas_used = 18;
  string excess_blob_gas = 19;
  string parent_beacon_block_root = 20;
}

message BlockTransaction {
  bytes raw_tx = 1;
  bytes from = 2;
}

message Withdrawal {
  string address = 1;
  string amount = 2;
  string index = 3;
  string validator_index = 4;
}
```

### Response Example

**Success**

```json
{
  "tx":[
     {
        "raw_tx":"+QH0gjOthDuaygCDBrbAlKoP7P6dEOH8IzwtDAw9whDVeHKRhwFrzEHpAAC5AYTVQ9H9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmsrt4HBPm7jWohanh7XmbX9S/gRLrth87fXF3H2gC0FAAAAAAAAAAAAAAAAbsa1rd5IJ6lr43ixr1+LWmT/OhgAAAAAAAAAAAAAAABV05gyb5kFn/d1SFJGmZAnsxl5VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVkLNFR0SQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZ7qkBM09jlPtkQprbOV2bITVAfdbvzTltwBYjUJu6OIzF3aAAAAAAAAAAAAAAAAHqXLqcmW4qO1ZEAZXn2nYI/dKV1AAAAAAAAAAAAAAAAVdOYMm+ZBZ/3dUhSRpmQJ7MZeVUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASvCnY7scAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABme6pAgZOgy2LsKlIqPeeM7d520T3eAwIVk9O+vY4wT+zifYp0GGOgTY7Z5J3zs/YCj1HvVXOZF9Q2rj5x421GBG9CrKmxVGo="
     }
   ]
}
```

**Error**

```
rpc error: code = Unknown desc = data streams have exceeded its max limit [5]
```


# BSC Private Mempool

This section introduces the services, application scenarios, advantages, and access methods of BlockRazor BSC Private Mempool.

### What is BSC Private Mempool

BSC Private Mempool is a private pending transaction service provided by BlockRazor, used to obtain private orderflow from BlockRazor RPC.

Unlike Public Mempool, which subscribes to pending transactions in public distribution, Private Mempool focuses on private transaction data that hasn't entered the public distribution path. This data is pushed via the SSE protocol, allowing users to directly parse, filter, and process it within their strategy systems. Private Mempool performs uniform anonymization on transaction content, disclosing only the transaction fields authorized for public access. This balances data privacy with the retention of critical information needed for strategy analysis.

### Scenarios of BSC Private Mempool

Private Mempool is suitable for users who want to monitor, judge, and execute strategies around private orderflows. Common scenarios include backrunning, copy trading, and sniping.

* **Backrunning**: When a trade appears in the private orderflow that may trigger arbitrage opportunities, users can construct a backrun trade based on these signals and execute the strategy after the target trade.
* **Copy Trading**: When target addresses, strategy accounts, or specific types of transactions appear in the private orderflow, users can identify these copy trading signals earlier and build follow strategies around actions such as buying, selling, adding to positions, or adjusting positions.
* Sniping: Identify key signals from private orderflow as early as possible when new pools are created, liquidity is injected, tokens open, or specific target trades are about to trigger market changes, providing an earlier response window for quick entry, signal following, and other timing-sensitive strategies.

### Why choose BSC Private Mempool?

In the BSC scenario, many high-value transactions do not appear in the public Mempool, but are instead included via a private routing provided by BlockRazor RPC. For users who want to build strategies around these transactions, waiting until the transactions are finally included to capture signals often means missing more valuable processing opportunities.

Private Mempool relies on [BEF](/core-technology/blockchain-edge-fabric) to provide users in different regions with access to private orderflow, enabling users to conduct earlier analysis and decisions based on the private transactions.

### Quick Start

{% stepper %}
{% step %}
**Purchase BSC Private Mempool**

Go to the [Pricing](https://blockrazor.io/#/pricing) page to purchase
{% endstep %}

{% step %}
**Apply for Auth**

For Details, see [Authentication](/get-started/authentication)
{% endstep %}

{% step %}
**Subscribe to BSC Private Mempool**

For Details, see [Request Example](#request-example)
{% endstep %}

{% step %}
**Construct &  submit bundle**

For Details, see [Bundle](/transaction-submission/rpc/bsc/orderflow-auction)
{% endstep %}
{% endstepper %}

### Price

The price is $100 / day and $1000 / month. 2 data streams are allowed per region. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_private_mempool&#x26;billing=day" class="button primary small">Subscribe</a>

### Endpoint

{% hint style="info" %}

* Please keep the domain for subscribing to bundles consistent with the domain for sending bundles. For example, if you subscribe to `https://jp-bscscutum.blockrazor.xyz/stream`, send bundles to `https://jp-bscscutum.blockrazor.xyz`.
* Private data streams vary across regions. It is recommended to subscribe to all three endpoints simultaneously.
  {% endhint %}

<table><thead><tr><th width="129.359375">地區</th><th>端點</th></tr></thead><tbody><tr><td>Tokyo</td><td>https://jp-bscscutum.blockrazor.xyz/stream</td></tr><tr><td>New York</td><td>https://us-bscscutum.blockrazor.xyz/stream</td></tr><tr><td>Frankfurt</td><td>https://ger-bscscutum.blockrazor.xyz/stream</td></tr><tr><td>Dublin</td><td>https://ire-bscscutum.blockrazor.xyz/stream</td></tr></tbody></table>

### Request Example

```json
curl -X GET \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <token>" \
    --data '{}' \
    https://jp-bscscutum.blockrazor.xyz/stream
```

```
https://jp-bscscutum.blockrazor.xyz/stream?token=<token>
```

### Data Stream Type

#### **Raw Bundle**

Raw Bundle refers to the bundle that has not been followed by the strategy transaction. Transactions in Raw Bundle come from two channels.

Transactions submitted through `eth_sendRawTransaction` will be automatically constructed as a bundle by BlockRazor RPC and pushed to Private Mempool. The Raw Bundle in this scenario only contains one transaction;

For the Raw Bundle submitted through `eth_sendMevBundle`, the transactions come from the public mempool or are self-constructed. The Raw Bundle in this scenario can contain up to 50 transactions.

#### Followed Bundle

After the client executes the backrun, coping trading or sniping strategy on Raw Bundle, it can choose to continue to disclose the bundle to Private Mempool to execute the nested backrun strategy. At this time, the bundle disclosed to the Private Mempool is the Followed Bundle, which contains all transactions in Raw Bundle, and one strategy transaction.

### Data Stream Structure

**Bundle**

<table><thead><tr><th width="169">Patameters</th><th width="131">Format</th><th>Remark</th></tr></thead><tbody><tr><td>chainID</td><td>string</td><td>ETH: 1, BSC:56</td></tr><tr><td>hash</td><td>string</td><td>bundle hash, data streams are pushed uniformly in the form of bundles</td></tr><tr><td><a href="#tx">txs</a></td><td><a href="#tx">[]tx</a></td><td>transactions included in bundle</td></tr><tr><td>nextBlockNumber</td><td>uint64</td><td>the block number where the bundle is going to be included</td></tr><tr><td>maxBlockNumber</td><td>uint64</td><td>the maximum block number valid for this bundle</td></tr><tr><td>proxyBidContract</td><td>string</td><td>proxy contract address of bundle bidding,  biding call for detail can be found in <a data-mention href="broken://pages/ugvQCt84QW0SIkVonADF">Broken link</a>.</td></tr><tr><td>refundAddress</td><td>string</td><td>input parameter of bidding call, the bidding will be refunded to refundAddress in proportion.</td></tr><tr><td>refundCfg</td><td>int</td><td>input parameter of bidding call</td></tr><tr><td>state</td><td>[]state</td><td>state change of state objects in EVM,   <a href="#data-stream-example-including-state">data stream example</a></td></tr></tbody></table>

**txs**

<table><thead><tr><th width="195">Patameters</th><th width="132">Format</th><th>Remark</th></tr></thead><tbody><tr><td>hash</td><td>string</td><td>transaction hash</td></tr><tr><td>from</td><td>string</td><td>sender of the transaction</td></tr><tr><td>to</td><td>string</td><td>receiver of the transaction</td></tr><tr><td>value</td><td>hex</td><td>value being transacted</td></tr><tr><td>nonce</td><td>uint64</td><td>nonce</td></tr><tr><td>calldata</td><td>string</td><td>calldata</td></tr><tr><td>functionSelector</td><td>string</td><td>the first 4 bytes of the contract function signature hash</td></tr><tr><td>logs</td><td><a href="#log">[]log</a></td><td>event logs emitted during transaction execution</td></tr></tbody></table>

**log**

<table><thead><tr><th width="199">Patameters</th><th width="132">Format</th><th>Remark</th></tr></thead><tbody><tr><td>address</td><td>string</td><td>the smart contract address that triggered the event</td></tr><tr><td>topics</td><td>[]string</td><td>event log topcis</td></tr><tr><td>data</td><td>string</td><td>storage area for non-index data</td></tr></tbody></table>

#### **state**

{% hint style="info" %}
the default data stream does not contain `state` field, if you need it, please modify the url of RPC Endpoint to <https://jp-bscscutum.blockrazor.xyz/stream?state=true>
{% endhint %}

<table><thead><tr><th width="208">Patameters</th><th width="123">Format</th><th>Remark</th></tr></thead><tbody><tr><td>"0x7C3b……3cb9E2"</td><td>[]string</td><td>The address of the state object where the data changes, which can be EOA or smart contract</td></tr><tr><td>"0x935b……6cf608"</td><td>string</td><td>the Key of the changed data in state object</td></tr><tr><td>"0x0000……3ffc00"</td><td>string</td><td>the Value of changed data in state object</td></tr></tbody></table>

### Data Stream Example(default)

```json
{
    "chainID":"56" //ETH: 1, BSC:56
    "hash":"0x2ba4c05436d4a48a0ce30341a3164b34b31c091a28ed62618f7b0512aba41f51" // bundle hash
    "txs":[{
          "hash":"0x2ba4c05436d4a48a0ce30341a3164b34b31c091a28ed62618f7b0512aba41f51"
          "from":"0xB4647b856CB9C3856d559C885Bed8B43e0846a47"
          "to":"0x0000000000000000000000000000000000001000"
          "value":"0x1c4eda9192000"
          "nonce":88036
          "calldata":"0xf340fa01000000000000000000000000b4647b856cb9c3856d559c885bed8b43e0846a47"
          "functionSelector":"0xe47d166c"
          "logs":[
              {
                "address": "0x6c1bcf1b99d9f0819459dad661795802d232437e",
                "topics": ["0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"],
                "data": "0x"
              }
              {
                "address": "0x6c1bcf1b99d9f0819459dad661795802d232437e",
                "topics": ["0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"],
                "data": "0x"
              }
          ]
    }]
    "nextBlockNumber":39177941  //the block number where the bundle is going to be included
    "maxBlockNumber":39177941  //the maximum block number valid for this bundle
    "proxyBidContract":"0x74Ce839c6aDff544139f27C1257D34944B794605" //bidding contract address, call the contract's proxyBid method to bid
    "refundAddress":"0x6c1bcf1b99d9f0819459dad661795802d232437e", //the bidding amount will be refunded to refundAddress in proportion
    "refundCfg":10380050 //refund configuration
}
```

### Data Stream Example(including state)

{% hint style="info" %}
The default data stream does not contain `state` field, if you need to obtain it, please modify the url of RPC Endpoint to <https://bsc.blockrazor.xyz/stream?state=true>
{% endhint %}

```json
{
    "chainID":"56" //ETH: 1, BSC:56
    "hash":"0x2ba4c05436d4a48a0ce30341a3164b34b31c091a28ed62618f7b0512aba41f51" // bundle hash
    "txs":[{
          "hash":"0x2ba4c05436d4a48a0ce30341a3164b34b31c091a28ed62618f7b0512aba41f51"
          "from":"0xB4647b856CB9C3856d559C885Bed8B43e0846a47"
          "to":"0x0000000000000000000000000000000000001000"
          "value":"0x1c4eda9192000"
          "nonce":88036
          "calldata":"0xf340fa01000000000000000000000000b4647b856cb9c3856d559c885bed8b43e0846a47"
          "functionSelector":"0xe47d166c"
          "logs":[
              {
                "address": "0x6c1bcf1b99d9f0819459dad661795802d232437e",
                "topics": ["0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"],
                "data": "0x"
              }
              {
                "address": "0x6c1bcf1b99d9f0819459dad661795802d232437e",
                "topics": ["0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000"],
                "data": "0x"
              }
          ]
    }]
    "nextBlockNumber":39177841  //the block number where the bundle is going to be included
    "maxBlockNumber":39177941  //the maximum block number valid for this bundle
    "proxyBidContract":"0x74Ce839c6aDff544139f27C1257D34944B794605" //bidding contract address, call the contract's proxyBid method to bid
    "refundAddress":"0x6c1bcf1b99d9f0819459dad661795802d232437e", //the bidding amount will be refunded to refundAddress in proportion
    "refundCfg":10380050 //refund configuration
    "state": { //state change of state objects
	"0x7C3b00CB3B40Cc77d88329A58574E29cFA3cb9E2": { //The address of the state object where the data changes, which can be EOA or smart contract
	      "0x935b605129a438014d6ae0692623c5e1fbf83d5a631f5a0f8489a301966cf608": "0x00000000000000000000000000000000000000000000010c86a7e418723ffc00"
	      //"the Key of the changed data in state object":"the Value of changed data in state object"
	      }
      }
}
```


# Block Stream

Viewing specific Block Stream services from a chain perspective

<table><thead><tr><th width="108.234375">Chain</th><th width="191.37109375">Service</th><th>Description</th></tr></thead><tbody><tr><td>Solana</td><td><a href="/streams/block-stream/solana/shred-stream">Shred Stream</a></td><td>Low-latency shred transmission</td></tr><tr><td>Solana</td><td><a href="/streams/block-stream/solana/geyser-stream">Geyser Stream</a></td><td>Real-time transmission of on-chain data from Solana, including accounts, slots, blocks, and transactions.</td></tr><tr><td>BSC</td><td><a href="/streams/block-stream/bsc/newblocks">NewBlocks</a></td><td>Low-latency subscription to BSC block data</td></tr><tr><td>Ethereum</td><td><a href="/streams/block-stream/ethereum/newblocks">NewBlocks</a></td><td>Low-latency subscription to Ethereum block data</td></tr><tr><td>Base</td><td><a href="/streams/block-stream/base/get-blockstream">Get BlockStream</a></td><td>Low-latency acquisition of Base Block data</td></tr><tr><td>Base</td><td><a href="/streams/block-stream/base/get-flashblockstream">Get FlashBlockStream</a></td><td>Low-latency acquisition of Base FlashBlock data</td></tr></tbody></table>


# Solana Block Stream

This section introduces BlockRazor's Block Stream services for Solana, primarily Shred Stream and Geyser Stream.


# Solana Shred Stream

This section introduces the services, target users, advantages, and integration methods of BlockRazor Solana Shred Stream.

### What is Shred Stream

Shred Stream is a shred data subscription service provided by BlockRazor for Solana. It has been deployed in multiple regions such as Frankfurt, Amsterdam, Tokyo, and New York, providing users worldwide with lower latency shred distribution capabilities.

### Who is Shred Stream suitable for?

* **Validators / RPCs**: For validators and RPC nodes that need to receive Solana Shred as early as possible, Shred Stream can serve as a lower-latency data input source.
* **Trading Bots:** Trading bots that are highly sensitive to Shred arrival time can leverage Shred Stream to receive Shred earlier, allowing for more response time for subsequent transaction parsing and strategy execution.
* **DeFi Builders:** For DeFi system builders who need to quickly detect on-chain changes and shorten data processing paths, Shred Stream offers a lower-latency way to obtain on-chain transactions.

### Why choose BlockRazor Shred Stream?

Shred Stream directly connects to high-staking validators in Solana to obtain shreds through [BEF](/core-technology/blockchain-edge-fabric). It uses UDP to forward data with minimal hops, resulting in a shorter overall link and faster speed, making it suitable for scenarios with extremely low latency requirements for obtaining Solana transaction data.

### FAQ

<details>

<summary>After receiving Shreds, how do I perform Shred parsing, and what data can I extract?</summary>

Shred Stream receives raw shred streams from the Solana network and cannot be used directly as structured transaction or block data. It typically requires 3 steps: receiving, recovering, and parsing, to extract usable transaction content.

You can directly refer to the [shreds-subscribe](https://github.com/BlockRazorinc/shreds-subscribe). It provides a complete parsing chain:

1. **UDP Reception:** Continuously receive shreds on the specified port.
2. **FEC Recovery:** Recovering partially lost shreds using the Reed-Solomon algorithm
3. **Deshred + Parsing:** Reassembles the shred into complete block entries and further parse the transactions within them.

After completing this process, you can usually obtain the following information:

* Transactions and transaction events related to the specified account
* Block entries

</details>

<details>

<summary>What is the difference between Shred Stream and Geyser Stream?</summary>

The core difference between the two lies in their data layer and ease of use. Shred Stream transmits lower-level raw shred data, resulting in shorter transmission chains and lower latency, making it suitable for bots, RPCs, and validators with extremely high timeliness requirements. However, the receiving party needs to perform reassembly and parsing themselves.&#x20;

[Geyser Stream](/streams/block-stream/solana/geyser-stream), on the other hand, transmits structured account, slot, block, and transaction data. Clients can directly subscribe via gRPC, making integration simpler and more suitable for account monitoring, transaction analysis, and stable production environments.&#x20;

If you need earlier transaction signals, choose Shred Stream; if you need more complete and easily consumable real-time on-chain data, choose Geyser Stream.

</details>

### Price

The price is $50 / stream / day and $500 / stream / month. <a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_shreds_stream&#x26;billing=day" class="button primary small">Subscribe</a>

### Instruction

1. Go to <https://www.blockrazor.io/>, click \[Register] in the upper right corner to complete the registration
2. Go to the [Pricing](https://blockrazor.io/#/pricing) page to complete the purchase
3. Log in to the console, go to Solana - Shred Stream, click Edit, enter IP:Port or domain:Port, and select the region closest to your server.

   <figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FuTkucLEXNHRAzdSdtKe6%2Fimage.png?alt=media&amp;token=b248ffc6-69bd-4752-a61c-7e221d9e0dab" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```bash
# Please ensure that the port is open. If your client is deployed on AWS or other cloud services, you should additionally configure inbound rules for the security group
# The steps to open the port are as follows
sudo ufw allow <port>/udp
sudo ufw reload
```

{% endcode %}

4. Complete the payment and return to \[Solana] - \[Shred], click \[Capture] to copy the command

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FlL62R0hP0zuaLk6qxgjS%2Fenode3.png?alt=media&amp;token=24c195d0-5eac-42e7-9710-4581bc96c8e8" alt="" width="563"><figcaption></figcaption></figure>

6. Access server to run the command to view the shreds delivered from the relay of Shred Stream


# Solana Geyser Stream

This section introduces the services, application scenarios, key features, and integration methods of BlockRazor Solana Geyser Stream.

### Introduction

**What is Geyser Stream**

Geyser is a plugin mechanism for Solana validators that enables real-time transmission of Solana's account, slot, block, and transaction data to external data storage media. Geyser Stream is a high-performance Solana data streaming service launched by BlockRazor based on the Yellowstone gRPC (Geyser plugin), allowing clients to subscribe to real-time Solana data streams with extremely low latency via the gRPC protocol.

**Applications of Geyser Stream**

Transaction Monitoring: Track transactions for specific accounts, ideal for following smart money trades or targeting new token launches on platforms like pump.fun.

Account Balance Tracking: Monitor balance changes in designated accounts, enabling real-time price calculations for token pairs in DEX pools (e.g., Raydium, Orca, Jupiter).

Block & Slot Insights: Analyze blocks and slots to assess network consensus and health status.

**Key Features of Geyser Stream**

High Performance: Geyser Stream delivers gRPC data streams to clients with ultra-low latency in real time.

Data Integrity: Supports transaction replay for the most recent 500 slots (200 seconds), ensuring seamless data continuity during disconnections.

High Stability: Operates across multiple cloud instances with seamless failover, guaranteeing long-term reliability.

### Endpoint

<table><thead><tr><th width="171.8828125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Tokyo</td><td>geyserstream-tokyo.blockrazor.xyz:443</td></tr></tbody></table>

### Pricing

{% hint style="info" %}
Geyser Stream charges based on monthly data usage, with the price remaining consistent across new purchases, renewals, and additional data allowances. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_geyser_stream&#x26;billing=month" class="button primary small">Subscribe</a>
{% endhint %}

<table data-search="false"><thead><tr><th width="210.109375">Periodic Data Allowance</th><th width="136.5546875">Discount</th><th>Price / Cycle</th></tr></thead><tbody><tr><td>5 TiB</td><td>100%</td><td>$250</td></tr><tr><td>10 TiB</td><td>100%</td><td>$500</td></tr><tr><td>50 TiB</td><td>100%</td><td>$2500</td></tr><tr><td>100 TiB</td><td>95%</td><td>$4750</td></tr><tr><td>150 TiB</td><td>90%</td><td>$6750</td></tr><tr><td>200 TiB</td><td>85%</td><td>$8500</td></tr><tr><td>250 TiB</td><td>80%</td><td>$10000</td></tr></tbody></table>

#### Procurement Instructions

<table><thead><tr><th width="101.15625"></th><th>New procurement</th><th>Renewal</th><th>Add periodic data allowance</th></tr></thead><tbody><tr><td>Scene</td><td>Geyser Stream for initial purchase or repurchase after traffic expires</td><td>Renew your subscription within the Geyser Stream traffic period</td><td>The Geyser Stream cycle data allowance was exhausted prematurely and needs to be replenished.</td></tr><tr><td>Result</td><td>Periodic traffic is generated based on the procurement duration.</td><td>Delayed traffic cycle</td><td>The validity period will not be extended; only the data allowance within the period will be increased.</td></tr><tr><td>Example</td><td><p></p><p>If a new 5 TiB cycle is purchased, the available flow rate will be:</p><ul><li>Starting cycle: 5 Tib</li></ul></td><td><p></p><p>If you purchase 5 TiB for one cycle and renew for one cycle within the initial cycle, the available traffic will be:</p><ul><li>Starting cycle: 5 TiB</li><li>Period 2: 10 Tib</li></ul></td><td><p></p><p>5 TiB was purchased for one cycle, and 10 TiB was renewed for another cycle. During the initial cycle, the data was found to be exhausted. Therefore, an additional 5 TiB was selected to be added to the initial cycle.</p><ul><li>Starting cycle: 5 Tib + 5 Tib</li><li>Period 2: 10 Tib</li></ul></td></tr></tbody></table>

### Integrate Geyser Stream

For auth obtaining steps, see [Authentication](/get-started/authentication) guide.

For multi-language integration details, see:

* [CLI](/streams/block-stream/solana/geyser-stream/cli)
* [Go](/streams/block-stream/solana/geyser-stream/go)
* [Rust](/streams/block-stream/solana/geyser-stream/rust)
* [JS](/streams/block-stream/solana/geyser-stream/js)


# Solana Geyser Stream CLI Subscription Example

Introducing the CLI request methods of BlockRazor Solana Geyser Stream

### Quick Start

{% stepper %}
{% step %}

#### Create Directory

```
mkdir geyser-stream
cd geyser-stream
```

{% endstep %}

{% step %}

#### Download Client

```
# Mac
curl -L -o client https://github.com/BlockRazorinc/geyserstream-client-go/releases/download/v1.0.0/client-darwin-arm64
```

```
# Ubuntu-22.04
curl -L -o client https://github.com/BlockRazorinc/geyserstream-client-go/releases/download/v1.0.0/client-ubuntu-22.04
```

```
# Ubuntu-24.04
curl -L -o client https://github.com/BlockRazorinc/geyserstream-client-go/releases/download/v1.0.0/client-ubuntu-24.04
```

{% endstep %}

{% step %}

#### Grant permission

```
chmod +x client
```

{% endstep %}

{% step %}

#### Run client

```
# Subscribe transaction
./client -e "https://geyserstream-tokyo.blockrazor.xyz" --x-token "$AUTH_TOKEN" subscribe --transactions --transactions-vote false --transactions-failed false
```

```
# Subscribe account
./client -e "https://geyserstream-tokyo.blockrazor.xyz" --x-token "$AUTH_TOKEN" subscribe --accounts --accounts-owner 11111111111111111111111111111111
```

```
# Subscribe block
./client -e "https://geyserstream-tokyo.blockrazor.xyz" --x-token "$AUTH_TOKEN" subsc
```

{% endstep %}
{% endstepper %}


# Solana Geyser Stream Go Subscription Example

Introducing the Go request methods of BlockRazor Solana Geyser Stream

### Quick Start

Please refer to [README.md](https://github.com/BlockRazorinc/geyserstream-client-go/blob/main/README.md)

### Code Example

```go
package main

import (
	"context"
	"crypto/x509"
	"encoding/json"
	"io"
	"log"
	"time"

	"github.com/BlockRazorinc/geyserstream-client-go/pb"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/metadata"
)

var (
	// grpc server address
	grpcAddr = "geyserstream-tokyo.blockrazor.xyz:443"
	// auth token
	token = ""

	// Subscribe to block update
	subscriptBlocks = false

	// Subscribe to accounts
	subscriptAccounts   = false
	accountsFilter      = []string{}
	accountOwnersFilter = []string{}

	// Subscribe to transactions, required for tx_account_include/tx_account_exclude and vote/failed.
	subscriptTransactions       = false
	failedTransactions          = true
	voteTransactions            = true
	transactionsAccountsInclude = []string{}
	transactionsAccountsExclude = []string{}
)

var kacp = keepalive.ClientParameters{
	Time:                10 * time.Second, // send pings every 10 seconds if there is no activity
	Timeout:             time.Second,      // wait 1 second for ping ack before considering the connection dead
	PermitWithoutStream: true,             // send pings even without active streams
}

func grpc_connect(address string, plaintext bool) *grpc.ClientConn {
	var opts []grpc.DialOption
	if plaintext {
		opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	} else {
		pool, _ := x509.SystemCertPool()
		creds := credentials.NewClientTLSFromCert(pool, "")
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}

	opts = append(opts, grpc.WithKeepaliveParams(kacp))

	log.Println("Starting grpc client, connecting to", address)
	conn, err := grpc.Dial(address, opts...)
	if err != nil {
		log.Fatalf("fail to dial: %v", err)
	}

	return conn
}

func grpc_subscribe(conn *grpc.ClientConn) {
	var err error
	client := pb.NewGeyserClient(conn)

	var subscription pb.SubscribeRequest

	if subscriptBlocks {
		if subscription.Blocks == nil {
			subscription.Blocks = make(map[string]*pb.SubscribeRequestFilterBlocks)
		}
		subscription.Blocks["blocks"] = &pb.SubscribeRequestFilterBlocks{}
	}

	if (len(accountsFilter)+len(accountOwnersFilter)) > 0 || (subscriptAccounts) {
		if subscription.Accounts == nil {
			subscription.Accounts = make(map[string]*pb.SubscribeRequestFilterAccounts)
		}

		subscription.Accounts["account_sub"] = &pb.SubscribeRequestFilterAccounts{}

		if len(accountsFilter) > 0 {
			subscription.Accounts["account_sub"].Account = accountsFilter
		}

		if len(accountOwnersFilter) > 0 {
			subscription.Accounts["account_sub"].Owner = accountOwnersFilter
		}
	}

	// Set up the transactions subscription
	if subscription.Transactions == nil {
		subscription.Transactions = make(map[string]*pb.SubscribeRequestFilterTransactions)
	}

	// Subscribe to generic transaction stream
	if subscriptTransactions {
		subscription.Transactions["transactions_sub"] = &pb.SubscribeRequestFilterTransactions{
			Failed: &failedTransactions,
			Vote:   &voteTransactions,
		}

		subscription.Transactions["transactions_sub"].AccountInclude = transactionsAccountsInclude
		subscription.Transactions["transactions_sub"].AccountExclude = transactionsAccountsExclude
	}

	subscriptionJson, err := json.Marshal(&subscription)
	if err != nil {
		log.Printf("Failed to marshal subscription request: %v", subscriptionJson)
	}
	log.Printf("Subscription request: %s", string(subscriptionJson))

	// Set up the subscription request
	ctx := context.Background()
	if token != "" {
		md := metadata.New(map[string]string{"x-token": token})
		ctx = metadata.NewOutgoingContext(ctx, md)
	}

	stream, err := client.Subscribe(ctx)
	if err != nil {
		log.Fatalf("%v", err)
	}
	err = stream.Send(&subscription)
	if err != nil {
		log.Fatalf("%v", err)
	}

	for {
		resp, err := stream.Recv()
		timestamp := time.Now().UnixNano()

		if err == io.EOF {
			return
		}
		if err != nil {
			log.Fatalf("Error occurred in receiving update: %v", err)
		}

		log.Printf("%v %v", timestamp, resp)
	}
}
func main() {
	conn := grpc_connect(grpcAddr, false)
	defer conn.Close()

	grpc_subscribe(conn)
}
```


# Solana Geyser Stream Rust Subscription Example

Introducing the Rust request methods of BlockRazor Solana Geyser Stream

### Quick Start

Please refer to [README.md](https://github.com/BlockRazorinc/geyserstream-client-rust/blob/main/README.md)

### Code Example

```rust
use {
	anyhow::Context,
    futures::{stream::StreamExt},
    solana_signature::Signature,
    std::{
        collections::{HashMap},
		error::Error,
    },
    tonic::transport::ClientTlsConfig,
	tonic::transport::Channel,
	tonic::IntoStreamingRequest,
    geyser_stream::geyser_client::GeyserClient,
	geyser_stream::subscribe_update::UpdateOneof,
	geyser_stream::SubscribeRequestFilterBlocks,
	geyser_stream::SubscribeRequestFilterTransactions,
	geyser_stream::SubscribeRequest,
	geyser_stream::SubscribeRequestFilterAccounts,
};

mod solana_storage_flat {
    include!(concat!(env!("OUT_DIR"), "/solana.storage.confirmed_block.rs"));
}

pub mod solana {
    pub mod storage {
        pub mod confirmed_block {
            pub use super::super::super::solana_storage_flat::*;
        }
    }
}

pub mod geyser_stream {
    pub use crate::solana;
    include!(concat!(env!("OUT_DIR"), "/geyser.rs"));
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
	let endpoint = "https://geyserstream-tokyo.blockrazor.xyz:443";
	let token = "";
	let subscribe_accounts = true;
	let subscribe_transactions = false;
	let subscribe_blocks = false;
	let subscribe_test_account = "HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K".to_string();

	let channel = Channel::from_shared(endpoint.to_string())
		.map_err(|e| Box::<dyn Error>::from(format!("Invalid URI: {}", e)))?
    	.tls_config(ClientTlsConfig::new().with_native_roots())
    	.map_err(|e| Box::<dyn Error>::from(format!("TLS config error: {}", e)))?
		.connect()
		.await
		.map_err(|e| Box::<dyn Error>::from(format!("Connection error: {}", e)))?;

	let mut client = GeyserClient::new(channel.clone());

	// subscribe account filters
	let mut accounts_filter = HashMap::new();
	if subscribe_accounts {
		accounts_filter.insert(
			"client".to_owned(),
			SubscribeRequestFilterAccounts {
				account: vec![subscribe_test_account],
				owner: vec![],
				filters: vec![],
				nonempty_txn_signature: Some(true),
			},
		);
	}

	// subscribe transaction filters
	let mut transactions_filter = HashMap::new();
	if subscribe_transactions {
		transactions_filter.insert(
			"client".to_string(),
			SubscribeRequestFilterTransactions {
				vote: Some(false),
				failed: Some(false),
				signature: None,
				account_include: vec![],
				account_exclude: vec![],
				account_required: vec![],
			},
		);
	}

	// subscribe block filters
	let mut blocks_filter = HashMap::new();
	if subscribe_blocks {
		blocks_filter.insert(
			"client".to_owned(),
			SubscribeRequestFilterBlocks {
				account_include: vec![],
				include_transactions: Some(false),
				include_accounts: Some(false),
				include_entries: Some(false),
			}
		);
	}
	
	// subscribe request
	let request = SubscribeRequest {
		accounts: accounts_filter,
		transactions: transactions_filter,
		blocks: blocks_filter,
		transactions_status: HashMap::new(),
		entry: HashMap::new(),
		blocks_meta: HashMap::new(),
		slots: HashMap::new(),
		commitment: None,
		accounts_data_slice: vec![],
		ping: None,
		from_slot: None,
	};
    let streaming_req = to_streaming_request(request, token.to_string());

	// subscribe
	let response = client.subscribe(streaming_req).await.unwrap();
	let mut resp_stream = response.into_inner();
    while let Some(message) = resp_stream.next().await {
        match message {
            Ok(msg) => {
                match msg.update_oneof {
                    Some(UpdateOneof::Account(msg)) => {
						println!("receive account: {:?}", msg.account);
                    }
                    Some(UpdateOneof::Transaction(msg)) => {
						let tx = msg
                            .transaction
                            .ok_or(anyhow::anyhow!("no transaction in the message"))?;
						println!("receive transaction: {:?}", Signature::try_from(tx.signature.as_slice()).context("invalid signature")?.to_string());
                    }
                    Some(UpdateOneof::Block(msg)) => {
						println!("receive block: {:?}", msg.slot);
                    }

					// other implementations can be added here

					_ => {
						println!("receive unknown message");
						continue
					}
                }
            }
            Err(error) => {
                println!("stream error: {error:?}");
                break;
            }
        }
    }

    Ok(())
}

fn to_streaming_request(req: SubscribeRequest, token: String) -> impl IntoStreamingRequest<Message = SubscribeRequest> {
    let stream = tokio_stream::iter(std::iter::once(req));
    let mut request = tonic::Request::new(stream);

    request.metadata_mut().insert(
        "x-token",
        token.parse().unwrap(),
    );

    request
}
```


# Solana Geyser Stream JS Subscription Example

Introducing the JS request methods of BlockRazor Solana Geyser Stream

### Quick Start

Please refer to [README.md](https://github.com/BlockRazorinc/geyserstream-client-js/blob/main/README.md)

### Code Example

```javascript
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const bs58 = require('bs58');

// Load proto file
const PROTO_PATH = __dirname + '/proto/geyser.proto';
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
});
const geyserProto = grpc.loadPackageDefinition(packageDefinition).geyser;

// Create gRPC client with SSL credentials and metadata
function createGeyserClient(host, token) {
  // Use system's root certificates for SSL verification
  // No need to specify certificate paths explicitly
  const sslCreds = grpc.credentials.createSsl();

  // Create metadata with x-token
  const metadata = new grpc.Metadata();
  metadata.add('x-token', token);

  return {
    client: new geyserProto.Geyser(host, sslCreds),
    metadata
  };
}

// Abstract method to build subscription request
function buildSubscribeRequest(config) {
  const request = {};

  // Add account subscription if enabled
  if (config.subscribeAccounts) {
    const { filterKey, owners, accounts, filters, nonemptyTxnSignature } = config.accountParams;
    request.accounts = {
      [filterKey]: {
        owner: owners,
        account: accounts,
        filters: filters,
        nonempty_txn_signature: nonemptyTxnSignature
      }
    };
  }

  // Add block subscription if enabled
  if (config.subscribeBlocks) {
    const { filterKey, includeTransactions, includeAccounts, includeEntries, accountInclude } = config.blockParams;
    request.blocks = {
      [filterKey]: {
        account_include: accountInclude,
        include_transactions: includeTransactions,
        include_accounts: includeAccounts,
        include_entries: includeEntries
      }
    };
  }

  // Add transaction subscription if enabled
  if (config.subscribeTransactions) {
    const { filterKey, vote, failed, accountInclude, accountExclude, accountRequired, signature } = config.transactionParams;
    request.transactions = {
      [filterKey]: {
        vote: vote,
        failed: failed,
        account_include: accountInclude,
        account_exclude: accountExclude,
        account_required: accountRequired,
        signature: signature
      }
    };
  }

  // Add common parameters
  request.commitment = config.commitment;

  return request;
}

// Handle incoming updates
function handleUpdate(update) {
  switch (update.update_oneof) {
    case 'account':
      console.log(`\nAccount update (slot: ${update.account.slot})`);
      console.log(`Account pubkey: ${bs58.encode(update.account.account.pubkey)}`);
      console.log(`Owner: ${bs58.encode(update.account.account.owner)}`);
      console.log(`Lamports: ${update.account.account.lamports}`);
      break;

    case 'block':
      console.log(`\nBlock update (slot: ${update.block.slot})`);
      console.log(`Blockhash: ${update.block.blockhash}`);
      console.log(`Transaction count: ${update.block.transactions.length}`);
      break;

    case 'transaction':
      console.log(`\nTransaction update (slot: ${update.transaction.slot})`);
      console.log(`Signature: ${bs58.encode(update.transaction.transaction.signature)}`);
      console.log(`Is vote: ${update.transaction.transaction.is_vote}`);
      break;

    default:
      console.log('\nUnknown update type');
  }
}

// Main subscription logic
async function subscribeToGeyser() {
  // Subscription configuration
  const subscribeConfig = {
    // Common configuration
    commitment: 'CONFIRMED', // Commitment level: PROCESSED/CONFIRMED/FINALIZED

    // Account subscription configuration
    subscribeAccounts: false, // Whether to subscribe to accounts
    accountParams: {
      filterKey: 'account-filter-1',
      owners: ['11111111111111111111111111111111'], // System program owner
      accounts: [], // Specific accounts to subscribe to (empty for all matching)
      filters: [], // Additional filters (e.g., memcmp, datasize)
      nonemptyTxnSignature: false // Whether to include only updates with transaction signatures
    },

    // Block subscription configuration
    subscribeBlocks: false, // Whether to subscribe to blocks
    blockParams: {
      filterKey: 'block-filter-1',
      accountInclude: [], // Include blocks involving these accounts (empty for all)
      includeTransactions: true, // Whether to include transactions in blocks
      includeAccounts: false, // Whether to include account updates in blocks
      includeEntries: false // Whether to include entries in blocks
    },

    // Transaction subscription configuration
    subscribeTransactions: true, // Whether to subscribe to transactions
    transactionParams: {
      filterKey: 'tx-filter-1',
      vote: false, // Whether to include only vote transactions
      failed: false, // Whether to include only failed transactions
      accountInclude: [], // Include transactions involving these accounts
      accountExclude: [], // Exclude transactions involving these accounts
      accountRequired: [], // Transactions must involve these accounts
      signature: null // Specific transaction signature (empty for all matching)
    }
  };

  // Client configuration
  const clientConfig = {
    host: 'geyserstream-tokyo.blockrazor.xyz:443', // gRPC server address
    token: '' // auth token
  };

  // Create client with SSL credentials
  const { client, metadata } = createGeyserClient(
    clientConfig.host,
    clientConfig.token
  );

  // Build subscription request
  const subscribeRequest = buildSubscribeRequest(subscribeConfig);
  console.log('Sending subscription request:', JSON.stringify(subscribeRequest, null, 2));

  // Establish stream and send request
  const stream = client.Subscribe(metadata);

  stream.on('data', (update) => handleUpdate(update));
  stream.on('error', (err) => console.error('Stream error:', err));
  stream.on('end', () => console.log('Stream ended'));

  stream.write(subscribeRequest);

  // Handle process exit
  process.on('SIGINT', () => {
    console.log('Closing subscription...');
    stream.end();
    process.exit(0);
  });
}

// Start subscription
subscribeToGeyser().catch(console.error);
```


# BSC Block Stream

This section introduces BlockRazor's Block Stream service for BSC, primarily NewBlocks.


# BSC Block Stream

Introducing the services, application scenarios, and integration methods of BlockRazor BSC NewBlocks.

### What is BSC NewBlocks

NewBlocks is a high-performance block stream service provided by BlockRazor, used for low-latency subscription to the latest blocks and confirmed transactions. NewBlocks helps users receive the latest block content earlier, and integrates the block header, transaction list, and next validator info into their monitoring or strategy systems with lower latency.

NewBlocks distributes the latest block data based on [BEF](/core-technology/blockchain-edge-fabric). When a block is generated in the network and begins to propagate, BlockRazor receives the block in multiple core areas as early as possible and then forwards it to subscribers through low-latency links, shortening the time for users to receive block data.

### Scenarios of BSC NewBlocks

* Confirmed Transaction Monitoring: Receives confirmed transactions from the latest block in real time, used to monitor target addresses, popular contracts, or abnormal transaction behavior.
* Block-level data analysis: Obtaining block headers, transaction lists, and next validator info for block research, node observation, and network state analysis.
* Strategy data input: Serving as the confirmed data source for the trading system, it works in conjunction with capabilities such as Public Mempool and Transaction Submission to build a more complete monitoring and execution chain.

### Benchmark

In our benchmark on the latency of receiving new blocks, we compared BlockRazor with a regular Node in four regions: Dublin, Frankfurt, Tokyo, and Virginia. The evaluation was based on the time difference for clients receiving the same block from BlockRazor and node in the comparable sample.

<table><thead><tr><th width="131.58203125">Region</th><th width="190.0703125">BlockRazor Lead Rate</th><th>Avg Lead</th><th>P50 Lead</th><th>P90 Lead</th></tr></thead><tbody><tr><td>Dublin</td><td><strong>100.0%</strong></td><td>60.4 ms</td><td>50.5 ms</td><td>90.7 ms</td></tr><tr><td>Frankfurt</td><td><strong>100.0%</strong></td><td>61.5 ms</td><td>54.7 ms</td><td>92.3 ms</td></tr><tr><td>Tokyo</td><td><strong>100.0%</strong></td><td>239.1 ms</td><td>145.7 ms</td><td>622.9 ms</td></tr><tr><td>Virginia</td><td><strong>99.5%</strong></td><td>69.0 ms</td><td>58.3 ms</td><td>121.6 ms</td></tr></tbody></table>

The results show that BlockRazor maintained a consistent lead across all four regions. In the Frankfurt, Tokyo, and  Dublin regions, BlockRazor achieved a 100% lead rate; in the Virginia region, the lead rate reached 99.5%. In terms of lead magnitude, BlockRazor's average lead time across different regions was approximately 60.4 ms, 61.5 ms, 239.1 ms, and 69.0ms; under the P90 dimension, the lead magnitudes were 90.7 ms, 92.3 ms, 622.9ms, and 121.6ms, respectively.

This indicates that BlockRazor is able to receive block data more consistently ahead of ordinary nodes when new blocks arrive.

### Price

<table><thead><tr><th width="167.296875">Payment Method</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$50 / stream / day<br>$500 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_block_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 / month <br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Endpoint

<table><thead><tr><th width="160">Region</th><th>Relay Address</th></tr></thead><tbody><tr><td>Frankfurt</td><td>64.130.47.75:50051</td></tr><tr><td>Tokyo</td><td>63.254.162.18:50051</td></tr><tr><td>Dublin</td><td>141.98.217.82:50051</td></tr><tr><td>Virginia</td><td>208.91.105.204:50051</td></tr></tbody></table>

### Request Parameters

<table><thead><tr><th width="164">Parameters</th><th width="117">Mandatory</th><th width="102">Format</th><th width="94">Example</th><th>Description</th></tr></thead><tbody><tr><td>NodeValidation</td><td>Mandatory</td><td>boolean</td><td>false</td><td>This field currently only supports being set to <code>false</code>, and the relay will push all new blocks (unchecked) with lower latency.</td></tr></tbody></table>

### Request Example

<https://github.com/BlockRazorinc/relay_example>

{% tabs %}
{% tab title="Go" %}

```go
package main

import (
	"context"
	"fmt"

	// directory of the generated code using the provided relay.proto file
	pb "github.com/BlockRazorinc/relay_example/protobuf"
	"google.golang.org/grpc"
)

// auth will be used to verify the credential
type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apiKey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func main() {

	// BlockRazor relay endpoint address
	blzrelayEndPoint := "ip:port"

	// auth will be used to verify the credential
	auth := Authentication{
		"your auth token",
	}

	// open gRPC connection to BlockRazor relay
	var err error
	conn, err := grpc.Dial(blzrelayEndPoint, grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth), grpc.WithWriteBufferSize(0), grpc.WithInitialConnWindowSize(128*1024))
	if err != nil {
		fmt.Println("error: ", err)
		return
	}

	// use the Gateway client connection interface
	client := pb.NewGatewayClient(conn)

	// create context and defer cancel of context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create a subscription using the stream-specific method and request
	stream, err := client.NewBlocks(ctx, &pb.BlocksRequest{NodeValidation: false})
	if err != nil {
		fmt.Println("failed to subscribe new block: ", err)
		return
	}

	for {

		reply, err := stream.Recv()
		if err != nil {
			fmt.Println("stream receive error: ", err)
		}

		fmt.Println("recieve new block, block hash is ", reply.Hash)
	}
}
```

{% endtab %}
{% endtabs %}

#### Proto

The code of `relay.proto` is as follows:

```go
syntax = "proto3";

package blockchain;

option go_package = "/Users/code/relay/grpcServer"; 
service Gateway {
  rpc SendTx (SendTxRequest) returns (SendTxReply) {}
  rpc SendTxs (SendTxsRequest) returns (SendTxsReply) {}
  rpc NewTxs (TxsRequest) returns (stream TxsReply){}
  rpc NewBlocks (BlocksRequest) returns (stream BlocksReply){}
}

message TxsRequest{
  bool node_validation = 1;
}

message Tx{
  bytes from = 1;
  int64 timestamp = 2;
  bytes raw_tx = 3;
}

message TxsReply{
   Tx tx = 1;
}

message BlocksRequest{
  bool node_validation = 1;
}

message BlockHeader{
  string parent_hash = 1;
  string sha3_uncles = 2;
  string miner = 3;
  string state_root = 4;
  string transactions_root = 5;
  string receipts_root = 6;
  string logs_bloom = 7;
  string difficulty = 8;
  string number = 9;
  uint64 gas_limit = 10;
  uint64 gas_used = 11;
  uint64 timestamp = 12;
  bytes extra_data = 13;
  string mix_hash = 14;
  uint64 nonce = 15;
  uint64 base_fee_per_gas = 16;
  string withdrawals_root = 17;
  uint64 blob_gas_used = 18;
  uint64 excess_blob_gas = 19;
  string parent_beacon_block_root = 20;
  string requests_hash = 21;
}

message NextValidator{
  string block_height = 1;
  string coinbase = 2;
}

message BlocksReply{
  string hash = 1;
  BlockHeader header = 2;
  repeated NextValidator nextValidator = 3;
  repeated Tx txs = 4;
}

message Transaction {
  string content = 1;
}

message Transactions {
  repeated Transaction transactions = 1;
}

message SendTxRequest {
  string transaction = 1;
}

message SendTxsRequest {
  string transactions = 1;
}

message SendTxReply {
  string tx_hash = 1;
}

message SendTxsReply {
  repeated string tx_hashs = 1;
}
```

### Response Example

**Success**

```json
{
	"hash": "0xe4a85aaa8cf4c85c4abf59c06b744ae680941e7ffba351fd4c166f0264e860de",
	"header": {
		"parent_hash": "0x0105992a6c305b0bbfbf7b4eebbbc92c4dca1bb2a18c1f93c551afc2c73c0668",
		"sha3_uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
		"miner": "0x9f1b7FAE54BE07F4FEE34Eb1aaCb39A1F7B6FC92",
		"state_root": "0xce2a7e35a643e5840e510df9eea8ba9177dd62fe743439ef159d7d9e783afd28",
		"transactions_root": "0x63c952adbe28ef60d8e723acec299ca62a915b51d05b060cb6fe97bb28814ab3",
		"receipts_root": "0xd3232a5308a69591dda132bd4ce1c1dbd5968ed9feee5f1385a5e7ad1b10527b",
		"logs_bloom": "8477149424673580296520836103527692456522284192111573121045681184670018737794069194486118998020237483301054022136573687133364882576385281526094331485603954025070865625599001965016471857986082361337851986790329901990002923474000909032395056767065187980257808021700240863783968353544053840361314138235944346718686049569138609472702141575196319415411746602993576034439427711191239874622732325947862558346553652593648034520192193214092215911630192819139737571343595880627533215841996399050061528845101804940660169259391681335118771766679784629118424769368884717750281985023526825099358014255162966726322082309693440030370",
		"difficulty": "2",
		"number": "40370748",
		"gas_limit": 139997863,
		"gas_used": 12180159,
		"timestamp": 1720674742,
		"extra_data": "2IMBBAqEZ2V0aIhnbzEuMjEuNYVsaW51eAAAAEj6ewX4tYMf//+4YKyfFdynrjSSLQKJj9FdhVSwaDFtnAdMnqNzKeVm2agkWE79uSpjZ2Wbrxt2eCFSvBRuPLIhONzRYpQgx/DeH/Xrhsz/A+EZw4BFaOVV3ch76i1QdJy1CADUqoc/XEaBHvhMhAJoAjqg3GksrNaxsaC5pxx8JS0YFpA6OQh7ofh7pTmra2Ve7lOEAmgCO6ABBZkqbDBbC7+/e07ru8ksTcobsqGMH5PFUa/CxzwGaIBHk8oYx40FGg4hnpMSQsTiOEAObH2DV3ytkUGOdEk4KENXgVf0/xsCA56w2r/VIqP7ux7HCD9vrh7H7fjxdU3xAA==",
		"mix_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
		"noncpe":0,
		"base_fee_per_gas":0,
		"withdrawals_root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
		"blob_gas_used": 131072,
		"excess_blob_gas":0,
		"parent_beacon_block_root":""
	
	},
	"nextValidator": [{
		"block_height": "40370749",
		"coinbase": "0xbdcc079bbb23c1d9a6f36aa31309676c258abac7"
	}, {
		"block_height": "40370750",
		"coinbase": "0xc2d534f079444e6e7ff9dabb3fd8a26c607932c8"
	}],
	"txs": [{
		"raw_tx": "+K6DzSWShQEqBfIAgwEwY5R2021E3EWV6NLrOtdF8XXtoTQoT4C4RKkFnLsAAAAAAAAAAAAAAAAFWjs3lXv70zRb7Zlo5+jdVtZwZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBV8GygaywAAgZOguQQg9l/3+bmldXAcm9lsSsgxwq8m+wqcXh/KwbKJ7E+gAS8g6ZuII8KJWFBFpVzTcyptzqfro00WDKR3oi1ly7g="
	}, {
		"raw_tx": "+QVPgxUyKIUBKgXyAIMHoSCUE3kk18NoFuDcrwFuthfMLJLAV4KAuQTkyYB1OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAGQptag/qdv1AH5JjIvJ4fgACqZ4AQoECA4FDA0HAQsPAgAJBgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw9aP+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD2B6d4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPYNaEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw9jajbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD2a1DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPZrUOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw9qNqnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD2o2qcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPajapwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw9u7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD3ePNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPiGlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw+IaWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD4n5xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPqOuWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw+o8GuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbIQBTEAMmDL3iUlt3D7Hrj6ACjQhWYvzwIOV06fd5+CxHWRbBH4sYJEbXQ/y6+cimsuGJSFGXoSkH5dZf7RDlpIA584nEMYIrJUVhB3PQZWCiY951T9xxDCPwvuqG40jEYJR68n788tw7y1nUnZwIJVWAoko+4Z/immcv2IcTzZ53J2RNPMiqAlb21gAwDfKQtEF7HpWEwn422R1b9VDfEey+3vxMAzZcisrwfRh49shbMLJpz4CnFt5FyAS9eI9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhy0jMwJkMmVvzRghVWVS2LwKSffduFZtzpO03R2sucDE1hqfyr8bpMWOG7oHhhdknlcQ/+leSGQf9X8GF1g7MBIWrGp/Ri03p4GYSY3Rj6RaN+SWlHEPySIll217WvNyHf3dtjfz9ic4gXWSc4l+vP5fXff/59EgoqDMhgEOP/iOfCAJsJt5Uisq/0Cpz/+2fzV4KknIckM8W1VsH8S34kLRauuV1UKaY7AU0K0BIA1dr8HMkOm/Ci+fDyuSFJY94GToC30fN+eoJ0+scIcdj9XpEmHUxWAT6TtSwk1zVm14HyboHCzcmyYjRIcHxPeMrJiwXSKXkgCTggJw9V0ETOLYuZL"
	}, {
		"raw_tx": "+K6DzSWThQEqBfIAgwEw4JRV05gyb5kFn/d1SFJGmZAnsxl5VYC4RKkFnLsAAAAAAAAAAAAAAADt2wDQgCLygqAXnLTDVpf1DvOxOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALA2UyUvg7vwAgZOgReRi2miA5qA+ERtjgmiOmqwmtCDhvtj0TFKrtUAuQWegP7xd7Gj89uQM03KMcfceKvAKkxwwajcJB1M8xHQlR4g="
	}, {
		"raw_tx": "+QFrBYTodUcAgwQ7j5Td7QSbsOJzaTkPQPjEolIpjjUwjoC5AQS39rSuAAAAAAAAAAAAAAAAVdOYMm+ZBZ/3dUhSRpmQJ7MZeVUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHP5UVLi+/8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcWBJiX8HPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAisdqUcyVDZgi1ouD/hrZezLNWA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGUoDDbxwrT1VjxX+u9C70/BMIgFYEATNA/P4NaAIu8AYupoFgRK+X8L9sYxgVNJobVpne44NbM/wQTYLLDGqanoAeH"
	}]
}
```

**Error**

```
rpc error: code = Unknown desc = data streams have exceeded its max limit [5]
```


# Ethereum Block Stream

This section introduces BlockRazor's Block Stream service for Ethereum, primarily NewBlocks.


# Ethereum Block Stream

This section introduces the services, application scenarios, and integration methods of BlockRazor Ethereum NewBlocks.

### What is Ethereum NewBlocks

NewBlocks is a high-performance block stream service provided by BlockRazor, used for low-latency subscription to the latest blocks and confirmed transactions. NewBlocks helps users receive the latest block content earlier, and integrates the block header, transaction list, and next validator info into their monitoring or strategy systems with lower latency.

NewBlocks distributes the latest block data based on [BEF](/core-technology/blockchain-edge-fabric). When a block is generated in the network and begins to propagate, BlockRazor receives the block in multiple core areas as early as possible and then forwards it to subscribers through low-latency links, shortening the time for users to receive block data.

### Scenarios of Ethereum NewBlocks

* Confirmed Transaction Monitoring: Receives confirmed transactions from the latest block in real time, used to monitor target addresses, popular contracts, or abnormal transaction behavior.
* Block-level data analysis: Obtaining block headers, transaction lists, and next validator info for block research, node observation, and network state analysis.
* Strategy data input: Serving as the confirmed data source for the trading system, it works in conjunction with capabilities such as Public Mempool and Transaction Submission to build a more complete monitoring and execution chain.

### Price

<table><thead><tr><th width="167.296875">Payment Method</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$50 / stream / day<br>$500 / stream / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_block_stream&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 / month <br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Endpoint

<table><thead><tr><th width="160">Region</th><th>Relay Address</th></tr></thead><tbody><tr><td>Frankfurt</td><td>64.130.47.75:50061</td></tr><tr><td>Tokyo</td><td>63.254.162.18:50061</td></tr><tr><td>Virginia</td><td>208.91.105.204:50061</td></tr></tbody></table>

### Request Example

<https://github.com/BlockRazorinc/eth_relay_example>

{% tabs %}
{% tab title="Go" %}

```go
package main

import (
	"context"
	"fmt"

	// directory of the generated code using the provided relay.proto file
	pb "github.com/BlockRazorinc/eth_relay_example/protobuf"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

// auth will be used to verify the credential
type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apikey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func main() {

	// BlockRazor relay endpoint address
	blzrelayEndPoint := "ip:port"

	// auth will be used to verify the credential
	auth := Authentication{
		"your auth token",
	}

	// open gRPC connection to BlockRazor relay
	var err error
	conn, err := grpc.NewClient(blzrelayEndPoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(&auth), grpc.WithWriteBufferSize(0), grpc.WithInitialConnWindowSize(128*1024))
	if err != nil {
		fmt.Println("error: ", err)
		return
	}
	defer conn.Close()

	// use the Relay client connection interface
	client := pb.NewRelayClient(conn)

	// create context and defer cancel of context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// create a subscription using the stream-specific method and request
	stream, err := client.NewBlocks(ctx, &pb.NewBlocksRequest{ParsedTxs: true})
	if err != nil {
		fmt.Println("failed to subscribe new block: ", err)
		return
	}

	for {
		reply, err := stream.Recv()
		if err != nil {
			fmt.Println("stream receive error: ", err)
			return
		}

		header := reply.GetHeader()
		fmt.Println("receive new block, block hash is ", reply.Hash, " number is ", header.GetNumber(), " txs are ", len(reply.Transactions))
	}
}
```

{% endtab %}
{% endtabs %}

#### Proto

The code of `relay.proto` is as follows:

```go
syntax = "proto3";

package relay.v1;

option go_package = "github.com/BlockRazorinc/eth_relay_example/protobuf";

service Relay {
  rpc SendTx(SendTxRequest) returns (SendTxReply);
  rpc NewTxs(NewTxsRequest) returns (stream NewTx);
  rpc NewBlocks(NewBlocksRequest) returns (stream NewBlock);
}

message SendTxRequest {
  bytes raw_tx = 1;
}

message SendTxReply {
  string tx_hash = 1;
}

message NewTxsRequest {
  bool include_raw_tx = 1;
}

message NewTx {
  string tx_hash = 1;
  bytes raw_tx = 2;
  int64 first_seen_unix_ns = 3;
  string source = 4;
}

message NewBlocksRequest {
  bool parsed_txs = 1;
}

message NewBlock {
  string hash = 1;
  BlockHeader header = 2;
  repeated BlockTransaction transactions = 3;
  repeated Withdrawal withdrawals = 4;
}

message BlockHeader {
  string parent_hash = 1;
  string sha3_uncles = 2;
  string miner = 3;
  string state_root = 4;
  string transactions_root = 5;
  string receipts_root = 6;
  string logs_bloom = 7;
  string difficulty = 8;
  string number = 9;
  string gas_limit = 10;
  string gas_used = 11;
  string timestamp = 12;
  string extra_data = 13;
  string mix_hash = 14;
  string nonce = 15;
  string base_fee_per_gas = 16;
  string withdrawals_root = 17;
  string blob_gas_used = 18;
  string excess_blob_gas = 19;
  string parent_beacon_block_root = 20;
}

message BlockTransaction {
  bytes raw_tx = 1;
  bytes from = 2;
}

message Withdrawal {
  string address = 1;
  string amount = 2;
  string index = 3;
  string validator_index = 4;
}
```

### Response Example

**Success**

```json
{
  "hash": "0xaf1f...4116",
  "header": {
    "parentHash": "0x4603...cfee",
    "sha3Uncles": "0x1dcc...9347",
    "miner": "0x0e33b1c214463062753ad849a28e54667e0c87c2",
    "stateRoot": "0x4c26...1c96",
    "transactionsRoot": "0xb6b8...731e",
    "receiptsRoot": "0xd9ae...75ea",
    "logsBloom": "0x263c...59fdb",
    "difficulty": "0x0",
    "number": "0x1845661",
    "gasLimit": "0x3938700",
    "gasUsed": "0x14c4d26",
    "timestamp": "0x6a475117",
    "extraData": "0x",
    "mixHash": "0xb048...3154",
    "nonce": "0x0000000000000000",
    "baseFeePerGas": "71769385",
    "withdrawalsRoot": "0xe5de...f2f2",
    "blobGasUsed": "0x20000",
    "excessBlobGas": "0xa8d8c53",
    "parentBeaconBlockRoot": "0x0319...7a00"
  },
  "transactions": [
    {
      "rawTx": "<base64 encoded raw transaction>",
      "from": "<base64 encoded sender>"
    }
  ],
  "withdrawals": [
    {
      "address": "0x1135fa96848f34bff9d003f4c1699ae97418de29",
      "amount": "0xda361b",
      "index": "0x80678fb",
      "validatorIndex": "0x1b1fc1"
    }
  ]
}
```

**Error**

```
rpc error: code = Unknown desc = data streams have exceeded its max limit [5]
```


# Base Block Stream

This section introduces the Block Stream services provided by BlockRazor for the Base, primarily Block Stream and FlashBlock Stream.


# Base Get BlockStream

Introducing the BlockRazor Base Get BlockStream service, its advantages, and how to integrate it.

### What is Base Get BlockStream

`Get BlockStream` is a real-time block data subscription interface provided by BlockRazor for Base, used to continuously retrieve the latest generated block data on Base in a low-latency manner. This interface is based on the gRPC protocol and is suitable for trading systems and infrastructure systems that need to continuously consume block data.

### Why choose Base Get BlockStream

For trading systems and infrastructure systems, the ability to acquire block data is not just about "getting new blocks," but also about the timeliness of receiving in different regions, the stability of data links, and overall performance under long-term operation. BlockRazor, based on [BEF](/core-technology/blockchain-edge-fabric), provides access points to multiple regions such as Frankfurt, Virginia, and Tokyo on Base. According to [Base Benchmark](https://blockrazor.io/zh/blog/20250922basebenchmark/), BlockRazor demonstrates an advantage over Base's official service in block reception latency across multiple regions, with a particularly significant lead in the mid-to-high percentile range.

### FAQ

<details>

<summary>What is the difference between Get BlockStream and Get FlashBlockStream</summary>

The core difference between the two lies in the different data granularity, time points, and applicable scenarios.

* Get BlockStream\
  It is used to retrieve block that has already been formed on Base, focusing on confirmed blocks and transactions within. It is more suitable for monitoring confirmation results, block-level analysis, on-chain data processing, and data systems that need to stably consume blocks.
* **Get FlashBlockStream**\
  Used to retrieve FlashBlock data on Base. FlashBlock is a "sub-block" of data pushed by Base approximately every 200ms, providing pre-confirmation information for transactions much earlier than the standard 2-second formal block time. It is more suitable for scenarios that are more sensitive to low latency and want to see on-chain changes as early as possible.

</details>

### Endpoint

{% tabs %}
{% tab title="gRPC" %}

<table><thead><tr><th width="138.33984375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Virginia</td><td>virginia.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.grpc.base.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Price

The price is $30 / stream / day and $300 / stream / month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_blockstream&#x26;billing=day" class="button primary small">Subscribe</a>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Request Example

{% tabs %}
{% tab title="gRPC" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/c4bec3d65e55ffb0da07253fa78aefe1b1c07e33/main.go#L42)

```go
// GetBlockStream provides a simplified example of subscribing to and processing the regular block stream.
// Note: This function attempts to connect and subscribe only once. For production use, implement your own reconnection logic.
func GetBlockStream(authToken string) {
	log.Printf("[BlockStream] Attempting to connect to gRPC server at %s...", grpcAddr)

	// Establish a connection to the gRPC server with a timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, grpcAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Printf("[BlockStream] Failed to connect to gRPC server: %v", err)
		return
	}
	defer conn.Close()

	log.Println("[BlockStream] Successfully connected to gRPC server.")
	client := basepb.NewBaseApiClient(conn)

	// Create a new context with authentication metadata for the stream subscription.
	streamCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", authToken))
	stream, err := client.GetBlockStream(streamCtx, &basepb.GetBlockStreamRequest{})
	if err != nil {
		log.Printf("[BlockStream] Failed to subscribe to stream: %v", err)
		return
	}

	log.Println("[BlockStream] Subscription successful. Waiting for new blocks...")

	// Loop indefinitely to receive messages from the stream.
	for {
		block, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				log.Println("[BlockStream] Stream closed by the server (EOF).")
			} else {
				log.Printf("[BlockStream] An error occurred while receiving data: %v", err)
			}
			break // Exit the loop on error or stream closure.
		}

		// Process the received block data.
		log.Printf("=> [BlockStream] Received new block: Number=%d, Hash=%s, TransactionCount=%d",
			block.GetBlockNumber(),
			block.GetBlockHash(),
			len(block.GetTransactions()),
		)
		// To decode transactions, you can call DecodeTransactions(block.Transactions).
	}
}
```

{% endtab %}
{% endtabs %}

#### [proto](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/proto/BaseApi.proto)

```go
syntax = "proto3";

option go_package = "./basepb";


import "google/protobuf/wrappers.proto";

message BaseBlock {
  string parent_hash = 1;
  string fee_recipient = 2;
  bytes state_root = 3;
  bytes receipts_root = 4;
  bytes logs_bloom = 5;
  bytes prev_randao = 6;
  uint64 block_number = 7;
  uint64 gas_limit = 8;
  uint64 gas_used = 9;
  uint64 timestamp = 10;
  bytes extra_data = 11;
  repeated uint64 base_fee_per_gas = 12;
  string block_hash = 13;
  repeated bytes transactions = 14;

  repeated Withdrawal withdrawals = 15;
  google.protobuf.UInt64Value blob_gas_used = 16;
  google.protobuf.UInt64Value excess_blob_gas = 17;
  google.protobuf.BytesValue withdrawals_root = 18;
}

message Withdrawal {
  uint64 index = 1;
  uint64 validator = 2;
  bytes address = 3;
  uint64 amount = 4;
}

message GetRawFlashBlocksStreamRequest {
}

message GetBlockStreamRequest {
}

message SendTransactionRequest {
  string rawTransaction = 1;
}

message SendTransactionResponse {
  string txHash = 1;
}

message FlashBlockStrRequest {
}

message RawFlashBlockStrResponse {
  bytes message = 1;
}

service BaseApi {
  rpc SendTransaction(SendTransactionRequest) returns (SendTransactionResponse);
  rpc GetBlockStream(GetBlockStreamRequest) returns (stream BaseBlock);
  rpc GetRawFlashBlockStream(GetRawFlashBlocksStreamRequest) returns (stream RawFlashBlockStrResponse);
}
```

## Response

**Normal**

```go
Number=35347872, Hash=0x1c1dd5911cf8fe47c227159f5e3dad08d289879a225b6d51840f4ecd0b212dd8, TransactionCount=252
```

**Abnormal**

```go
rpc error: code = Unknown desc = Authentication information is missing. Please provide a valid auth token
```


# Base Get FlashBlockStream

Introducing the BlockRazor Base Get FlashBlockStream service, its advantages, and how to access it.

### What is Base Get FlashBlockStream

`Get FlashBlockStream` is a real-time FlashBlock data subscription interface provided by BlockRazor for Base, used to obtain earlier stages of block data on Base with lower latency. This interface supports both gRPC and WebSocket protocols, making it suitable for trading systems, and monitoring systems that are more sensitive to data arrival time.

On Base, FlashBlock can be understood as a "sub-block" data stream that occurs before the formal block is formed, typically pushed continuously at a frequency of about 200ms. Compared to the standard block time of about 2 seconds, FlashBlock can provide transaction-related feedback much earlier, making it more suitable for low-latency scenarios that require quick on-chain awareness of changes.

For trading bots, quantitative strategies, real-time monitoring platforms, and front-end trading applications, waiting for blocks often means longer response times. The value of Get FlashBlockStream is to help the system receive transaction and block change signals earlier, before the block arrives, thus buying more time for subsequent judgment and response.

### Why choose Base Get FlashBlockStream

BlockRazor, based on [BEF](/core-technology/blockchain-edge-fabric), provides access points to multiple regions on Base, including Frankfurt, Virginia, and Tokyo. According to [Base Benchmark](https://blockrazor.io/zh/blog/20250922basebenchmark/), BlockRazor demonstrates an advantage over the official Base service in FlashBlock reception latency across multiple regions, with a particularly significant lead in the mid-to-high percentile range.

### FAQ

<details>

<summary>What is the difference between Get BlockStream and Get FlashBlockStream</summary>

The core difference between the two lies in the different data granularity, time points, and applicable scenarios.

* Get BlockStream\
  It is used to retrieve block that has already been formed on Base, focusing on confirmed blocks and transactions within. It is more suitable for monitoring confirmation results, block-level analysis, on-chain data processing, and data systems that need to stably consume blocks.
* **Get FlashBlockStream**\
  Used to retrieve FlashBlock data on Base. FlashBlock is a "sub-block" of data pushed by Base approximately every 200ms, providing pre-confirmation information for transactions much earlier than the standard 2-second formal block time. It is more suitable for scenarios that are more sensitive to low latency and want to see on-chain changes as early as possible.

</details>

### Endpoint

{% tabs %}
{% tab title="gRPC" %}

<table><thead><tr><th width="138.33984375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Virginia</td><td>virginia.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.grpc.base.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}

{% tab title="WebSocket" %}

<table><thead><tr><th width="118.8203125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>ws://frankfurt.base.blockrazor.xyz:81/ws</td></tr><tr><td>Virginia</td><td>ws://virginia.base.blockrazor.xyz:81/ws</td></tr><tr><td>Tokyo</td><td>ws://tokyo.base.blockrazor.xyz:81/ws</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Price

The price is $25 / stream / day and $250 / stream / month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_flashblock&#x26;billing=day" class="button primary small">Subscribe</a>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Request Example

{% tabs %}
{% tab title="gRPC" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/c4bec3d65e55ffb0da07253fa78aefe1b1c07e33/main.go#L93)

```go
// GetFlashBlockStream provides a simplified example of subscribing to and processing the flash block stream.
// Note: This function attempts to connect and subscribe only once. For production use, implement your own reconnection logic.
func GetFlashBlockStream(authToken string) {
	log.Printf("[FlashStream] Attempting to connect to gRPC server at %s...", grpcAddr)

	// Establish a connection to the gRPC server with a timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, grpcAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Printf("[FlashStream] Failed to connect to gRPC server: %v", err)
		return
	}
	defer conn.Close()

	log.Println("[FlashStream] Successfully connected to gRPC server.")
	client := basepb.NewBaseApiClient(conn)

	// Create a new context with authentication metadata for the stream subscription.
	streamCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", authToken))
	stream, err := client.GetRawFlashBlockStream(streamCtx, &basepb.GetRawFlashBlocksStreamRequest{})
	if err != nil {
		log.Printf("[FlashStream] Failed to subscribe to stream: %v", err)
		return
	}

	log.Println("[FlashStream] Subscription successful. Waiting for new flash blocks...")

	// Loop indefinitely to receive messages from the stream.
	for {
		block, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				log.Println("[FlashStream] Stream closed by the server (EOF).")
			} else {
				log.Printf("[FlashStream] An error occurred while receiving data: %v", err)
			}
			break // Exit the loop on error or stream closure.
		}

		// Process the received flash block data.
		jsonString, err := ParseFlashBlockByte(block.Message)
		if err != nil {
			log.Printf("[FlashStream] Failed to parse flash block data: %v", err)
			continue
		}

		var jsonMap map[string]interface{}
		if err := json.Unmarshal([]byte(jsonString), &jsonMap); err != nil {
			log.Printf("[FlashStream] Failed to unmarshal flash block JSON: %v", err)
			continue
		}
		printPretty(jsonMap)
	}
}
```

{% endtab %}

{% tab title="WebSocket-Go" %}
Access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/main.go#L181)

```go
// GetWebSocketFlashBlockStream provides a simplified example of using the WebSocket API to subscribe to the flash block stream.
// Note: This function attempts to connect only once and will exit on a read error. For production use, implement your own reconnection logic.
func GetWebSocketFlashBlockStream(authToken string) {
	// Set up the HTTP header with the authorization token.
	header := http.Header{}
	header.Set("Authorization", authToken)

	// Dial the WebSocket server.
	dialer := websocket.DefaultDialer
	conn, resp, err := dialer.Dial(websocketAddr, header)
	if err != nil {
		if resp != nil {
			log.Fatalf("[WebSocket] Dial failed: %v (HTTP status: %s)", err, resp.Status)
		}
		log.Fatalf("[WebSocket] Dial failed: %v", err)
	}
	defer conn.Close()
	log.Printf("[WebSocket] Successfully connected to %s", websocketAddr)

	// Prepare the JSON-RPC subscription request.
	req := map[string]interface{}{
		"jsonrpc": "2.0",
		"method":  "subscribe_FlashBlock",
		"params":  []interface{}{},
		"id":      1,
	}
	reqB, _ := json.Marshal(req)
	if err := conn.WriteMessage(websocket.TextMessage, reqB); err != nil {
		log.Fatalf("[WebSocket] Failed to send subscription request: %v", err)
	}
	log.Printf("[WebSocket] Subscription request sent: %s", string(reqB))

	// Loop indefinitely to read messages from the server.
	for {
		msgType, msg, err := conn.ReadMessage()
		if err != nil {
			log.Printf("[WebSocket] Error reading message: %v", err)
			return // Exit the function on any read error.
		}
		if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage {
			continue // Ignore messages that are not text or binary.
		}

		// Parse the outer JSON-RPC response wrapper.
		var outer = &FlashBlockWebSocketResponse{}
		if err := json.Unmarshal(msg, &outer); err != nil {
			log.Printf("[WebSocket] Failed to parse JSON from server: %v\nRaw data: %s", err, string(msg))
			continue
		}

		// Extract the "result" field, which contains the actual flash block data, and process it.
		resultRaw := outer.Result
		if jsonString, err := ParseFlashBlockByte(resultRaw); err == nil {
			printPretty(jsonString)
		} else {
			log.Printf("[WebSocket] Received message without a valid result field. Raw data: %s", string(msg))
		}
	}
}
```

{% endtab %}

{% tab title="WebSocket-Cli" %}

```
wscat -H "Authorization: <AUTH_HEADER>" \
  -c ws://frankfurt.base.blockrazor.xyz:81/ws \
  --wait 1000 \
  --execute '{"jsonrpc": "2.0", "id": 1, "method": "subscribe_FlashBlock", "params": []}'
```

{% endtab %}

{% tab title="JS" %}

```javascript
const WebSocket = require('ws'); // WebSocket library for Node.js, install via npm(npm install ws) if not already installed
const zlib = require('zlib'); // Node.js built-in library for compression/decompression

// --- Configuration Parameters ---
// Replace with your actual authorization token
const AUTH_TOKEN = "<YOUR_AUTH_TOKEN>";
const WS_URL = "<WEBSOCKET_URL>"; // websocket URL

const SUBSCRIPTION_MESSAGE = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "subscribe_FlashBlock",
    "params": []
};
const WAIT_TIME_MS = 1000; // Time to wait before sending the subscription message


/**
 * Decompresses a Buffer using the Brotli algorithm.
 * @param {Buffer} dataBuffer - The data buffer to be decompressed.
 * @returns {Promise<string>} - A promise that resolves with the decompressed string.
 */
function ParseBrotliData(dataBuffer) {
    return new Promise((resolve, reject) => {
        // zlib.brotliDecompress is used for Brotli decompression
        zlib.brotliDecompress(dataBuffer, (err, decompressedBuffer) => {
            if (err) {
                // Return error if decompression fails
                return reject(new Error("Brotli decompression failed."));
            }
            // Convert the decompressed buffer to a string and resolve
            resolve(decompressedBuffer.toString('utf8'));
        });
    });
}

// --- WebSocket Connection and Operations ---

function connectWebSocket() {
    console.log(`Attempting to connect to: ${WS_URL}`);

    // Create custom Headers for authorization
    const headers = {
        'Authorization': AUTH_TOKEN
    };

    // Establish WebSocket connection, passing headers
    const ws = new WebSocket(WS_URL, {
        headers: headers
    });

    // 1. Connection established successfully
    ws.on('open', () => {
        console.log('✅ Connection established.');

        // Mimicking --wait 1000, wait 1 second before sending the message
        setTimeout(() => {
            const messageString = JSON.stringify(SUBSCRIPTION_MESSAGE);
            
            console.log(`➡️ Sending subscription message (after waiting ${WAIT_TIME_MS}ms):`);
            console.log(messageString);
            
            ws.send(messageString);
        }, WAIT_TIME_MS);
    });

    // 2. Message received
    ws.on('message', async (data) => {
        // Data is a Buffer received from the server, which we suspect is uncompressed JSON string.
        const rawJsonString = data.toString('utf8');
        
        try {
            // STEP 1: Parse the outer JSON-RPC wrapper
            const rpcResponse = JSON.parse(rawJsonString);

            // Check if 'result' or 'params' field exists and contains the Base64 data
            const base64Data = rpcResponse.result || (rpcResponse.params && rpcResponse.params.data);

            if (!base64Data || typeof base64Data !== 'string') {
                console.log(`\n⬅️ Received non-compressed JSON message:`);
                console.log(JSON.stringify(rpcResponse, null, 2));
                return;
            }

            console.log(`\n⬅️ Received compressed data in JSON wrapper. Base64 length: ${base64Data.length}`);

            // STEP 2: Decode Base64 string back into raw Buffer
            const rawBrotliBuffer = Buffer.from(base64Data, 'base64');
            console.log(`   Decoded Base64 to Buffer. Brotli Buffer size: ${rawBrotliBuffer.length} bytes.`);
            
            // STEP 3: Decompress the raw Brotli Buffer
            const decompressedString = await ParseBrotliData(rawBrotliBuffer);
            
            console.log("🌟 Successfully decompressed and parsed message:");

            // STEP 4: Parse the inner JSON content
            const finalData = JSON.parse(decompressedString);
            console.log(JSON.stringify(finalData, null, 2));

        } catch (e) {
            // Handle any error during the 4 steps (JSON parse, Base64 decode, Brotli decompress, final JSON parse)
            console.error("\n❌ Error during processing compressed payload:");
            console.error(`   Error message: ${e.message}`);
            console.log(`   Raw received string (first 200 chars): ${rawJsonString.substring(0, 200)}...`);
        }
    });

    // 3. Connection closed
    ws.on('close', (code, reason) => {
        console.log(`\n❌ Connection closed. Code: ${code}, Reason: ${reason.toString()}`);
    });

    // 4. Connection error
    ws.on('error', (error) => {
        console.error(`\n🔥 An error occurred: ${error.message}`);
    });
}

// Start the client
connectWebSocket();
```

{% endtab %}
{% endtabs %}

#### [proto](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/proto/BaseApi.proto)

```go
syntax = "proto3";

option go_package = "./basepb";


import "google/protobuf/wrappers.proto";

message BaseBlock {
  string parent_hash = 1;
  string fee_recipient = 2;
  bytes state_root = 3;
  bytes receipts_root = 4;
  bytes logs_bloom = 5;
  bytes prev_randao = 6;
  uint64 block_number = 7;
  uint64 gas_limit = 8;
  uint64 gas_used = 9;
  uint64 timestamp = 10;
  bytes extra_data = 11;
  repeated uint64 base_fee_per_gas = 12;
  string block_hash = 13;
  repeated bytes transactions = 14;

  repeated Withdrawal withdrawals = 15;
  google.protobuf.UInt64Value blob_gas_used = 16;
  google.protobuf.UInt64Value excess_blob_gas = 17;
  google.protobuf.BytesValue withdrawals_root = 18;
}

message Withdrawal {
  uint64 index = 1;
  uint64 validator = 2;
  bytes address = 3;
  uint64 amount = 4;
}

message GetRawFlashBlocksStreamRequest {
}

message GetBlockStreamRequest {
}

message SendTransactionRequest {
  string rawTransaction = 1;
}

message SendTransactionResponse {
  string txHash = 1;
}

message FlashBlockStrRequest {
}

message RawFlashBlockStrResponse {
  bytes message = 1;
}

service BaseApi {
  rpc SendTransaction(SendTransactionRequest) returns (SendTransactionResponse);
  rpc GetBlockStream(GetBlockStreamRequest) returns (stream BaseBlock);
  rpc GetRawFlashBlockStream(GetRawFlashBlocksStreamRequest) returns (stream RawFlashBlockStrResponse);
}
```

### Response

**Normal**

```go
{
    message: "185329……7e04b7"
}
```

**Abnormal**

```
rpc error: code = Unknown desc = Authentication information is missing. Please provide a valid auth token
```


# Base ParseFlashBlock

Introduction to the method to parse BlockRazor Base FlashBlock

The parsing method for FlashBlock is as follows:

```go
// ParseFlashBlockByte decompresses the brotli-compressed binary data of a flash block.
func ParseFlashBlockByte(data []byte) (string, error) {
	br := brotli.NewReader(bytes.NewReader(data))
	var buf bytes.Buffer
	_, err := buf.ReadFrom(br)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}
```

You can access the example [here](https://github.com/BlockRazorinc/base-api-client-go/blob/1d46c2983420d6da645992a9f3ed51688f7dac88/main.go#L242C1-L242C56).


# Base Get FlashBlockTransaction

Introduction to the BlockRazor Base Get FlashBlockTransaction service and its access methods

### What is Base Get FlashBlockTransaction

`Get FlashBlockTransaction` is a real-time FlashBlock transaction data stream provided by BlockRazor for Base, used to retrieve transaction data from FlashBlocks on the Base with lower latency. This interface supports both gRPC and WebSocket protocols, making it suitable for transaction systems, monitoring systems, and real-time processing systems that are more sensitive to data arrival time.

On Base, FlashBlock can be understood as a "sub-block" data stream that occurs before the formal block is formed, typically pushed continuously at a frequency of about 200ms. Compared to the standard block time of about 2 seconds, FlashBlock can provide transaction-related feedback much earlier, making it more suitable for low-latency scenarios that require quick on-chain awareness of changes.

For trading bots, quantitative strategies, real-time monitoring platforms, and front-end trading applications, waiting for blocks often means longer response times. The value of  `Get FlashBlockTransaction`  is to help the system receive transaction and block change signals earlier, before the block arrives, thus buying more time for subsequent judgment and response.

### FAQ

Both Get FlashBlockStream and Get FlashBlockTransaction subscribe to FlashBlock data, but the key difference lies in the returned data format. Get FlashBlockStream returns binary data, which needs to be parsed into structured data after acquisition; while Get FlashBlockTransaction directly returns structured transaction data.

<details>

<summary>What is the difference between <code>Get FlashBlockTransaction</code> and <code>Get FlashBlockStream</code></summary>

Both `Get FlashBlockStream` and `Get FlashBlockTransaction` subscribe to FlashBlock data, but the key difference lies in the returned data format. Get FlashBlockStream returns binary data, which needs to be parsed into structured data after acquisition; while Get FlashBlockTransaction directly returns structured transaction data.

</details>

### Endpoint

{% tabs %}
{% tab title="gRPC" %}

<table><thead><tr><th width="136.83203125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Virginia</td><td>virginia.grpc.base.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.grpc.base.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}

{% tab title="WebSocket" %}

<table><thead><tr><th width="139.7421875">地區</th><th>端點</th></tr></thead><tbody><tr><td>法蘭克福</td><td>ws://frankfurt.base.blockrazor.xyz:81/ws</td></tr><tr><td>弗吉尼亞</td><td>ws://virginia.base.blockrazor.xyz:81/ws</td></tr><tr><td>日本</td><td>ws://tokyo.base.blockrazor.xyz:81/ws</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

### Price

The price is $25 / stream / day and $250 / stream / month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=base&#x26;serviceId=base_flashblock&#x26;billing=day" class="button primary small">Subscribe</a>

{% hint style="info" %}
The number of data streams that can be subscribed to is calculated on a shared basis across all regions. For example, if you purchase one stream, you can only subscribe in one region; you will not be able to subscribe in other regions.
{% endhint %}

### Request Example

{% tabs %}
{% tab title="CLI" %}
{% code overflow="wrap" %}

```javascript
wscat -H "Authorization: <AUTH_HEADER>" \
  -c ws://frankfurt.base.blockrazor.xyz:81/ws \
  --wait 1000 \
  --execute '{"jsonrpc": "2.0", "id": 1, "method": "subscribe_FlashTransaction", "params": []}'
```

{% endcode %}
{% endtab %}

{% tab title="Websocket" %}
{% code overflow="wrap" %}

```go
type FlashTransactionWebSocketResponse struct {
    JsonRPC string                           `json:"jsonrpc"`
    Result  *basepb.NewFlashblockTransaction `json:"result"`
}

func GetWebSocketFlashTransactionStream(authToken string) {
    header := http.Header{}
    header.Set("Authorization", authToken)

    // Dial the WebSocket server.
    dialer := websocket.DefaultDialer
    conn, resp, err := dialer.Dial(websocketAddr, header)
    if err != nil {
       if resp != nil {
          log.Fatalf("[WebSocket] Dial failed: %v (HTTP status: %s)", err, resp.Status)
       }
       log.Fatalf("[WebSocket] Dial failed: %v", err)
    }
    defer conn.Close()
    log.Printf("[WebSocket] Successfully connected to %s", websocketAddr)

    // Prepare the JSON-RPC subscription request.
    req := map[string]interface{}{
       "jsonrpc": "2.0",
       "method":  "subscribe_FlashTransaction",
       "params":  []interface{}{},
       "id":      1,
    }
    reqB, _ := json.Marshal(req)
    if err := conn.WriteMessage(websocket.TextMessage, reqB); err != nil {
       log.Fatalf("[WebSocket] Failed to send subscription request: %v", err)
    }
    log.Printf("[WebSocket] Subscription request sent: %s", string(reqB))

    for {
       _, msg, err := conn.ReadMessage()
       if err != nil {
          log.Printf("[WebSocket] read error: %v", err)
          return
       }

       var outer FlashTransactionWebSocketResponse
       if err := json.Unmarshal(msg, &outer); err != nil {
          log.Printf("[WebSocket] unmarshal outer message failed: %v, raw=%s", err, string(msg))
          continue
       }

       if outer.Result == nil {
          log.Printf("[WebSocket] Received message without a valid result field. Raw data: %s", string(msg))
          continue
       }

       tx := outer.Result

       b, err := protojson.Marshal(tx)
       if err != nil {
          continue
       }

       jsonString := string(b)
       printPretty(jsonString)
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```go
func GetFlashTransactionStream(authToken string) {
    log.Printf("[FlashTransactionStream] Attempting to connect to gRPC server at %s...", grpcAddr)

    // Establish a connection to the gRPC server with a timeout.
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    conn, err := grpc.DialContext(ctx, grpcAddr,
       grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
       log.Printf("[FlashTransactionStream] Failed to connect to gRPC server: %v", err)
       return
    }
    defer conn.Close()

    log.Println("[FlashTransactionStream] Successfully connected to gRPC server.")
    client := basepb.NewBaseApiClient(conn)

    // Create a new context with authentication metadata for the stream subscription.
    streamCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("authorization", authToken))
    stream, err := client.GetNewFlashblockTransactionsStream(streamCtx, &basepb.GetNewFlashblockTransactionsStreamRequest{})
    if err != nil {
       log.Printf("[FlashTransactionStream] Failed to subscribe to stream: %v", err)
       return
    }

    log.Println("[FlashTransactionStream] Subscription successful. Waiting for new txs...")

    // Loop indefinitely to receive messages from the stream.
    for {
       tx, err := stream.Recv()
       if err != nil {
          if err == io.EOF {
             log.Println("[FlashTransactionStream] Stream closed by the server (EOF).")
          } else {
             log.Printf("[FlashTransactionStream] An error occurred while receiving data: %v", err)
          }
          break
       }

       log.Printf("=> [FlashTransactionStream] Received new tx: BlockNumber=%s, txHash=%s, TransactionIndex=%s",
          tx.GetBlockNumber(),
          tx.GetHash(),
          tx.GetTransactionIndex(),
       )
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```go
syntax = "proto3";

option go_package = "./basepb";


import "google/protobuf/wrappers.proto";

message BaseBlock {
  string parent_hash = 1;
  string fee_recipient = 2;
  bytes state_root = 3;
  bytes receipts_root = 4;
  bytes logs_bloom = 5;
  bytes prev_randao = 6;
  uint64 block_number = 7;
  uint64 gas_limit = 8;
  uint64 gas_used = 9;
  uint64 timestamp = 10;
  bytes extra_data = 11;
  repeated uint64 base_fee_per_gas = 12;
  string block_hash = 13;
  repeated bytes transactions = 14;

  repeated Withdrawal withdrawals = 15;
  google.protobuf.UInt64Value blob_gas_used = 16;
  google.protobuf.UInt64Value excess_blob_gas = 17;
  google.protobuf.BytesValue withdrawals_root = 18;
}

message Withdrawal {
  uint64 index = 1;
  uint64 validator = 2;
  bytes address = 3;
  uint64 amount = 4;
}

message GetRawFlashBlocksStreamRequest {
}

message GetBlockStreamRequest {
}

message SendTransactionRequest {
  string rawTransaction = 1;
}

message SendTransactionResponse {
  string txHash = 1;
}

message FlashBlockStrRequest {
}

message RawFlashBlockStrResponse {
  bytes message = 1;
}

message GetNewFlashblockTransactionsStreamRequest {
}

message NewFlashblockTransactionAccessListEntry {
  string address = 1;
  repeated string storage_keys = 2;
}

message NewFlashblockTransactionLog {
  string address = 1;
  repeated string topics = 2;
  string data = 3;
  string block_hash = 4;
  string block_number = 5;
  string block_timestamp = 6;
  string transaction_hash = 7;
  string transaction_index = 8;
  string log_index = 9;
  bool removed = 10;
}

message NewFlashblockTransaction {
  string type = 1;
  string chain_id = 2;
  string nonce = 3;
  string gas = 4;
  string max_fee_per_gas = 5;
  string max_priority_fee_per_gas = 6;
  google.protobuf.StringValue to = 7;
  string value = 8;
  repeated NewFlashblockTransactionAccessListEntry access_list = 9;
  string input = 10;
  string r = 11;
  string s = 12;
  string y_parity = 13;
  string v = 14;
  string hash = 15;
  google.protobuf.StringValue block_hash = 16;
  string block_number = 17;
  string transaction_index = 18;
  string block_timestamp = 19;
  string from = 20;
  string gas_price = 21;
  repeated NewFlashblockTransactionLog logs = 22;
  string gas_used = 23;
  string status = 24;
  string cumulative_gas_used = 25;
  google.protobuf.StringValue contract_address = 26;
  string logs_bloom = 27;
}


service BaseApi {
  rpc SendTransaction(SendTransactionRequest) returns (SendTransactionResponse);
  rpc GetBlockStream(GetBlockStreamRequest) returns (stream BaseBlock);
  rpc GetRawFlashBlockStream(GetRawFlashBlocksStreamRequest) returns (stream RawFlashBlockStrResponse);
  rpc GetNewFlashblockTransactionsStream(GetNewFlashblockTransactionsStreamRequest) returns (stream NewFlashblockTransaction);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Response Example

Normal

{% tabs %}
{% tab title="Websocket" %}
{% code overflow="wrap" %}

```javascript
{"jsonrpc":"2.0","result":{"type":"0x2","chain_id":"0x2105","nonce":"0x57f5bd","gas":"0x1d4c0","max_fee_per_gas":"0x1dcf6155","max_priority_fee_per_gas":"0x0","to":{"value":"0xec0e36a6060339694c618ffffcc9ec7da21cb0cc"},"value":"0x0","input":"0xd67704ad0000000000000000000000000000000000000000000000000000000365682750","r":"0x452d04d6fa2805151f35a6372c84ae6ed1afeb3fa7e681bfe12eb86fec4856f1","s":"0x73abdc6f5066c211b940746c7d507947c4a3ff3e9a007388a173de0063bc627e","y_parity":"0x1","v":"0x1","hash":"0xb2008689261342c043d1c29a1986bfc307d2add6e1b8b05bd095460b2762c27c","block_number":"0x2e31a2d","transaction_index":"0xb1","block_timestamp":"0x6a50913d","from":"0xabbac9becc5b171842ae47703dfa6640b23c9710","gas_price":"0x4c4b40","gas_used":"0x67a4","status":"0x1","cumulative_gas_used":"0x273580b","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"id":null}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```python
type:"0x2"  chain_id:"0x2105"  nonce:"0x1bdf78"  gas:"0x3d0900"  max_fee_per_gas:"0x1012aca"  max_priority_fee_per_gas:"0x4a0fca"  to:{value:"0x1722b0b1a1ff934b82d07d7ab540d60d94648cbd"}  value:"0x0"  input:"0xb802000d8b00010000640001609a9cffe20cb54633a58d2ddaa0afd9d283192ecaa77e89243131d31901a07a0001000bb800c8609a9cffe20cb54633a58d2dd9a250114b47885fe27d9c9734a21378a456f267"  r:"0x67fe6c98362d0184052a69ea2422bc00dfaddeb8ab5224a11881fee82a1087a6"  s:"0x20d7a774e39d864017ac7174857dc65c2468d6bd82741302a7abffedbd2630e6"  y_parity:"0x1"  v:"0x1"  hash:"0x224eff2fee593c016e271911e68795f6b88a3d8867c1c650678c2169f7a28a17"  block_number:"0x2cfe45c"  transaction_index:"0xe"  block_timestamp:"0x6a2a259b"  from:"0xfea6e8b9d60ccfc36498f56bf316b63e2812047b"  gas_price:"0x965b0a"  gas_used:"0xa25f"  status:"0x1"  cumulative_gas_used:"0x2f2ca6"  logs_bloom:"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
```

{% endcode %}
{% endtab %}
{% endtabs %}

Abnormal

{% tabs %}
{% tab title="Websocket" %}
{% code overflow="wrap" %}

```json
2026/07/10 11:27:30 [WebSocket] Subscription request sent: {"id":1,"jsonrpc":"2.0","method":"subscribe_FlashTransaction","params":[]}
2026/07/10 11:27:31 [WebSocket] Received message without a valid result field. Raw data: {"jsonrpc":"2.0","error":{"code":-32000,"message":"invalid authentication credentials. please ensure your auth token is correct and try again"},"id":null}
2026/07/10 11:27:31 [WebSocket] read error: websocket: close 1006 (abnormal closure): unexpected EOF
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```
[FlashTransactionStream] An error occurred while receiving data: rpc error: code = FailedPrecondition desc = invalid authentication credentials. please ensure your auth token is correct and try again
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Node Stream

Viewing specific Node Stream services from a chain perspective

<table><thead><tr><th width="158.85546875">Chain</th><th width="174.63671875">Service</th><th>Description</th></tr></thead><tbody><tr><td>BSC</td><td><a href="/streams/node-stream/bsc/full-node-synchronization">Full Node Synchronization</a></td><td>Low-latency synchronized world state of BSC</td></tr><tr><td>Ethereum</td><td><a href="/streams/node-stream/ethereum/cl-el-client-sync">CL/EL Client Sync</a></td><td>Low-latency synchronized world state of Ethereum</td></tr><tr><td>Robinhood Chain</td><td><a href="/streams/node-stream/robinhood-chain/sequencer-feed">Sequencer Feed</a></td><td>Low-latency synchronized world state of Robinhood Chain Sequencer</td></tr></tbody></table>


# BSC Node Stream

This section introduces BlockRazor's Node Stream service for BSC, primarily full node synchronization.


# BSC Full Node Synchronization

This section introduces the services, advantages, target users, and access methods for BlockRazor BSC full node synchronization.

### What is Full Node Synchronization

Full Node Synchronization is a low-latency node synchronization service provided by BlockRazor under Node Stream, which helps users' own BSC full nodes synchronize to the latest blocks and world state faster.

### Why choose Full Node Synchronization

For many high-frequency trading systems and infrastructure systems, "whether the local node can synchronize to the latest state fast enough" is a common problem. Even if the application logic itself is fast, subsequent strategy judgment, data analysis and trade execution calculation will still be affected by the lagging state.

The advantage of Full Node Synchronization is not just "helping nodes connect to the network," but that it provides a low-latency synchronization method more suitable for production environments for systems that rely on local node state. Unlike simply subscribing to block streams, full node synchronization does not continuously push a set of structured data to users. Instead, it allows users' own full nodes to establish P2P connections directly with BlockRazor's high-performance network nodes, leveraging BlockRazor's [BEF](/core-technology/blockchain-edge-fabric) to receive the latest blocks and state updates from high-quality nodes more quickly.

### Which users are suitable for Full Node Synchronization

* **Quant Team / Trading Bot / Searcher**\
  A quantitative and trading system that relies on the state of local nodes to make strategy judgments, prepare for transactions, or perform on-chain analysis.
* **Infra / Node Teams**\
  The engineering team is responsible for node deployment and status synchronization

If your goal is simply to obtain confirmed block data with low latency, Block Stream is usually sufficient.\
If your system needs its local nodes to synchronize with the latest blocks and world state as quickly as possible, then full node synchronization would be more appropriate.

### Benchmark

We compared nodes connected to BlockRazor Relay with nodes not connected to Relay in four regions: Dublin, Frankfurt, Tokyo, and Virginia. The evaluation method was based on the block reception logs of nodes, comparing the time difference between the two nodes receiving the same block to verify the improvement effect of full node synchronization on the local full node synchronization speed.

<table><thead><tr><th width="117.09765625">Region</th><th width="264.55859375">Relay-Connected Node Lead Rate</th><th>Avg Lead</th><th>P90 Lead</th></tr></thead><tbody><tr><td>Dublin</td><td>98.92%</td><td>32 ms</td><td>66 ms</td></tr><tr><td>Frankfurt</td><td>98.92%</td><td>44 ms</td><td>63 ms</td></tr><tr><td>Tokyo</td><td>99.51%</td><td>113 ms</td><td>654 ms</td></tr><tr><td>Virginia</td><td>98.64%</td><td>33 ms</td><td>98 ms</td></tr></tbody></table>

The results show that nodes connected to the Relay exhibit a consistent advantage across all four regions. Based on matched block samples, the leading percentage of nodes connected to the Relay in Dublin, Frankfurt, Tokyo, and Virginia reached 98.92%, 98.92%, 99.51%, and 98.64%. This indicates that in the vast majority of comparable samples, nodes connected to the BlockRazor Relay synchronize to new blocks earlier, thus obtaining the latest on-chain state more quickly.

In terms of lead time, the average lead time of nodes connected to the relay in the four regions is approximately 32ms, 44ms, 113ms, and 33ms, respectively; in the P90 dimension, the lead time reaches 66ms, 63ms, 654ms, and 98ms, respectively. This indicates that full-node synchronization not only improves the lead probability but also maintains a considerable synchronization advantage in higher quantile scenarios.

### Price

<table><thead><tr><th width="173.8984375">Payment Method</th><th width="295.85546875">Price</th><th width="254.1796875">Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$80 / enode / day<br>$800 / enode / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_enode&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 / month<br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Relay IP

<table><thead><tr><th width="160">Region</th><th>Relay IP</th></tr></thead><tbody><tr><td>Frankfurt</td><td>64.130.47.75</td></tr><tr><td>Tokyo</td><td>63.254.162.18</td></tr><tr><td>Dublin</td><td>141.98.217.82</td></tr><tr><td>Virginia</td><td>208.91.105.204</td></tr></tbody></table>

### Instruction

#### Step 1: Purchase Node Stream

1. Go to [https://www.blockrazor.io](https://www.blockrazor.io/), click \[Register] in the upper right corner to complete the registration
2. Log in to the console, go to \[Pricing] - Node Stream, and complete the purchase.
3. Go to \[Services] - \[Streams] - \[Node Stream], and click \[Edit].

   <figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2Fb7dq9FG8Tl9oUX3jrk12%2Fimage.png?alt=media&amp;token=508503f0-865c-444e-aa42-e386ce4a6c47" alt=""><figcaption></figcaption></figure>
4. Select the region where Relay is located (it is recommended to select the region closest to the geographical location of your Geth node), enter the Enode of the Ethereum client that needs to be connected, and click \[Confirm] to complete the addition.

   <figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FqZTehTU4KhxlSg2S4KyB%2Fimage.png?alt=media&amp;token=b586556d-97e0-4f59-bce0-5d48e18b37cc" alt="" width="375"><figcaption></figcaption></figure>
5. Return to the Enode list and click \[Copy Relay Enode]

#### Step 2:  Open ports to allow relay access

{% hint style="info" %}
If your Ethereum client is deployed on AWS or other cloud services, you should additionally configure inbound rules for the security group.
{% endhint %}

1. Access your own Ethereum client's server and execute commands to set up the firewall to allow Relay access.

```
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="63.254.162.18" port port="30311" protocol="tcp" accept'
```

* "source address" is the IP of Relay which can be acquired from [Relay IP](#relay-ip)
* The port for the Ethereum client to allow Relay access is generally set as the default, which is 30311. You can modify this based on your own node configuration.

2. Reload the firewall configuration to make the changes take effect.

```
sudo firewall-cmd --reload
```

#### Step 3: Set the Relay to a TrustedNode (taking a Geth node as an example)

To ensure a continuous connection between the Geth node and the Relay, it is recommended to add the Relay Enode to the Geth node's config.toml file.

1. In the config.toml file, locate the TrustedNodes field in Node.P2P and add the Relay Enode obtained in step 1.

```scheme
[Node.P2P]
TrustedNodes = ["enode://b5b4e5aa8d8f4568af755af6da0d4642b6475d8d87c3470632bdecab8f54e4e2936ec8ae0d6f34cff8b052235e81a281912c17dfcdbf40d6d3c281b78ada4134"]
```

2. Restart the Geth node, specifying config.toml as the starting command: `--config config.toml`&#x20;

#### Step 4: Check the connection status（Enable the admin namespace in the Geth node as an example）

1. Wait for 10 minutes, access the Geth node, execute the curl command to check the connection status

```json
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"admin_peers","params":[],"id":1}' http://localhost:8545
```

2. In the returned data, query the Relay Enode address (which can be copied from the Portal). If the address is found, it proves that the connection is successful.

```json
[
    {
        "enode": "enode://9ddacbcca0dc1d1b112d470552acc795fce5c3e9f50983fcd5cee7b47289914295acaef3163bea819bcc967461978425def13595deb7de4063295c40e593f320@52.205.173.134:53754",
        "id": "8be29a75ac2cf81e3aa37ccc119630a9dfc43c88d7b5200398a466f5ef9097c4",
        "name": "Geth/v1.4.5/linux-amd64/go1.21.7",
        "caps": [
            "eth/68"
        ],
        "network": {
            "localAddress": "127.0.0.1:30311",
            "remoteAddress": "52.205.173.134:53754",
            "inbound": true,
            "trusted": false,
            "static": false
        },
        "protocols": {
            "eth": {
                "version": 68
            }
        }
    }
]
```

{% hint style="info" %}
If the connection status is found to be abnormal after inquiry, it may be due to network communication problems between nodes. Please go to [Discord](https://discord.com/invite/qqJuwRb8Nh) to contact us.
{% endhint %}


# Ethereum Node Stream

This section introduces BlockRazor's Node Stream service for Ethereum, primarily CL/EL client synchronization.


# Ethereum CL/EL Client Sync

This section introduces the services, advantages, target users, and integration methods of the BlockRazor Ethereum CL/EL client synchronization feature.

### What is CL/EL client sync?

CL/EL client sync is a low-latency node synchronization capability designed by BlockRazor for Ethereum node architecture in the Node Stream scenario, used to help users synchronize their own Ethereum nodes with the latest world state faster.

Unlike BSC full node synchronization, which primarily targets a single EVM execution node, Ethereum adopted a CL/EL dual-client architecture after The Merge. Therefore, Ethereum's Node Stream not only enables EL to execute new blocks faster, but also allows CL to keep up with the correct head, safe/finalized blocks, and consensus layer messages more quickly.

### Why choose CL/EL client sync?

For many high-frequency trading systems, searchers, quantitative strategies, and infrastructure teams, a core issue is whether the local Ethereum node can synchronize the latest head, execute the latest block, and update the world state quickly enough. Even if the strategy system, matching logic, or transaction construction itself is fast, if the chain head or state seen by the local node is lagging behind, subsequent strategy judgments, simulations, risk control, transaction replacements, and execution decisions will still be affected.

The value of CL/EL client synchronization is not just "helping nodes connect to the network," but also providing a lower latency and more stable synchronization entry point for production systems that rely on the state of local Ethereum nodes, reducing the impact of synchronization lag of local nodes in strategy judgment, trading simulation, and MEV scenarios on trading and analysis systems.

### Which users are suitable for CL/EL client sync

* **Quant Team / Trading Bot / Searcher**\
  A quantitative and trading system that relies on the state of local nodes to make strategy judgments, prepare for transactions, or perform on-chain analysis.
* **Infra / Node Teams**\
  The engineering team is responsible for node deployment and status synchronization

If your goal is simply to obtain confirmed block data with low latency, Block Stream is usually sufficient.\
If your system needs its local nodes to synchronize with the latest blocks and world state as quickly as possible, then CL/EL client sync would be more appropriate.

### Price

<table><thead><tr><th width="177.73046875">Payment Method</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td>$80 / client / day<br>$800 / client / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=ethereum&#x26;serviceId=ethereum_enode&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td>$1250 / month<br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Relay IP

<table><thead><tr><th width="190">地區</th><th>Relay IP</th></tr></thead><tbody><tr><td>法蘭克福</td><td>64.130.47.75</td></tr><tr><td>東京</td><td>63.254.162.18</td></tr><tr><td>弗吉尼亞</td><td>208.91.105.204</td></tr></tbody></table>

### EL Client User Guide

#### Step 1: Purchase Node Stream

1. Go to [https://www.blockrazor.io/](https://www.blockrazor.io/https://www.blockrazor.io/), click on "Sign up" in the upper right corner, and complete the registration.
2. Log in to the console, go to \[Pricing] and complete the purchase.
3. Go to \[Services] - \[Streams] - \[Node Stream], and click \[Edit].
4. Select the region closest to the EL client, enter the EL client Enode that needs to be connected to the relay, and click \[Confirm] to complete the addition.
5. Return to the Node list and click "Copy Relay Addr".

#### Step 2:  Open ports to allow relay access

{% hint style="info" %}
If your EL client is deployed on AWS or other cloud services, you should additionally configure inbound rules for the security group.
{% endhint %}

1. Access your own EL client's server and execute commands to set up the firewall to allow Relay access.

```bash
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="63.254.162.18" port port="30303" protocol="tcp" accept'
```

* "source address" is the IP of Relay which can be acquired from [Relay IP](#relay-ip)
* The port for the EL client to allow Relay access is generally set as the default, which is 30303. You can modify this based on your own node configuration

2. Reload the firewall configuration to make the changes take effect.

```
sudo firewall-cmd --reload
```

#### Step 3: Set the Relay to a TrustedNode (taking a Geth node as an example)

To ensure a continuous connection between the Geth node and the Relay, it is recommended to add the Relay Enode to the Geth node's config.toml file.

1. In the config.toml file, locate the TrustedNodes field in Node.P2P and add the Relay Enode obtained in step 1.

```scheme
[Node.P2P]
TrustedNodes = ["enode://b5b4e5aa8d8f4568af755af6da0d4642b6475d8d87c3470632bdecab8f54e4e2936ec8ae0d6f34cff8b052235e81a281912c17dfcdbf40d6d3c281b78ada4134"]
```

2. Restart the Geth node, specifying config.toml as the starting command: `--config config.toml`&#x20;

#### Step 4: Check the connection status（Enable the admin namespace in the Geth node as an example）

1. Wait for 10 minutes, access the Geth node, execute the curl command to check the connection status

```bash
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"admin_peers","params":[],"id":1}' http://localhost:8545
```

2. In the returned data, query the Relay Enode address (which can be copied from the Portal). If the address is found, it proves that the connection is successful.

```json
[
    {
        "enode": "enode://9ddacbcca0dc1d1b112d470552acc795fce5c3e9f50983fcd5cee7b47289914295acaef3163bea819bcc967461978425def13595deb7de4063295c40e593f320@52.205.173.134:53754",
        "id": "8be29a75ac2cf81e3aa37ccc119630a9dfc43c88d7b5200398a466f5ef9097c4",
        "name": "Geth/v1.4.5/linux-amd64/go1.21.7",
        "caps": [
            "eth/68"
        ],
        "network": {
            "localAddress": "127.0.0.1:30311",
            "remoteAddress": "52.205.173.134:53754",
            "inbound": true,
            "trusted": false,
            "static": false
        },
        "protocols": {
            "eth": {
                "version": 68
            }
        }
    }
]
```

{% hint style="info" %}
If the connection status is found to be abnormal after inquiry, it may be due to network communication problems between nodes. Please go to [Discord](https://discord.com/invite/qqJuwRb8Nh) to contact us.
{% endhint %}

### CL Client User Guide

#### Step 1: Purchase Node Stream

1. Go to [https://www.blockrazor.io/](https://www.blockrazor.io/https://www.blockrazor.io/), click on "Sign up" in the upper right corner, and complete the registration.
2. Log in to the console, go to \[Pricing] and complete the purchase.
3. Go to \[Services] - \[Streams] - \[Node Stream], and click \[Edit].
4. Select the region closest to the CL client, enter your CL client ENR and click \[Confirm] to complete the addition.
5. Return to the Node list and click "Copy Relay Addr".

#### Step 2:  Open ports to allow relay access

{% hint style="info" %}
If your CL client is deployed on AWS or other cloud services, you should additionally configure inbound rules for the security group.
{% endhint %}

1. Access your own CL client's server and execute commands to set up the firewall to allow Relay access.

```bash
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="63.254.162.18" port port="9000" protocol="tcp" accept'

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="63.254.162.18" port port="9000" protocol="udp" accept'
```

* "source address" is the IP of Relay which can be acquired from [Relay IP](#relay-ip)
* "port" is the port that the CL client allows the relay to access. Users can modify it according to the default value based on the CL client type.

2. Reload the firewall configuration to make the changes take effect.

```bash
sudo firewall-cmd --reload
```

#### Step 3: Set Relay to TrustedNode

Obtain Relay Multiaddr `/ip4/<Relay_IP>/tcp/<Relay_P2P_PORT>/p2p/${Relay_Peer_ID}`  in step 1, configure the startup parameters and restart the CL client.

{% tabs %}
{% tab title="Lighthouse" %}
{% code overflow="wrap" %}

```bash
--boot-nodes <'Relay_Multiaddr','Bootnodes_ENR'> --trusted-peers "$Relay_Peer_ID"

## Since the boot-nodes parameter is overwritten and updated after a reboot, to ensure connection stability, please include the default bootnodes ENR parameter after Relay_Multiaddr. See reference https://github.com/sigp/lighthouse/blob/120c3c6dac9df8ee4d83f055919bd3488abae4f6/common/eth2_network_config/built_in_network_configs/mainnet/bootstrap_nodes.yaml#L16
```

{% endcode %}
{% endtab %}

{% tab title="Prysm" %}

```bash
--peer "$Relay_Multiaddr"
```

{% endtab %}

{% tab title="Nimbus" %}

```bash
--netkey-file /path/to/netkey --direct-peer "$Relay_Multiaddr"
```

{% endtab %}

{% tab title="Teku" %}

```bash
--p2p-direct-peers "$Relay_Multiaddr"
```

{% endtab %}
{% endtabs %}

#### Step 4: Query connection status

1. Wait 10 minutes, access the node and execute the command, and check if the Relay's Peer ID appears in the peers list where `state=connected`.

```bash
Relay_Peer_ID="16Uiu2..."
REST_PORT="<CLIENT_REST_PORT>"

curl -s "http://127.0.0.1:${REST_PORT}/eth/v1/node/peers?state=connected" \
  | jq --arg id "$Relay_Peer_ID" '.data[] | select(.peer_id == $id) | {
      peer_id,
      state,
      direction,
      last_seen_p2p_address,
      enr
    }'
```

2. If there is output and you see `"state": "connected"`, it means that you have connected to the target node.


# Robinhood Chain Sequencer Feed

This section introduces BlockRazor's Node Stream service for Robinhood Chain, primarily Sequencer Feed.

### What is Sequencer Feed?

The Sequencer Feed is a real-time data stream pushed by the Robinhood Chain Sequencer. Nodes subscribe to the Feed via WebSocket to quickly receive block data and keep track of the latest on-chain status.

For node operators, e.g. searchers, the transmission speed and stability of the Sequencer Feed directly impact the speed at which nodes catch up with blocks and update their state. When the official Feed endpoint experiences issues such as network latency, network jitter, or unstable connections, nodes may fail to receive the latest data in a timely manner, resulting in node height lag and data update delays.

### Why BlockRazor Sequencer Feed

BlockRazor Sequencer Feed provides Robinhood Chain nodes with a more stable and efficient Sequencer Feed access service.

Compared to directly connecting to the official feed endpoint, BlockRazor Sequencer Feed reduces latency caused by network jitter and connection interruptions during feed synchronization by using the nearest access point and optimizing the transmission path.

Furthermore, the BlockRazor Sequencer Feed is compatible with the official standard access method. Nodes only need to replace the Feed URL to receive the latest data faster and more stably, reducing block tracking latency and keeping the on-chain state synchronized in real time.

### FAQ

<details>

<summary><strong>What is the difference between the Node-required Sequencer Feed and the Direct Sequencer Feed?</strong></summary>

<table><thead><tr><th width="127.98828125">Comparison</th><th width="255.9140625">Node-required Sequencer Feed</th><th>Direct Sequencer Feed</th></tr></thead><tbody><tr><td>Access Method</td><td>Must be received through a node</td><td>Clients can connect directly without running a nod</td></tr><tr><td>Block Delivery</td><td>Delivers blocks sequentially by block height without skipping any blocks</td><td>Prioritizes the latest block; intermediate blocks may be skipped during network congestion</td></tr><tr><td>Node State Dependency</td><td>Relies on the node’s low-latency synchronized state</td><td>Does not require a local node to maintain complete and continuous state synchronization</td></tr><tr><td>Deployment Cost</td><td>Requires node deployment, maintenance, and monitoring</td><td>Easy to integrate, with lower operational costs</td></tr><tr><td>Suitable Use Cases</td><td>Backrunning and order flow projects</td><td>Sniping and copy trading</td></tr></tbody></table>

</details>


# Robinhood Chain Node-required Sequencer Feed

This section introduces benchmark, price and integration methods of the BlockRazor Robinhood Chain Node-required Sequencer Feed.

### Benchmark

We established WSS connections with both the Robinhood Chain Sequencer Feed and the BlockRazor Sequencer Feed using the same test client, comparing the relative latency of receiving blocks from both. The Sequencer Feed that received the block first had a relative latency of 0ms, while the Sequencer Feed that received the block later had a relative latency equal to the difference in timestamps between the received blocks. Specific data is as follows:

We established WSS connections with both the Robinhood Chain Sequencer Feed and the BlockRazor Sequencer Feed using one test client deployed in each AWS US East (Ohio) Availability Zone (use2-az1, use2-az2, use2-az3) , comparing the relative latency of receiving blocks from both. The Sequencer Feed that received the block first had a relative latency of 0ms, while the Sequencer Feed that received the block later had a relative latency equal to the difference in timestamps between the received blocks.&#x20;

You can go to [GitHub - BlockRazorinc/robinhood-feed-speed](https://github.com/BlockRazorinc/robinhood-feed-speed) to get the benckmark tool.

Benchmark data is as follows:

{% tabs %}
{% tab title="use2-az1" %}
快照時間：2026-08-12T10:18:57.825039121Z，测试測試區塊總數：252798

<table><thead><tr><th>Sequencer Feed</th><th width="109.0546875">Win rate</th><th width="101.1484375">P50</th><th width="110.5859375">P90</th><th width="105.84375">P95</th><th width="117.4296875">P99</th></tr></thead><tbody><tr><td>BlockRazor Sequencer Feed</td><td><strong>80.27%</strong></td><td><strong>0.000 ms</strong></td><td><strong>0.000 ms</strong></td><td><strong>2.771 ms</strong></td><td><strong>6.116 ms</strong></td></tr><tr><td>Robinhood Chain Sequencer Feed</td><td><strong>19.73%</strong></td><td><strong>4.640 ms</strong></td><td><strong>9.604 ms</strong></td><td><strong>15.651 ms</strong></td><td><strong>19.749 ms</strong></td></tr></tbody></table>
{% endtab %}

{% tab title="use2-az2" %}
快照時間：2026-08-12T10:18:56.218075456Z，测试測試區塊總數：252451

<table><thead><tr><th>Sequencer Feed</th><th width="103.23046875">Win rate</th><th width="108.203125">P50</th><th width="105.90234375">P90</th><th width="101.8671875">P95</th><th width="109.13671875">P99</th></tr></thead><tbody><tr><td>BlockRazor Sequencer Feed</td><td><strong>87.01%</strong></td><td><strong>0.000 ms</strong></td><td><strong>0.445 ms</strong></td><td><strong>1.659 ms</strong></td><td><strong>4.831 ms</strong></td></tr><tr><td>Robinhood Chain Sequencer Feed</td><td><strong>12.98%</strong></td><td><strong>5.302 ms</strong></td><td><strong>17.379 ms</strong></td><td><strong>23.194 ms</strong></td><td><strong>70.723 ms</strong></td></tr></tbody></table>
{% endtab %}

{% tab title="use2-az3" %}
快照時間：2026-08-12T10:18:17.587552975Z，测试測試區塊總數：251670

<table><thead><tr><th>Sequencer Feed</th><th width="97.79296875">Win rate</th><th width="108.33984375">P50</th><th width="103.75">P90</th><th width="107.421875">P95</th><th width="102.87890625">P99</th></tr></thead><tbody><tr><td>BlockRazor Sequencer Feed</td><td><strong>76.48%</strong></td><td><strong>0.000 ms</strong></td><td><strong>1.640 ms</strong></td><td><strong>2.842 ms</strong></td><td><strong>6.336 ms</strong></td></tr><tr><td>Robinhood Chain Sequencer Feed</td><td><strong>23.52%</strong></td><td><strong>2.742 ms</strong></td><td><strong>11.219 ms</strong></td><td><strong>16.373 ms</strong></td><td><strong>32.839 ms</strong></td></tr></tbody></table>
{% endtab %}
{% endtabs %}

In terms of latency distribution, BlockRazor not only arrives first on most blocks, but this lead is also highly consistent; in contrast, Robinhood Chain Sequencer Feed is more often in a lagging position and has more pronounced latency fluctuations.

In summary, BlockRazor Sequencer Feed demonstrates significant advantages in block transmission speed, first-to-delivery rate, and latency stability, providing a more reliable first-to-delivery window for latency-sensitive transactions.

### FAQ

<details>

<summary><strong>What is the difference between the Node-required Sequencer Feed and the Direct Sequencer Feed?</strong></summary>

<table><thead><tr><th width="127.98828125">Comparison</th><th width="255.9140625">Node-required Sequencer Feed</th><th>Direct Sequencer Feed</th></tr></thead><tbody><tr><td>Access Method</td><td>Must be received through a node</td><td>Clients can connect directly without running a nod</td></tr><tr><td>Block Delivery</td><td>Delivers blocks sequentially by block height without skipping any blocks</td><td>Prioritizes the latest block; intermediate blocks may be skipped during network congestion</td></tr><tr><td>Node State Dependency</td><td>Relies on the node’s low-latency synchronized state</td><td>Does not require a local node to maintain complete and continuous state synchronization</td></tr><tr><td>Deployment Cost</td><td>Requires node deployment, maintenance, and monitoring</td><td>Easy to integrate, with lower operational costs</td></tr><tr><td>Suitable Use Cases</td><td>Backrunning and order flow projects</td><td>Sniping and copy trading</td></tr></tbody></table>

</details>

### Price

The price is $80 per unit per day and $800 per unit per month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a>

### Endpoint

<table><thead><tr><th width="148.26171875">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Ohio</td><td>wss://us.robinhood-feeder.blockrazor.io/ws/{authToken}</td></tr><tr><td>Tokyo</td><td>wss://jp.robinhood-feeder.blockrazor.io/ws/{authToken}</td></tr></tbody></table>

### Usage Instructions

{% stepper %}
{% step %} <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a> **BlockRazor Sequencer Feed**
{% endstep %}

{% step %}
**Retrieve the auth from the portal and append it as the URI to the WSS URL.**

wss\://us.robinhood-feeder.blockrazor.io/ws/{authToken}
{% endstep %}

{% step %}
**Stop the running Robinhood Chain node**

The specific command depends on the current deployment method, such as Docker, Docker Compose, or systemd. Before stopping, it is recommended to ensure that the node data directory is correctly mounted to avoid losing existing synchronized data after restarting.
{% endstep %}

{% step %}
**Add Feed URL**

The following configuration can be found in the node startup command:

```bash
--node.feed.input.url=wss://feed.mainnet.chain.robinhood.com
```

replace it with BlockRazor Sequencer Feed：

```bash
--node.feed.input.url=wss://feed.mainnet.chain.robinhood.com
--node.feed.input.url=wss://us.robinhood-feeder.blockrazor.io/ws/{authToken}
```

The complete mainnet startup example is as follows:

```bash
DATA_DIR="$HOME/rh/robinhood-nitro-data"

docker run --rm -it \
  -v "$DATA_DIR":/home/nitro/.arbitrum \
  -v "$HOME/rh/config":/home/nitro/config \
  -p 8547:8547 \
  -p 8548:8548 \
  offchainlabs/nitro-node:v3.11.2-3599aca \
    --chain.info-files=/home/nitro/config/robinhood-chain-info.json \
    --parent-chain.connection.url=<L1_EXECUTION_RPC_URL> \
    --parent-chain.blob-client.beacon-url=<L1_BEACON_URL> \
    --init.genesis-json-file=/home/nitro/config/robinhood-genesis.json \
    --node.feed.input.url=wss://<BLOCKRAZOR_FEED_URL> \
    --http.addr=0.0.0.0 \
    --http.port=8547 \
    --http.api=net,web3,eth
```

{% endstep %}

{% step %}
**Restart the node**

After saving the configuration and restarting the node, the node will receive Robinhood Sequencer data via the BlockRazor Sequencer Feed.

Check the node logs to confirm:

* BlockRazor Feed connection successful
* No persistent reconnection, timeout, or WebSocket error.
* The node continuously receives the latest Sequencer data.
* Node height is keeping up with Robinhood Chain
  {% endstep %}

{% step %}
**Verify node status**

Check synchronization status:

```bash
curl -d '{"id":0,"jsonrpc":"2.0","method":"eth_syncing","params":[]}' \
  -H "Content-Type: application/json" \
  http://localhost:8547
```

After full synchronization, eth\_syncing should return:

```bash
false
```

{% endstep %}
{% endstepper %}


# Robinhood Chain Node-required Sequencer Feed(Ultra)

This section introduces benchmark, price and integration methods of the BlockRazor Robinhood Chain Node-required Sequencer Feed(Ultra).

### What is Node-required Sequencer Feed(Ultra)

Node-required Sequencer Feed (Ultra) is an ultra-low-latency data transmission solution built on the [Standard version](/streams/node-stream/robinhood-chain/sequencer-feed). It deeply optimizes network routing and underlying transmission mechanisms to further reduce the end-to-end latency of Sequencer Feed delivery.

Powered by BEF technology, the Ultra version delivers ordered block data to your local node with the lowest possible latency, enabling trading systems to access critical on-chain state earlier.

The solution is purpose-built for latency-sensitive applications, including advanced arbitrage, order flow analysis, and quantitative trading. In an environment where competition is measured in microseconds, earlier access to ordered block data provides more time for strategy computation and transaction execution. Microseconds define the edge.

### Benchmark

We established WSS connections with both the Robinhood Chain Sequencer Feed and the BlockRazor Sequencer Feed using the same test client. The Robinhood Chain Sequencer Feed endpoint is wss\://[feed.mainnet.chain.robinhood.com](http://feed.mainnet.chain.robinhood.com/), and the BlockRazor used the `/ws/ultra` endpoint.

One test client was deployed in each AWS US East (Ohio) Availability Zone (`use2-az1`, `use2-az2`, and `use2-az3`) to compare the relative block delivery latency of the two Sequencer Feeds. For each block, the Sequencer Feed that delivered the block first was assigned a relative latency of `0 ms`. The relative latency of the other feed was calculated from the difference between their block arrival timestamps.

You can use the [robinhood-feed-speed benchmark tool](https://github.com/BlockRazorinc/robinhood-feed-speed) to reproduce the test.

Benchmark data is as follows:

{% tabs %}
{% tab title="use2-az1" %}
Total samples: `4,232`

| Sequencer Feed                 |          P50 |          P90 |          P95 |          P99 |          Max |
| ------------------------------ | -----------: | -----------: | -----------: | -----------: | -----------: |
| **BlockRazor Sequencer Feed**  | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** |
| Robinhood Chain Sequencer Feed |    28.026 ms |    45.177 ms |    52.780 ms |    85.805 ms |   818.664 ms |
| {% endtab %}                   |              |              |              |              |              |

{% tab title="use2-az2" %}
Total samples: `4,305`

| Sequencer Feed                 |          P50 |          P90 |          P95 |          P99 |          Max |
| ------------------------------ | -----------: | -----------: | -----------: | -----------: | -----------: |
| **BlockRazor Sequencer Feed**  | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** |
| Robinhood Chain Sequencer Feed |    97.404 ms |   195.406 ms |   301.405 ms |   953.111 ms | 1,616.810 ms |
| {% endtab %}                   |              |              |              |              |              |

{% tab title="use2-az3" %}
Total samples: `4,714`

| Sequencer Feed                 |          P50 |          P90 |          P95 |          P99 |          Max |
| ------------------------------ | -----------: | -----------: | -----------: | -----------: | -----------: |
| **BlockRazor Sequencer Feed**  | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** | **9.580 ms** |
| Robinhood Chain Sequencer Feed |    27.310 ms |    52.828 ms |    66.711 ms |   111.686 ms |   736.300 ms |
| {% endtab %}                   |              |              |              |              |              |

{% tab title="Tokyo" %}
Total samples: `3,996`

| Sequencer Feed                 |          P50 |          P90 |          P95 |          P99 |           Max |
| ------------------------------ | -----------: | -----------: | -----------: | -----------: | ------------: |
| **BlockRazor Sequencer Feed**  | **0.000 ms** | **0.000 ms** | **0.000 ms** | **0.000 ms** | **68.232 ms** |
| Robinhood Chain Sequencer Feed |    71.365 ms |   108.596 ms |   116.049 ms |   220.859 ms |   1969.576 ms |

{% endtab %}
{% endtabs %}

Across all three Availability Zones, the BlockRazor Sequencer Feed maintained a relative latency of `0 ms` through P99. In comparison, the Robinhood Chain Sequencer Feed recorded median relative latencies ranging from `27.310 ms` to `97.404 ms`.

The difference was most pronounced in `use2-az2`, where the Robinhood Chain Sequencer Feed reached `97.404 ms` at P50, `953.111 ms` at P99, and a maximum relative latency of `1,616.810 ms`.

In summary, the benchmark results show that the BlockRazor Sequencer Feed consistently delivered blocks earlier and with substantially lower relative latency across all three tested Availability Zones. This provides a faster and more stable first-delivery window for latency-sensitive applications and transactions.

### FAQ

<details>

<summary><strong>What is the difference between the Node-required Sequencer Feed and the Direct Sequencer Feed?</strong></summary>

<table><thead><tr><th width="127.98828125">Comparison</th><th width="255.9140625">Node-required Sequencer Feed</th><th>Direct Sequencer Feed</th></tr></thead><tbody><tr><td>Access Method</td><td>Must be received through a node</td><td>Clients can connect directly without running a nod</td></tr><tr><td>Block Delivery</td><td>Delivers blocks sequentially by block height without skipping any blocks</td><td>Prioritizes the latest block; intermediate blocks may be skipped during network congestion</td></tr><tr><td>Node State Dependency</td><td>Relies on the node’s low-latency synchronized state</td><td>Does not require a local node to maintain complete and continuous state synchronization</td></tr><tr><td>Deployment Cost</td><td>Requires node deployment, maintenance, and monitoring</td><td>Easy to integrate, with lower operational costs</td></tr><tr><td>Suitable Use Cases</td><td>Backrunning and order flow projects</td><td>Sniping and copy trading</td></tr></tbody></table>

</details>

### Price

The price is $200 per unit per day and $2000 per unit per month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_feed_stream_speedup&#x26;billing=day" class="button primary small">Subscribe</a>

### Endpoint

<table><thead><tr><th width="148.26171875">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Ohio</td><td>wss://us.robinhood-feeder.blockrazor.io/ws/ultra/{authToken}</td></tr><tr><td>Tokyo</td><td>wss://jp.robinhood-feeder.blockrazor.io/ws/ultra/{authToken}</td></tr></tbody></table>

### Usage Instructions

{% stepper %}
{% step %} <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_feed_stream_speedup&#x26;billing=day" class="button primary small">Subscribe</a> **BlockRazor Sequencer Feed**
{% endstep %}

{% step %}
**Retrieve the auth from the portal and append it as the URI to the WSS URL.**

wss\://us.robinhood-feeder.blockrazor.io/ws/{authToken}
{% endstep %}

{% step %}
**Stop the running Robinhood Chain node**

The specific command depends on the current deployment method, such as Docker, Docker Compose, or systemd. Before stopping, it is recommended to ensure that the node data directory is correctly mounted to avoid losing existing synchronized data after restarting.
{% endstep %}

{% step %}
**Add Feed URL**

The following configuration can be found in the node startup command:

```bash
--node.feed.input.url=wss://feed.mainnet.chain.robinhood.com
```

replace it with BlockRazor Sequencer Feed：

```bash
--node.feed.input.url=wss://feed.mainnet.chain.robinhood.com
--node.feed.input.url=wss://us.robinhood-feeder.blockrazor.io/ws/{authToken}
```

The complete mainnet startup example is as follows:

```bash
DATA_DIR="$HOME/rh/robinhood-nitro-data"

docker run --rm -it \
  -v "$DATA_DIR":/home/nitro/.arbitrum \
  -v "$HOME/rh/config":/home/nitro/config \
  -p 8547:8547 \
  -p 8548:8548 \
  offchainlabs/nitro-node:v3.11.2-3599aca \
    --chain.info-files=/home/nitro/config/robinhood-chain-info.json \
    --parent-chain.connection.url=<L1_EXECUTION_RPC_URL> \
    --parent-chain.blob-client.beacon-url=<L1_BEACON_URL> \
    --init.genesis-json-file=/home/nitro/config/robinhood-genesis.json \
    --node.feed.input.url=wss://<BLOCKRAZOR_FEED_URL> \
    --http.addr=0.0.0.0 \
    --http.port=8547 \
    --http.api=net,web3,eth
```

{% endstep %}

{% step %}
**Restart the node**

After saving the configuration and restarting the node, the node will receive Robinhood Sequencer data via the BlockRazor Sequencer Feed.

Check the node logs to confirm:

* BlockRazor Feed connection successful
* No persistent reconnection, timeout, or WebSocket error.
* The node continuously receives the latest Sequencer data.
* Node height is keeping up with Robinhood Chain
  {% endstep %}

{% step %}
**Verify node status**

Check synchronization status:

```bash
curl -d '{"id":0,"jsonrpc":"2.0","method":"eth_syncing","params":[]}' \
  -H "Content-Type: application/json" \
  http://localhost:8547
```

After full synchronization, eth\_syncing should return:

```bash
false
```

{% endstep %}
{% endstepper %}


# Robinhood Chain Direct Sequencer Feed

This section introduces services, price and integration methods of the BlockRazor Robinhood Chain Direct Sequencer Feed.

### What Is the Direct Sequencer Feed?

Compared with the Node-required Sequencer Feed, the Direct Sequencer Feed does not require node deployment. Standard clients can receive the latest blocks directly, enabling lower-latency tracking of on-chain signals. It is well suited for use cases such as sniping and copy trading that do not depend on full node state synchronization.

Please note that the Direct Sequencer Feed may skip blocks during periods of network congestion.

### FAQ

<details>

<summary><strong>What is the difference between the Node-required Sequencer Feed and the Direct Sequencer Feed?</strong></summary>

<table><thead><tr><th width="127.98828125">Comparison</th><th width="255.9140625">Node-required Sequencer Feed</th><th>Direct Sequencer Feed</th></tr></thead><tbody><tr><td>Access Method</td><td>Must be received through a node</td><td>Clients can connect directly without running a nod</td></tr><tr><td>Block Delivery</td><td>Delivers blocks sequentially by block height without skipping any blocks</td><td>Prioritizes the latest block; intermediate blocks may be skipped during network congestion</td></tr><tr><td>Node State Dependency</td><td>Relies on the node’s low-latency synchronized state</td><td>Does not require a local node to maintain complete and continuous state synchronization</td></tr><tr><td>Deployment Cost</td><td>Requires node deployment, maintenance, and monitoring</td><td>Easy to integrate, with lower operational costs</td></tr><tr><td>Suitable Use Cases</td><td>Backrunning and order flow projects</td><td>Sniping and copy trading</td></tr></tbody></table>

</details>

### Price

The price is $80 per unit per day and $800 per unit per month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_direct_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a>

### Endpoint

<table><thead><tr><th width="148.26171875">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Ohio</td><td>wss://us.robinhood-feeder.blockrazor.io/ws/direct/{authToken}</td></tr><tr><td>Tokyo</td><td>wss://jp.robinhood-feeder.blockrazor.io/ws/direct/{authToken}</td></tr></tbody></table>

### Usage Instructions

{% stepper %}
{% step %} <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_direct_feed_stream&#x26;billing=day" class="button primary small">Subscribe</a> **BlockRazor Sequencer Feed**
{% endstep %}

{% step %}
**Retrieve the auth from the portal and append it as the URI to the WSS URL.**

wss\://us.robinhood-feeder.blockrazor.io/ws/{authToken}
{% endstep %}

{% step %}
**Choose a WebSocket Client and Establish a WSS Connection**

Use any standard WebSocket client, such as a WebSocket library for Node.js, Go, Python, or Rust. There is no need to deploy a Robinhood Chain node or configure `--node.feed.input.url`.
{% endstep %}

{% step %}
**Receive and parse the data. The parsed data structure is as follows:**

{% code overflow="wrap" %}

```json
{
  "version": 1,
  "messages": [
    {
      "sequenceNumber": 50543784,
      "message": {
        "message": {
          "header": {
            "kind": 3,
            "sender": "0xa4b000000000000000000073657175656e636572",
            "blockNumber": 25872577,
            "timestamp": 1788146984,
            "requestId": null,
            "baseFeeL1": 0
          },
          "l2Msg": {
            "encoding": "Nitro L2 message",
            "kind": 3,
            "kindName": "Batch",
            "decodedByteLength": 3487,
            "transactionCount": 10,
            "items": [
              {
                "index": 0,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 244,
                "transaction": {
                  "hash": "0xc4d8935f3e63b7b4…1e46fd37",
                  "from": "0x0dd10d651d18bc70594cb070e1117b6cd5e4a8a4",
                  "type": 2,
                  "typeName": "EIP-1559",
                  "chainId": 4663,
                  "nonce": 901,
                  "maxPriorityFeePerGas": 1,
                  "maxFeePerGas": 466288000,
                  "gasLimit": 120000,
                  "to": "0x000000000022d473030f116ddee9f6b43ac78ba3",
                  "value": 0,
                  "input": "0x87517c4500000000…6a94fc2f",
                  "accessList": [],
                  "yParity": 0,
                  "r": "0x1a6881f8f30266eb…090c7b15",
                  "s": "0x15e0901b330dfdba…0e9eb53a",
                  "rawTransaction": "0x02f8f08212378203…0e9eb53a"
                }
              },
              {
                "index": 2,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 341,
                "transaction": {
                  "hash": "0xef96fbbeb7072b85…6c67ee47",
                  "from": "0x137fe0e0fdbfe6487588f0ef842f418bbb738b8e",
                  "type": 0,
                  "typeName": "Legacy",
                  "chainId": 4663,
                  "nonce": 14,
                  "gasPrice": 250000000,
                  "gasLimit": 400000,
                  "to": "0xcaf681a66d020601342297493863e78c959e5cb2",
                  "value": 40000000000000,
                  "input": "0x04e45aaf00000000…00000000",
                  "v": 9362,
                  "yParity": 1,
                  "r": "0xe65d56b7f3eb63d7…4656bb9c",
                  "s": "0x4362c24ab37d7590…88c290ce",
                  "rawTransaction": "0xf901510e840ee6b2…88c290ce"
                }
              },
              {
                "index": 4,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 119,
                "transaction": {
                  "hash": "0x00f4bb0661c05fe8…3938c8bf",
                  "from": "0x6ae3de978e63b63e8486d1f495339633495d8264",
                  "type": 2,
                  "typeName": "EIP-1559",
                  "chainId": 4663,
                  "nonce": 3,
                  "maxPriorityFeePerGas": 234192000,
                  "maxFeePerGas": 542634600,
                  "gasLimit": 35010,
                  "to": "0xc45aa399aa75ace0ae56c372898abcb36d33161f",
                  "value": 330170416874734,
                  "input": "0x",
                  "accessList": [],
                  "yParity": 1,
                  "r": "0xdda460f047c07f2a…67caa4a0",
                  "s": "0x5f82128ed043cc80…4d2f6024",
                  "rawTransaction": "0x02f8738212370384…4d2f6024"
                }
              }
            ]
          },
          "l2MsgBase64": "AwAAAAAAAAD0BAL48I…jqF1iw=="
        },
        "delayedMessagesRead": 193164
      },
      "blockHash": "0x220c2e737333e10d…2e261230",
      "signatureV2": "Rg3J2yhBQ8pxYUqc6e…xkNk7wA=",
      "blockMetadata": null
    }
  ]
}
```

{% endcode %}
{% endstep %}
{% endstepper %}


# Robinhood Chain Direct Sequencer Feed(Ultra)

This section introduces services, price and integration methods of the BlockRazor Robinhood Chain Direct Sequencer Feed.

### What Is the Direct Sequencer Feed(Ultra)?

Direct Sequencer Feed (Ultra) is an ultra-low-latency data transmission solution built on the [Standard version](/streams/node-stream/robinhood-chain/direct-sequencer-feed). It deeply optimizes network routing and underlying transmission mechanisms to further reduce the end-to-end latency of Sequencer Feed delivery.

Powered by BEF technology, the Ultra version provides access to the latest block data at exceptional speed—without requiring users to deploy or operate a node.

The solution is purpose-built for advanced sniping and copy-trading strategies where timing is critical. It enables trading systems to capture on-chain activity earlier and secure valuable time for strategy computation and transaction execution—turning every microsecond into a competitive advantage.

### FAQ

<details>

<summary><strong>What is the difference between the Node-required Sequencer Feed and the Direct Sequencer Feed?</strong></summary>

<table><thead><tr><th width="127.98828125">Comparison</th><th width="255.9140625">Node-required Sequencer Feed</th><th>Direct Sequencer Feed</th></tr></thead><tbody><tr><td>Access Method</td><td>Must be received through a node</td><td>Clients can connect directly without running a nod</td></tr><tr><td>Block Delivery</td><td>Delivers blocks sequentially by block height without skipping any blocks</td><td>Prioritizes the latest block; intermediate blocks may be skipped during network congestion</td></tr><tr><td>Node State Dependency</td><td>Relies on the node’s low-latency synchronized state</td><td>Does not require a local node to maintain complete and continuous state synchronization</td></tr><tr><td>Deployment Cost</td><td>Requires node deployment, maintenance, and monitoring</td><td>Easy to integrate, with lower operational costs</td></tr><tr><td>Suitable Use Cases</td><td>Backrunning and order flow projects</td><td>Sniping and copy trading</td></tr></tbody></table>

</details>

### Price

The price is $200 per unit per day and $2000 per unit per month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_direct_feed_stream_speedup&#x26;billing=day" class="button primary small">Subscribe</a>

### Endpoint

<table><thead><tr><th width="148.26171875">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Ohio</td><td>wss://us.robinhood-feeder.blockrazor.io/ws/direct/ultra/{authToken}</td></tr><tr><td>Tokyo</td><td>wss://jp.robinhood-feeder.blockrazor.io/ws/direct/ultra/{authToken}</td></tr></tbody></table>

### Usage Instructions

{% stepper %}
{% step %} <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=robinhood&#x26;serviceId=robinhood_direct_feed_stream_speedup&#x26;billing=day" class="button primary small">Subscribe</a> **BlockRazor Sequencer Feed**
{% endstep %}

{% step %}
**Retrieve the auth from the portal and append it as the URI to the WSS URL.**

wss\://us.robinhood-feeder.blockrazor.io/ws/{authToken}
{% endstep %}

{% step %}
**Choose a WebSocket Client and Establish a WSS Connection**

Use any standard WebSocket client, such as a WebSocket library for Node.js, Go, Python, or Rust. There is no need to deploy a Robinhood Chain node or configure `--node.feed.input.url`.
{% endstep %}

{% step %}
**Receive and parse the data. The parsed data structure is as follows:**

{% code overflow="wrap" %}

```json
{
  "version": 1,
  "messages": [
    {
      "sequenceNumber": 50543784,
      "message": {
        "message": {
          "header": {
            "kind": 3,
            "sender": "0xa4b000000000000000000073657175656e636572",
            "blockNumber": 25872577,
            "timestamp": 1788146984,
            "requestId": null,
            "baseFeeL1": 0
          },
          "l2Msg": {
            "encoding": "Nitro L2 message",
            "kind": 3,
            "kindName": "Batch",
            "decodedByteLength": 3487,
            "transactionCount": 10,
            "items": [
              {
                "index": 0,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 244,
                "transaction": {
                  "hash": "0xc4d8935f3e63b7b4…1e46fd37",
                  "from": "0x0dd10d651d18bc70594cb070e1117b6cd5e4a8a4",
                  "type": 2,
                  "typeName": "EIP-1559",
                  "chainId": 4663,
                  "nonce": 901,
                  "maxPriorityFeePerGas": 1,
                  "maxFeePerGas": 466288000,
                  "gasLimit": 120000,
                  "to": "0x000000000022d473030f116ddee9f6b43ac78ba3",
                  "value": 0,
                  "input": "0x87517c4500000000…6a94fc2f",
                  "accessList": [],
                  "yParity": 0,
                  "r": "0x1a6881f8f30266eb…090c7b15",
                  "s": "0x15e0901b330dfdba…0e9eb53a",
                  "rawTransaction": "0x02f8f08212378203…0e9eb53a"
                }
              },
              {
                "index": 2,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 341,
                "transaction": {
                  "hash": "0xef96fbbeb7072b85…6c67ee47",
                  "from": "0x137fe0e0fdbfe6487588f0ef842f418bbb738b8e",
                  "type": 0,
                  "typeName": "Legacy",
                  "chainId": 4663,
                  "nonce": 14,
                  "gasPrice": 250000000,
                  "gasLimit": 400000,
                  "to": "0xcaf681a66d020601342297493863e78c959e5cb2",
                  "value": 40000000000000,
                  "input": "0x04e45aaf00000000…00000000",
                  "v": 9362,
                  "yParity": 1,
                  "r": "0xe65d56b7f3eb63d7…4656bb9c",
                  "s": "0x4362c24ab37d7590…88c290ce",
                  "rawTransaction": "0xf901510e840ee6b2…88c290ce"
                }
              },
              {
                "index": 4,
                "messageKind": 4,
                "messageKindName": "SignedTx",
                "messageLength": 119,
                "transaction": {
                  "hash": "0x00f4bb0661c05fe8…3938c8bf",
                  "from": "0x6ae3de978e63b63e8486d1f495339633495d8264",
                  "type": 2,
                  "typeName": "EIP-1559",
                  "chainId": 4663,
                  "nonce": 3,
                  "maxPriorityFeePerGas": 234192000,
                  "maxFeePerGas": 542634600,
                  "gasLimit": 35010,
                  "to": "0xc45aa399aa75ace0ae56c372898abcb36d33161f",
                  "value": 330170416874734,
                  "input": "0x",
                  "accessList": [],
                  "yParity": 1,
                  "r": "0xdda460f047c07f2a…67caa4a0",
                  "s": "0x5f82128ed043cc80…4d2f6024",
                  "rawTransaction": "0x02f8738212370384…4d2f6024"
                }
              }
            ]
          },
          "l2MsgBase64": "AwAAAAAAAAD0BAL48I…jqF1iw=="
        },
        "delayedMessagesRead": 193164
      },
      "blockHash": "0x220c2e737333e10d…2e261230",
      "signatureV2": "Rg3J2yhBQ8pxYUqc6e…xkNk7wA=",
      "blockMetadata": null
    }
  ]
}
```

{% endcode %}
{% endstep %}
{% endstepper %}


# Network Fee Stream

Viewing specific Network Fee Stream services from a chain perspective

<table><thead><tr><th width="108.30078125">Chain</th><th width="265.84375">Service</th><th>Description</th></tr></thead><tbody><tr><td>Solana</td><td><a href="/streams/network-fee-stream/solana/get-transactionfee">Get TransactionFee</a></td><td>Get Solana priority fee and tip data</td></tr><tr><td>BSC</td><td><a href="/streams/network-fee-stream/bsc/getgaspricestream">GetGasPriceStream</a></td><td>Get BSC gas price data</td></tr><tr><td>BSC</td><td><a href="/streams/network-fee-stream/bsc/getallgaspricestream">GetAllGasPriceStream</a></td><td>Get BSC gas price data</td></tr></tbody></table>


# Solana Network Fee Stream

This section introduces BlockRazor's Network Fee Stream service for Solana, primarily Transaction Fees.


# Solana Get TransactionFee

Introducing the access method of BlockRazor Solana Get TransactionFee

### Introduction

`Get TransactionFee` is used to obtain the priority fee and tip of Solana transactions in aggregate, supports gRPC protocol, endpoint: <mark style="color:$primary;">grpc.solana-fee.blockrazor.me:443</mark>

### Limit

| User Type | Limit  | Price        | Action                                                                                                                                                                                                         |
| --------- | ------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| paid user | 10 QPS | $300 / month | <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=solana&#x26;serviceId=solana_network_fee_stream&#x26;billing=day" class="button primary small">Subscribe</a> |

### Request Parameter

<table><thead><tr><th width="111.8515625">Parameters</th><th width="129.8203125">Mandatory</th><th width="99.4765625">Format</th><th width="121.24609375">Exampl</th><th>Remark</th></tr></thead><tbody><tr><td>accounts</td><td>optional</td><td>string[]</td><td>["DH4xma……HFtNYJ"]</td><td>If account is not specified, priority fee and tip of all transactions in the specified slot range will be counted.</td></tr><tr><td>percentile</td><td>mandatory</td><td>int</td><td>50</td><td>Get the priority fee and tip of the specified quantile, enumerated value: 25, 50, 75, 95, 99</td></tr><tr><td>slotRange</td><td>mandatory</td><td>int</td><td>150</td><td>Statistics of priorityFee and tip of transactions in the last N confirmed slots, N ranges from 1 to 150</td></tr></tbody></table>

### Request Example

```go
package main

import (
	"context"
	"crypto/tls"
	"log"

	// directory of the generated code using the provided proto file
	pb "fee-test/feepb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	gRPCEndpoint = "grpc.solana-fee.blockrazor.xyz:443" // endpoint address
	auth         = "your auth" // auth to be verified
	testAccount1 = "DH4xmaWDnTzKXehVaPSNy9tMKJxnYL5Mo5U3oTHFtNYJ" // query priority fee and tip of transactions involving a specified account 
	testAccount2 = "CAPhoEse9xEH95XmdnJjYrZdNCA8xfUWdy3aWymHa1Vj" 
)

func main() {
	// open gRPC connection to endpoint
	conn, err := grpc.Dial(
		gRPCEndpoint,
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
		grpc.WithPerRPCCredentials(&Authentication{auth}),
	)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// use the gRPC client
	client := pb.NewServerClient(conn)

	req := &pb.TransactionFee{
		Accounts:   []string{}, //do not set accounts
		Percentile: 75,
		SlotRange:  150,
	}
  
  // send gRPC request1
	resp, err := client.GetTransactionFee(context.Background(), req)
	if err != nil {
		panic(err)
	}
	log.Printf("Response PriorityFee Percentile: %+v", resp.PriorityFee.Percentile)
	log.Printf("Response PriorityFee Value: %+v", resp.PriorityFee.Value)
	log.Printf("Response Tip Percentile: %+v", resp.Tip.Percentile)
	log.Printf("Response Tip Value: %+v", resp.Tip.Value)
	
	req2 := &pb.TransactionFee{
		Accounts:   []string{testAccount1, testAccount2}, //set specified accounts
		Percentile: 75,
		SlotRange:  150,
	}
	
	// send gRPC request2
	resp2, err := client.GetTransactionFee(context.Background(), req2)
	if err != nil {
		panic(err)
	}
	log.Printf("Response2 PriorityFee Percentile: %+v", resp2.PriorityFee.Percentile)
	log.Printf("Response2 PriorityFee Value: %+v", resp2.PriorityFee.Value)
	log.Printf("Response2 Tip Percentile: %+v", resp2.Tip.Percentile)
	log.Printf("Response2 Tip Value: %+v", resp2.Tip.Value)
}

type Authentication struct {
	auth string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apiKey": a.auth}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

```

#### Proto

```json
syntax = "proto3";

package feepb;

option go_package = "./pb/feepb";

service Server {
    rpc GetTransactionFee(TransactionFee) returns(TransactionFeeResponse) {};
}

message TransactionFee {
    repeated string accounts = 1;
    int32 percentile = 2;
    int32 slotRange = 3;
}

message TransactionFeeResponse {
    FeeValue priorityFee = 1;
    FeeValue tip = 2;
}

message FeeValue {
    int32 percentile = 1;
    double value = 2;
}
```

### Response Example

```json
Response PriorityFee Percentile: 75
Response PriorityFee Value: 10000 // The priority fee corresponding to the quantile, in micro-lamports
Response Tip Percentile: 75 
Response Tip Value: 0.0001 // The tip corresponding to the quantile, in Sol
Response2 PriorityFee Percentile: 75
Response2 PriorityFee Value: 432313.2435833086
Response2 Tip Percentile: 75
Response2 Tip Value: 0.0001
```


# BSC Network Fee Stream

This section introduces BlockRazor's Network Fee Stream service for BSC, primarily focusing on Gas Price Stream.


# BSC GetGasPriceStream

Introducing the access method of BlockRazor BSC GetGasPriceStream

### Introduction

`GetGasPriceStream` provides gas price subscription service on BSC. It pushes the gas price of BSC transactions in recent block to users at a specified percentile based on gRPC stream. The endpoint is: <mark style="color:$primary;">grpc.bsc-fee.blockrazor.me:443</mark>

### Rate Limit

The price is $30 / day and $300 / month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fee_stream&#x26;billing=day" class="button primary small">Subscribe</a>

### Request Parameter

<table><thead><tr><th width="131.4765625">Parameters</th><th width="131.76171875">Mandatory</th><th width="86.74609375">Format</th><th width="104.44140625">Example</th><th>Remarks</th></tr></thead><tbody><tr><td>percentile</td><td>mandatory</td><td>int</td><td>50</td><td>Get the gas price of the specified percentile, enumerated value: 25, 50, 75, 95, 99</td></tr><tr><td>blockRange</td><td>mandatory</td><td>int</td><td>20</td><td>Statistics of gas prices of transactions in the last N blocks, where N ranges from 1 to 20</td></tr></tbody></table>

### Request Example

```go
package main

import (
	"context"
	"crypto/tls"
	"log"

	// directory of the generated code using the provided proto file
	pb "fee-test/bscfeepb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	gRPCEndpoint = "grpc.bsc-fee.blockrazor.xyz:443" // endpoint address
	auth         = "your_auth" // auth to be verified
)

func main() {
	// open gRPC connection to endpoint
	conn, err := grpc.Dial(
		gRPCEndpoint,
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
		grpc.WithPerRPCCredentials(&Authentication{auth}),
	)
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	
	// create the gRPC client
	client := pb.NewServerClient(conn)

	// send gRPC request
	req := &pb.TransactionFee{
		Percentile: 95,
		BlockRange: 10,
	}
	stream, err := client.GetGasPriceStream(context.Background(), req)
	if err != nil {
		panic(err)
	}

	for {
		message, err := stream.Recv()
		if err != nil {
			log.Printf("Failed to receive message: %v", err)
			break
		}
		log.Printf("Received message: %s\n", message.Time.AsTime().Format("2006-01-02 15:04:05"))
		log.Printf("Received GasPrice: %v\n", message.GasPrice.Percentile)
		log.Printf("Received GasPrice: %v\n", message.GasPrice.Value)
	}
}

type Authentication struct {
	auth string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apiKey": a.auth}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}


```

#### Proto

```json
syntax = "proto3";

package bscfeepb;

import "google/protobuf/timestamp.proto";

option go_package = "./pb/bscfeepb";

service Server {
    rpc GetGasPriceStream(TransactionFee) returns(stream TransactionFeeStreamResponse) {};
}

message TransactionFee {
    int32 percentile = 1;
    int32 blockRange = 2;
}

message FeeValue {
    int32 percentile = 1;
    string value = 2;
}

message TransactionFeeStreamResponse {
    FeeValue gasPrice = 1;
    google.protobuf.Timestamp time = 2;
}
```

### Response Example

```json
2025/03/26 10:25:42 Received message: 2025-03-26 02:25:42
2025/03/26 10:25:42 Received GasPrice: 95
2025/03/26 10:25:42 Received GasPrice: 1000000000 // The gas price corresponding to the quantile, in wei
2025/03/26 10:25:45 Received message: 2025-03-26 02:25:45
2025/03/26 10:25:45 Received GasPrice: 95
2025/03/26 10:25:45 Received GasPrice: 1000000000
```


# BSC GetAllGasPriceStream

Introducing the access method of BlockRazor BSC GetAllGasPriceStream

### Introduction

`GetAllGasPriceStream` provides gas price subscription service on BSC. It pushes the gas price of BSC transactions in recent block to users at all percentile based on gRPC stream. The endpoint is: <mark style="color:$primary;">grpc.bsc-fee.blockrazor.me:443</mark>

### Price

The price is $30 / day and $300 / month. <a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fee_stream&#x26;billing=day" class="button primary small">Subscribe</a>

### Request Parameter

<table><thead><tr><th width="131.4765625">Parameters</th><th width="131.76171875">Mandatory</th><th width="86.74609375">Format</th><th width="104.44140625">Example</th><th>Remarks</th></tr></thead><tbody><tr><td>blockRange</td><td>mandatory</td><td>int</td><td>20</td><td>Statistics of gas prices of transactions in the last N blocks, where N ranges from 1 to 20</td></tr></tbody></table>

### Request Example

```go
package main

import (
	"context"
	"crypto/tls"
	"log"

	pb "fee-test/bscfeepb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

const (
	gRPCEndpoint = "grpc.bsc-fee.blockrazor.xyz:443"
	auth         = "your_auth"
)

func main() {
	conn, err := grpc.Dial(
		gRPCEndpoint,
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
		grpc.WithPerRPCCredentials(&Authentication{auth}),
	)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	client := pb.NewServerClient(conn)

	req := &pb.BlockRange{
		BlockRange: 5,
	}
	stream, err := client.GetAllGasPriceStream(context.Background(), req)
	if err != nil {
		panic(err)
	}

	for {
		message, err := stream.Recv()
		if err != nil {
			log.Printf("Failed to receive message: %v", err)
			break
		}
		log.Printf("Received Time: %s\n", message.Time.AsTime().Format("2006-01-02 15:04:05"))
		for _, gasPrice := range message.AllGasPrice {
			log.Printf("Received message percentile: %d\n", gasPrice.Percentile)
			log.Printf("Received message value: %s\n", gasPrice.Value)
		}
	}
}

type Authentication struct {
	auth string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apiKey": a.auth}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}
```

#### Proto

```json
syntax = "proto3";

package bscfeepb;

import "google/protobuf/timestamp.proto";

option go_package = "./pb/bscfeepb";

service Server {
    rpc GetGasPriceStream(TransactionFee) returns(stream TransactionFeeStreamResponse) {};
    rpc GetAllGasPriceStream(BlockRange) returns(stream AllGasPriceStreamResponse) {};
}

message TransactionFee {
    int32 percentile = 1;
    int32 blockRange = 2;
}

message FeeValue {
    int32 percentile = 1;
    string value = 2;
}

message TransactionFeeStreamResponse {
    FeeValue gasPrice = 1;
    google.protobuf.Timestamp time = 2;
}

message BlockRange {
    int32 blockRange = 2;
}

message AllGasPriceStreamResponse {
    repeated FeeValue allGasPrice = 1;
    google.protobuf.Timestamp time = 2;
}
```

### Response Example

```json
2025/04/08 10:18:21 Received Time: 2025-04-08 02:18:21
2025/04/08 10:18:21 Received message percentile: 25
2025/04/08 10:18:21 Received message value: 1000000000 //// The gas price corresponding to the quantile, in wei
2025/04/08 10:18:21 Received message percentile: 50
2025/04/08 10:18:21 Received message value: 1100000000
2025/04/08 10:18:21 Received message percentile: 75
2025/04/08 10:18:21 Received message value: 2020000000
2025/04/08 10:18:21 Received message percentile: 95
2025/04/08 10:18:21 Received message value: 5000000000
2025/04/08 10:18:21 Received message percentile: 99
2025/04/08 10:18:21 Received message value: 20000000000
```


# Transaction Submission Overview

This section introduces the transaction submission modes, including RPC, Block Builder, Transaction Sending, and Gas Sponsor, and explains how to choose the appropriate mode based on the scena

### What is Transaction Submission

Transaction Submission is a collection of transaction submission capabilities in BlockRazor. Based on different transaction submission modes, it meets users' needs for fast transaction inclusion, gas sponsorship, MEV protection, and real-time rebates, providing a better trading experience and execution results for wallets, DEXs, Trading Bots, Searchers, and quantitative trading systems.

### What are the modes of Transaction Submission?

#### RPC

RPC is the standard transaction submission entry point for Transaction Submission. Unlike ordinary public RPCs, BlockRazor RPC improves transaction speed and stability while providing private routing, MEV protection, and a refund mechanism, making it suitable as the default transaction channel for most businesses. BlockRazor RPC also provides a bundle submission entry point to meet the needs of scenarios such as Approve + Swap / Backrun / Copy Trading / Sniping.

#### Block Builder

Block Builder is the block building infrastructure provided by BlockRazor on BSC, supporting core capabilities such as bundle submission, private transaction submission, and bundle trace. It enhances block building competitiveness and block success rate through global deployment, low-latency communication with validators, and various block building algorithms.

#### Transaction Sending

Fast is the "speed-first" mode in Transaction Submission, suitable for scenarios highly sensitive to on-chain latency. It uses [BEF](/core-technology/blockchain-edge-fabric) to allow transactions to reach the block-producing node within a shorter time window.

#### Gas Sponsor

Gas Sponsor is a "cost-first" model within Transaction Submission. By paying gas fees on behalf of users, Gas Sponsor allows users to complete transactions without holding native tokens (such as ETH, BNB, and SOL), helping projects lower the barrier to entry for users and optimize trading experience.

### How to choose the Transaction Submission mode

<table><thead><tr><th width="287">Scene</th><th width="223.80859375">User</th><th width="213">Mode</th></tr></thead><tbody><tr><td>Protect against MEV attack and get refunds</td><td>Wallets / DEX</td><td>RPC - RawTransaction</td></tr><tr><td>Extreme speed boost and lower latency</td><td>Wallets / DEX / Trading Bot</td><td>Transaction Sending</td></tr><tr><td>No gas cost for users</td><td>Wallets / DEX</td><td>Gas Sponsor</td></tr><tr><td>Approve + Swap / Backrun / Copy Trading / Sniping </td><td>Wallets / DEX / Trading Bot / Searcher</td><td>RPC - Bundle</td></tr></tbody></table>

### FAQ

<details>

<summary>What is the difference between submitting a Bundle to an RPC and submitting a Bundle to a Block Builder?</summary>

The core difference between the two lies in the different submission paths and final destinations of the Bundle.\
When submitting a Bundle to BlockRazor RPC, BlockRazor RPC forwards the Bundle to mainstream builders with low latency. This approach is more suitable as a unified access point, allowing users to submit Bundles without having to connect to different builders individually.\
When you submit a Bundle to Block Builder, the Bundle is sent directly to BlockRazor Builder. This is more suitable for scenarios where you explicitly want to use BlockRazor Builder capabilities and the access path.

</details>


# BlockRazor RPC

BlockRazor provides BSC RPC and Ethereum RPC services to users.


# BSC RPC

BlockRazor BSC RPC provides standard JSON-RPC access with private transaction routing, MEV protection, low-latency inclusion, and real-time backrun rebates on BNB Smart Chain.

### What Is BSC RPC

BSC RPC is an RPC service provided by BlockRazor for BNB Smart Chain (BSC). It supports commonly used standard JSON-RPC methods, allowing users to query on-chain data and submit transactions. When sending transactions, users can also benefit from transaction privacy, MEV protection, low-latency inclusion, and real-time rebates.

### Why Choose BSC RPC

**Transaction Privacy**: Transactions are submitted through private paths, reducing the risk of exposing transaction intent through public propagation and protecting users against malicious MEV attacks such as sandwich attacks and frontrunning.

**Real-Time Rebates**: Controlled data disclosure enables harmless backruns. By default, 60% of the distributable backrun revenue is returned to users in real time.

**Low-Latency Inclusion**: Powered by BEF path and topology optimization, along with global and regional endpoints, BSC RPC provides more stable transaction submission for latency-sensitive, high-frequency, and multi-region deployments.

**Zero-Barrier Integration**: BSC RPC can be added to a wallet with one click. It uses standard JSON-RPC methods, requires no authentication, and can quickly replace an existing BSC RPC endpoint.

### Who Is BSC RPC For

**Wallets / DEXs**: Teams that want to improve transaction protection and execution quality on BSC while offering rebates to their users.

**Trading Bots / Quant Teams**: Teams focused on low latency, transaction ordering, inclusion stability, and execution quality across regions.

**Searchers**: Professional users who need to submit Bundles or participate in the Orderflow Auction.

**Project Builders**: Projects that require a dedicated RPC, low-latency Builder connections, and custom data disclosure and rebate configurations.

**Individual Traders**: Users who want a safer transaction path on BSC and the opportunity to receive MEV rebates.

### FAQ

<details>

<summary>How can a transaction receive a rebate while still being protected from MEV?</summary>

MEV protection primarily prevents strategies that harm users, such as sandwich attacks and frontrunning. For safe backruns that do not negatively affect the user’s expected execution outcome, BlockRazor can provide eligible Searchers with the necessary information within the user-authorized disclosure scope. A portion of the resulting revenue is then returned to the user.

</details>

<details>

<summary>In which regions is BSC RPC available?</summary>

BSC RPC is currently deployed across NewYork, Tokyo, Frankfurt, and Dublin.

</details>

<details>

<summary>What is the difference between BSC RPC and BSC Block Builder?</summary>

BSC RPC serves as a unified transaction submission endpoint and a standard JSON-RPC access point for wallets, DEXs, trading systems, and individual users. It can also forward Bundles to mainstream Builders with low latency. BSC Block Builder is designed for professional use cases that specifically require block building, transaction ordering, Bundle submission, or private transaction capabilities.

</details>

<details>

<summary>When will the rebate arrive?</summary>

If a transaction has an executable backrun opportunity and the backrun successfully generates revenue, the rebate is usually processed in real time through an on-chain transaction and may be included in the same block as the user’s transaction.

</details>

### Privacy Statement

BlockRazor does not collect users’ personal information, such as IP addresses or location data, for advertising or tracking purposes. Data is processed only when necessary to provide services and improve the user experience. BlockRazor may retain information that is already publicly available on-chain, such as transaction timestamps. For complete details, please refer to the latest BlockRazor Privacy Statement.


# BSC RPC Endpoint

### General Endpoints

<table data-search="false"><thead><tr><th width="127.3359375"></th><th width="186">default</th><th width="196">fullprivacy</th><th>maxbackrun</th></tr></thead><tbody><tr><td>URL</td><td>https://bsc.blockrazor.xyz</td><td>https://bsc.blockrazor.xyz/fullprivacy</td><td>https://bsc.blockrazor.xyz/maxbackrun</td></tr><tr><td>MEV Protection</td><td>Enabled</td><td>Enabled</td><td>Enabled</td></tr><tr><td>Transaction Privacy</td><td>Minimal disclosure</td><td>Full privacy</td><td>Maximum disclosure</td></tr><tr><td>Rebate Potential</td><td>Medium</td><td>No rebate</td><td>High</td></tr><tr><td>Rebate Percentage</td><td>Supported</td><td>No rebate</td><td>Supported</td></tr><tr><td>Revert Protection</td><td>Disabled</td><td>Enabled</td><td>Enabled</td></tr></tbody></table>

<details>

<summary><strong>default Mode</strong></summary>

In `default` mode, transactions submitted through BSC RPC disclose only the necessary transaction data to Searchers (`hash`, `logs`, and state changes). This provides opportunities for rebates while protecting transaction privacy as much as possible. To prioritize fast block inclusion, revert protection is not enabled in this mode.

</details>

<details>

<summary><strong>fullprivacy Mode</strong></summary>

In `fullprivacy` mode, transactions submitted through BSC RPC do not disclose any transaction data. BSC RPC forwards the transactions directly to mainstream Builders. Because no transaction data is disclosed, transactions in this mode do not generate rebates, and no rebate percentage needs to be configured. Revert protection is enabled in this mode.

</details>

<details>

<summary><strong>maxbackrun Mode</strong></summary>

In `maxbackrun` mode, transactions submitted through BSC RPC disclose the transaction data required to maximize rebate opportunities while maintaining privacy protection. The disclosed fields include `hash`, `to`, `calldata`, `functionSelector`, `logs`, and state changes. Revert protection is enabled in this mode.

</details>

### Dedicated Endpoints

A dedicated endpoint is a private transaction channel provided by BlockRazor for an individual user or project. Users can configure dedicated endpoints in the BlockRazor console, including customizing the endpoint URL for easier identification and configuring transaction disclosure parameters and rebate recipient addresses.

After adding a dedicated endpoint to a wallet or integrating it into a project, users can view its transactions and rebate records in the BlockRazor console.

<table><thead><tr><th width="148.8359375">Endpoint</th><th width="551.25390625">Example URL</th></tr></thead><tbody><tr><td>Dedicated Endpoint URL</td><td>https://bsc.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Custom Endpoint URL</td><td>https://&#x3C;custom_content>.bsc.blockrazor.xyz</td></tr></tbody></table>

### Regional Endpoints

<table><thead><tr><th width="148.99609375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Tokyo</td><td>https://jp-bscscutum.blockrazor.xyz</td></tr><tr><td>New York</td><td>https://us-bscscutum.blockrazor.xyz</td></tr><tr><td>Frankfurt</td><td>https://ger-bscscutum.blockrazor.xyz</td></tr><tr><td>Dublin</td><td>https://ire-bscscutum.blockrazor.xyz</td></tr></tbody></table>

Regional endpoints are suitable for projects that are highly sensitive to transaction latency and whose transaction sources are concentrated in specific regions.


# How to integrate BSC RPC into Project

Introduce the steps for integrating BlockRazor BSC RPC.

{% hint style="info" %}
BlockRazor RPC is open to all users; no service purchase or authentication application is required.
{% endhint %}

### Endpoint

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://bsc.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://bsc.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.bsc.blockrazor.xyz</td></tr></tbody></table>

### How to integrate RPC into your project

#### 1. Configure RPC

1. Register and log in to the portal at [blockrazor.io](https://blockrazor.io/).
2. Under the RPC module, click on the RPC page to view the configuration information of your RPC.
3. Click on **Update** to enter the configuration update page, and adjust the parameters according to your needs. The meaning of the parameters is shown in the following table.

<table><thead><tr><th width="181">Parameters</th><th>Meaning</th></tr></thead><tbody><tr><td>Default RPC URL</td><td>Each account automatically generates 1 Ethereum RPC and 1 BSC RPC by default. The Default RPC URL is automatically generated and cannot be modified.</td></tr><tr><td>Custom RPC URL</td><td>The third-level domain is allowed to be modified, and the Custom RPC URL can be promoted to the project's end-users through websites or docs, guiding them to add custom RPC within their wallets.</td></tr><tr><td>Hint</td><td>The system defaults to sharing the transaction's hash and logs with the Searcher. The more fields shared, the greater the possibility of obtaining refunds, please proceed with caution after evaluating the need for disclosure of transaction data.</td></tr><tr><td>Refund Address</td><td>The default refund address is tx.origin, which means the refund will be returned to the sender of the transaction. It can be modified to a fixed refund address (EOA).</td></tr><tr><td>Revert Protection</td><td>revert protection is enabled by default; if a transaction is detected to revert, it will not be included in the block. To ensure fast inclusion in a block, it is recommended to set priority fee (Ethereum) when sending transactions.</td></tr></tbody></table>

4. Click **Confirm**, the system will update the RPC configuration in real time.

#### 2. Integrate RPC

1. Find the configuration file or code: Open the project workspace and locate the file or code segment that configures the RPC node in the DApp project. This could be a configuration file such as .env, config.js, truffle-config.js, etc., or it could be hardcoded directly in the code.
2. Modify the RPC URL: Change the RPC URL in the configuration file or code to the Scutum RPC URL.
3. Test the connection: After making the change, run the DApp or the corresponding test script locally to ensure that the new RPC URL works properly. You can use methods like `web3.eth.net.isListening()` or `ethers.provider.pollingInterval` to check if the connection is successful.
4. Deploy the update: If the test passes, you can deploy the changes to the production environment.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// import Web3
const Web3 = require('web3');

// Create a Web3 instance and connect to the RPC.
const web3 = new Web3('https://ethereum-rpc.publicnode.com'); // You can replace the RPC URL with the Scutum RPC URL here.

// check the connection
web3.eth.net.isListening()
  .then((listening) => {
    console.log('Web3 connected: ', listening);
  })
  .catch((err) => {
    console.error('Web3 connection error: ', err);
  });
```

{% endtab %}
{% endtabs %}

#### 3. Query Transactions

1. Log in to [blockrazor.io](https://blockrazor.io).
2. Under the Scutum module, click on **Refunds** to view the refund, and click on **Transactions** to view the transactions submitted to dedicated RPC.


# BSC RPC Refund Mechanism

Learn how BlockRazor BSC RPC protects transactions and returns 60% of distributable backrun revenue to users through private orderflow auctions.

## BSC RPC Refund Mechanism

When a transaction is sent through BlockRazor BSC RPC, it can enter the orderflow auction under predefined disclosure rules without being exposed to the public mempool. Searchers use the permitted information to independently identify backrun opportunities and submit bids. If the winning backrun Bundle is successfully included on-chain and generates revenue, the user receives **60% of the distributable revenue by default**.

#### How Are Refunds Generated?

```mermaid
graph LR
    A[User sends a transaction] --> B[Data is disclosed under predefined rules]
    B --> C[Searcher submits a backrun and bid]
    C --> D[Auction selects the winning Bundle]
    D --> E[Bundle is sent to Builders with low latency]
    E --> F{Successfully included and generates revenue}
    F -->|Yes| G[User receives 60% by default]
    F -->|No| H[No refund is generated]
```

**1. The User Sends a Transaction Normally**

Users can send transactions through `eth_sendRawTransaction` in the same way they would with a standard BSC RPC. BlockRazor BSC RPC is compatible with standard JSON-RPC methods, so users do not need to modify the transaction itself.

After entering BlockRazor’s private path, the complete transaction is not directly broadcast to the public mempool. This reduces exposure to malicious MEV attacks such as sandwich attacks and frontrunning.

**2. Transaction Information Is Disclosed Under Predefined Rules**

The scope of transaction data disclosure is determined during integration. Configurable fields include the transaction hash, sender, recipient, `value`, `nonce`, `calldata`, `function selector`, and `logs`.

Only explicitly enabled fields are disclosed. Fields that are not enabled remain private.

**3. Searchers Independently Identify Backrun Opportunities**

Eligible Searchers can subscribe to the permitted transaction information through the BSC Private Mempool. Searchers independently analyze this information and determine whether they can construct a backrun strategy that does not harm the user’s transaction outcome.

If a Searcher identifies an executable opportunity, it constructs a backrun Bundle and submits a bid with the Bundle. If no Searcher submits a valid backrun Bundle, or if the Bundle does not generate revenue, the user will not receive a refund.

**4. The Winning Bundle Is Selected Through the Orderflow Auction**

BlockRazor RPC conducts an English auction based on the bid amount. Searchers can continuously submit different backrun Bundles to participate in the auction. BlockRazor sends the winning Bundle to mainstream Builders with low latency.

The refund recipient address, refund configuration, and bid amount are validated. Revenue collection and distribution are handled through smart contracts.

**5. Revenue Is Refunded to the User After Successful Inclusion**

Revenue is distributed only when the user’s transaction and the winning backrun Bundle are successfully included on-chain and the backrun generates actual revenue.

By default, the user receives **60% of the distributable backrun revenue**. The remaining portion covers service fees and the Builder’s transaction execution costs.

#### What Does the User Need to Do?

Individual users only need to switch the BSC RPC in their wallet to BlockRazor BSC RPC and send transactions as usual. Users do not need to manually manage Searcher participation or determine whether a transaction generates a refund.

Wallets, DEXs, and other projects using a dedicated RPC can define transaction disclosure rules, refund recipient addresses, and other settings during integration. After configuration, BlockRazor processes subsequent transactions according to the predefined rules.

#### Does Every Transaction Receive a Refund?

No. A refund requires all of the following conditions to be met:

* The transaction enters the orderflow auction under the predefined disclosure rules
* A Searcher identifies an executable backrun opportunity using the disclosed information
* The Searcher submits a valid backrun Bundle and participates in the auction
* The winning Bundle and the user’s transaction are successfully included on-chain
* The backrun generates distributable revenue

A refund is therefore not a fixed reward and is not guaranteed for every transaction. Even when no refund is generated, the user’s transaction continues through the BlockRazor BSC RPC submission path and receives the applicable transaction privacy and malicious MEV protection.

#### When Will the Refund Arrive?

After the winning Bundle is successfully included on-chain and generates revenue, the refund is distributed through an on-chain smart contract. It can usually be processed in the same block as the user’s transaction. The actual arrival time depends on the on-chain inclusion of the transaction and Bundle.


# BSC RPC eth\_sendRawTransaction

Introduce how to integrate BlockRazor BSC RPC’s \`eth\_sendRawTransaction\` method.

&#x20;`eth_sendRawTransaction` of BlockRazor RPC is compatible with native JSON-RPC methods and requires no additional modifications.

If you need to modify parameters such as hint, refund address and revert protection, you can configure the dedicated RPC in the [portal](https://www.blockrazor.io/#/login)

### Endpoint

{% hint style="info" %}
For project RPC, please refer to the [Integration](/transaction-submission/rpc/bsc/integration)
{% endhint %}

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://bsc.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://bsc.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.bsc.blockrazor.xyz</td></tr></tbody></table>

### Request parameters

<table><thead><tr><th width="124">Parameters</th><th width="127">Mandatory</th><th width="110">Format</th><th width="180">Example</th><th>Remark</th></tr></thead><tbody><tr><td>-</td><td>Mandatory</td><td>bytes</td><td>"0xd46e……445675"</td><td>raw tx</td></tr></tbody></table>

### Request Example

```json
curl -X POST -H "Content-Type: application/json" --data '{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": [
        "0xd46e……445675"
    ]
}' https://bsc.blockrazor.xyz
```

### Response Example

**normal**

```json
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0xe670……527331"
}
```

**abnormal**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
	"code": -32000,
	"message": "rlp: element is larger than containing list"
  }
}
```

### Other JSON-RPC Methods

BlockRazor RPC supports standard JSON-RPC method, you can refer to <https://ethereum.org/zh/developers/docs/apis/json-rpc/#json-rpc-methods>


# BSC RPC eth\_sendBundle

Introduce how Project Builders can integrate BlockRazor BSC RPC’s \`eth\_sendMevBundle\` method.

### Endpoint

{% hint style="info" %}
For project RPC, please refer to the [Integration](/transaction-submission/rpc/bsc/integration)
{% endhint %}

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://bsc.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://bsc.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.bsc.blockrazor.xyz</td></tr></tbody></table>

### Request parameters

{% hint style="info" %}
On BSC, `eth_sendMevBundle` allows transactions with 0 gwei in the bundle, but the average gasPrice of transactions(excluding those from the public mempool) in the bundle must still be no less than 0.05 gwei. Since head builders of BSC have a preference for this model, it is recommended to construct transactions with 0 gwei.
{% endhint %}

#### **Bundle**

<table><thead><tr><th width="183">Parameters</th><th width="118">Mandatory</th><th width="98">Format</th><th width="132">Example</th><th>Remark</th></tr></thead><tbody><tr><td>txs</td><td>mandatory</td><td>[]bytes</td><td>[ "0xf84a……e54284" ]</td><td>raw txs, up to 50 transactions allowed to be set</td></tr><tr><td>revertingTxHashes</td><td>optional</td><td>[]hash</td><td>["0x1f23……0abb1e"]</td><td>Transactions that allow to be reverted, a subset of txs</td></tr><tr><td>maxBlockNumber</td><td>optional</td><td>uint64</td><td>39177941</td><td>The maximum block number for the bundle to be valid, with the default set to the current block number + 100</td></tr></tbody></table>

### Request Example

```json
curl -X POST -H "Content-Type: application/json" --data '{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "eth_sendMevBundle",
  "params": [{
    "Txs": [
"0xf8668203988405f5e100825208942ee393c739036a7660ec11bf2101d537eb52f3ac80808193a06836d5f4052376dc3114794da5fedd7a5b8090ddae0ec45dfa66c234fcabb6efa07cf17dad992c33e8e3e4d82355cfe12d3be2560fc2b0873c36dce398088d8e4f"
    ],
    "revertingTxHashes":[],
    "maxBlockNumber":62934913
  }]
}' https://bsc.blockrazor.xyz
```

### Response Example

normal

```json
{"jsonrpc":"2.0","id":1,"result": "0x11111111..."}
```

abnormal

```json
{"jsonrpc":"2.0","id":1,"jsonerror":{"code":-38000,"message":"nonce too low: address 0x9Abae1b279A4Be25AEaE49a33e807cDd3cCFFa0C, tx: 0 state: 45"}}
```


# BSC RPC Transaction Trace

Introduce how to integrate BlockRazor BSC RPC’s \`scutum\_queryTxProcessStatus\` method.

`scutum_queryTxProcessStatus` is used to query the real-time process status of transactions sent to BlockRazor RPC. It is currently available on BSC.

### Request Example

```json
curl -X POST -H "Content-Type: application/json" --data'{
	"id": 1,
	"jsonrpc": "2.0",
	"method": "scutum_queryTxProcessStatus",
	"params": ["0xf84a……e54284"]
}'<ETH_NODE_URL>
```

### Response Example

**Normal**

```json
{
  "jsonrpc": "2.0",
  "result": "{"msg\":\"tx is included on chain\",\"status\":\"included\"}",
  "id": "1"
} // the tx is executed on chain, success or reverted
```

```json
{
  "jsonrpc": "2.0",
  "result": "{"msg\":\"tx is expired and discarded\",\"status\":\"expired\"}",
  "id": "1"
} // 100 blocks has passed since submission of the tx
```

```json
{
  "jsonrpc": "2.0",
  "result": "{"msg\":\"nonce too high\",\"status\":\"pending\"}",
  "id": "1"
} // the tx is queued since the nonce is too high
```

```json
{
  "jsonrpc": "2.0",
  "result": "{"msg\":\"tx is pending\",\"status\":\"pending\"}",
  "id": "1"
} // the tx is being processed by Scutum
```

```json
{
  "jsonrpc": "2.0",
  "result": "{"msg\":\"simulation error: xxxxxxx\",\"status\":\"failed\"}",
  "id": "1"
} // the tx failed to be processed by Scutum due to the simulation error
```

**Abnomal**

```json
{
  "jsonrpc": "2.0",
  "error": "{\"code\":-32000,\"message\":\"tx not found\"}",
  "id": "1"
}  //the tx has not been sent to Scutum or has exceeded the Scutum processing time limit
```


# BSC RPC Orderflow Auction

Introduce how Seachers can integrate BlockRazor BSC RPC’s \`eth\_sendMevBundle\` method.

### Introduction

Searcher can subscribe [Private Mempool](/streams/private-mempool) to execute the backrun strategy, and then send the backrun bundles to BlockRazor RPC to obtain benefits via backrun auction.

In addition, Searcher can also skip bundle subscription and send the raw bundle directly to BlockRazor RPC. With the high-performance network, BlockRazor can forward the bundle to mainstream builders with extremely low latency, eliminating the need for repeated integrations with each builder.

### Auction Mechanism

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FShMRdK1EgzEQo6c6QKn2%2Fimage.png?alt=media&amp;token=5155e5f0-c88a-49b1-b50f-99e11423ad10" alt=""><figcaption><p>bundle flow</p></figcaption></figure>

#### Bidding timing

Searcher can continuously submit bundles (repeated submissions are not allowed), and BlockRazor RPC will choose the best time to submit winning bundles to top builders. If the bundle has been included in the block or has expired, it will stop being disclosed in the data stream.

#### Auction rules

BlockRazor RPC conducts English bidding based on the bid value, of which the receipt and distribution is realized via smart contract.

#### Bidding method

When constructing a backrun transaction, the backrun contract could call the proxyBid method of the bidding proxy contract as follows.&#x20;

```
interface IProxyBid { 
    function proxyBid(address refundAddress, uint256 refundCfg) external payable; 
}
```

The biding proxy contract address (proxyBidContract), refundAddress and refundCfg can be obtained from [Private Mempool](/streams/private-mempool) and msg.value(the biding value) must be greater than 0.

The correctness of the parameters will be strictly verified by BlockRazor RPC. Please do not directly transfer to the refundAddress and the address of bidding proxy contract or perform other operations that may cause changes to the balance of the above account.

### RPC Endpoint

{% hint style="info" %}
Please keep the domain for subscribing to the bundle consistent with the domain for sending the bundle. For example, if subscribing to <mark style="color:$info;"><https://jp-bscscutum.blockrazor.xyz/stream></mark>, then send the bundle to <mark style="color:$info;"><https://jp-bscscutum.blockrazor.xyz></mark>
{% endhint %}

<table><thead><tr><th width="149.453125">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Tokyo</td><td>https://jp-bscscutum.blockrazor.xyz</td></tr><tr><td>New York</td><td>https://us-bscscutum.blockrazor.xyz</td></tr><tr><td>Frankfurt</td><td>https://ger-bscscutum.blockrazor.xyz</td></tr><tr><td>Dublin</td><td>https://ire-bscscutum.blockrazor.xyz</td></tr></tbody></table>

### Request Parameters

#### **Bundle**

<table><thead><tr><th width="138">Parameters</th><th width="123">Mandatory</th><th width="102">Format</th><th width="124">Example</th><th>Remark</th></tr></thead><tbody><tr><td>hash</td><td>optional</td><td>hash</td><td>"0xa06b……f7e8ec"</td><td>The bundle hash received from the data stream, that is, the object being backrun</td></tr><tr><td>txs</td><td>mandatory</td><td>[]bytes</td><td>[ "0xf84a……e54284" ]</td><td>raw tx. If the <code>hash</code> is empty, up to 50 raw txs are allowed to set up, and if the <code>hash</code> is not empty, only 1 raw tx is allowed to set up</td></tr><tr><td>revertingTxHashes</td><td>optional</td><td>[]hash</td><td>["0x1f23……0abb1e"]</td><td>Transactions that allow to be reverted, a subset of txs</td></tr><tr><td>maxBlockNumber</td><td>optional</td><td>uint64</td><td>39177941</td><td>The maximum block number for the bundle to be valid, with the default set to the current block number + 100</td></tr><tr><td><a href="#hint">hint</a></td><td>optional</td><td><a href="#hint">map[string]bool</a></td><td></td><td>See <a href="#hint">hint</a> for details</td></tr><tr><td>refundAddress</td><td>optional</td><td>address</td><td>"0x9abae1b279a4be25aeae49a33e807cdd3ccffa0c"</td><td>If there is a field with a value of true in hint, this field should be set to EOA.</td></tr></tbody></table>

#### hint

The disclosure for the transaction data in field `txs`is set by hint.  If it is set to true, it will be regarded as disclosing the corresponding transaction field. If it is false, it will be regarded as not disclosing the corresponding transaction field. If it is not set, the default is false.

<table data-search="false"><thead><tr><th width="143">Parameters</th><th width="113">Mandatory</th><th width="97">Format</th><th width="104">Example</th><th>Remark</th></tr></thead><tbody><tr><td>hash</td><td>optional</td><td>bool</td><td>true</td><td>transaction hash</td></tr><tr><td>from</td><td>optional</td><td>bool</td><td>false</td><td>sender of the transaction</td></tr><tr><td>to</td><td>optional</td><td>bool</td><td>true</td><td>receiver of the transaction</td></tr><tr><td>value</td><td>optional</td><td>bool</td><td>false</td><td>value being transacted</td></tr><tr><td>nonce</td><td>optional</td><td>bool</td><td>false</td><td>nonce</td></tr><tr><td>calldata</td><td>optional</td><td>bool</td><td>true</td><td>calldata</td></tr><tr><td>functionSelector</td><td>optional</td><td>bool</td><td>true</td><td>the first 4 bytes of the contract function signature hash</td></tr><tr><td>logs</td><td>optional</td><td>bool</td><td>true</td><td>event logs emitted during transaction execution(this field synchronously sets whether to disclose state changes in the state object)</td></tr></tbody></table>

### Request Example

#### **Raw Bundle**

There is no backrun object so the hash field should not be set. The txs in the bundle come from the public mempool or are self-constructed, and up to 50 rawtransactions can be set. Searchers can authorize the disclosure of txs in raw bundles to allow other Searchers to backrun, or they can keep transactions private and Scutum will forward the raw bundles to mainstream builders.

```json
curl -X POST -H "Content-Type: application/json" --data'{
	"id": 1,
	"jsonrpc": "2.0",
	"method": "eth_sendMevBundle",
	"params": [{
		"txs": ["0xf84a8080808080808193a0437a5584216e68d1ff5bd7803161865e058f9bf4637fd1391213eac03ae64444a00df12bffe475d5dd8cc1544b72ee280471f1dcb5173827ba41eb25cfc3e54284"],
		"revertingTxHashes": [],
		"maxBlockNumber": 39177941,
		"hint": {
			"hash": true,
			"from": false,
			"to": false,
			"value": false,
			"nonce": false,
			"calldata": false,
			"functionSelector": false,		
			"logs": true
		},
		"refundAddress": "0x9abae1b279a4be25aeae49a33e807cdd3ccffa0c"
	}]
}'<ETH_NODE_URL>
```

#### First Backrun Bundle

Searcher executes the backrun strategy on Raw Bundle, and can choose to continue to disclose the bundle to other Searchers to execute nested backrun strategies. The overall structure of First Backrun Bundle is generally \[Raw Bundle tx1, backrun tx1].

Searcher executes the backrun strategy on the raw bundle to form the first backrun bundle.  The `hash` field sets the raw bundle hash received in the data stream, and `txs` field sets the backrun tx. Searchers can disclose the backrun bundle to other Searchers to execute nested backrun strategy. The overall structure of the backrun bundle in the data stream is generally \[raw bundle txs…, backrun tx].

```json
curl -X POST -H "Content-Type: application/json" --data'{
	"id": 1,
	"jsonrpc": "2.0",
	"method": "eth_sendMevBundle",
	"params": [{
		"hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
		"txs": ["0xf84a8080808080808193a0437a5584216e68d1ff5bd7803161865e058f9bf4637fd1391213eac03ae64444a00df12bffe475d5dd8cc1544b72ee280471f1dcb5173827ba41eb25cfc3e54284"],
		"revertingTxHashes": [],
		"maxBlockNumber": 39177941,
		"hint": {
			"hash": true,
			"from": false,
			"to": true,
			"value": false,
			"nonce": false,
			"calldata": true,
			"functionSelector": true,		
			"logs": true
		},
		"refundAddress": "0x9abae1b279a4be25aeae49a33e807cdd3ccffa0c"
	}]
}'<ETH_NODE_URL>
```

#### Second Backrun Bundle

Searcher can execute the backrun strategy again on the first backrun bundle submitted by other Searchers, forming a nested bundle that is backrun twice. The `hash` field sets the hash of the first backrun bundle, and `txs` sets the second backrun tx. The overall structure of the second backrun bundle is generally \[raw bundle txs…, first backrun tx, second backrun tx].

{% hint style="info" %}
The second backrun bundle will no longer be disclosed to other Searchers, and the parameters hint, refundRecipient will be invalid.
{% endhint %}

```json
curl -X POST -H "Content-Type: application/json" --data'{
	"id": 1,
	"jsonrpc": "2.0",
	"method": "eth_sendMevBundle",
	"params": [{
		"hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
		"txs": ["0xf84a8080808080808193a0437a5584216e68d1ff5bd7803161865e058f9bf4637fd1391213eac03ae64444a00df12bffe475d5dd8cc1544b72ee280471f1dcb5173827ba41eb25cfc3e54284"],
		"revertingTxHashes": [],
		"maxBlockNumber": 39177941,
	}]
}'<ETH_NODE_URL>
```

### Response Example

**normal**

```json
{"jsonrpc":"2.0","id":1,"result": "0x11111111..."}
```

**abnormal**

```json
{"jsonrpc":"2.0","id":1,"jsonerror":{"code":-38000,"message":"nonce too low: address 0x9Abae1b279A4Be25AEaE49a33e807cDd3cCFFa0C, tx: 0 state: 45"}}
```

### FAQ

<details>

<summary>What is the difference between submitting a Bundle to an RPC and submitting a Bundle to a Block Builder?</summary>

The core difference between the two lies in the different submission paths and final destinations of the Bundle.\
When submitting a Bundle to BlockRazor RPC, BlockRazor RPC forwards the Bundle to mainstream builders with low latency. This approach is more suitable as a unified access point, allowing users to submit Bundles without having to connect to different builders individually.\
When you submit a Bundle to Block Builder, the Bundle is sent directly to BlockRazor Builder. This is more suitable for scenarios where you explicitly want to use BlockRazor Builder capabilities and the access path.

</details>


# Ethereum RPC

This document introduces the integration methods and interface access documentation for BlockRazor Ethereum RPC.

### What Is Ethereum RPC

Ethereum RPC is an RPC service provided by BlockRazor for Ethereum. It supports commonly used standard JSON-RPC methods, allowing users to query on-chain data and submit transactions. When sending transactions, users can also benefit from transaction privacy, MEV protection, low-latency inclusion, backrun refunds, and Gas refunds.

### Why Choose Ethereum RPC

**Transaction Privacy**: Transactions are submitted through private paths, reducing the risk of exposing transaction intent through the public mempool and protecting users against malicious MEV attacks such as sandwich attacks and frontrunning.

**Backrun Refunds**: Transactions enter the orderflow under predefined data disclosure rules. Eligible Searchers use the permitted information to independently identify backrun opportunities that do not harm the user’s transaction outcome. If a Searcher’s backrun is successfully included on-chain and generates revenue, the user receives **90% of the distributable backrun revenue by default**.

**Gas Refunds**: In addition to backrun refunds, Ethereum transactions that meet the Ethereum Builder refund policy may receive a Gas refund, reducing the user’s effective transaction cost.

**Low-Latency Inclusion**: Powered by BEF path and topology optimization, BlockRazor Ethereum RPC sends transactions to more effective processing entry points. This reduces propagation latency and network jitter while providing more stable inclusion for latency-sensitive, high-frequency, and multi-region deployments.

**Zero-Barrier Integration**: BlockRazor Ethereum RPC can be added to a wallet with one click and uses standard JSON-RPC methods. The public RPC requires no authentication, allowing users and projects to quickly replace their existing Ethereum RPC. Projects that need custom transaction disclosure rules, refund recipient addresses, dedicated domains, or revert protection can apply for a dedicated RPC.

### Who Is Ethereum RPC For

**Wallets / DEXs**: Teams that want to improve Ethereum transaction protection and execution quality while providing backrun and Gas refunds to their users.

**Trading Bots / Quant Teams**: Teams focused on low latency, transaction privacy, inclusion stability, and execution quality across regions.

**Project Builders**: Projects that require a dedicated RPC, custom data disclosure rules, refund recipient addresses, and revert protection configurations.

**Individual Traders**: Users who want a safer transaction path on Ethereum and the opportunity to receive backrun and Gas refunds.

### FAQ

<details>

<summary>What is the difference between a backrun refund and a Gas refund?</summary>

A backrun refund comes from the distributable revenue generated when a Searcher executes a harmless backrun on the orderflow. A Gas refund is related to the transaction’s Priority Fee and is available to Ethereum transactions that meet the Ethereum Builder refund policy. The refund percentage for both types is 90%.

</details>

<details>

<summary>Does every transaction receive both types of refunds?</summary>

No. A backrun refund requires a Searcher to identify a valid opportunity, successfully submit a backrun, and generate revenue. A Gas refund requires the transaction to meet the current Gas refund policy. A transaction may receive one type of refund, both types, or no refund.

</details>

<details>

<summary>Do I need to modify my transaction code to use Ethereum RPC?</summary>

No. BlockRazor Ethereum RPC is compatible with standard JSON-RPC methods, including `eth_sendRawTransaction`. Individual users can simply replace the RPC in their wallet, while applications and trading systems usually only need to replace their RPC endpoint.

</details>

### Privacy Statement

BlockRazor does not collect users’ personal information, such as IP addresses or location data, for advertising or tracking purposes. Data is processed only when necessary to provide services and improve the user experience. BlockRazor may retain information that is already publicly available on-chain, such as transaction timestamps. For complete details, please refer to the latest BlockRazor Privacy Statement.


# Ethereum RPC Endpoint

### General Endpoints

<table data-search="false"><thead><tr><th width="127.3359375"></th><th width="186">default</th><th width="196">fullprivacy</th><th>maxbackrun</th></tr></thead><tbody><tr><td>URL</td><td>https://eth.blockrazor.xyz</td><td>https://eth.blockrazor.xyz/fullprivacy</td><td>https://eth.blockrazor.xyz/maxbackrun</td></tr><tr><td>MEV Protection</td><td>Enabled</td><td>Enabled</td><td>Enabled</td></tr><tr><td>Transaction Privacy</td><td>Minimal disclosure</td><td>Full privacy</td><td>Maximum disclosure</td></tr><tr><td>Refund Potential</td><td>Medium</td><td>No refund</td><td>High</td></tr><tr><td>Refund Percentage</td><td>Supported</td><td>No refund</td><td>Supported</td></tr><tr><td>Revert Protection</td><td>Disabled</td><td>Enabled</td><td>Enabled</td></tr></tbody></table>

<details>

<summary><strong>default Mode</strong></summary>

In `default` mode, transactions submitted through Ethereum RPC disclose only the necessary transaction data to Searchers (`hash`, `logs`, and state changes). This provides opportunities for refunds while protecting transaction privacy as much as possible.

To prioritize fast block inclusion, revert protection is not enabled in this mode. Users are advised to set an appropriate transaction Priority Fee to improve inclusion speed.

</details>

<details>

<summary><strong>fullprivacy Mode</strong></summary>

In `fullprivacy` mode, transactions submitted through Ethereum RPC do not disclose any transaction data. Ethereum RPC forwards the transactions directly to mainstream Builders.

Because no transaction data is disclosed, transactions in this mode do not generate refunds, and no refund percentage needs to be configured. Revert protection is enabled in this mode.

</details>

<details>

<summary><strong>maxbackrun Mode</strong></summary>

In `maxbackrun` mode, transactions submitted through Ethereum RPC disclose the transaction data required to maximize refund opportunities while maintaining privacy protection. The disclosed fields include `hash`, `to`, `calldata`, `functionSelector`, `logs`, and state changes.

Revert protection is enabled in this mode. Users are advised to set an appropriate transaction Priority Fee to improve inclusion speed.

</details>

### Dedicated Endpoints

A dedicated endpoint is a private transaction channel provided by BlockRazor for an individual user or project. Users can configure dedicated endpoints in the BlockRazor console, including customizing the endpoint URL for easier identification and configuring transaction disclosure parameters and refund recipient addresses.

After adding a dedicated endpoint to a wallet or integrating it into a project, users can view its transactions and refund records in the BlockRazor console.

<table><thead><tr><th width="148.8359375">Endpoint</th><th width="551.25390625">Example URL</th></tr></thead><tbody><tr><td>Dedicated Endpoint URL</td><td>https://eth.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Custom Endpoint URL</td><td>https://&#x3C;custom_content>.eth.blockrazor.xyz</td></tr></tbody></table>

### Regional Endpoints

<table><thead><tr><th width="148.99609375">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Tokyo</td><td>https://jp-ethscutum.blockrazor.xyz</td></tr><tr><td>New York</td><td>https://us-ethscutum.blockrazor.xyz</td></tr></tbody></table>

Regional endpoints are suitable for projects that are highly sensitive to transaction latency and whose transaction sources are concentrated in specific regions.


# How to Integrate Ethereum RPC into Project

Introduce the steps for integrating BlockRazor Ethereum RPC

{% hint style="info" %}
BlockRazor RPC is open to all users; no service purchase or authentication application is required.
{% endhint %}

### Endpoint

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://eth.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://eth.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.eth.blockrazor.xyz</td></tr></tbody></table>

### How to integrate RPC into your project

#### 1. Configure RPC

1. Register and log in to the portal at [blockrazor.io](https://blockrazor.io/).
2. Under the RPC module, click on the RPC page to view the configuration information of your RPC.
3. Click on **Update** to enter the configuration update page, and adjust the parameters according to your needs. The meaning of the parameters is shown in the following table.

<table><thead><tr><th width="181">Parameters</th><th>Meaning</th></tr></thead><tbody><tr><td>Default RPC URL</td><td>Each account automatically generates 1 Ethereum RPC and 1 BSC RPC by default. The Default RPC URL is automatically generated and cannot be modified.</td></tr><tr><td>Custom RPC URL</td><td>The domain is allowed to be modified, and the Custom RPC URL can be promoted to the project's end-users through websites or docs, guiding them to add custom RPC within their wallets.</td></tr><tr><td>Hint</td><td>The system defaults to sharing the transaction's hash and logs with the Searcher. The more fields shared, the greater the possibility of obtaining refunds, please proceed with caution after evaluating the need for disclosure of transaction data.</td></tr></tbody></table>

4. Click **Confirm**, the system will update the RPC configuration in real time.

#### 2. Integrate RPC

1. Find the configuration file or code: Open the project workspace and locate the file or code segment that configures the RPC node in the DApp project. This could be a configuration file such as .env, config.js, truffle-config.js, etc., or it could be hardcoded directly in the code.
2. Modify the RPC URL: Change the RPC URL in the configuration file or code to the Scutum RPC URL.
3. Test the connection: After making the change, run the DApp or the corresponding test script locally to ensure that the new RPC URL works properly. You can use methods like `web3.eth.net.isListening()` or `ethers.provider.pollingInterval` to check if the connection is successful.
4. Deploy the update: If the test passes, you can deploy the changes to the production environment.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// import Web3
const Web3 = require('web3');

// Create a Web3 instance and connect to the RPC.
const web3 = new Web3('https://ethereum-rpc.publicnode.com'); // You can replace the RPC URL with the Scutum RPC URL here.

// check the connection
web3.eth.net.isListening()
  .then((listening) => {
    console.log('Web3 connected: ', listening);
  })
  .catch((err) => {
    console.error('Web3 connection error: ', err);
  });
```

{% endtab %}
{% endtabs %}

#### 3. Query Transactions

1. Log in to [blockrazor.io](https://blockrazor.io).
2. Under the Scutum module, click on **Refunds** to view the refund, and click on **Transactions** to view the transactions submitted to dedicated RPC.


# Ethereum RPC Refund Mechanism

Learn how BlockRazor Ethereum RPC protects transactions and returns 90% of backrun revenue and priority fee to users

When a transaction is sent through BlockRazor Ethereum RPC, it can enter the private orderflow under predefined disclosure rules without being exposed to the public mempool. Searchers use the permitted information to independently identify backrun opportunities. If a backrun is successfully included on-chain and generates revenue, the user receives **90% of the distributable backrun revenue by default**.

In addition to backrun refunds, transactions that meet the predefined Gas refund rules may also receive a **Gas refund**. These are two independent sources of refunds. A transaction may receive either one, both, or neither.

### How Are Refunds Generated

```mermaid
graph LR
    A[User sends a transaction] --> B[Transaction enters the private path]
    B --> C[Data is disclosed under predefined rules]
    C --> D[Searcher submits a backrun]
    D --> E{Backrun succeeds and generates revenue}
    E -->|Yes| F[User receives 90% by default]
    E -->|No| G[No backrun refund]
    B --> H{Eligible for a Gas refund}
    H -->|Yes| I[Gas refund is sent to the user]
    H -->|No| J[No Gas refund]
```

**1. The User Sends a Transaction Normally**

Users can send transactions through `eth_sendRawTransaction` in the same way they would with a standard Ethereum RPC. BlockRazor Ethereum RPC is compatible with standard JSON-RPC methods, so users do not need to modify the transaction itself.

**2. Transaction Information Is Disclosed Under Predefined Rules**

The scope of transaction data disclosure is determined during integration. Configurable fields include the transaction hash, sender, recipient, `value`, `nonce`, and `calldata`. Only explicitly enabled fields are disclosed. Fields that are not enabled remain private.

**3. Searchers Independently Identify Backrun Opportunities**

Eligible Searchers can subscribe to the permitted transaction information and independently analyze whether they can construct a backrun strategy that does not harm the user’s transaction outcome.

**4. Backrun Revenue Is Refunded to the User**

Revenue is distributed only when the user’s transaction and the backrun are successfully included on-chain and the backrun generates actual revenue. By default, the user receives **90% of the distributable backrun revenue**.

**5. Eligible Transactions Receive a Gas Refund**

Gas refunds and backrun refunds are two separate refund mechanisms. Transactions that meet the predefined Gas refund rules may receive a refund for their qualifying Gas costs, reducing the user’s effective transaction cost.

A Gas refund does not depend on whether the transaction has a backrun opportunity. Even if a transaction generates no backrun revenue, it may still receive a Gas refund as long as it meets the Gas refund rules.

### What Does the User Need to Do

Individual users only need to switch the Ethereum RPC in their wallet to BlockRazor Ethereum RPC and send transactions as usual. Users do not need to manually manage Searcher participation or determine whether their transactions qualify for Gas refunds.

Wallets, DEXs, and other projects using a dedicated RPC can define transaction disclosure rules, refund recipient addresses, and other refund settings during integration. After configuration, BlockRazor processes subsequent transactions according to the predefined rules.

### Does Every Transaction Receive a Refund

No. The two refund types have different requirements.

**A Backrun Refund Requires**

* The transaction enters the private orderflow under the predefined disclosure rules
* A Searcher identifies an executable backrun opportunity using the disclosed information
* The Searcher submits a valid backrun
* The user’s transaction and the backrun are successfully included on-chain
* The backrun generates distributable revenue

**A Gas Refund Requires**

* The user’s transaction is included on-chain and incurs qualifying Gas expenditure

Refunds are therefore not fixed rewards and are not guaranteed for every transaction. Even when no refund is generated, the transaction continues through the BlockRazor Ethereum RPC submission path and receives the applicable transaction privacy and malicious MEV protection.

### When Will the Refund Arrive

After a backrun is successfully included on-chain and generates revenue, the backrun refund is distributed in real time according to the predefined rules.

Gas refunds are processed according to the transaction execution result and the current Gas refund policy.


# Ethereum RPC eth\_sendRawTransaction

Introduce how to integrate BlockRazor Ethereum RPC’s \`eth\_sendRawTransaction\` method.

&#x20;`eth_sendRawTransaction` of BlockRazor RPC is compatible with native JSON-RPC methods and requires no additional modifications.&#x20;

If you need to modify parameters such as hint, refund address and revert protection, you can configure the dedicated RPC in the [portal](https://www.blockrazor.io/#/login).

### Endpoint

{% hint style="info" %}
For project RPC, please refer to the [Integration](/transaction-submission/rpc/ethereum/integration)
{% endhint %}

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://eth.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://eth.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.eth.blockrazor.xyz</td></tr></tbody></table>

### Request parameters

<table><thead><tr><th width="124">Parameters</th><th width="127">Mandatory</th><th width="110">Format</th><th width="180">Example</th><th>Remark</th></tr></thead><tbody><tr><td>-</td><td>Mandatory</td><td>bytes</td><td>"0xd46e……445675"</td><td>raw tx</td></tr></tbody></table>

### Request Example

```json
curl -X POST -H "Content-Type: application/json" --data '{
    "id": 1,
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": [
        "0xd46e……445675"
    ]
}' https://bsc.blockrazor.xyz
```

### Response Example

**normal**

```json
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0xe670……527331"
}
```

**abnormal**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
	"code": -32000,
	"message": "rlp: element is larger than containing list"
  }
}
```

### Other JSON-RPC Methods

BlockRazor RPC supports standard JSON-RPC method, you can refer to <https://ethereum.org/zh/developers/docs/apis/json-rpc/#json-rpc-methods>


# Ethereum RPC eth\_sendBundle

Introduce how to integrate BlockRazor Ethereum RPC’s \`eth\_sendBundle\` method.

### Endpoint

{% hint style="info" %}
For project RPC, please refer to the [Integration](/transaction-submission/rpc/ethereum/integration)
{% endhint %}

<table><thead><tr><th width="203.8515625">Endpoint type</th><th width="483.47265625">URL</th></tr></thead><tbody><tr><td>General RPC</td><td>https://eth.blockrazor.xyz</td></tr><tr><td>Project Default RPC</td><td>https://eth.blockrazor.xyz/&#x3C;rpc_id></td></tr><tr><td>Project Custom RPC</td><td>https://&#x3C;custom_domain>.eth.blockrazor.xyz</td></tr></tbody></table>

### Request parameters

<table><thead><tr><th width="183">Parameters</th><th width="118">Mandatory</th><th width="98">Format</th><th width="132">Example</th><th>Remark</th></tr></thead><tbody><tr><td>txs</td><td>mandatory</td><td>[]bytes</td><td>[ "0xf84a……e54284" ]</td><td>raw txs, up to 50 transactions allowed to be set</td></tr><tr><td>revertingTxHashes</td><td>optional</td><td>[]hash</td><td>["0x1f23……0abb1e"]</td><td>Transactions that allow to be reverted, a subset of txs</td></tr><tr><td>blockNumber</td><td>optional</td><td>uint64</td><td>"0x3C04F81"</td><td>The maximum block number for the bundle to be valid, with the default set to the current block number + 100</td></tr></tbody></table>

### Request Example

```json
curl -X POST -H "Content-Type: application/json" --data '{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "eth_sendBundle",
  "params": [{
    "Txs": [
"0xf8668203988405f5e100825208942ee393c739036a7660ec11bf2101d537eb52f3ac80808193a06836d5f4052376dc3114794da5fedd7a5b8090ddae0ec45dfa66c234fcabb6efa07cf17dad992c33e8e3e4d82355cfe12d3be2560fc2b0873c36dce398088d8e4f"
    ]
    "revertingTxHashes":[],
    "blockNumber":"0x3C04F81"
  }]
}' https://eth.blockrazor.xyz
```

### Response Example

normal

```json
{"jsonrpc":"2.0","id":1,"result": "0x11111111..."}
```

abnormal

```json
{"jsonrpc":"2.0","id":1,"jsonerror":{"code":-38000,"message":"nonce too low: address 0x9Abae1b279A4Be25AEaE49a33e807cDd3cCFFa0C, tx: 0 state: 45"}}
```


# BSC Block Builder

Introducing BlockRazor Block Builder: Who is it suitable for? What capabilities does it provide? And how to choose endpoint?

### What is Block Builder?

Block Builder is a BSC block building service provided by BlockRazor. It maintains competitive block building and high block production success rate based on [BEF](/core-technology/blockchain-edge-fabric) and Flow Coordination Engine. Block Builder currently ranks first in block production rate across the entire BSC chain. Real-time block production rate can be viewed at <https://dune.com/bnbchain/bnb-smart-chain-mev-stats>.

Block Builder is designed for users who require high-quality BSC transaction execution. Its core value includes:

* Higher win rate block building capability
* Low-latency access capabilities for global deployment
* Supports multiple transaction submission modes to adapt to different execution needs.
* Provide more suitable submission paths for latency-sensitive, privacy-sensitive, and sequence-sensitive scenarios.

### Who is Block Builder suitable for?

* **Searcher**: Users who need to submit bundles, capture MEV opportunities, and pay attention to the order of transactions and the timing of execution.
* **Trading Bot / Quant Team**: Team with specific requirements for latency, execution stability, and cross-regional performance.
* **Wallets / DEXs**: Teams that need a better trade execution experience, privacy protection, or a better submission path.

### What capabilities does Block Builder provide?

Block Builder currently supports the following core capabilities:

* **Send Bundle**: Suitable for scenarios that require transaction order, atomicity, and execution within the same block.
* **Send PrivateTransaction**: Suitable for scenarios where you want to avoid exposing transactions to the public mempool and reduce the risk of being preempted or maliciously observed.
* **Trace Bundle**: Suitable for scenarios that track and analyze Bundle submission results and execution performance. Combined with Bundle Explorer, it can help users observe the performance of Bundle in the Builder chain in a more granular way, providing a reference for strategy optimization, problem investigation and effect review.

### Quick Start

{% stepper %}
{% step %}
Apply for auth

For details, see [Authentication](/get-started/authentication)
{% endstep %}

{% step %}
Select the appropriate capabilities and endpoints based on the scenario and requirements.

* [Send Bundle](/transaction-submission/block-builder/send-bundle)
* [Send PrivateTransaction](/transaction-submission/block-builder/send-privatetransaction)
* [Trace Bundle](/transaction-submission/block-builder/trace-bundle)
* [Call Bundle](/transaction-submission/block-builder/call-bundle)
  {% endstep %}

{% step %}
Verify latency and stability performance in the real deployment area.
{% endstep %}
{% endstepper %}

### FAQ

<details>

<summary>What is the difference between submitting a Bundle to an RPC and submitting a Bundle to a Block Builder?</summary>

The core difference between the two lies in the different submission paths and final destinations of the Bundle.

When submitting a Bundle to BlockRazor RPC, BlockRazor RPC forwards the Bundle to mainstream builders with low latency. This approach is more suitable as a unified access point, allowing users to submit Bundles without having to connect to different builders individually.

When you submit a Bundle to Block Builder, the Bundle is sent directly to BlockRazor Builder. This is more suitable for scenarios where you explicitly want to use BlockRazor Builder capabilities and the access path.

</details>

<details>

<summary><strong>How to understand that the average gas price of a bundle must not be less than 0.05 gwei?</strong></summary>

Assume a bundle contains three transactions: `{tx1, tx2, tx3}`. Since `tx1` comes from the mempool, BlockRazor Builder excludes it and calculates the average gas price using only `tx2` and `tx3`. If `tx3` includes an additional tip paid to the Builder, the tip amount is added to the numerator, while the denominator still includes only the gas used by `tx2` and `tx3`. The formula is:

`(tx2.gasPrice × tx2.gasUsed + tx3.gasPrice × tx3.gasUsed + tx3.tip) / (tx2.gasUsed + tx3.gasUsed)`

</details>


# BSC Block Builder Endpoint

### **Default Access**

Prioritize using a global, universal entry point, suitable for quickly completing integration and serving global requests. Endpoint: <mark style="color:$primary;">**<https://rpc.blockrazor.builders>**</mark>

### **Regional optimization**

If your bot or service is already deployed in a specific region and is more sensitive to latency and consistency, you can further connect to a regional entry point on top of connecting to a globally universal endpoint.

<table><thead><tr><th width="127">Region</th><th width="190.97265625">Available area（AWS）</th><th>RPC Endpoint</th></tr></thead><tbody><tr><td>Tokyo</td><td>apne1-az4</td><td>https://tokyo.builder.blockrazor.io</td></tr><tr><td>Frankfurt</td><td>euc1-az2</td><td>https://frankfurt.builder.blockrazor.io</td></tr><tr><td>Virginia</td><td>use1-az4</td><td>https://virginia.builder.blockrazor.io</td></tr><tr><td>Dublin</td><td>euw1-az1</td><td>https://dublin.builder.blockrazor.io</td></tr></tbody></table>

### **Quality Enhancement**

If you have already completed the basic integration and are starting to focus on the additional overhead in the commit path, cross-region fluctuations, and stability under high load, you can further integrate [Fast Submit](/transaction-submission/block-builder/fast-submit).


# BSC Block Builder Fast Submit

Introduction to Fast Submit of BlockRazor BSC Block Builder and access methods

### What is Fast Submit?

Fast Submit is a dedicated transaction submission channel provided by BlockRazor for BSC Block Builder. It optimizes the network path and request processing path of transactions from the client to the Builder, improving submission speed and stability.

In the BSC Builder scenario, transaction quality depends on more than just transaction content, gas, slippage, and timing of delivery. For latency-sensitive users, those deploying across regions, or those prioritizing stability, the path a transaction takes to reach the Builder is itself a part of the commit quality.

Fast Submit does not change the transaction logic, nor does it require users to rewrite the existing submission process. It optimizes the submission link between the client and the Builder, allowing transactions to reach the Builder faster and making the submission link more stable under high load and cross-region scenarios, reducing the additional overhead caused by intermediate forwarding, proxy processing, and public network fluctuations.

### Benchmark

According to benchmark results, Fast Submit can reduce latency by about 50ms on intercontinental routes and about 20ms on inland cross-region routes.

**1. For orderflow projects**\
For orderflow projects such as DEXs and wallets, transaction send timing is not fully controllable. In this case, a shorter submission path means the transaction has a higher probability of entering the Builder’s processing window earlier, helping deliver an optimal 0-block inclusion experience for end users.\
Using BSC’s current block time of about 450ms as a rough reference, 50ms and 20ms of latency reduction correspond to roughly 11% and 4.4% improvements in time-window efficiency. If block time further shortens to around 250ms in the future, the improvement could expand to about 20% and 8%.

**2. For trading systems competing for transaction position**\
For strategy-driven systems such as backrunning, sniping, and copy trading, the value of Fast Submit is not only a better chance of making the target block, but also that under the same competitive conditions and the same target Builder arrival time, it leaves more time available for strategy computation.

### Price

<table><thead><tr><th width="134.90625">Payment</th><th width="370.83984375">Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td><strong>$100</strong> / day<br><strong>$1000</strong> / month</td><td><a href="https://blockrazor.io/#/portal/pricing?purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_fast_submit&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td><strong>$1250 / month</strong><br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Why is Fast Submit faster and more stable?

**Dedicated HTTP domain**

Transaction requests in the regular submission channel pass through the CloudFront proxy layer before reaching the Block Builder, incurring more intermediate forwarding, network hops, and additional TLS and proxy processing overhead. In scenarios with network congestion or high concurrency, the queuing, forwarding, and processing time caused by these additional steps is often amplified, making request latency and fluctuations more pronounced.

In contrast, Fast Submit supports submitting requests via a dedicated HTTP domain, removes the CloudFront proxy layer, compresses the submission path between the client and the Block Builder, and reduces the additional overhead caused by intermediate layers.

**Intercontinental express lines**

In cross-regional scenarios, regular submission channels rely on public network paths, making them susceptible to routing fluctuations, link congestion, and network jitter. Especially during intercontinental transmissions, these unstable factors amplify request latency and volatility, reducing the consistency of the submission link.

Fast Submit submits transactions to Block Builder via intercontinental dedicated network lines, minimizing the impact of public network instability on the request link and providing a more stable and lower latency submission experience.

### What users is Fast Submit suitable for?

Fast Submit is more suitable for users who already have a mature submission process and have specific requirements for latency, stability, and cross-region performance.

* **Searcher**: MEV participants who focus on transaction submission timing, path quality, and cross-regional stability.
* **Trading Bot / Quant Team**: Team with specific requirements for execution speed, high-frequency submissions, and regional consistency.
* **Wallets / DEXs**: Team aiming to optimize the user transaction submission experience without altering the existing sending logic.

### Access method

Developers simply need to switch their existing HTTP submission requests to the dedicated Fast Submit domain. The integration process is as follows:

1. Go to the Pricing page to purchase the package
2. Contact us to obtain your dedicated Fast Submit domain.
3. Keep the original request logic unchanged, and send the existing transaction submission request to this dedicated HTTP domain.
4. The submission latency and stability performance were evaluated by testing in actual deployment areas and compared with the original submission channel.

### Precautions

Fast Submit aims to optimize the speed and stability of transactions reaching the BSC Block Builder, but it does not guarantee that a transaction will be packaged. Fast Submit is an infrastructure optimization capability for the submission chain, not a promise of a inclusion result.

Whether a transaction is ultimately included in a block still depends on a variety of factors beyond the submission chain, including but not limited to: market competition, Builder processing strategy, current block space, transaction quality, and real-time on-chain congestion.


# BSC Block Builder Send Bundle

Introduction to Send Bundle of BlockRazor BSC Block Builder and integration methods

### Introduction

This API is used to receive bundles, with the method name `eth_sendBundle`.

The block construction algorithm of BlockRazor Builder favors bundles that transfer more native tokens (BNB) to BlockRazor Builder EOA of which address is 0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20 currently.

The gas price for transactions within the bundle must be at least the minimum standard required by BSC Validators (Currently at 0.05 gwei).

### Request Parameter

<table data-search="false"><thead><tr><th width="144">Parameters</th><th width="118">Mandatory</th><th width="116">Format</th><th width="136">Example</th><th>Description</th></tr></thead><tbody><tr><td>txs</td><td>Mandatory</td><td>array[hex]</td><td>["0x…4b", "0x…5c"]</td><td>List of signed raw transactions</td></tr><tr><td>maxBlockNumber</td><td>Optional</td><td>uint64</td><td>39177941</td><td>The maximum block number for the bundle to be valid, with the default set to the current block number + 100</td></tr><tr><td>minTimestamp</td><td>Optional</td><td>uint64</td><td>1710229370</td><td>Expected minimum Unix timestamp (in seconds) for the bundle to be valid</td></tr><tr><td>maxTimestamp</td><td>Optional</td><td>uint64</td><td>1710829390</td><td>Expected maximum Unix timestamp (in seconds) for the bundle to be valid</td></tr><tr><td>revertingTxHashes</td><td>Optional</td><td>array[hash]</td><td>["0x…c7", "0x…b7"]</td><td>List of transaction hashes allowed for revert</td></tr><tr><td>noMerge</td><td>Optional</td><td>bool</td><td>false</td><td>Bundle merge can increase block value and inclusion rate. If not set, the default value is false (allowing bundle merging)</td></tr><tr><td>positionFirst</td><td>Optional</td><td>bool</td><td>false</td><td>Strictly order bundles by priorityFee. If set to false (default), late-arriving bundles may be appended to the block tail to guarantee inclusion in the current block.</td></tr></tbody></table>

### Request Example

{% tabs %}
{% tab title="HTTPS" %}

```bash
curl https://virginia.builder.blockrazor.io \
  -H 'content-type: application/json' \
  -H 'Authorization: <auth-token>' \
  --data '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "eth_sendBundle",
    "params": [{
      "txs": [
        "0x...4b",
        "0x...5c"
      ],
      "maxBlockNumber": 39177941,
      "minTimestamp": 1710229370,
      "maxTimestamp": 1710829390,
      "revertingTxHashes": [
        "0x44b89abe860142d3c3bda789cf955b69ba00b71882cd968ec407a70f4719ff06",
        "0x7d7652c685e9fda4fe2e41bad017519cffeed8ba03d59aa6401284be2ec4244c"
      ],
      "noMerge": false
    }]
  }'
```

{% endtab %}
{% endtabs %}

### Response Example

```json
{
 "jsonrpc":"2.0",
 "id":"1",
 "result":"0xa06b……f7e8ec"  //bundle hash
}‍
```

```json
{
  "jsonrpc":"2.0",
  "id":"1",
  "error":{
    "code":-38000,
    "message":"the maxBlockNumber should not be smaller than currentBlockNum"
    }
}
```


# BSC Block Builder Send PrivateTransaction

Introduction to Send PrivateTransaction of BlockRazor BSC Block Builder and integration methods

### Introduction

This API is used to receive private transactions submitted by users, with the method name `eth_sendPrivateTransaction`

### Request Parameters

<table><thead><tr><th width="169">Parameters</th><th width="117">Mandatory</th><th width="90">Format</th><th width="114">Example</th><th>Description</th></tr></thead><tbody><tr><td>transaction</td><td>Mandatory</td><td>String</td><td>"0x…4b"</td><td>signed raw transaction </td></tr></tbody></table>

### Request Example

{% tabs %}
{% tab title="HTTPS" %}

```javascript
curl https://virginia.builder.blockrazor.io \
  -H 'content-type: application/json' \
  -H 'Authorization: <auth-token>' \
  --data '{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "eth_sendPrivateTransaction",
  "params": ["0x…9c"]
}'
```

{% endtab %}
{% endtabs %}

#### Proto

```go
syntax = "proto3";

package sendbundle;

option go_package = "internal/ethapi/sendbundle;sendbundle";

service BundleService {
  rpc SendBundle (SendBundleArgs) returns (SendBundleResponse);
  rpc SendTransaction (SendTransactionArgs) returns (SendTransactionResponse);
}

message SendBundleArgs {
  repeated bytes txs = 1;
  uint64 maxBlockNumber = 2;
  uint64 minTimestamp = 3;
  uint64 maxTimestamp = 4;
  repeated string revertingTxHashes = 5;
}

message SendTransactionArgs {
  bytes tx = 1;
}

message SendBundleResponse {
    string result = 1;
}

message SendTransactionResponse {
    string result = 1;
}
```

### Reponse Example

```json
{
 "jsonrpc":"2.0",
 "id":"1",
 "result":"0xa06b……f7e8ec"  // tx hash
}‍‍
```

```json
{
  "jsonrpc":"2.0",
  "id":"1",
  "error":{
    "code":-32000,
    "message":"nonce too low: next nonce 57, tx nonce 56"
    }
}
```


# BSC Block Builder Trace Bundle

Introduction to Trace Bundle of BlockRazor Block Builder and integration methods

### Introduction

This method supports querying the current status of the bundle by bundle hash. Please query 5 minutes after sending the bundle to the builder. Endpoint: <https://bsc-bundle-stats.blockrazor.io/>

### Price

{% hint style="info" %}
Users who purchase the Trace Bundle service can also access the [Bundle Explorer](/transaction-submission/block-builder/bundle-explorer) in the portal.
{% endhint %}

<table><thead><tr><th width="131.859375">User Type</th><th width="179.0234375">Limit</th><th>Price</th><th>Action</th></tr></thead><tbody><tr><td>Paid User</td><td>1000 txs / day</td><td>$150 / day<br>$1500 / month</td><td><a href="https://blockrazor.io/#/login?redirect=pricing&#x26;purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_bundle_tracing&#x26;billing=day" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Request Parameter

<table><thead><tr><th width="156">Parameters</th><th width="124">Mandatory</th><th width="122">Format</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td>hash</td><td>Mandatory</td><td>hash</td><td>0x25f9……317097</td><td>bundle hash</td></tr></tbody></table>

### Request Example

{% tabs %}
{% tab title="HTTPS" %}

```json
curl -X GET "https://bsc-bundle-stats.blockrazor.io/bundlestate?hash=0x25f9fc35e978709195c00e864b9a19fb41ad5c5c5b8a3e003813ae9727317097" \
     -H "Content-Type: application/json" \
     -H "Authorization: M2ZiZj……JhODA1"‍
```

{% endtab %}
{% endtabs %}

### Response Example

**Success**

```json
{
  "bundle": {
    "timestamp": "2025-04-07T09:04:40Z", // The time when the builder receives the bundle (UTC)
    "bundleHash": "0x25f9fc35e978709195c00e864b9a19fb41ad5c5c5b8a3e003813ae9727317097", // bundle hash
    "state": "onchain", // bundle is included on chain
    "blockNumber": 48144954, // The block number where the bundle is located
    "priority": "358911000000000" // bundle value, in wei
  }
}
```

**Error**

```json
{
  "bundle": {
    "timestamp": "2025-04-08T05:21:10Z", // The time when the builder receives the bundle (UTC)
    "bundleHash": "0xe06a923bce1f46b2a0602b8fb2263dffde22f21277b0b71d84b79aff6a58772b", // bundle hash
    "state": "failed", // builder has received bundle，but the bundle is not included
    "err": "non-reverting tx in bundle failed" // reason why the bundle is not included
  }
}
```

```json
{
  "bundle": {
    "bundleHash": "0xabc", // bundle hash in the request
    "state": "not found" // bundle is not found
  }
}
```


# BSC Block Builder Bundle Explorer

{% hint style="info" %}
Bundle Explorer is packaged with the Bundle Tracing service. Users who purchase Bundle Tracing can access Bundle Explorer after signing in to the BlockRazor Portal.
{% endhint %}

## BSC Bundle Explorer

### Product Definition

BSC Bundle Explorer is a block and bundle data exploration tool built by BlockRazor for the BSC Block Builder ecosystem.

It presents block rewards and bundle rewards from three perspectives: blocks, dates, and validators. Users can also inspect the bundles and transaction composition within a block. This helps searchers, builders, validators, and researchers understand on-chain bundle execution and reward performance.

### Core Capabilities

#### Block Reward

View BSC blocks, validators, block rewards, bundle counts, and bundle rewards. Users can search by block number and expand an individual block to inspect its bundles and transaction details, including addresses, gas used, gas price, and transaction fees.

<div><figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FmCrnFbaEEiI2uXqH0U4D%2FSnipaste_2026-08-19_15-16-28.png?alt=media&amp;token=6d44394c-a5e0-45b9-ae89-bc86e2c08b91" alt=""><figcaption></figcaption></figure> <figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2FHLV7v2qUTaelAtF6wcnG%2FSnipaste_2026-08-19_15-16-57.png?alt=media&amp;token=8164bbd7-bd79-46ba-9775-784e37925ce7" alt=""><figcaption></figcaption></figure></div>

#### Daily Reward

Review daily totals for blocks, bundles, block rewards, bundle rewards, and the highest bundle reward. This provides a clear view of BSC bundle activity and reward trends over time.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2F4kyUXexY92vy3NUTqDWA%2FSnipaste_2026-08-19_15-17-20.png?alt=media&amp;token=89d0aca3-424c-4e4d-87eb-88bb6fb1afc0" alt="" width="375"><figcaption></figcaption></figure>

#### Validator Reward

View block rewards and bundle rewards for different validators on a selected date. This makes it easier to compare validator reward performance and composition.

<figure><img src="https://2581477772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjbyfG8gOgcdsK3wVxNdQ%2Fuploads%2Fq2teTmMjrJFwzZyQzzJV%2FSnipaste_2026-08-19_15-17-32.png?alt=media&amp;token=a4e3ae0a-e82b-43ed-ac28-a6bb6331b379" alt="" width="375"><figcaption></figcaption></figure>

### Use Cases

* **Searcher strategy review**: Confirm whether a bundle was included in a block and inspect its transaction composition, including 0 Gwei transactions.
* **Builder and validator analysis**: Compare block rewards and bundle rewards to understand validator performance.
* **On-chain research**: Track daily bundle volume and reward changes to observe activity across the BSC bundle market.
* **Issue investigation**: Start from a block number and drill down into bundles and transactions to verify on-chain execution results.

### Product Value

BSC Bundle Explorer brings block, bundle, transaction, and validator data into one interface, reducing the cost of manual on-chain research and data collection. Users can review strategies, analyze rewards, and investigate execution issues more efficiently while gaining a unified view of bundle activity across the BSC Block Builder ecosystem.

### Data Notes

* All reward values are displayed in BNB.
* Block Reward displays blocks produced at least five minutes ago.
* Daily Reward data is updated every 24 hours.


# BSC Block Builder 0 Gwei

This section introduces BlockRazor Block Builder's eth\_sendBackBundle (0 Gwei) and its integration method.

### Introduction

This interface is used to receive bundle which is put at the block tail, allowing all transactions within the bundle to have a gas price of 0 gwei. Currently, this interface only supports the Virginia endpoint, it is recommended to deploy your service nearby.

### Price

<table><thead><tr><th width="134.90625">Payment</th><th width="370.83984375">Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td><strong>$100</strong> / day</td><td><a href="https://blockrazor.io/#/portal/pricing?purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_0_gwei&#x26;billing=day" class="button primary small">Subscribe</a></td></tr><tr><td>Package</td><td><strong>$1250</strong> / month<br>packaged with 9 other services. </td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">Subscribe</a></td></tr></tbody></table>

### Endpoint

<https://virginia.builder.blockrazor.io>

### Request Parameter

<table><thead><tr><th width="136.6875">Parameters</th><th width="118.74609375">Mandatory</th><th width="136">Format</th><th width="124">Example</th><th>Description</th></tr></thead><tbody><tr><td>txs</td><td>true</td><td>array[hex]</td><td>["0x…4b"]</td><td>signed raw transaction</td></tr><tr><td>blockNumber</td><td>true</td><td>uint64</td><td>106211355</td><td>The target block in which the bundle is expected to be included</td></tr></tbody></table>

### Request Example

```json
curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: <YOUR_TOKEN>" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_sendBackBundle",
    "params": [
      {
        "txs": ["0x...4b"],
        "blockNumber": 106211355
      }
    ]
  }' \
  https://virginia.builder.blockrazor.io
```

### Response Example

Normal

```json
{
 "jsonrpc":"2.0",
 "id":"1",
 "result":null
}‍
```

Abnormal

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "bundle missing blockNumber"
  }
}
```


# BSC Block Builder Call Bundle

Introduction to Call Bundle of BlockRazor BSC Block Builder and integration methods

### Introduction

This API is used to receive requests for simulating bundles. The method name is `eth_callBundle`.

### Price

<table><thead><tr><th width="134.90625">Payment</th><th width="370.83984375">Price</th><th>Action</th></tr></thead><tbody><tr><td>Personalized</td><td><strong>$20</strong> / day</td><td><a href="https://blockrazor.io/#/portal/pricing?purchaseMode=personalized&#x26;chain=bsc&#x26;serviceId=bsc_call_bundle&#x26;billing=day" class="button primary small">訂閱</a></td></tr><tr><td>Package</td><td><strong>$1250</strong> / month<br>packaged with 9 other services.</td><td><a href="https://blockrazor.io/#/portal/pricing?redirect=pricing&#x26;purchaseMode=package&#x26;billing=month" class="button primary small">訂閱</a></td></tr></tbody></table>

### Rate Limit

1 TPS

### Request Parameter

<table><thead><tr><th width="156">Parameters</th><th width="124">Mandatory</th><th width="122">Format</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td>txs</td><td>Mandatory</td><td>array[hex]</td><td>["0x…4b", "0x…5c"]</td><td>List of signed raw transactions</td></tr><tr><td>blockNumber</td><td>Mandatory</td><td>number</td><td>39177941</td><td>Current block number + 1</td></tr></tbody></table>

### Request Example

{% tabs %}
{% tab title="HTTPS" %}

```bash
curl https://virginia.builder.blockrazor.io \
  -H 'content-type: application/json' \
  -H 'Authorization: <auth-token>' \
  --data '{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "eth_callBundle",
  "params": [
    {
      "txs":["0x…4b"],
      "blockNumber":39177941
    }
  ]
  }'
```

{% endtab %}
{% endtabs %}

### Response Example

**Success**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "bundleHash": "0xb13bb92ebee57b42b7e8e91b41891e1693a449dcdb8530c6bffe684157e988da",
    "ethSentToBuilder": "63003000000000",
    "gasFees": "21000000000000",
    "results": [
      {
        "ethSentToBuilder": "63003000000000",
        "fromAddress": "0x12994B3004Daab21035EDa1D4b31F7F63D606128",
        "gasFees": "21000000000000",
        "gasPrice": "4000142857",
        "gasUsed": 21000,
        "toAddress": "0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20",
        "txHash": "0x0fbaa913aa0ffc22bba63a81ca2958ad14d630928c5a5240130fec7037605648",
        "value": "0x"
      }
    ],
    "stateBlockNumber": 756983,
    "totalGasUsed": 21000
  }
}
```

**Error**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "missing auth token"
  }
}
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "rate limit exceeded, try again later"
  }
}
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "err: nonce too low: address 0x6c85F133fa06Fe5eb185743FB6c79f4a7cb9C076, tx: 28 state: 29; txhash 0x300c95d2b3086ddc1836de1e8c87878916d332ff42e8312cd404264ab8cdcd18"
  }
}
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "bundleHash": "0x592da70a510720d149567bfbc5935a05780963607c3c75b93a9190bb60818f21",
    "ethSentToBuilder": "0",
    "gasFees": "218070000000000",
    "results": [
      {
        "error": "execution reverted",
        "ethSentToBuilder": "0",
        "fromAddress": "0xb0b10B09780aa6A315158EF724404aa1497e9E6E",
        "gasFees": "218070000000000",
        "gasPrice": "10000000000",
        "gasUsed": 21807,
        "revert": "unlock error: locked",
        "toAddress": "0xdA51Cf6ed22740FD8fAfbBe61577A577915e7526",
        "txHash": "0xa09745617e0a212c5da379ea066f64e33f5215ce235d653bbbc1359b38d78baa",
      }
    ],
    "stateBlockNumber": 29115,
    "totalGasUsed": 21807
  }
}
```


# Transaction Sending

This document introduces BlockRazor's Transaction Sending mode, along with the provided services and API integration documentation.

### What is Transaction Sending mode

Transaction Sending is a fast transaction sending mode provided by BlockRazor, aimed at users who have higher requirements for the speed of transaction inclusion, and is applicable to Solana, BSC, Robinhood Chain, Ethereum, and Base.

Compared to RPC, Transaction Sending focuses more on how to get transactions into the on-chain execution process more quickly after they are sent from the client. It leverages [BEF](/core-technology/blockchain-edge-fabric) to fully utilize the underlying mechanisms of different chains, providing a lower latency sending experience for the trading system.

### What users are suitable for Transaction Sending mode

* **Wallets / DEXs**: Team aiming to provide a faster sending experience for users worldwide.
* **Trading Bot / Quant Team:** Team with specific requirements for transaction inclusion speed and execution timing

### FAQ

<details>

<summary>What is the difference between Fast and RPC?</summary>

Both Fast and RPC are transaction sending capabilities, but they have different design goals.

RPC focuses more on transaction protection and general access capabilities. It provides standard JSON-RPC methods, focusing on addressing the MEV risks that transactions may encounter during public dissemination, and supports refund and disclosure policy configuration and customized RPC access, making it suitable for Wallets, DEXs, and project teams as a standard transaction sending entry point.

Fast prioritizes the speed of transaction on-chain processing. It optimizes the sending path through [BEF](/core-technology/blockchain-edge-fabric), helping transactions enter the on-chain execution process with lower latency. It is suitable for trading bots, quantitative strategies, and timing-sensitive trading scenarios that have higher requirements for on-chain timeliness.

</details>


# Solana Transaction Sending

This document introduces the endpoints, API access documentation, Priority Fee & Tips, and Keep Alive for BlockRazor Solana Transaction Sending mode.


# Solana Transaction Sending Endpoint

Introducing the endpoints of BlockRazor Solana Transaction Sending mode

{% tabs %}
{% tab title="HTTP" %}

<table data-search="false"><thead><tr><th width="141.54296875">Region</th><th>URL</th></tr></thead><tbody><tr><td>Frankfurt</td><td>http://frankfurt.solana.blockrazor.xyz:443</td></tr><tr><td>Frankfurt</td><td>http://frankfurt-allnodes.solana.blockrazor.xyz:443</td></tr><tr><td>Frankfurt</td><td>http://frankfurt-cherryservers.solana.blockrazor.xyz:443</td></tr><tr><td>New York</td><td>http://newyork.solana.blockrazor.xyz:443</td></tr><tr><td>Tokyo</td><td>http://tokyo.solana.blockrazor.xyz:443</td></tr><tr><td>Amsterdam</td><td>http://amsterdam.solana.blockrazor.xyz:443</td></tr><tr><td>Amsterdam</td><td>http://amsterdam-cherryservers.solana.blockrazor.xyz:443</td></tr><tr><td>London</td><td>http://london.solana.blockrazor.xyz:443</td></tr><tr><td>Toronto</td><td>http://toronto.solana.blockrazor.xyz:443</td></tr><tr><td>Singapore</td><td>http://singapore.solana.blockrazor.xyz:443</td></tr><tr><td>Los Angeles</td><td>http://losangeles.solana.blockrazor.xyz:443</td></tr></tbody></table>
{% endtab %}

{% tab title="HTTPS" %}

<table><thead><tr><th width="129.390625">Region</th><th>Endpoint</th></tr></thead><tbody><tr><td>Frankfurt</td><td>https://frankfurt.solana.blockrazor.io</td></tr><tr><td>New York</td><td>https://newyork.solana.blockrazor.io</td></tr><tr><td>Tokyo</td><td>https://tokyo.solana.blockrazor.io</td></tr></tbody></table>
{% endtab %}

{% tab title="gRPC" %}

<table data-search="false"><thead><tr><th width="134.9765625">Region</th><th>URL</th></tr></thead><tbody><tr><td>Frankfurt</td><td>frankfurt.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Frankfurt</td><td>frankfurt-allnodes.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Frankfurt</td><td>frankfurt-cherryservers.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>New York</td><td>newyork.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Tokyo</td><td>tokyo.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Amsterdam</td><td>amsterdam.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Amsterdam</td><td>amsterdam-cherryservers.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>London</td><td>london.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Toronto</td><td>toronto.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Singapore</td><td>singapore.solana-grpc.blockrazor.xyz:80</td></tr><tr><td>Los Angeles</td><td>losangeles.solana-grpc.blockrazor.xyz:80</td></tr></tbody></table>
{% endtab %}
{% endtabs %}


# Solana Send Transaction

Introduction to Send Transaction of BlockRazor Solana Transaction Sending mode

{% hint style="warning" %}
Solana's transaction sending service is not bound to the subscription plan, with rate limit default to 3 TPS. API key could be required from [Authentication](/get-started/authentication). If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

`Send Transaction` is a fast transaction sending interface provided by BlockRazor for Solana, used to send signed transactions to the blockchain with lower latency. This service offers access via HTTP and gRPC, making it suitable for users with high requirements for transaction inclusion speed.

Send Transaction, while maintaining compatibility with existing sending methods, leverages [BEF](/core-technology/blockchain-edge-fabric) to shorten the transaction path from the client to the Leader node, thereby improving transaction inclusion speed. For Send Transaction's inclusion routing, testing instructions, and Benchamark results, please refer to [BenchMarking Solana Send Transaction Service](https://blockrazor.io/blog/20250801Benchmarking/).

Currently, Send Transaction supports two modes: fast and sandwichMitigation.

* Fast mode is suitable for scenarios with higher requirements for sending speed.
* The sandwichMitigation model is suitable for scenarios where a balance needs to be struck between speed and specific risk control.

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST /sendTransaction`
* `gRPC`

### Request Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

* [Curl](/transaction-submission/transaction-sending/solana/send-transaction/request-example/curl)
* [Go](/transaction-submission/transaction-sending/solana/send-transaction/request-example/go)
* [Rust](/transaction-submission/transaction-sending/solana/send-transaction/request-example/rust)
* [JS](/transaction-submission/transaction-sending/solana/send-transaction/request-example/js)

### Request Parameter

<table><thead><tr><th width="111.44921875">Parameters</th><th width="115.421875">Mandatory</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transaction</td><td>Mandatory</td><td>"4hXTCk……tAnaAT"</td><td>Fully signed transactions, compatible with encoding protocal Base64 and Base58, with Base64 being recommended</td></tr><tr><td>mode</td><td>Optional</td><td>"fast"<br>"sandwichMitigation"</td><td>BlockRazor offers two modes: Fast and SandwichMitigation, with Fast as the default.<br><br>In fast mode, transactions are sent based on globally distributed high-performance network and high-quality SWQoS, reaching the Leader node with the lowest latency.<br><br>In sandwichMitigation mode, BlockRazoz will route transactions to the trusted SWQoS and skip the slot of the blacklisted Leader (dynamically identified by the BlockRazor sandwich monitoring mechanism). In this mode, <strong>DO NOT</strong> send transactions using durable nonce, as it will cause the sandwich protection to become ineffective.</td></tr><tr><td>safeWindow</td><td>Optional</td><td>3</td><td>safeWindow is used to determine the timing of transaction sending in sandwichMitigation mode and represents the number of consecutive slots of  whitelist validators. For example, if it is set to 3, the transaction will only be sent when 3 consecutive slots from the current slot belong to whitelist validators.<br><br>The range of safeWindow is 3-13. The larger the number, the better the effect of mitigating the sandwich attack, but it may have a certain impact on the rate of inclusion. If not set, the default is 3.</td></tr><tr><td>revertProtection</td><td>Optional</td><td>false</td><td>The default value is false. If set to true, the transaction will not fail on chain, but the speed of inclusion will be affected and there is a possibility that it cannot be included. Please choose to enable it carefully according to actual needs.</td></tr></tbody></table>


# Solana Send Transaction Request Example

This section presents an example of a Send Transaction request in BlockRazor Solana Transaction Sending mode.


# Solana Send Transaction Curl Example

This page describes how to send Solana transactions using curl

### Request Example

```json
// below is the example of fast mode

curl --request POST \
  --url http://frankfurt.solana.blockrazor.xyz:443/sendTransaction \
  --header 'Content-Type: application/json' \
  --header 'apikey: $auth_token' \
  --data '{
  "transaction":"$base64_tx",
  "mode":"fast",
  "revertProtection":false
}'
```


# Solana Send Transaction Go Example

This page describes how to build and send Solana transactions using Go

{% hint style="info" %}
Solana's transaction sending service is no longer bound to the subscription plan, with rate limit default to 3 TPS. API key could be required from [Authentication](/get-started/authentication). If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

[Code example](https://github.com/BlockRazorinc/solana-trader-client-go/tree/main/example)

### Request Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

{% tabs %}
{% tab title="HTTP" %}

```go
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)

const (
	httpEndpoint     = "http://frankfurt.solana.blockrazor.xyz:443/sendTransaction"
	healthEndpoint   = "http://frankfurt.solana.blockrazor.xyz:443/health"
	mainNetRPC       = ""
	authKey          = ""
	privateKey       = ""
	publicKey        = ""
	amount           = 200_000
	tipAmount        = 100_000
	mode             = "fast"
	safeWindow       = 5
	revertProtection = false
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}

type SendRequest struct {
	Transaction      string `json:"transaction"`
	Mode             string `json:"mode"`
	SafeWindow       int    `json:"safeWindow"`
	RevertProtection bool   `json:"revertProtection"`
}

type SendResponse struct {
	Signature string `json:"signature"`
}
type HealthResponse struct {
	Result string `json:"result"`
}

func main() {
	// Pre-warm: perform an initial health check to establish the HTTP connection
	err := pingHealth()
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the HTTP connection alive (warm)
	go func() {
		for {
			err := pingHealth()
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()
	// send transactions
	if err := sendTx(); err != nil {
		fmt.Printf("send tx failed: %v\n", err)
	}
}

func pingHealth() error {
	req, err := http.NewRequest("GET", healthEndpoint, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// ⚠️ Important Note:
	// According to the Go net/http documentation, in order for the underlying TCP connection
	// to be reused (i.e. kept alive), the response body must be fully read and closed.
	// Otherwise, the Transport may not reuse the connection for future requests.
	// Reference: https://pkg.go.dev/net/http#Response
	// > "The default HTTP client's Transport may not reuse HTTP/1.x 'keep-alive' TCP connections
	//    if the Body is not read to completion and closed."

	// Read the full response body to enable connection reuse
	bodyBytes, _ := io.ReadAll(resp.Body)
	var healthRes HealthResponse
	if err := json.Unmarshal(bodyBytes, &healthRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}

	return nil
}

func sendTx() error {
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	if err != nil {
		return err
	}
	receivePub := solana.MustPublicKeyFromBase58(publicKey)
	tipPub := solana.MustPublicKeyFromBase58(tipAccounts[rand.Intn(len(tipAccounts))])

	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("[get blockhash] %v", err)
	}

	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), tipPub).Build()

	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("build tx error: %v", err)
	}

	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if account.PublicKey().Equals(key) {
			return &account.PrivateKey
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("sign tx error: %v", err)
	}

	txBase64, err := tx.ToBase64()
	if err != nil {
		return err
	}

	reqBody := SendRequest{
		Transaction:      txBase64,
		Mode:             mode,
		SafeWindow:       safeWindow,
		RevertProtection: revertProtection,
	}
	jsonBody, _ := json.Marshal(reqBody)

	httpReq, err := http.NewRequest("POST", httpEndpoint, bytes.NewBuffer(jsonBody))
	if err != nil {
		return err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(httpReq)
	if err != nil {
		return fmt.Errorf("send http error: %v", err)
	}
	defer resp.Body.Close()

	bodyBytes, _ := io.ReadAll(resp.Body)
	var sendRes SendResponse
	if err := json.Unmarshal(bodyBytes, &sendRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}
	fmt.Printf("[send tx] response: %+v\n", sendRes)
	return nil
}
```

{% endtab %}

{% tab title="gRPC" %}

```go
package main

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	pb "github.com/BlockRazorinc/solana-trader-client-go/pb/serverpb"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

const (
	// BlockRazor relay endpoint address
	blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80"
	// replace your solana rpc endpoint
	mainNetRPC = ""
	// replace your authKey
	authKey = ""
	// relace your private key(base58)
	privateKey = ""
	// publicKey(base58)
	publicKey = ""
	// transfer amount
	amount = 200_000
	// send mode
	mode = "fast"
	// safeWindow
	safeWindow = 5
	// revertProtection
	revertProtection = false
	// tip amount
	tipAmount = 100_000
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

func main() {
	var err error
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	receivePub := solana.MustPublicKeyFromBase58(publicKey)

	// setup grpc connect
	conn, err := grpc.NewClient(blzRelayEndpoint,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithPerRPCCredentials(&Authentication{authKey}),
	)
	if err != nil {
		panic(fmt.Sprintf("connect error: %v", err))
	}

	// use the Gateway client connection interface
	client := pb.NewServerClient(conn)

	// Pre-warm: perform an initial health check to establish the grpc connection
	err = pingHealth(client)
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
	go func() {
		for {
			err := pingHealth(client)
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()

	// new rpc client and get latest block hash
	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		panic(fmt.Sprintf("[get latest block hash] error: %v", err))
	}

	tipAccount := tipAccounts[rand.Intn(len(tipAccounts))]

	// construct instruction
	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), solana.MustPublicKeyFromBase58(tipAccount)).Build()

	// construct transation, replace your transation
	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		panic(fmt.Sprintf("new tx error: %v", err))
	}

	// transaction sign
	_, err = tx.Sign(
		func(key solana.PublicKey) *solana.PrivateKey {
			if account.PublicKey().Equals(key) {
				return &account.PrivateKey
			}
			return nil
		},
	)
	if err != nil {
		panic(fmt.Sprintf("sign tx error: %v", err))
	}

	txBase64, _ := tx.ToBase64()
	sendRes, err := client.SendTransaction(context.TODO(), &pb.SendRequest{
		Transaction:      txBase64,
		Mode:             mode,
		SafeWindow:       safeWindow,
		RevertProtection: revertProtection,
	})
	if err != nil {
		panic(fmt.Sprintf("[send tx] error: %v", err))
	}

	fmt.Printf("[send tx] response: %+v \n", sendRes)
	return
}

type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apikey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func pingHealth(client pb.ServerClient) error {
	// grpc request warmup
	healthRes, err := client.GetHealth(context.Background(), &pb.HealthRequest{})
	if err != nil {
		return err
	}
	fmt.Printf("[health] response: %+v \n", healthRes.Status)
	return err
}
```

{% endtab %}

{% tab title="Proto" %}

```go
syntax = "proto3";
package serverpb;
option go_package = "./pb/serverpb";
service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};
    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};
    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};
    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}
message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendResponse {
    string signature = 1;
}
message SendBundleRequest {
    repeated string transactions = 1;
}
message SendBundleResponse {
    string signature = 1;
}
message HealthRequest {
}
message HealthResponse {
    string status = 1;
}
```

{% endtab %}
{% endtabs %}


# Solana Send Transaction Rust Example

This page describes how to build and send Solana transactions using Rust

{% hint style="info" %}
Solana's transaction sending service is no longer bound to the subscription plan, with rate limit default to 3 TPS. API key could be required from [Authentication](/get-started/authentication). If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

[Code example](https://github.com/BlockRazorinc/solana-trader-client-rust/tree/main/src)

### Request Example

{% tabs %}
{% tab title="HTTP" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::transfer;
use std::str::FromStr;
use std::time::Duration;
use tokio::time::sleep;

#[derive(Serialize)]
struct SendRequest {
    transaction: String,
    mode: String,
    #[serde(rename = "safeWindow")]
    safe_window: u32,
    #[serde(rename = "revertProtection")]
    revert_proctection: bool,
}

#[derive(Deserialize, Debug)]
struct SendResponse {
    signature: String,
}

#[derive(Deserialize, Debug)]
struct HealthResponse {
    result: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configuration values
    let http_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendTransaction";
    let health_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey = "";
    // relace your target public key
    let publickey = "";
    // transaction amount
    let amount: u64 = 200_000;
    // tip amount
    let tipamount: u64 = 100_000;
    // safe window
    let safe_window: u32 = 5;
    // revert protection
    let revert_protection = false;
    // send mode
    let mode = "fast";

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];
    // Create a shared HTTP client with keep-alive support
    let client = reqwest::Client::new();

    // Perform initial health check to warm up the connection
    ping_health(&client, health_endpoint, authkey).await?;

    // Start background health pinger to keep the connection alive
    let health_endpoint_clone = health_endpoint.to_string();
    let authkey_clone = authkey.to_string();
    let client_clone = client.clone();
    tokio::spawn(async move {
        loop {
            if let Err(e) = ping_health(&client_clone, &health_endpoint_clone, &authkey_clone).await
            {
                eprintln!("Health check failed: {:?}", e);
            }
            sleep(Duration::from_secs(30)).await;
        }
    });

    // Send Solana transaction
    send_transaction(
        &client,
        mainnetrpc,
        authkey,
        privatekey,
        publickey,
        &tip_accounts,
        tipamount,
        amount,
        mode,
        safe_window,
        revert_protection,
        http_endpoint,
    )
    .await?;

    Ok(())
}

/// Perform GET /health to keep HTTP connection alive
async fn ping_health(
    client: &reqwest::Client,
    health_endpoint: &str,
    authkey: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client.get(health_endpoint).headers(headers).send().await?;

    let body = res.text().await?;
    let parsed: Result<HealthResponse, _> = serde_json::from_str(&body);
    if let Ok(hr) = parsed {
        println!("Health result: {}", hr.result);
    } else {
        return Err("Failed to parse health response".into());
    }
    Ok(())
}

/// Build and send a base64-encoded transaction via HTTP POST
async fn send_transaction(
    client: &reqwest::Client,
    mainnetrpc: &str,
    authkey: &str,
    privatekey: &str,
    publickey: &str,
    tip_accounts: &[&str],
    tipamount: u64,
    amount: u64,
    mode: &str,
    safe_window: u32,
    revert_protection: bool,
    http_endpoint: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let from = Keypair::from_base58_string(privatekey);
    let receiver = Pubkey::from_str(publickey)?;
    let tip = Pubkey::from_str(tip_accounts[rand::thread_rng().gen_range(0..tip_accounts.len())])?;

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");

    let ix_tip = transfer(&from.pubkey(), &tip, tipamount);
    let ix_main = transfer(&from.pubkey(), &receiver, amount);

    let tx = Transaction::new_signed_with_payer(
        &[ix_tip, ix_main],
        Some(&from.pubkey()),
        &[&from],
        recent_blockhash,
    );

    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let request = SendRequest {
        transaction: base64_encoded,
        mode: mode.to_string(),
        safe_window: safe_window,
        revert_proctection: revert_protection,
    };
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client
        .post(http_endpoint)
        .headers(headers)
        .json(&request)
        .send()
        .await?;

    let text = res.text().await?;
    let parsed: Result<SendResponse, _> = serde_json::from_str(&text);
    match parsed {
        Ok(r) => {
            println!("Transaction Signature: {}", r.signature); // use field
        }
        Err(_) => println!("RAW RESPONSE: {}", text),
    }

    Ok(())
}
```

{% endtab %}

{% tab title="gRPC" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use server::server_client::ServerClient;
use server::{HealthRequest, SendRequest};
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
use solana_system_interface::instruction::transfer;
use std::error::Error;
use std::str::FromStr;
use std::time::Duration;
use tonic::metadata::AsciiMetadataValue;
use tonic::transport::Channel;
use tokio::time::interval;

pub mod server {
    tonic::include_proto!("serverpb");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // BlockRazor relay endpoint address
    let blzendpoint = "http://frankfurt.solana-grpc.blockrazor.xyz:80";
    // replace your solana rpc endpoint
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey = "";
    // send mode
    let mode = "fast"; // set to "sandwichMitigation" to mitigate sandwich attacks on tx
    // safe window
    let safe_window = None; // only take effect in sandwichMitigation mode
    // revert protection
    let revert_protection = false;
    // tip amount
    let tipamount = 100_000;

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];

    let mut rng = rand::thread_rng();
    let random_index = rng.gen_range(0..tip_accounts.len());
    let tip_account = tip_accounts[random_index];

    let channel = Channel::from_shared(blzendpoint.to_string())
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid URI: {}", e)))?
        .connect()
        .await
        .map_err(|e| Box::<dyn Error>::from(format!("Connection error: {}", e)))?;

    let apikeyvalue = AsciiMetadataValue::try_from(authkey)
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid API key format: {}", e)))?;

    let mut client = ServerClient::new(channel.clone());
    let mut request = tonic::Request::new(HealthRequest {});
    request.metadata_mut().insert("apikey", apikeyvalue.clone());
    let response = client.get_health(request).await?;
    println!("health check response: {:?}", response.into_inner().status);

    // Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
    let mut health_client = ServerClient::new(channel); 
    let health_apikey = apikeyvalue.clone();
    tokio::spawn(async move {
        let mut interval = interval(Duration::from_secs(30));
        loop {
            interval.tick().await;
            let mut request = tonic::Request::new(HealthRequest {});
            request.metadata_mut().insert("apikey", health_apikey.clone());
            match health_client.get_health(request).await {
                Ok(response) => {
                    println!("[Health Check] Response: {:?}", response.into_inner().status);
                }
                Err(e) => {
                    eprintln!("[Health Check] Failed: {}", e);
                }
            }
        }
    });

    let from = Keypair::from_base58_string(privatekey);
    let frompubkey = Signer::pubkey(&from);
    let topubkey = Pubkey::from_str(tip_account).expect("Failed to parse receivers pubkey");

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());

    let ix = transfer(&frompubkey, &topubkey, tipamount);
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");
    let tx =
        Transaction::new_signed_with_payer(&[ix], Some(&frompubkey), &[&from], recent_blockhash);

    // tx base64
    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let mut tx_request: tonic::Request<SendRequest> = tonic::Request::new(SendRequest {
        transaction: base64_encoded,
        mode: mode.to_string(),
        safe_window: safe_window,
        revert_protection: revert_protection,
    });
    tx_request
        .metadata_mut()
        .insert("apikey", apikeyvalue.clone());
    let response = client.send_transaction(tx_request).await?;
    println!("SEND TX RESPONSE={:?}", response);

    Ok(())
}
```

{% endtab %}

{% tab title="Proto" %}

```go
syntax = "proto3";
package serverpb;
option go_package = "./pb/serverpb";
service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};
    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};
    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};
    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}
message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendResponse {
    string signature = 1;
}
message SendBundleRequest {
    repeated string transactions = 1;
}
message SendBundleResponse {
    string signature = 1;
}
message HealthRequest {
}
message HealthResponse {
    string status = 1;
}
```

{% endtab %}
{% endtabs %}


# Solana Send Transaction JS Example

This page describes how to build and send Solana transactions using JS

{% hint style="info" %}
Solana's transaction sending service is no longer bound to the subscription plan, with rate limit default to 3 TPS. API key could be required from [Authentication](/get-started/authentication). If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

[Code example](https://github.com/BlockRazorinc/solana-trader-client-js)

### Request Example

{% tabs %}
{% tab title="HTTP" %}

```javascript
const axios = require('axios');
const web3 = require('@solana/web3.js');
const bs58 = require('bs58');

// ------------------ Configuration Constants ------------------
// BlockRazor relay endpoint address
const httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendTransaction";
const healthEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
// Replace with your Solana RPC endpoint
const mainNetRPC = "";
// Replace with your authKey
const authKey = "";
// Replace with your private key (base58)
const privateKey = "";
// Replace with your target public key
const publicKey = "";
// Send mode
const mode = "fast";
// Safe window
const safeWindow = 5; // only take effect in sandwichMitigation mod
// Revert protection
const revertProtection = false;
// Transaction amount
const amount = 200_000;
// Tip amount
const tipAmount = 100_000;

const tipAccounts = [
    "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
    "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
    "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
    "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
    "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
    "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
    "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
    "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
    "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
    "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
    "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
    "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
    "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
    "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

// ------------------ Axios HTTP Client (Connection Reuse Enabled) ------------------
const httpClient = axios.create({
    timeout: 10000,
    headers: {
        'Content-Type': 'application/json',
        'apikey': authKey,
    },
    httpAgent: new (require('http').Agent)({ keepAlive: true }),
    httpsAgent: new (require('https').Agent)({ keepAlive: true }),
});

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
    try {
        const res = await httpClient.get(healthEndpoint);
        console.log(`Health result:`, res.data);
    } catch (err) {
        console.error('Health check failed:', err.message);
    }
}

// ------------------ Build and Send Transaction ------------------
async function sendTx() {
    const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
    const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);
    const receiver = new web3.PublicKey(publicKey);
    const tipAccount = new web3.PublicKey(tipAccounts[Math.floor(Math.random() * tipAccounts.length)]);

    const connection = new web3.Connection(mainNetRPC);
    const { blockhash } = await connection.getLatestBlockhash('finalized');

    const tx = new web3.Transaction()
        .add(web3.SystemProgram.transfer({
            fromPubkey: senderKeypair.publicKey,
            toPubkey: tipAccount,
            lamports: tipAmount,
        }))
        .add(web3.SystemProgram.transfer({
            fromPubkey: senderKeypair.publicKey,
            toPubkey: receiver,
            lamports: amount,
        }));

    tx.recentBlockhash = blockhash;
    tx.feePayer = senderKeypair.publicKey;
    tx.sign(senderKeypair);

    const serialized = tx.serialize();
    const base64Tx = serialized.toString('base64');

    const payload = {
        transaction: base64Tx,
        mode: mode,
        safeWindow: safeWindow,
		RevertProtection: revertProtection,
    };

    try {
        const res = await httpClient.post(httpEndpoint, payload);
        console.log('[send tx] response:', res.data);
    } catch (err) {
        console.error('SendTx failed:', err.response?.data || err.message);
    }
}

// ------------------ Main Entry ------------------
(async () => {
    // Initial health check (establish connection)
    await pingHealth();

    // Periodically send /health to keep connection alive
    setInterval(pingHealth, 30 * 1000);
    sendTx().catch(console.error);
})();
```

{% endtab %}

{% tab title="gRPC" %}

```go
const web3 = require("@solana/web3.js");
const bs58 = require("bs58");

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const PROTO_PATH = __dirname + '/server.proto';
const packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {
        keepCase: true,
        longs: String,
        enums: String,
        defaults: true,
        oneofs: true
    }
);

const serverProto = grpc.loadPackageDefinition(packageDefinition).serverpb;

// BlockRazor relay endpoint address
const blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80";
// replace your solana rpc endpoint
const mainNetRPC = "";
// replace your authKey
const authKey = "";
// relace your private key(base58)
const privateKey = "";
// send mode
const mode = "fast"; // set to "sandwichMitigation" to mitigate sandwich attacks on tx
// safeWindow
const safeWindow = 3; // only take effect in sandwichMitigation mode
// Revert protection
const revertProtection = false;
// tip amount
const tipAmount = 100_000;

const tipAccounts = [
    "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
    "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
    "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
    "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
    "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
    "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
    "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
    "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
    "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
    "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
    "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
    "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
    "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
    "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

const client = new serverProto.Server(
    blzRelayEndpoint,
    grpc.credentials.createInsecure()
);

var meta = new grpc.Metadata();

function getRandomAccount() {
    const randomIndex = Math.floor(Math.random() * tipAccounts.length);
    return tipAccounts[randomIndex];
}

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
    client.getHealth({}, meta, (err, response) => {
        if (err) {
            console.error('[get health] error:', err);
            return;
        }

        console.log('[get health] response:', response);
    });
}

(async () => {
    meta.add('apikey', authKey);

    // Initial health check (establish connection)
    await pingHealth();

    // Periodically send /health to keep connection alive
    setInterval(pingHealth, 30 * 1000);

    const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
    const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);

    const tipAccount = getRandomAccount();
    const recipientPublicKey = new web3.PublicKey(tipAccount);

    const transaction = new web3.Transaction().add(
        web3.SystemProgram.transfer({
            fromPubkey: senderKeypair.publicKey,
            toPubkey: recipientPublicKey,
            lamports: tipAmount,
        })
    );

    const connection = new web3.Connection(mainNetRPC);

    transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
    transaction.feePayer = senderKeypair.publicKey;
    transaction.sign(senderKeypair);

    const serializedTransaction = transaction.serialize();
    const base64Tx = serializedTransaction.toString('base64');

    client.SendTransaction({ transaction: base64Tx, mode: mode, safeWindow: safeWindow, revertProtection: revertProtection}, meta, (err, response) => {
        if (err) {
            console.error('[send tx] error:', err);
            return;
        }

        console.log('[send tx] response:', response);
    });
})();
```

{% endtab %}

{% tab title="Proto" %}

```go
syntax = "proto3";
package serverpb;
option go_package = "./pb/serverpb";
service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};
    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};
    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};
    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}
message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}
message SendResponse {
    string signature = 1;
}
message SendBundleRequest {
    repeated string transactions = 1;
}
message SendBundleResponse {
    string signature = 1;
}
message HealthRequest {
}
message HealthResponse {
    string status = 1;
}
```

{% endtab %}
{% endtabs %}


# Solana Send Transaction in Plain Text

Introduce the integration of 'Send Transaction in Plain Text' in BlockRazor Solana Transaction Sending mode

### Introduction <a href="#jie-shao" id="jie-shao"></a>

{% hint style="warning" %}
Solana's transaction sending service is not bound to the subscription plan, with rate limit default to 3 TPS. If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

`Send in Plain Text` is used to send signed transaction on Solana based on HTTP. It presents a much more streamlined and rapid method for submitting transactions compared with [Send Transaction](/transaction-submission/transaction-sending/solana/send-transaction)

* Bypasses CORS Preflight: It eliminates the delay (50-100ms) that is typically incurred by OPTIONS preflight.
* Plain Text over JSON: Employing a simple plain text transmission circumvents the computational burden associated with parsing JSON. Furthermore, the resulting smaller data size serves to cut down on network transfer time and costs.
* Base64: Comparing with base58, encoding and decoding of Base64 are significantly faster, while its more compact serialization results in a reduced overall body size.

`Send in Plain Text`'s features make it more suitable for front-end transaction applications with a global user base.

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST /v2/sendTransaction`

### Request Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

{% tabs %}
{% tab title="CURL" %}

```javascript
curl -X POST 'http://frankfurt.solana.blockrazor.xyz:443/v2/sendTransaction?auth=<auth_token>' \
-H "Content-Type: text/plain" \
-d "<base64_endcoded_tx>"
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Note:**

* **the `auth` and `request parameter`  are compulsory to be added in URI params, e.g.,** `http://frankfurt.solana.blockrazor.xyz:443/v2/sendTransaction?auth=<auth_token>&mode=fast&revertProtection=true`
* **the only header permitted in the request is `Content-Type: text/plain`**
* **Tx should be in Base64 encoded**
  {% endhint %}

### Request Parameter

<table><thead><tr><th width="111.44921875">Parameters</th><th width="115.421875">Mandatory</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transaction</td><td>Mandatory</td><td>"4hXTCk……tAnaAT"</td><td>Fully signed transactions, Base64 encoded</td></tr><tr><td>mode</td><td>Optional</td><td>"fast"<br>"sandwichMitigation"</td><td>BlockRazor offers two modes: Fast and SandwichMitigation, with Fast as the default.<br><br>In fast mode, transactions are sent based on globally distributed high-performance network and high-quality SWQoS, reaching the Leader node with the lowest latency.<br><br>In sandwichMitigation mode, BlockRazoz will route transactions to the trusted SWQoS and skip the slot of the blacklisted Leader (dynamically identified by the BlockRazor sandwich monitoring mechanism). In this mode, <strong>DO NOT</strong> send transactions using durable nonce, as it will cause the sandwich protection to become ineffective.</td></tr><tr><td>safeWindow</td><td>Optional</td><td>3</td><td>safeWindow is used to determine the timing of transaction sending in sandwichMitigation mode and represents the number of consecutive slots of  whitelist validators. For example, if it is set to 3, the transaction will only be sent when 3 consecutive slots from the current slot belong to whitelist validators.<br><br>The range of safeWindow is 3-13. The larger the number, the better the effect of mitigating the sandwich attack, but it may have a certain impact on the rate of inclusion. If not set, the default is 3.</td></tr><tr><td>revertProtection</td><td>Optional</td><td>false</td><td>The default value is false. If set to true, the transaction will not fail on chain, but the speed of inclusion will be affected and there is a possibility that it cannot be included. Please choose to enable it carefully according to actual needs.</td></tr></tbody></table>

### Response

<table><thead><tr><th width="141.20703125">Status Code</th><th width="201.421875">Message</th><th>Meaning</th></tr></thead><tbody><tr><td>200</td><td>OK</td><td>The request is normal</td></tr><tr><td>400</td><td>BadRequest</td><td>Invalid parameter</td></tr><tr><td>403</td><td>Forbidden</td><td>Request denied, as the authentication (auth) is empty, invalid, or expired.</td></tr><tr><td>500</td><td>InternalServerError</td><td>The server encountered an unexpected condition that prevented it from fulfilling the request</td></tr></tbody></table>

111111111


# Solana Send Transaction in Binary

Introduce the integration of 'Send Transaction in Binary' in BlockRazor Solana Transaction Sending mode

### Introduction <a href="#jie-shao" id="jie-shao"></a>

{% hint style="warning" %}
Solana's transaction sending service is not bound to the subscription plan, with rate limit default to 3 TPS. If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

`Send in Binary` is used to send signed transactions on Solana. Compared with the [Send Transaction](/transaction-submission/transaction-sending/solana/send-transaction), it allows signed transactions to be submitted in binary format instead of being converted to Base64 first. The benefits are that it removes one layer of encoding and decoding overhead, and because the payload is smaller, the transaction is less likely to be split into multiple packets during transmission, which can help reduce the resulting latency.

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST /v2/sendBinaryTransaction`

### Request Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

{% tabs %}
{% tab title="CURL" %}

```javascript
curl --location 'http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryTransaction?auth=<auth_token>' \
--data-binary @transaction.bin
```

{% endtab %}

{% tab title="Go" %}

```go
package main
import (
	"bytes"
	"context"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"
	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)
const (
	httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryTransaction?auth=<auth_token>"
	mainNetRPC   = ""
	privateKey   = ""
	publicKey    = ""
	amount       = 200_000
	tipAmount    = 1_000_000
)
var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}
var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}
func main() {
	if err := sendTx(); err != nil {
		fmt.Printf("send tx failed: %v\n", err)
	}
}
func sendTx() error {
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	if err != nil {
		return err
	}
	receivePub := solana.MustPublicKeyFromBase58(publicKey)
	tipPub := solana.MustPublicKeyFromBase58(tipAccounts[rand.Intn(len(tipAccounts))])
	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("[get blockhash] %v", err)
	}
	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), tipPub).Build()
	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("build tx error: %v", err)
	}
	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if account.PublicKey().Equals(key) {
			return &account.PrivateKey
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("sign tx error: %v", err)
	}
	binTx, err := tx.MarshalBinary()
	if err != nil {
		return fmt.Errorf("marshal tx error: %v", err)
	}
	req, err := http.NewRequest("POST", httpEndpoint, bytes.NewReader(binTx))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/octet-stream")
	resp, err := httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("send http error: %v", err)
	}
	defer resp.Body.Close()
	bodyBytes, _ := io.ReadAll(resp.Body)
	fmt.Printf("[send tx] response: %s\n", string(bodyBytes))
	return nil
}
```

{% endtab %}

{% tab title="Rust" %}

```rust
use bincode;
use rand::Rng;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::transfer;
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let http_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryTransaction?auth=<auth_token>";
    let mainnetrpc = "";
    let privatekey = "";
    let publickey = "";
    let amount: u64 = 200_000;
    let tipamount: u64 = 1_000_000;
    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];
    let client = reqwest::Client::new();
    send_transaction(
        &client,
        mainnetrpc,
        privatekey,
        publickey,
        &tip_accounts,
        tipamount,
        amount,
        http_endpoint,
    )
    .await?;
    Ok(())
}
async fn send_transaction(
    client: &reqwest::Client,
    mainnetrpc: &str,
    privatekey: &str,
    publickey: &str,
    tip_accounts: &[&str],
    tipamount: u64,
    amount: u64,
    http_endpoint: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let from = Keypair::from_base58_string(privatekey);
    let receiver = Pubkey::from_str(publickey)?;
    let tip = Pubkey::from_str(tip_accounts[rand::thread_rng().gen_range(0..tip_accounts.len())])?;
    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");
    let ix_tip = transfer(&from.pubkey(), &tip, tipamount);
    let ix_main = transfer(&from.pubkey(), &receiver, amount);
    let tx = Transaction::new_signed_with_payer(
        &[ix_tip, ix_main],
        Some(&from.pubkey()),
        &[&from],
        recent_blockhash,
    );
    let serialized = bincode::serialize(&tx)?;
    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/octet-stream"));
    let res = client
        .post(http_endpoint)
        .headers(headers)
        .body(serialized)
        .send()
        .await?;
    let text = res.text().await?;
    println!("[send tx] response: {}", text);
    Ok(())
}
```

{% endtab %}

{% tab title="JS" %}

```javascript
const axios = require("axios");
const web3 = require("@solana/web3.js");
const bs58 = require("bs58");
const http = require("http");
const https = require("https");
const httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryTransaction?auth=<auth_token>";
const mainNetRPC = "";
const privateKey = "";
const publicKey = "";
const amount = 200_000;
const tipAmount = 1_000_000;
const tipAccounts = [
  "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
  "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
  "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
  "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
  "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
  "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
  "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
  "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
  "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
  "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
  "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
  "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
  "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
  "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];
const httpClient = axios.create({
  timeout: 10000,
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),
});
async function sendTx() {
  const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
  const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);
  const receiver = new web3.PublicKey(publicKey);
  const tipAccount = new web3.PublicKey(
    tipAccounts[Math.floor(Math.random() * tipAccounts.length)]
  );
  const connection = new web3.Connection(mainNetRPC);
  const { blockhash } = await connection.getLatestBlockhash("finalized");
  const tx = new web3.Transaction()
    .add(
      web3.SystemProgram.transfer({
        fromPubkey: senderKeypair.publicKey,
        toPubkey: tipAccount,
        lamports: tipAmount,
      })
    )
    .add(
      web3.SystemProgram.transfer({
        fromPubkey: senderKeypair.publicKey,
        toPubkey: receiver,
        lamports: amount,
      })
    );
  tx.recentBlockhash = blockhash;
  tx.feePayer = senderKeypair.publicKey;
  tx.sign(senderKeypair);
  const serialized = tx.serialize();
  try {
    const res = await httpClient.post(httpEndpoint, Buffer.from(serialized), {
      headers: {
        "Content-Type": "application/octet-stream",
      },
      responseType: "text",
    });
    console.log("[send tx] response:", res.data);
  } catch (err) {
    console.error("SendTx failed:", err.response?.data || err.message);
  }
}
sendTx().catch(console.error);
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Note:**

* **the `auth` and `request parameter`  are compulsory to be added in URI params, e.g.,** `http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryTransaction?auth=<auth_token>&mode=fast&revertProtection=true`
  {% endhint %}

### Request Parameter

<table><thead><tr><th width="111.44921875">Parameters</th><th width="115.421875">Mandatory</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transaction</td><td>Mandatory</td><td>transaction.bin</td><td>Fully signed transactions, binary format</td></tr><tr><td>mode</td><td>Optional</td><td>"fast"<br>"sandwichMitigation"</td><td>BlockRazor offers two modes: Fast and SandwichMitigation, with Fast as the default.<br><br>In fast mode, transactions are sent based on globally distributed high-performance network and high-quality SWQoS, reaching the Leader node with the lowest latency.<br><br>In sandwichMitigation mode, BlockRazoz will route transactions to the trusted SWQoS and skip the slot of the blacklisted Leader (dynamically identified by the BlockRazor sandwich monitoring mechanism). In this mode, <strong>DO NOT</strong> send transactions using durable nonce, as it will cause the sandwich protection to become ineffective.</td></tr><tr><td>safeWindow</td><td>Optional</td><td>3</td><td>safeWindow is used to determine the timing of transaction sending in sandwichMitigation mode and represents the number of consecutive slots of  whitelist validators. For example, if it is set to 3, the transaction will only be sent when 3 consecutive slots from the current slot belong to whitelist validators.<br><br>The range of safeWindow is 3-13. The larger the number, the better the effect of mitigating the sandwich attack, but it may have a certain impact on the rate of inclusion. If not set, the default is 3.</td></tr><tr><td>revertProtection</td><td>Optional</td><td>false</td><td>The default value is false. If set to true, the transaction will not fail on chain, but the speed of inclusion will be affected and there is a possibility that it cannot be included. Please choose to enable it carefully according to actual needs.</td></tr></tbody></table>

### Response

<table><thead><tr><th width="141.20703125">Status Code</th><th width="201.421875">Message</th><th>Meaning</th></tr></thead><tbody><tr><td>200</td><td>OK</td><td>The request is normal</td></tr><tr><td>400</td><td>BadRequest</td><td>Invalid parameter</td></tr><tr><td>403</td><td>Forbidden</td><td>Request denied, as the authentication (auth) is empty, invalid, or expired.</td></tr><tr><td>500</td><td>InternalServerError</td><td>The server encountered an unexpected condition that prevented it from fulfilling the request</td></tr></tbody></table>


# Solana Send Bundle

Introducing the integration method of BlockRazor Solana's Send Bundle in Transaction Sending mode.

{% hint style="warning" %}
Solana's bundle sending service is not bound to the subscription plan and is not available by default. If you need to increase the BPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

`Send Bundle` is a bundle sending interface provided by BlockRazor for Solana, used to send signed transactions to the blockchain in bundle form with low latency.

A maximum of four transactions can be sent in a single bundle. These transactions are executed sequentially, and if any one transaction fails, the entire bundle will fail.

To enhance MEV protection, use any valid Solana public key starting with `jitodontfront` and `dontbund1e` in the instructions of the first transaction in the bundle. If the transaction is subjected to a frontrun attack, it will be rejected by the Jito/Harmonic block engine.

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST /sendBundle`
* `gRPC`

### Transaction Construction Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

* [Curl](/transaction-submission/transaction-sending/solana/send-bundle/curl)
* [Go](/transaction-submission/transaction-sending/solana/send-bundle/go)
* [Rust](/transaction-submission/transaction-sending/solana/send-bundle/rust)
* [JS](/transaction-submission/transaction-sending/solana/send-bundle/js)

### Request Parameter

<table><thead><tr><th width="111.44921875">Parameters</th><th width="115.421875">Mandatory</th><th width="104.94140625">Format</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transactions</td><td>Mandatory</td><td>string[]</td><td>["$base64_tx_1","$base64_tx_2","$base64_tx_3"]</td><td>Fully signed transactions, Base64 encoding protocal </td></tr></tbody></table>


# Solana Send Bundle Curl Example

This section introduces a Curl request example for Send Bundle in BlockRazor Solana Transaction Sending mode.

#### Request Example <a href="#qing-qiu-shi-li" id="qing-qiu-shi-li"></a>

```bash
curl --request POST \
  --url http://frankfurt.solana.blockrazor.xyz:443/sendBundle \
  --header 'Content-Type: application/json' \
  --header 'apikey: $auth_token' \
  --data '{
  "transactions":["$base64_tx_1","$base64_tx_2","$base64_tx_3"]
}'
```

#### Response Example <a href="#fan-hui-shi-li" id="fan-hui-shi-li"></a>

Normal

```json
{"signature":"$first_tx_signature","error":""}
```

Abnormal

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```


# Solana Send Bundle Go Example

This section introduces a Go request example for Send Bundle in BlockRazor Solana Transaction Sending mode.

#### Request Example <a href="#qing-qiu-shi-li" id="qing-qiu-shi-li"></a>

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```go
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)

const (
	httpEndpoint   = "http://frankfurt.solana.blockrazor.xyz:443/sendBundle"
	healthEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/health"
	mainNetRPC     = ""
	authKey        = ""
	privateKey     = ""
	publicKey      = ""
	amount         = 200_000
	tipAmount      = 1_000_000
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}

type SendRequest struct {
	Transactions []string `json:"transactions"`
}

type SendResponse struct {
	Signature string `json:"signature"`
}
type HealthResponse struct {
	Result string `json:"result"`
}

func main() {
	// Pre-warm: perform an initial health check to establish the HTTP connection
	err := pingHealth()
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the HTTP connection alive (warm)
	go func() {
		for {
			err := pingHealth()
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()
	// send bundle
	if err := sendBundle(); err != nil {
		fmt.Printf("send tx failed: %v\n", err)
	}
}

func pingHealth() error {
	req, err := http.NewRequest("GET", healthEndpoint, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// ⚠️ Important Note:
	// According to the Go net/http documentation, in order for the underlying TCP connection
	// to be reused (i.e. kept alive), the response body must be fully read and closed.
	// Otherwise, the Transport may not reuse the connection for future requests.
	// Reference: https://pkg.go.dev/net/http#Response
	// > "The default HTTP client's Transport may not reuse HTTP/1.x 'keep-alive' TCP connections
	//    if the Body is not read to completion and closed."

	// Read the full response body to enable connection reuse
	bodyBytes, _ := io.ReadAll(resp.Body)
	var healthRes HealthResponse
	if err := json.Unmarshal(bodyBytes, &healthRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}

	return nil
}

func sendBundle() error {
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	if err != nil {
		return err
	}
	receivePub := solana.MustPublicKeyFromBase58(publicKey)
	tipPub := solana.MustPublicKeyFromBase58(tipAccounts[rand.Intn(len(tipAccounts))])

	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("[get blockhash] %v", err)
	}

	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), tipPub).Build()

	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("build tx error: %v", err)
	}

	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if account.PublicKey().Equals(key) {
			return &account.PrivateKey
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("sign tx error: %v", err)
	}

	txBase64, err := tx.ToBase64()
	if err != nil {
		return err
	}

	reqBody := SendRequest{
		Transactions: []string{txBase64},
	}
	jsonBody, _ := json.Marshal(reqBody)

	httpReq, err := http.NewRequest("POST", httpEndpoint, bytes.NewBuffer(jsonBody))
	if err != nil {
		return err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(httpReq)
	if err != nil {
		return fmt.Errorf("send bundle error: %v", err)
	}
	defer resp.Body.Close()

	bodyBytes, _ := io.ReadAll(resp.Body)
	var sendRes SendResponse
	if err := json.Unmarshal(bodyBytes, &sendRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}
	fmt.Printf("[send bundle] response: %+v\n", sendRes)
	return nil
}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```python
package main

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	pb "github.com/BlockRazorinc/solana-trader-client-go/pb/serverpb"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

const (
	// BlockRazor relay endpoint address
	blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80"
	// replace your solana rpc endpoint
	mainNetRPC = ""
	// replace your authKey
	authKey = ""
	// relace your private key(base58)
	privateKey = ""
	// publicKey(base58)
	publicKey = ""
	// transfer amount
	amount = 200_000

	// tip amount
	tipAmount = 1_000_000
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

func main() {
	var err error
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	receivePub := solana.MustPublicKeyFromBase58(publicKey)

	// setup grpc connect
	conn, err := grpc.NewClient(blzRelayEndpoint,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithPerRPCCredentials(&Authentication{authKey}),
	)
	if err != nil {
		panic(fmt.Sprintf("connect error: %v", err))
	}

	// use the Gateway client connection interface
	client := pb.NewServerClient(conn)

	// Pre-warm: perform an initial health check to establish the grpc connection
	err = pingHealth(client)
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
	go func() {
		for {
			err := pingHealth(client)
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()

	// new rpc client and get latest block hash
	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		panic(fmt.Sprintf("[get latest block hash] error: %v", err))
	}

	tipAccount := tipAccounts[rand.Intn(len(tipAccounts))]

	// construct instruction
	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), solana.MustPublicKeyFromBase58(tipAccount)).Build()

	// construct transation, replace your transation
	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		panic(fmt.Sprintf("new tx error: %v", err))
	}

	// transaction sign
	_, err = tx.Sign(
		func(key solana.PublicKey) *solana.PrivateKey {
			if account.PublicKey().Equals(key) {
				return &account.PrivateKey
			}
			return nil
		},
	)
	if err != nil {
		panic(fmt.Sprintf("sign tx error: %v", err))
	}

	txBase64, err := tx.ToBase64()
	if err != nil {
		panic(fmt.Sprintf("encode tx error: %v", err))
	}

	sendRes, err := client.SendBundle(context.TODO(), &pb.SendBundleRequest{
		Transactions: []string{txBase64},
	})
	if err != nil {
		panic(fmt.Sprintf("[send bundle] error: %v", err))
	}

	fmt.Printf("[send bundle] response: %+v \n", sendRes)
	return
}

type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apikey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func pingHealth(client pb.ServerClient) error {
	// grpc request warmup
	healthRes, err := client.GetHealth(context.Background(), &pb.HealthRequest{})
	if err != nil {
		return err
	}
	fmt.Printf("[health] response: %+v \n", healthRes.Status)
	return err
}
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```ruby
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}


message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Response Example <a href="#fan-hui-shi-li" id="fan-hui-shi-li"></a>

Normal

```json
{"signature":"$first_tx_signature","error":""}
```

Abnormal

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```


# Solana Send Bundle Rust Example

This section introduces a Rust request example for Send Bundle in BlockRazor Solana Transaction Sending mode.

#### 請求示例 <a href="#qing-qiu-shi-li" id="qing-qiu-shi-li"></a>

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::transfer;
use std::str::FromStr;
use std::time::Duration;
use tokio::time::sleep;

#[derive(Serialize)]
struct SendBundleRequest {
    transactions: Vec<String>,
}

#[derive(Deserialize, Debug)]
struct SendBundleResponse {
    signature: String,
}

#[derive(Deserialize, Debug)]
struct HealthResponse {
    result: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configuration values
    let http_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendBundle";
    let health_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey ="";
    // relace your target public key
    let publickey = "";
    // transaction amount
    let amount: u64 = 200_000;
    // tip amount
    let tipamount: u64 = 1_000_000;

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];
    // Create a shared HTTP client with keep-alive support
    let client = reqwest::Client::new();

    // Perform initial health check to warm up the connection
    ping_health(&client, health_endpoint, authkey).await?;

    // Start background health pinger to keep the connection alive
    let health_endpoint_clone = health_endpoint.to_string();
    let authkey_clone = authkey.to_string();
    let client_clone = client.clone();
    tokio::spawn(async move {
        loop {
            if let Err(e) = ping_health(&client_clone, &health_endpoint_clone, &authkey_clone).await
            {
                eprintln!("Health check failed: {:?}", e);
            }
            sleep(Duration::from_secs(30)).await;
        }
    });

    // Send Solana Bundle
    send_bundle(
        &client,
        mainnetrpc,
        authkey,
        privatekey,
        publickey,
        &tip_accounts,
        tipamount,
        amount,
        http_endpoint,
    )
    .await?;

    Ok(())
}

/// Perform GET /health to keep HTTP connection alive
async fn ping_health(
    client: &reqwest::Client,
    health_endpoint: &str,
    authkey: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client.get(health_endpoint).headers(headers).send().await?;

    let body = res.text().await?;
    let parsed: Result<HealthResponse, _> = serde_json::from_str(&body);
    if let Ok(hr) = parsed {
        println!("Health result: {}", hr.result);
    } else {
        return Err("Failed to parse health response".into());
    }
    Ok(())
}

/// Build and send a base64-encoded transaction via HTTP POST
async fn send_bundle(
    client: &reqwest::Client,
    mainnetrpc: &str,
    authkey: &str,
    privatekey: &str,
    publickey: &str,
    tip_accounts: &[&str],
    tipamount: u64,
    amount: u64,
    http_endpoint: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let from = Keypair::from_base58_string(privatekey);
    let receiver = Pubkey::from_str(publickey)?;
    let tip = Pubkey::from_str(tip_accounts[rand::thread_rng().gen_range(0..tip_accounts.len())])?;

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");

    let ix_tip = transfer(&from.pubkey(), &tip, tipamount);
    let ix_main = transfer(&from.pubkey(), &receiver, amount);

    let tx = Transaction::new_signed_with_payer(
        &[ix_tip, ix_main],
        Some(&from.pubkey()),
        &[&from],
        recent_blockhash,
    );

    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let request = SendBundleRequest {
        transactions: vec![base64_encoded],
    };
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client
        .post(http_endpoint)
        .headers(headers)
        .json(&request)
        .send()
        .await?;

    let text = res.text().await?;
    let parsed: Result<SendBundleResponse, _> = serde_json::from_str(&text);
    match parsed {
        Ok(r) => {
            println!("First Transaction Signature: {}", r.signature); // use field
        }
        Err(_) => println!("RAW RESPONSE: {}", text),
    }

    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use server::server_client::ServerClient;
use server::{HealthRequest, SendBundleRequest};
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
use solana_system_interface::instruction::transfer;
use std::error::Error;
use std::str::FromStr;
use std::time::Duration;
use tonic::metadata::AsciiMetadataValue;
use tonic::transport::Channel;
use tokio::time::interval;

pub mod server {
    tonic::include_proto!("serverpb");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // BlockRazor relay endpoint address
    let blzendpoint = "http://frankfurt.solana-grpc.blockrazor.xyz:80";
    // replace your solana rpc endpoint
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey = "";
    // tip amount
    let tipamount = 1_000_000;

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];

    let mut rng = rand::thread_rng();
    let random_index = rng.gen_range(0..tip_accounts.len());
    let tip_account = tip_accounts[random_index];

    let channel = Channel::from_shared(blzendpoint.to_string())
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid URI: {}", e)))?
        .connect()
        .await
        .map_err(|e| Box::<dyn Error>::from(format!("Connection error: {}", e)))?;

    let apikeyvalue = AsciiMetadataValue::try_from(authkey)
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid API key format: {}", e)))?;

    let mut client = ServerClient::new(channel.clone());
    let mut request = tonic::Request::new(HealthRequest {});
    request.metadata_mut().insert("apikey", apikeyvalue.clone());
    let response = client.get_health(request).await?;
    println!("health check response: {:?}", response.into_inner().status);

    // Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
    let mut health_client = ServerClient::new(channel); 
    let health_apikey = apikeyvalue.clone();
    tokio::spawn(async move {
        let mut interval = interval(Duration::from_secs(30));
        loop {
            interval.tick().await;
            let mut request = tonic::Request::new(HealthRequest {});
            request.metadata_mut().insert("apikey", health_apikey.clone());
            match health_client.get_health(request).await {
                Ok(response) => {
                    println!("[Health Check] Response: {:?}", response.into_inner().status);
                }
                Err(e) => {
                    eprintln!("[Health Check] Failed: {}", e);
                }
            }
        }
    });

    let from = Keypair::from_base58_string(privatekey);
    let frompubkey = Signer::pubkey(&from);
    let topubkey = Pubkey::from_str(tip_account).expect("Failed to parse receivers pubkey");

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());

    let ix = transfer(&frompubkey, &topubkey, tipamount);
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");
    let tx =
        Transaction::new_signed_with_payer(&[ix], Some(&frompubkey), &[&from], recent_blockhash);

    // tx base64
    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let mut bundle_request: tonic::Request<SendBundleRequest> = tonic::Request::new(SendBundleRequest {
        transactions: vec![base64_encoded],
    });
    bundle_request
        .metadata_mut()
        .insert("apikey", apikeyvalue.clone());
    let response = client.send_bundle(bundle_request).await?;
    println!("SEND BUNDLE RESPONSE={:?}", response);

    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```rust
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}


message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### 返回示例 <a href="#fan-hui-shi-li" id="fan-hui-shi-li"></a>

正常

```json
{"signature":"$first_tx_signature","error":""}
```

異常

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```


# Solana Send Bundle JS Example

This section introduces a JS request example for Send Bundle in BlockRazor Solana Fast mode.

#### Request Example <a href="#qing-qiu-shi-li" id="qing-qiu-shi-li"></a>

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```javascript
const axios = require('axios');
const web3 = require('@solana/web3.js');
const bs58 = require('bs58');

// ------------------ Configuration Constants ------------------
// BlockRazor relay endpoint address
const httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendBundle";
const healthEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
// Replace with your Solana RPC endpoint
const mainNetRPC = "";
// Replace with your authKey
const authKey = "";
// Replace with your private key (base58)
const privateKey = "";
// Replace with your target public key
const publicKey = "";
// Transaction amount
const amount = 200_000;
// Tip amount
const tipAmount = 1000000;

const tipAccounts = [
		"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
		"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
		"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
		"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
		"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
		"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
		"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
		"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
		"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
		"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
		"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
		"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
		"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
		"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

// ------------------ Axios HTTP Client (Connection Reuse Enabled) ------------------
const httpClient = axios.create({
		timeout: 10000,
		headers: {
				'Content-Type': 'application/json',
				'apikey': authKey,
		},
		httpAgent: new (require('http').Agent)({ keepAlive: true }),
		httpsAgent: new (require('https').Agent)({ keepAlive: true }),
});

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
		try {
				const res = await httpClient.get(healthEndpoint);
				console.log(`Health result:`, res.data);
		} catch (err) {
				console.error('Health check failed:', err.message);
		}
}

// ------------------ Build and Send Bundle ------------------
async function sendBundle() {
		const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
		const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);
		const receiver = new web3.PublicKey(publicKey);
		const tipAccount = new web3.PublicKey(tipAccounts[Math.floor(Math.random() * tipAccounts.length)]);

		const connection = new web3.Connection(mainNetRPC);
		const { blockhash } = await connection.getLatestBlockhash('finalized');

		const tx = new web3.Transaction()
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: tipAccount,
						lamports: tipAmount,
				}))
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: receiver,
						lamports: amount,
				}));

		tx.recentBlockhash = blockhash;
		tx.feePayer = senderKeypair.publicKey;
		tx.sign(senderKeypair);

		const serialized = tx.serialize();
		const base64Tx = serialized.toString('base64');

		const payload = {
				transactions: [base64Tx],
		};

		try {
				const res = await httpClient.post(httpEndpoint, payload);
				console.log('[send bundle] response:', res.data);
		} catch (err) {
				console.error('sendBundle failed:', err.response?.data || err.message);
		}
}

// ------------------ Main Entry ------------------
(async () => {
		// Initial health check (establish connection)
		await pingHealth();

		// Periodically send /health to keep connection alive
		setInterval(pingHealth, 30 * 1000);
		sendBundle().catch(console.error);
})();
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```javascript
const web3 = require("@solana/web3.js");
const bs58 = require("bs58");

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const PROTO_PATH = __dirname + '/server.proto';
const packageDefinition = protoLoader.loadSync(
		PROTO_PATH,
		{
				keepCase: true,
				longs: String,
				enums: String,
				defaults: true,
				oneofs: true
		}
);

const serverProto = grpc.loadPackageDefinition(packageDefinition).serverpb;

// BlockRazor relay endpoint address
const blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80";
// replace your solana rpc endpoint
const mainNetRPC = "";
// replace your authKey
const authKey = "";
// relace your private key(base58)
const privateKey = "";

// tip amount
const tipAmount = 1000000;

const tipAccounts = [
		"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
		"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
		"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
		"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
		"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
		"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
		"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
		"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
		"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
		"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
		"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
		"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
		"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
		"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

const client = new serverProto.Server(
		blzRelayEndpoint,
		grpc.credentials.createInsecure()
);

var meta = new grpc.Metadata();

function getRandomAccount() {
		const randomIndex = Math.floor(Math.random() * tipAccounts.length);
		return tipAccounts[randomIndex];
}

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
		client.getHealth({}, meta, (err, response) => {
				if (err) {
						console.error('[get health] error:', err);
						return;
				}

				console.log('[get health] response:', response);
		});
}

(async () => {
		meta.add('apikey', authKey);

		// Initial health check (establish connection)
		await pingHealth();

		// Periodically send /health to keep connection alive
		setInterval(pingHealth, 30 * 1000);

		const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
		const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);

		const tipAccount = getRandomAccount();
		const recipientPublicKey = new web3.PublicKey(tipAccount);

		const transaction = new web3.Transaction().add(
				web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: recipientPublicKey,
						lamports: tipAmount,
				})
		);

		const connection = new web3.Connection(mainNetRPC);

		transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
		transaction.feePayer = senderKeypair.publicKey;
		transaction.sign(senderKeypair);

		const serializedTransaction = transaction.serialize();
		const base64Tx = serializedTransaction.toString('base64');

		client.SendBundle({ transactions: [base64Tx] }, meta, (err, response) => {
				if (err) {
						console.error('[send bundle] error:', err);
						return;
				}

				console.log('[send bundle] response:', response);
		});
})();
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```javascript
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}


message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### 返回示例 <a href="#fan-hui-shi-li" id="fan-hui-shi-li"></a>

正常

```json
{"signature":"$first_tx_signature","error":""}
```

異常

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```


# Solana Send Batch

Introducing the integration method of BlockRazor Solana's Send Batch in Transaction Sending mode.

{% hint style="warning" %}
Solana's batch sending service is not bound to the subscription plan, with rate limit default to 3 TPS. API key could be required from [Authentication](/get-started/authentication). If you need to increase the TPS limit, please [contact](https://discord.com/invite/qqJuwRb8Nh) us and we will handle it as soon as possible.
{% endhint %}

`Send Batch` is a batch sending interface provided by BlockRazor for Solana, used to send signed transactions to the blockchain in batch form with low latency. A maximum of 25 transactions can be sent in a single batch.

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST /sendBatch`
* `gRPC`

### Transaction Construction Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

* [Curl](/transaction-submission/transaction-sending/solana/send-batch/request-example/curl)
* [Go](/transaction-submission/transaction-sending/solana/send-batch/request-example/go)
* [Rust](/transaction-submission/transaction-sending/solana/send-batch/request-example/rust)
* [JS](/transaction-submission/transaction-sending/solana/send-batch/request-example/js)

### Request Parameter

<table><thead><tr><th width="120.8515625">Parameters</th><th width="115.421875">Mandatory</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transactions</td><td>Mandatory</td><td>string[]</td><td>Signed transactions, Base64 encoded, maximum 25 transactions</td></tr><tr><td>mode</td><td>Optional</td><td>"fast"<br>"sandwichMitigation"</td><td>BlockRazor offers two modes: Fast and SandwichMitigation, with Fast as the default.<br><br>In fast mode, transactions are sent based on globally distributed high-performance network and high-quality SWQoS, reaching the Leader node with the lowest latency.<br><br>In sandwichMitigation mode, BlockRazoz will route transactions to the trusted SWQoS and skip the slot of the blacklisted Leader (dynamically identified by the BlockRazor sandwich monitoring mechanism). In this mode, <strong>DO NOT</strong> send transactions using durable nonce, as it will cause the sandwich protection to become ineffective.</td></tr><tr><td>safeWindow</td><td>Optional</td><td>3</td><td>safeWindow is used to determine the timing of transaction sending in sandwichMitigation mode and represents the number of consecutive slots of  whitelist validators. For example, if it is set to 3, the transaction will only be sent when 3 consecutive slots from the current slot belong to whitelist validators.<br><br>The range of safeWindow is 3-13. The larger the number, the better the effect of mitigating the sandwich attack, but it may have a certain impact on the rate of inclusion. If not set, the default is 3.</td></tr><tr><td>revertProtection</td><td>Optional</td><td>false</td><td>The default value is false. If set to true, the transaction will not fail on chain, but the speed of inclusion will be affected and there is a possibility that it cannot be included. Please choose to enable it carefully according to actual needs.</td></tr></tbody></table>


# Solana Send Batch Request Example

This section introduces an example of a Send Batch request in BlockRazor Solana Transaction Sending mode.


# Solana Send Batch Curl Example

This section introduces a Curl request example for Send Batch in BlockRazor Solana Transaction Sending mode.

### Request Example

{% code overflow="wrap" %}

```bash
curl --request POST \
  --url http://frankfurt.solana.blockrazor.xyz:443/sendBatch \
  --header 'Content-Type: application/json' \
  --header 'apikey: $auth_token' \
  --data '{
  "transactions":["$base64_tx_1","$base64_tx_2","$base64_tx_3"],
  "mode": "fast"
}'
```

{% endcode %}

### Response Example

Normal

{% code overflow="wrap" %}

```json
{
  "result": [{
    "signature": "58pw71yy2LDSw......Kdx5akJeRGUTS9bwGctXcHPAu",
    "error": ""
  }],
  "error": ""
}
```

{% endcode %}

Abnormal

{% code overflow="wrap" %}

```json
{"result":[],"error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```

{% endcode %}


# Solana Send Batch Go Example

This section introduces a Go request example for Send Batch in BlockRazor Solana Transaction Sending mode.

### Request Example

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```go
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)

const (
	httpEndpoint     = "http://frankfurt.solana.blockrazor.xyz:443/sendBatch"
	healthEndpoint   = "http://frankfurt.solana.blockrazor.xyz:443/health"
	mainNetRPC       = ""
	authKey          = ""
	privateKey       = ""
	publicKey        = ""
	amount           = 200_000
	tipAmount        = 1_000_000
	mode             = "fast"
	safeWindow       = 5
	revertProtection = false
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}

type SendRequest struct {
	Transactions     []string `json:"transactions"`
	Mode             string   `json:"mode"`
	SafeWindow       int      `json:"safeWindow"`
	RevertProtection bool     `json:"revertProtection"`
}

// BatchResponse .
type BatchResponse struct {
	Result []BatchResponseItem `json:"result"`
	Error  string              `json:"error"`
}

// BatchResponseItem .
type BatchResponseItem struct {
	Signature string `json:"signature"`
	Error     string `json:"error"`
}

type HealthResponse struct {
	Result string `json:"result"`
}

func main() {
	// Pre-warm: perform an initial health check to establish the HTTP connection
	err := pingHealth()
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the HTTP connection alive (warm)
	go func() {
		for {
			err := pingHealth()
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()
	// send batch
	if err := sendBatch(); err != nil {
		fmt.Printf("send tx failed: %v\n", err)
	}
}

func pingHealth() error {
	req, err := http.NewRequest("GET", healthEndpoint, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// ⚠️ Important Note:
	// According to the Go net/http documentation, in order for the underlying TCP connection
	// to be reused (i.e. kept alive), the response body must be fully read and closed.
	// Otherwise, the Transport may not reuse the connection for future requests.
	// Reference: https://pkg.go.dev/net/http#Response
	// > "The default HTTP client's Transport may not reuse HTTP/1.x 'keep-alive' TCP connections
	//    if the Body is not read to completion and closed."

	// Read the full response body to enable connection reuse
	bodyBytes, _ := io.ReadAll(resp.Body)
	var healthRes HealthResponse
	if err := json.Unmarshal(bodyBytes, &healthRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}

	return nil
}

func sendBatch() error {
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	if err != nil {
		return err
	}
	receivePub := solana.MustPublicKeyFromBase58(publicKey)
	tipPub := solana.MustPublicKeyFromBase58(tipAccounts[rand.Intn(len(tipAccounts))])

	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("[get blockhash] %v", err)
	}

	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), tipPub).Build()

	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("build tx error: %v", err)
	}

	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if account.PublicKey().Equals(key) {
			return &account.PrivateKey
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("sign tx error: %v", err)
	}

	txBase64, err := tx.ToBase64()
	if err != nil {
		return err
	}

	reqBody := SendRequest{
		Transactions:     []string{txBase64},
		Mode:             mode,
		SafeWindow:       safeWindow,
		RevertProtection: revertProtection,
	}
	jsonBody, _ := json.Marshal(reqBody)

	httpReq, err := http.NewRequest("POST", httpEndpoint, bytes.NewBuffer(jsonBody))
	if err != nil {
		return err
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(httpReq)
	if err != nil {
		return fmt.Errorf("send batch error: %v", err)
	}
	defer resp.Body.Close()

	bodyBytes, _ := io.ReadAll(resp.Body)
	var sendRes BatchResponse
	if err := json.Unmarshal(bodyBytes, &sendRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}
	fmt.Printf("[send batch] response: %+v\n", sendRes)
	return nil
}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```go
package main

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	pb "github.com/BlockRazorinc/solana-trader-client-go/pb/serverpb"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

const (
	// BlockRazor relay endpoint address
	blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80"
	// replace your solana rpc endpoint
	mainNetRPC = ""
	// replace your authKey
	authKey = ""
	// relace your private key(base58)
	privateKey = ""
	// publicKey(base58)
	publicKey = ""
	// transfer amount
	amount = 200_000

	// send mode
	mode = "fast"
	// safeWindow
	safeWindow = 5
	// revertProtection
	revertProtection = false
	// tip amount
	tipAmount = 1_000_000
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

func main() {
	var err error
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	receivePub := solana.MustPublicKeyFromBase58(publicKey)

	// setup grpc connect
	conn, err := grpc.NewClient(blzRelayEndpoint,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithPerRPCCredentials(&Authentication{authKey}),
	)
	if err != nil {
		panic(fmt.Sprintf("connect error: %v", err))
	}

	// use the Gateway client connection interface
	client := pb.NewServerClient(conn)

	// Pre-warm: perform an initial health check to establish the grpc connection
	err = pingHealth(client)
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
	go func() {
		for {
			err := pingHealth(client)
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()

	// new rpc client and get latest block hash
	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		panic(fmt.Sprintf("[get latest block hash] error: %v", err))
	}

	tipAccount := tipAccounts[rand.Intn(len(tipAccounts))]

	// construct instruction
	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), solana.MustPublicKeyFromBase58(tipAccount)).Build()

	// construct transation, replace your transation
	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		panic(fmt.Sprintf("new tx error: %v", err))
	}

	// transaction sign
	_, err = tx.Sign(
		func(key solana.PublicKey) *solana.PrivateKey {
			if account.PublicKey().Equals(key) {
				return &account.PrivateKey
			}
			return nil
		},
	)
	if err != nil {
		panic(fmt.Sprintf("sign tx error: %v", err))
	}

	txBase64, err := tx.ToBase64()
	if err != nil {
		panic(fmt.Sprintf("encode tx error: %v", err))
	}

	sendRes, err := client.SendBatch(context.TODO(), &pb.SendBatchRequest{
		Transactions:     []string{txBase64},
		Mode:             mode,
		SafeWindow:       safeWindow,
		RevertProtection: revertProtection,
	})
	if err != nil {
		panic(fmt.Sprintf("[send batch] error: %v", err))
	}

	fmt.Printf("[send batch] response: %+v \n", sendRes)
	return
}

type Authentication struct {
	apiKey string
}

func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
	return map[string]string{"apikey": a.apiKey}, nil
}

func (a *Authentication) RequireTransportSecurity() bool {
	return false
}

func pingHealth(client pb.ServerClient) error {
	// grpc request warmup
	healthRes, err := client.GetHealth(context.Background(), &pb.HealthRequest{})
	if err != nil {
		return err
	}
	fmt.Printf("[health] response: %+v \n", healthRes.Status)
	return err
}
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```go
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBatch(SendBatchRequest) returns(SendBatchResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}

message SendBatchRequest {
    repeated string transactions = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBatchResponse {
    repeated BatchResponse result = 1;
}

message BatchResponse {
    string signature = 1;
    string error = 2;
}

message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Response Example

Normal

```json
{"signature":"$first_tx_signature","error":""}
```

Abnormal

{% code overflow="wrap" %}

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```

{% endcode %}


# Solana Send Batch Rust Example

This section introduces a Rust request example for Send Batch in BlockRazor Solana Transaction Sending mode.

### Request Example

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::transfer;
use std::str::FromStr;
use std::time::Duration;
use tokio::time::sleep;

#[derive(Serialize)]
struct SendBatchRequest {
    transactions: Vec<String>,
    mode: String,
    #[serde(rename = "safeWindow")]
    safe_window: u32,
    #[serde(rename = "revertProtection")]
    revert_proctection: bool,
}

#[derive(Deserialize, Debug)]
struct SendBatchResponse {
    result: Vec<BatchResponseItem>,
    error: String,
}

#[derive(Deserialize, Debug)]
struct BatchResponseItem {
    signature: String,
    error: String,
}

#[derive(Deserialize, Debug)]
struct HealthResponse {
    result: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configuration values
    let http_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendBatch";
    let health_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey ="";
    // relace your target public key
    let publickey = "";
    // transaction amount
    let amount: u64 = 200_000;
    // tip amount
    let tipamount: u64 = 1_000_000;
    // safe window
    let safe_window: u32 = 5;
    // revert protection
    let revert_protection = false;
    // send mode
    let mode = "fast";

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];
    // Create a shared HTTP client with keep-alive support
    let client = reqwest::Client::new();

    // Perform initial health check to warm up the connection
    ping_health(&client, health_endpoint, authkey).await?;

    // Start background health pinger to keep the connection alive
    let health_endpoint_clone = health_endpoint.to_string();
    let authkey_clone = authkey.to_string();
    let client_clone = client.clone();
    tokio::spawn(async move {
        loop {
            if let Err(e) = ping_health(&client_clone, &health_endpoint_clone, &authkey_clone).await
            {
                eprintln!("Health check failed: {:?}", e);
            }
            sleep(Duration::from_secs(30)).await;
        }
    });

    // Send Solana Batch
    send_batch(
        &client,
        mainnetrpc,
        authkey,
        privatekey,
        publickey,
        &tip_accounts,
        tipamount,
        amount,
        mode,
        safe_window,
        revert_protection,
        http_endpoint,
    )
    .await?;

    Ok(())
}

/// Perform GET /health to keep HTTP connection alive
async fn ping_health(
    client: &reqwest::Client,
    health_endpoint: &str,
    authkey: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client.get(health_endpoint).headers(headers).send().await?;

    let body = res.text().await?;
    let parsed: Result<HealthResponse, _> = serde_json::from_str(&body);
    if let Ok(hr) = parsed {
        println!("Health result: {}", hr.result);
    } else {
        return Err("Failed to parse health response".into());
    }
    Ok(())
}

/// Build and send base64-encoded transactions via HTTP POST
async fn send_batch(
    client: &reqwest::Client,
    mainnetrpc: &str,
    authkey: &str,
    privatekey: &str,
    publickey: &str,
    tip_accounts: &[&str],
    tipamount: u64,
    amount: u64,
    mode: &str,
    safe_window: u32,
    revert_protection: bool,
    http_endpoint: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let from = Keypair::from_base58_string(privatekey);
    let receiver = Pubkey::from_str(publickey)?;
    let tip = Pubkey::from_str(tip_accounts[rand::thread_rng().gen_range(0..tip_accounts.len())])?;

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");

    let ix_tip = transfer(&from.pubkey(), &tip, tipamount);
    let ix_main = transfer(&from.pubkey(), &receiver, amount);

    let tx = Transaction::new_signed_with_payer(
        &[ix_tip, ix_main],
        Some(&from.pubkey()),
        &[&from],
        recent_blockhash,
    );

    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let request = SendBatchRequest {
        transactions: vec![base64_encoded],
        mode: mode.to_string(),
        safe_window: safe_window,
        revert_proctection: revert_protection,
    };
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client
        .post(http_endpoint)
        .headers(headers)
        .json(&request)
        .send()
        .await?;

    let text = res.text().await?;
    let parsed: Result<SendBatchResponse, _> = serde_json::from_str(&text);
    match parsed {
        Ok(r) => {
            println!("Result: {:?}", r.result); // use field
        }
        Err(_) => println!("RAW RESPONSE: {}", text),
    }

    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use server::server_client::ServerClient;
use server::{HealthRequest, SendBatchRequest};
use solana_client::rpc_client::RpcClient;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
use solana_system_interface::instruction::transfer;
use std::error::Error;
use std::str::FromStr;
use std::time::Duration;
use tonic::metadata::AsciiMetadataValue;
use tonic::transport::Channel;
use tokio::time::interval;

pub mod server {
    tonic::include_proto!("serverpb");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // BlockRazor relay endpoint address
    let blzendpoint = "http://frankfurt.solana-grpc.blockrazor.xyz:80";
    // replace your solana rpc endpoint
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey = "";
    // tip amount
    let tipamount = 1_000_000;
    // send mode
    let mode = "fast";
    // safe window
    let safe_window = None;
    // revert protection
    let revert_protection = false;

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];

    let mut rng = rand::thread_rng();
    let random_index = rng.gen_range(0..tip_accounts.len());
    let tip_account = tip_accounts[random_index];

    let channel = Channel::from_shared(blzendpoint.to_string())
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid URI: {}", e)))?
        .connect()
        .await
        .map_err(|e| Box::<dyn Error>::from(format!("Connection error: {}", e)))?;

    let apikeyvalue = AsciiMetadataValue::try_from(authkey)
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid API key format: {}", e)))?;

    let mut client = ServerClient::new(channel.clone());
    let mut request = tonic::Request::new(HealthRequest {});
    request.metadata_mut().insert("apikey", apikeyvalue.clone());
    let response = client.get_health(request).await?;
    println!("health check response: {:?}", response.into_inner().status);

    // Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the grpc connection alive (warm)
    let mut health_client = ServerClient::new(channel); 
    let health_apikey = apikeyvalue.clone();
    tokio::spawn(async move {
        let mut interval = interval(Duration::from_secs(30));
        loop {
            interval.tick().await;
            let mut request = tonic::Request::new(HealthRequest {});
            request.metadata_mut().insert("apikey", health_apikey.clone());
            match health_client.get_health(request).await {
                Ok(response) => {
                    println!("[Health Check] Response: {:?}", response.into_inner().status);
                }
                Err(e) => {
                    eprintln!("[Health Check] Failed: {}", e);
                }
            }
        }
    });

    let from = Keypair::from_base58_string(privatekey);
    let frompubkey = Signer::pubkey(&from);
    let topubkey = Pubkey::from_str(tip_account).expect("Failed to parse receivers pubkey");

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());

    let ix = transfer(&frompubkey, &topubkey, tipamount);
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");
    let tx =
        Transaction::new_signed_with_payer(&[ix], Some(&frompubkey), &[&from], recent_blockhash);

    // tx base64
    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);
    let mut batch_request: tonic::Request<SendBatchRequest> = tonic::Request::new(SendBatchRequest {
        transactions: vec![base64_encoded],
        mode: mode.to_string(),
        safe_window: safe_window.unwrap_or_default(),
        revert_protection: revert_protection
    });
    batch_request
        .metadata_mut()
        .insert("apikey", apikeyvalue.clone());
    let response = client.send_batch(batch_request).await?;
    println!("SEND BATCH RESPONSE={:?}", response);

    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```go
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBatch(SendBatchRequest) returns(SendBatchResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    optional int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    optional int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}

message SendBatchRequest {
    repeated string transactions = 1;
    string mode = 2;
    optional int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBatchResponse {
    repeated BatchResponse result = 1;
}

message BatchResponse {
    string signature = 1;
    string error = 2;
}

message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Response Example

Normal

```json
{"signature":"$first_tx_signature","error":""}
```

Abnormal

{% code overflow="wrap" %}

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```

{% endcode %}


# Solana Send Batch JS Example

This section introduces a JS request example for Send Batch in BlockRazor Solana Transaction Sending mode.

### Request Example

{% tabs %}
{% tab title="HTTP" %}
{% code overflow="wrap" %}

```javascript
const axios = require('axios');
const web3 = require('@solana/web3.js');
const bs58 = require('bs58');

// ------------------ Configuration Constants ------------------
// BlockRazor relay endpoint address
const httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/sendBatch";
const healthEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
// Replace with your Solana RPC endpoint
const mainNetRPC = "";
// Replace with your authKey
const authKey = "";
// Replace with your private key (base58)
const privateKey = "";
// Replace with your target public key
const publicKey = "";
// Transaction amount
const amount = 200_000;
// Tip amount
const tipAmount = 1000000;

const tipAccounts = [
		"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
		"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
		"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
		"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
		"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
		"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
		"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
		"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
		"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
		"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
		"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
		"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
		"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
		"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

// ------------------ Axios HTTP Client (Connection Reuse Enabled) ------------------
const httpClient = axios.create({
		timeout: 10000,
		headers: {
				'Content-Type': 'application/json',
				'apikey': authKey,
		},
		httpAgent: new (require('http').Agent)({ keepAlive: true }),
		httpsAgent: new (require('https').Agent)({ keepAlive: true }),
});

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
		try {
				const res = await httpClient.get(healthEndpoint);
				console.log(`Health result:`, res.data);
		} catch (err) {
				console.error('Health check failed:', err.message);
		}
}

// ------------------ Build and Send Batch ------------------
async function sendBatch() {
		const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
		const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);
		const receiver = new web3.PublicKey(publicKey);
		const tipAccount = new web3.PublicKey(tipAccounts[Math.floor(Math.random() * tipAccounts.length)]);

		const connection = new web3.Connection(mainNetRPC);
		const { blockhash } = await connection.getLatestBlockhash('finalized');

		const tx = new web3.Transaction()
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: tipAccount,
						lamports: tipAmount,
				}))
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: receiver,
						lamports: amount,
				}));

		tx.recentBlockhash = blockhash;
		tx.feePayer = senderKeypair.publicKey;
		tx.sign(senderKeypair);

		const serialized = tx.serialize();
		const base64Tx = serialized.toString('base64');

		const payload = {
				transactions: [base64Tx],
				mode: 'fast',
		};

		try {
				const res = await httpClient.post(httpEndpoint, payload);
				console.log('[send batch] response:', res.data);
		} catch (err) {
				console.error('sendBatch failed:', err.response?.data || err.message);
		}
}

// ------------------ Main Entry ------------------
(async () => {
		// Initial health check (establish connection)
		await pingHealth();

		// Periodically send /health to keep connection alive
		setInterval(pingHealth, 30 * 1000);
		sendBatch().catch(console.error);
})();

```

{% endcode %}
{% endtab %}

{% tab title="gRPC" %}
{% code overflow="wrap" %}

```javascript
const web3 = require("@solana/web3.js");
const bs58 = require("bs58");

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const PROTO_PATH = __dirname + '/server.proto';
const packageDefinition = protoLoader.loadSync(
		PROTO_PATH,
		{
				keepCase: true,
				longs: String,
				enums: String,
				defaults: true,
				oneofs: true
		}
);

const serverProto = grpc.loadPackageDefinition(packageDefinition).serverpb;

// BlockRazor relay endpoint address
const blzRelayEndpoint = "frankfurt.solana-grpc.blockrazor.xyz:80";
// replace your solana rpc endpoint
const mainNetRPC = "";
// replace your authKey
const authKey = "";
// relace your private key(base58)
const privateKey = "";

// tip amount
const tipAmount = 1000000;

const tipAccounts = [
		"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
		"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
		"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
		"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
		"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
		"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
		"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
		"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
		"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
		"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
		"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
		"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
		"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
		"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

const client = new serverProto.Server(
		blzRelayEndpoint,
		grpc.credentials.createInsecure()
);

var meta = new grpc.Metadata();

function getRandomAccount() {
		const randomIndex = Math.floor(Math.random() * tipAccounts.length);
		return tipAccounts[randomIndex];
}

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
		client.getHealth({}, meta, (err, response) => {
				if (err) {
						console.error('[get health] error:', err);
						return;
				}

				console.log('[get health] response:', response);
		});
}

(async () => {
		meta.add('apikey', authKey);

		// Initial health check (establish connection)
		await pingHealth();

		// Periodically send /health to keep connection alive
		setInterval(pingHealth, 30 * 1000);

		const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
		const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);

		const tipAccount = getRandomAccount();
		const recipientPublicKey = new web3.PublicKey(tipAccount);

		const transaction = new web3.Transaction().add(
				web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: recipientPublicKey,
						lamports: tipAmount,
				})
		);

		const connection = new web3.Connection(mainNetRPC);

		transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
		transaction.feePayer = senderKeypair.publicKey;
		transaction.sign(senderKeypair);

		const serializedTransaction = transaction.serialize();
		const base64Tx = serializedTransaction.toString('base64');

		client.SendBatch({ transactions: [base64Tx], mode: "fast" }, meta, (err, response) => {
				if (err) {
						console.error('[send batch] error:', err);
						return;
				}

				console.log('[send batch] response:', response);
		});
})();
```

{% endcode %}
{% endtab %}

{% tab title="Proto" %}
{% code overflow="wrap" %}

```javascript
syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";

service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc SendBinaryTransaction(SendBinaryRequest) returns(SendResponse) {};

    rpc SendBatch(SendBatchRequest) returns(SendBatchResponse) {};

    rpc SendBundle(SendBundleRequest) returns(SendBundleResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBinaryRequest {
    bytes binaryTransaction = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendResponse {
    string signature = 1;
}

message SendBundleRequest {
    repeated string transactions = 1;
}

message SendBundleResponse {
    string signature = 1;
}

message SendBatchRequest {
    repeated string transactions = 1;
    string mode = 2;
    int32 safeWindow = 3;
    bool revertProtection = 4;
}

message SendBatchResponse {
    repeated BatchResponse result = 1;
}

message BatchResponse {
    string signature = 1;
    string error = 2;
}

message HealthRequest {
}

message HealthResponse {
    string status = 1;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Response Example

Normal

```json
{"signature":"$first_tx_signature","error":""}
```

Abnormal

{% code overflow="wrap" %}

```json
{"signature":"","error":"error: Invalid authentication credentials. Please ensure your auth token is correct and try again"}
```

{% endcode %}


# Solana Send Batch in Binary

Introducing the integration method of 'Send Batch in Binary' in BlockRazor Solana Transaction Sending mode.

`Send in Binary` is a batch sending interface provided by BlockRazor for Solana, used to send signed transactions to the blockchain in batch form with low latency. A maximum of 25 transactions can be sent in a single batch.

Compared to `Send Batch`, `Send in Binary` supports sending batch transactions in binary format. A transaction is a raw binary stream.

{% code overflow="wrap" %}

```
[ 2 bytes: tx length (big-endian u16) ][ N bytes: bincode-serialized transaction ]
```

{% endcode %}

Batch transactions are consecutive without separators.

{% code overflow="wrap" %}

```
+--------+----------+--------+----------+-----+
| len_0  |   tx_0   | len_1  |   tx_1   | ... |
| u16 BE | bincode  | u16 BE | bincode  |     |
+--------+----------+--------+----------+-----+
```

{% endcode %}

### Endpoint <a href="#xian-liu" id="xian-liu"></a>

* `POST v2/sendBinaryBatch`

### Request Parameter

<table><thead><tr><th width="120.8515625">Parameters</th><th width="115.421875">Mandatory</th><th width="109.98046875">Example</th><th>Description</th></tr></thead><tbody><tr><td>transactions</td><td>Mandatory</td><td>binary</td><td>signed transactions in binary format</td></tr><tr><td>mode</td><td>Optional</td><td>"fast"<br>"sandwichMitigation"</td><td>BlockRazor offers two modes: Fast and SandwichMitigation, with Fast as the default.<br><br>In fast mode, transactions are sent based on globally distributed high-performance network and high-quality SWQoS, reaching the Leader node with the lowest latency.<br><br>In sandwichMitigation mode, BlockRazoz will route transactions to the trusted SWQoS and skip the slot of the blacklisted Leader (dynamically identified by the BlockRazor sandwich monitoring mechanism). In this mode, <strong>DO NOT</strong> send transactions using durable nonce, as it will cause the sandwich protection to become ineffective.</td></tr><tr><td>safeWindow</td><td>Optional</td><td>3</td><td>safeWindow is used to determine the timing of transaction sending in sandwichMitigation mode and represents the number of consecutive slots of  whitelist validators. For example, if it is set to 3, the transaction will only be sent when 3 consecutive slots from the current slot belong to whitelist validators.<br><br>The range of safeWindow is 3-13. The larger the number, the better the effect of mitigating the sandwich attack, but it may have a certain impact on the rate of inclusion. If not set, the default is 3.</td></tr><tr><td>revertProtection</td><td>Optional</td><td>false</td><td>The default value is false. If set to true, the transaction will not fail on chain, but the speed of inclusion will be affected and there is a possibility that it cannot be included. Please choose to enable it carefully according to actual needs.</td></tr></tbody></table>

### Request Example <a href="#jiao-yi-gou-jian-dai-ma-shi-li" id="jiao-yi-gou-jian-dai-ma-shi-li"></a>

{% hint style="info" %}
**Note:**

* **the `auth` and `request parameter`  are compulsory to be added in URI params, e.g.,** `http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryBatch?auth=<auth_token>&mode=fast&revertProtection=true`
* **the only header permitted in the request is** `Content-Type: application/octet-stream`
  {% endhint %}

{% tabs %}
{% tab title="Go" %}
{% code overflow="wrap" %}

```go
package main

import (
	"bytes"
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)

const (
	httpBinaryEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryBatch"
	healthEndpoint     = "http://frankfurt.solana.blockrazor.xyz:443/health"
	mainNetRPC         = ""
	authKey            = ""
	privateKey         = ""
	publicKey          = ""
	amount             = 200_000
	tipAmount          = 1_000_000
	mode               = "fast"
	safeWindow         = 5
	revertProtection   = false
)

var tipAccounts = []string{
	"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
	"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
	"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
	"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
	"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
	"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
	"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
	"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
	"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
	"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
	"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
	"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
	"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
	"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
}

var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}

// BatchResponse .
type BatchResponse struct {
	Result []BatchResponseItem `json:"result"`
	Error  string              `json:"error"`
}

// BatchResponseItem .
type BatchResponseItem struct {
	Signature string `json:"signature"`
	Error     string `json:"error"`
}

type HealthResponse struct {
	Result string `json:"result"`
}

func main() {
	// Pre-warm: perform an initial health check to establish the HTTP connection
	err := pingHealth()
	if err != nil {
		fmt.Printf("health check failed: %v\n", err)
	}
	// Start a background goroutine to periodically send /health requests
	// For low-frequency users, this keeps the HTTP connection alive (warm)
	go func() {
		for {
			err := pingHealth()
			if err != nil {
				fmt.Printf("health check failed: %v\n", err)
			}
			time.Sleep(30 * time.Second)
		}
	}()
	// send binary batch
	if err := sendBinaryBatch(); err != nil {
		fmt.Printf("send binary batch failed: %v\n", err)
	}
}

func pingHealth() error {
	req, err := http.NewRequest("GET", healthEndpoint, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("apikey", authKey)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// ⚠️ Important Note:
	// According to the Go net/http documentation, in order for the underlying TCP connection
	// to be reused (i.e. kept alive), the response body must be fully read and closed.
	// Otherwise, the Transport may not reuse the connection for future requests.
	// Reference: https://pkg.go.dev/net/http#Response
	// > "The default HTTP client's Transport may not reuse HTTP/1.x 'keep-alive' TCP connections
	//    if the Body is not read to completion and closed."

	// Read the full response body to enable connection reuse
	bodyBytes, _ := io.ReadAll(resp.Body)
	var healthRes HealthResponse
	if err := json.Unmarshal(bodyBytes, &healthRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}

	return nil
}

func sendBinaryBatch() error {
	account, err := solana.WalletFromPrivateKeyBase58(privateKey)
	if err != nil {
		return err
	}
	receivePub := solana.MustPublicKeyFromBase58(publicKey)
	tipPub := solana.MustPublicKeyFromBase58(tipAccounts[rand.Intn(len(tipAccounts))])

	rpcClient := rpc.New(mainNetRPC)
	blockhash, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("[get blockhash] %v", err)
	}

	transferIx := system.NewTransferInstruction(amount, account.PublicKey(), receivePub).Build()
	tipIx := system.NewTransferInstruction(tipAmount, account.PublicKey(), tipPub).Build()

	tx, err := solana.NewTransaction(
		[]solana.Instruction{tipIx, transferIx},
		blockhash.Value.Blockhash,
		solana.TransactionPayer(account.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("build tx error: %v", err)
	}

	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if account.PublicKey().Equals(key) {
			return &account.PrivateKey
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("sign tx error: %v", err)
	}

	txBin, err := tx.MarshalBinary()
	if err != nil {
		return err
	}

	txBinBody, err := buildBinaryBody([][]byte{txBin})
	if err != nil {
		return err
	}

	u, err := url.Parse(httpBinaryEndpoint)
	if err != nil {
		return err
	}
	q := u.Query()
	q.Set("mode", mode)
	q.Set("safeWindow", strconv.Itoa(safeWindow))
	q.Set("revertProtection", strconv.FormatBool(revertProtection))
	q.Set("auth", authKey)
	u.RawQuery = q.Encode()

	httpReq, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(txBinBody))
	if err != nil {
		return err
	}
	httpReq.Header.Set("Content-Type", "application/octet-stream")
	resp, err := httpClient.Do(httpReq)
	if err != nil {
		return fmt.Errorf("send http error: %v", err)
	}
	defer resp.Body.Close()

	bodyBytes, _ := io.ReadAll(resp.Body)
	var sendRes BatchResponse
	if err := json.Unmarshal(bodyBytes, &sendRes); err != nil {
		return fmt.Errorf("decode error: %v", err)
	}
	fmt.Printf("[send batch] response: %+v\n", sendRes)
	return nil
}

func buildBinaryBody(binaryTransactions [][]byte) ([]byte, error) {
	var body bytes.Buffer

	for i, txBytes := range binaryTransactions {
		if len(txBytes) > 65535 {
			return nil, fmt.Errorf("tx[%d] too large: %d bytes", i, len(txBytes))
		}

		txLen := uint16(len(txBytes))

		if err := binary.Write(&body, binary.BigEndian, txLen); err != nil {
			return nil, fmt.Errorf("write tx[%d] length failed: %w", i, err)
		}

		if _, err := body.Write(txBytes); err != nil {
			return nil, fmt.Errorf("write tx[%d] bytes failed: %w", i, err)
		}
	}

	return body.Bytes(), nil
}
```

{% endcode %}
{% endtab %}

{% tab title="Rust" %}
{% code overflow="wrap" %}

```rust
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use solana_system_interface::instruction::transfer;
use std::str::FromStr;
use std::time::Duration;
use tokio::time::sleep;


#[derive(Deserialize, Debug)]
struct SendBatchResponse {
    result: Vec<BatchResponseItem>,
    error: String,
}

#[derive(Deserialize, Debug)]
struct BatchResponseItem {
    signature: String,
    error: String,
}

#[derive(Deserialize, Debug)]
struct HealthResponse {
    result: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configuration values
    let http_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryBatch";
    let health_endpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
    let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key
    let privatekey ="";
    // relace your target public key
    let publickey = "";
    // transaction amount
    let amount: u64 = 200_000;
    // tip amount
    let tipamount: u64 = 1_000_000;
    // safe window
    let safe_window: u32 = 5;
    // revert protection
    let revert_protection = false;
    // send mode
    let mode = "fast";

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
        "68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
        "4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
        "B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
        "5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
        "5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
        "295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
        "EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
        "BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
        "Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
        "AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
    ];
    // Create a shared HTTP client with keep-alive support
    let client = reqwest::Client::new();

    // Perform initial health check to warm up the connection
    ping_health(&client, health_endpoint, authkey).await?;

    // Start background health pinger to keep the connection alive
    let health_endpoint_clone = health_endpoint.to_string();
    let authkey_clone = authkey.to_string();
    let client_clone = client.clone();
    tokio::spawn(async move {
        loop {
            if let Err(e) = ping_health(&client_clone, &health_endpoint_clone, &authkey_clone).await
            {
                eprintln!("Health check failed: {:?}", e);
            }
            sleep(Duration::from_secs(30)).await;
        }
    });

    // Send Solana Batch
    send_batch(
        &client,
        mainnetrpc,
        authkey,
        privatekey,
        publickey,
        &tip_accounts,
        tipamount,
        amount,
        mode,
        safe_window,
        revert_protection,
        http_endpoint,
    )
    .await?;

    Ok(())
}

/// Perform GET /health to keep HTTP connection alive
async fn ping_health(
    client: &reqwest::Client,
    health_endpoint: &str,
    authkey: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut headers = HeaderMap::new();
    headers.insert("apikey", HeaderValue::from_str(authkey)?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let res = client.get(health_endpoint).headers(headers).send().await?;

    let body = res.text().await?;
    let parsed: Result<HealthResponse, _> = serde_json::from_str(&body);
    if let Ok(hr) = parsed {
        println!("Health result: {}", hr.result);
    } else {
        return Err("Failed to parse health response".into());
    }
    Ok(())
}

fn build_binary_body(transactions: &[Transaction]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let mut body = Vec::new();

    for tx in transactions {
        let tx_bytes = bincode::serialize(tx)?;

        if tx_bytes.len() > u16::MAX as usize {
            return Err(format!("transaction too large: {} bytes", tx_bytes.len()).into());
        }

        let len = tx_bytes.len() as u16;
        body.extend_from_slice(&len.to_be_bytes());
        body.extend_from_slice(&tx_bytes);
    }

    Ok(body)
}

/// Build and send base64-encoded transactions via HTTP POST
async fn send_batch(
    client: &reqwest::Client,
    mainnetrpc: &str,
    authkey: &str,
    privatekey: &str,
    publickey: &str,
    tip_accounts: &[&str],
    tipamount: u64,
    amount: u64,
    mode: &str,
    safe_window: u32,
    revert_protection: bool,
    http_endpoint: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let from = Keypair::from_base58_string(privatekey);
    let receiver = Pubkey::from_str(publickey)?;
    let tip = Pubkey::from_str(tip_accounts[rand::thread_rng().gen_range(0..tip_accounts.len())])?;

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");

    let ix_tip = transfer(&from.pubkey(), &tip, tipamount);
    let ix_main = transfer(&from.pubkey(), &receiver, amount);

    let tx = Transaction::new_signed_with_payer(
        &[ix_tip, ix_main],
        Some(&from.pubkey()),
        &[&from],
        recent_blockhash,
    );

    let transactions = vec![tx];
    let binary_body = build_binary_body(&transactions)?;

    let request_url = format!(
        "{}?auth={}&mode={}&safeWindow={}&revertProtection={}",
        http_endpoint,
        authkey,
        mode,
        safe_window,
        revert_protection
    );

    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/octet-stream"));

    let res = client
        .post(&request_url)
        .headers(headers)
        .body(binary_body)
        .send()
        .await?;

    let text = res.text().await?;
    let parsed: Result<SendBatchResponse, _> = serde_json::from_str(&text);
    match parsed {
        Ok(r) => {
            println!("Result: {:?}", r.result); // use field
        }
        Err(_) => println!("RAW RESPONSE: {}", text),
    }

    Ok(())
}
```

{% endcode %}
{% endtab %}

{% tab title="JS" %}
{% code overflow="wrap" %}

```javascript
const axios = require('axios');
const web3 = require('@solana/web3.js');
const bs58 = require('bs58');

// ------------------ Configuration Constants ------------------
// BlockRazor relay endpoint address
const httpEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/v2/sendBinaryBatch";
const healthEndpoint = "http://frankfurt.solana.blockrazor.xyz:443/health";
// Replace with your Solana RPC endpoint
const mainNetRPC = "";
// Replace with your authKey
const authKey = "";
// Replace with your private key (base58)
const privateKey = "";
// Replace with your target public key
const publicKey = "";
// Send mode
const mode = "fast";
// Safe window
const safeWindow = 5;
// Revert protection
const revertProtection = false;
// Transaction amount
const amount = 200_000;
// Tip amount
const tipAmount = 1000000;

const tipAccounts = [
		"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm",
		"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9",
		"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG",
		"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22",
		"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o",
		"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9",
		"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM",
		"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf",
		"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61",
		"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV",
		"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK",
		"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6",
		"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq",
		"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S",
];

// ------------------ Axios HTTP Client (Connection Reuse Enabled) ------------------
const httpClientHealth = axios.create({
		timeout: 10000,
		headers: {
				'Content-Type': 'application/octet-stream',
				'apikey': authKey,
		},
		httpAgent: new (require('http').Agent)({ keepAlive: true }),
		httpsAgent: new (require('https').Agent)({ keepAlive: true }),
});

const httpClient = axios.create({
		timeout: 10000,
		headers: {
				'Content-Type': 'application/octet-stream',
		},
		httpAgent: new (require('http').Agent)({ keepAlive: true }),
		httpsAgent: new (require('https').Agent)({ keepAlive: true }),
});

// ------------------ Periodic Health Ping to Keep Connection Alive ------------------
async function pingHealth() {
		try {
				const res = await httpClientHealth.get(healthEndpoint);
				console.log(`Health result:`, res.data);
		} catch (err) {
				console.error('Health check failed:', err.message);
		}
}

function buildBinaryBatchBody(serializedTxs) {
  const parts = [];

  for (let i = 0; i < serializedTxs.length; i++) {
    const txBytes = Buffer.from(serializedTxs[i]);

    if (txBytes.length > 65535) {
      throw new Error(`tx[${i}] too large: ${txBytes.length} bytes`);
    }

    const lenBuf = Buffer.alloc(2);
    lenBuf.writeUInt16BE(txBytes.length, 0);

    parts.push(lenBuf);
    parts.push(txBytes);
  }

  return Buffer.concat(parts);
}

// ------------------ Build and Send Binary Batch ------------------
async function sendBinaryBatch() {
		const senderPrivateKey = new Uint8Array(bs58.decode(privateKey));
		const senderKeypair = web3.Keypair.fromSecretKey(senderPrivateKey);
		const receiver = new web3.PublicKey(publicKey);
		const tipAccount = new web3.PublicKey(tipAccounts[Math.floor(Math.random() * tipAccounts.length)]);

		const connection = new web3.Connection(mainNetRPC);
		const { blockhash } = await connection.getLatestBlockhash('finalized');

		const tx = new web3.Transaction()
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: tipAccount,
						lamports: tipAmount,
				}))
				.add(web3.SystemProgram.transfer({
						fromPubkey: senderKeypair.publicKey,
						toPubkey: receiver,
						lamports: amount,
				}));

		tx.recentBlockhash = blockhash;
		tx.feePayer = senderKeypair.publicKey;
		tx.sign(senderKeypair);

		const serialized = tx.serialize();

		const serializedTxs = [];
		serializedTxs.push(serialized);
		const binaryBody = buildBinaryBatchBody(serializedTxs);

		const url = new URL(httpEndpoint);
		url.searchParams.set("auth", authKey);
		url.searchParams.set("mode", mode);
		url.searchParams.set("safeWindow", safeWindow);
		url.searchParams.set("revertProtection", revertProtection);

		const fullEndpoint = url.toString();
		
		try {
				const res = await httpClient.post(fullEndpoint, binaryBody);
				console.log('[send binary batch] response:', res.data);
		} catch (err) {
				console.error('sendBinaryBatch failed:', err.response?.data || err.message);
		}
}

// ------------------ Main Entry ------------------
(async () => {
		// Initial health check (establish connection)
		await pingHealth();

		// Periodically send /health to keep connection alive
		setInterval(pingHealth, 30 * 1000);
		sendBinaryBatch().catch(console.error);
})();
```

{% endcode %}
{% endtab %}
{% endtabs %}




---

[Next Page](/llms-full.txt/1)

