Quickstart guide
This guide will walk you through building, running, and using reth-private-transaction to send your first private transaction.
Prerequisites
Before you begin, ensure you have:
- Rust toolchain (1.75 or later)
- Git
- At least 2TB of free disk space for a full Ethereum node
- A synced execution client (or prepare to sync one)
Running a Reth node requires significant disk space and bandwidth. Make sure you have adequate resources before proceeding.
Installation
Clone the repository
Clone the reth-private-transaction repository to your local machine:git clone https://github.com/Quertyy/reth-private-transaction.git
cd reth-private-transaction
Build the binary
Compile the project in release mode for optimal performance:This will create an optimized binary at ./target/release/reth-private-transaction.The first build may take 10-20 minutes depending on your system. Subsequent builds will be faster.
Prepare your JWT secret
Create a JWT secret for authenticated communication between execution and consensus clients:mkdir -p /data/mainnet/secrets
openssl rand -hex 32 > /data/mainnet/secrets/jwt.hex
Start the node
Launch your Reth node with the private transaction extension enabled:./target/release/reth-private-transaction node \
--datadir /data/mainnet/ \
--authrpc.jwtsecret /data/mainnet/secrets/jwt.hex \
--http --ws \
--http.addr 0.0.0.0 --ws.addr 0.0.0.0 \
--http.api txpool,web3,eth,debug,trace \
--ws.api txpool,web3,eth,debug,trace
The RPC method eth_sendPrivateRawTransaction will be automatically registered under the eth namespace.Look for the log message: Private transaction method added to confirm successful registration.
Send your first private transaction
Once your node is running, you can send private transactions using the new RPC method.
Using curl
Sign your transaction
First, create and sign a transaction using your preferred method (web3.js, ethers.js, etc.). You’ll need the raw signed transaction as a hex string.Example using ethers.js:const { Wallet, parseEther } = require('ethers');
const wallet = new Wallet('YOUR_PRIVATE_KEY');
const tx = await wallet.signTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
value: parseEther('0.01'),
gasLimit: 21000,
gasPrice: parseEther('0.00000002'), // 20 gwei
nonce: await wallet.getNonce(),
chainId: 1
});
console.log('Signed transaction:', tx);
Submit via RPC
Send the signed transaction to your Reth node using the private RPC method:curl -X POST http://localhost:8545 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_sendPrivateRawTransaction",
"params": ["0x..."],
"id": 1
}'
Replace 0x... with your actual signed transaction hex. Get your transaction hash
If at least one builder accepts your transaction, you’ll receive a response with your transaction hash:{
"jsonrpc": "2.0",
"id": 1,
"result": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}
Use this hash to track your transaction on Etherscan or via eth_getTransactionReceipt.
Using ethers.js
You can also integrate the private transaction method directly into your JavaScript application:
const { JsonRpcProvider, Wallet, parseEther } = require('ethers');
const provider = new JsonRpcProvider('http://localhost:8545');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);
async function sendPrivateTransaction() {
// Create and sign the transaction
const tx = await wallet.signTransaction({
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
value: parseEther('0.01'),
gasLimit: 21000,
gasPrice: parseEther('0.00000002'),
nonce: await wallet.getNonce(),
chainId: 1
});
// Send via private RPC method
const txHash = await provider.send('eth_sendPrivateRawTransaction', [tx]);
console.log('Transaction hash:', txHash);
// Wait for confirmation
const receipt = await provider.waitForTransaction(txHash);
console.log('Transaction confirmed in block:', receipt.blockNumber);
}
sendPrivateTransaction();
Understanding the response
Success response
When your transaction is successfully accepted by at least one builder:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}
The result field contains your transaction hash. Your transaction is now privately queued with one or more builders.
Error responses
All builders failed
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "All builders failed to send tx"
}
}
This means none of the builders accepted your transaction. Common causes:
- Invalid transaction signature
- Insufficient gas price
- Nonce issues
- Network connectivity problems
No builders available
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "No builders available"
}
}
The node couldn’t initialize connections to any builders. Check your network connectivity.
Behind the scenes
When you call eth_sendPrivateRawTransaction, here’s what happens:
- Transaction validation: Your raw transaction is decoded and validated (src/main.rs:103)
- Builder initialization: The node creates HTTP clients for all supported builders (src/lib.rs:52-63)
- Concurrent submission: Your transaction is sent to all builders in parallel (src/main.rs:86)
- Response handling: If at least one builder succeeds, your transaction hash is returned (src/main.rs:88-92)
The implementation uses Rust’s async runtime to send to multiple builders simultaneously:
async fn send_tx_to_builders(&self, tx: Bytes, builders: Vec<Builder>) -> RpcResult<()> {
let results = join_all(builders.iter().map(|builder| builder.send_tx(tx.clone()))).await;
if results.iter().all(|r| r.is_err()) {
return Err(PrivateTransactionError::AllBuildersFailed.into());
}
Ok(())
}
This ensures maximum redundancy and speed.
Next steps
Now that you’ve sent your first private transaction, you can:
- Monitor builder logs to see which builders accepted your transactions
- Integrate the RPC method into your dApp or trading bot
- Configure custom builder endpoints if needed
- Set up monitoring and alerting for your Reth node
Remember that private transactions still require competitive gas prices to be included by builders. Privacy doesn’t guarantee inclusion - it only guarantees protection from public mempool MEV.