HTTP Provider
Basic Usage
import sh.brane.rpc.Brane;
// Simple read-only client
Brane client = Brane.connect("https://eth.llamarpc.com");
var block = client.getLatestBlock();
System.out.println("Block #" + block.number());For more control, use the builder:
import sh.brane.rpc.Brane;
import sh.brane.core.chain.ChainProfiles;
Brane client = Brane.builder()
.rpcUrl("https://eth.llamarpc.com")
.chain(ChainProfiles.MAINNET)
.retries(5)
.build();Why Loom (Virtual Threads)?
Brane's HTTP provider is built from the ground up for Java 21's virtual threads:
// Under the hood, Brane uses virtual threads
// Each HTTP request runs on a lightweight virtual thread
// that parks efficiently during I/O wait
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
// 10,000 concurrent requests? No problem.
for (int i = 0; i < 10_000; i++) {
exec.submit(() -> client.getLatestBlock());
}
}Virtual Threads vs Platform Threads
| Aspect | Platform Threads | Virtual Threads (Loom) |
|---|---|---|
| Memory per thread | ~1MB stack | ~1KB |
| Max concurrent | Hundreds to low thousands | Millions |
| I/O blocking | Ties up OS thread | Parks cheaply |
| Best for | CPU-bound work | I/O-bound work |
Builder Options
Brane client = Brane.builder()
.rpcUrl("https://eth.llamarpc.com") // HTTP/HTTPS RPC endpoint
.chain(ChainProfiles.MAINNET) // Network-specific config
.retries(5) // Retry transient failures
.build();The builder provides a clean fluent API for configuration:
| Option | Description |
|---|---|
rpcUrl(String) | HTTP/HTTPS RPC endpoint URL (required unless using provider()) |
chain(ChainProfile) | Network-specific configuration (EIP-1559 support, etc.) |
retries(int) | Max retry attempts for transient failures (default: 3) |
retryConfig(RpcRetryConfig) | Custom backoff timing configuration |
Raw JSON-RPC Access
For low-level access to any Ethereum RPC method, use the provider directly:
import sh.brane.rpc.BraneProvider;
import sh.brane.rpc.JsonRpcResponse;
import java.util.List;
BraneProvider provider = BraneProvider.http("https://eth.llamarpc.com");
// Any standard JSON-RPC method
JsonRpcResponse response = provider.send("eth_chainId", List.of());
System.out.println("Chain ID: " + response.result());
// With parameters
JsonRpcResponse balance = provider.send("eth_getBalance",
List.of("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest"));Parallel Requests
For maximum throughput, fire requests in parallel:
import sh.brane.rpc.BraneExecutors;
import java.util.concurrent.Future;
import java.util.ArrayList;
Brane client = Brane.connect("https://eth.llamarpc.com");
try (var exec = BraneExecutors.newIoBoundExecutor()) {
var futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futures.add(exec.submit(() -> client.getLatestBlock()));
}
for (var f : futures) {
f.get(); // Process results
}
}HTTP vs WebSocket
| Use Case | Recommended |
|---|---|
| Serverless / Lambda | HTTP |
| Mobile apps (battery) | HTTP |
| Simple scripts | HTTP |
| Low request volume (<100/s) | HTTP |
| Real-time subscriptions | WebSocket |
| High-frequency trading | WebSocket |
| Persistent connections | WebSocket |
Client Types
The builder creates different client types based on configuration:
import sh.brane.rpc.Brane;
import sh.brane.core.crypto.PrivateKeySigner;
import sh.brane.core.types.Address;
// Read-only client (Brane.Reader)
Brane.Reader reader = Brane.builder()
.rpcUrl("https://eth.llamarpc.com")
.buildReader();
var balance = reader.getBalance(new Address("0x..."));
// Signing client (Brane.Signer) - can send transactions
Brane.Signer signer = Brane.builder()
.rpcUrl("https://eth.llamarpc.com")
.signer(new PrivateKeySigner("0x..."))
.buildSigner();
Hash txHash = signer.sendTransaction(request);Error Handling
HTTP transport errors are wrapped in RpcException:
import sh.brane.core.error.RpcException;
try {
var block = client.getLatestBlock();
} catch (RpcException e) {
System.err.println("RPC Error: " + e.getMessage());
System.err.println("Error Code: " + e.code());
}When to Use HTTP
Use HTTP when:- You don't need real-time subscriptions
- Your application is serverless or short-lived
- You want simple, stateless connections
- Battery efficiency matters (mobile)
- You need
eth_subscribefor blocks/logs - You're building MEV or HFT systems
- You want persistent connections with lower per-request overhead