Nethereum.JsonRpc.RpcClient
NuGet:
Nethereum.JsonRpc.RpcClient| Source:src/Nethereum.JsonRpc.RpcClient/
Nethereum.JsonRpc.RpcClient
Production-ready HTTP/HTTPS JSON-RPC client for Ethereum node communication.
Overview
Nethereum.JsonRpc.RpcClient provides the standard HTTP/HTTPS transport implementation for communicating with Ethereum nodes via JSON-RPC. This is the most commonly used RPC client in Nethereum, offering robust connection management, automatic retries, authentication support, and production-tested reliability.
Key Features:
- HTTP/HTTPS transport with HttpClient
- Connection pooling and lifecycle management
- Basic authentication support (username/password)
- Configurable timeouts
- Automatic HttpClient rotation (for older .NET versions)
- Thread-safe concurrent requests
- Production-tested reliability
- Support for custom HttpClientHandler
Use Cases:
- Connecting to Ethereum nodes (Geth, Erigon, Besu, Nethermind)
- Querying blockchain data
- Sending transactions
- Contract interactions
- Load balancer/proxy integration
- Production dApp backends
Installation
dotnet add package Nethereum.JsonRpc.RpcClient
This is typically the default RPC client used by Nethereum.Web3:
dotnet add package Nethereum.Web3
Dependencies
Nethereum:
- Nethereum.JsonRpc.Client - Core RPC abstraction (which provides JSON serialization support via Newtonsoft.Json or System.Text.Json)
Framework:
- System.Net.Http - HTTP/HTTPS communication (built-in .NET library)
Quick Start
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth;
// Connect to local node
var client = new RpcClient(new Uri("http://localhost:8545"));
// Use with RPC services
var ethBlockNumber = new EthBlockNumber(client);
var blockNumber = await ethBlockNumber.SendRequestAsync();
Console.WriteLine($"Current block: {blockNumber.Value}");
Usage Examples
Example 1: Basic Connection to Ethereum Node
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth;
using Nethereum.Hex.HexTypes;
// Local Geth/Erigon/Besu node
var client = new RpcClient(new Uri("http://localhost:8545"));
// Infura
var infuraClient = new RpcClient(
new Uri("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
);
// Alchemy
var alchemyClient = new RpcClient(
new Uri("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY")
);
// Use with any RPC service
var ethChainId = new EthChainId(client);
HexBigInteger chainId = await ethChainId.SendRequestAsync();
Console.WriteLine($"Chain ID: {chainId.Value}");
// Output: Chain ID: 1 (mainnet)
Example 2: HTTP Basic Authentication
using Nethereum.JsonRpc.Client;
using System.Net.Http.Headers;
using System.Text;
// Option 1: URL-based authentication (simplest)
var client = new RpcClient(
new Uri("http://username:password@localhost:8545")
);
// Option 2: Explicit AuthenticationHeaderValue
var credentials = Convert.ToBase64String(
Encoding.UTF8.GetBytes("admin:secretpassword")
);
var authHeader = new AuthenticationHeaderValue("Basic", credentials);
var authenticatedClient = new RpcClient(
new Uri("http://localhost:8545"),
authHeaderValue: authHeader
);
// Use authenticated client
var ethAccounts = new EthAccounts(authenticatedClient);
var accounts = await ethAccounts.SendRequestAsync();
Console.WriteLine($"Accounts: {string.Join(", ", accounts)}");
Example 3: Custom Connection Timeout
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth;
var client = new RpcClient(new Uri("http://localhost:8545"));
// Default timeout is 120 seconds (2 minutes)
Console.WriteLine($"Default timeout: {client.ConnectionTimeout.TotalSeconds}s");
// Set custom timeout for slow networks
client.ConnectionTimeout = TimeSpan.FromSeconds(30);
try
{
var ethGasPrice = new EthGasPrice(client);
var gasPrice = await ethGasPrice.SendRequestAsync();
Console.WriteLine($"Gas price: {gasPrice.Value} wei");
}
catch (RpcClientTimeoutException ex)
{
Console.WriteLine($"Request timed out after 30 seconds: {ex.Message}");
}
Example 4: Using Custom HttpClient for Advanced Configuration
using Nethereum.JsonRpc.Client;
using System.Net.Http;
// Create custom HttpClient with specific settings
var httpClient = new HttpClient(new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(15),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(10),
MaxConnectionsPerServer = 50,
EnableMultipleHttp2Connections = true
})
{
Timeout = TimeSpan.FromSeconds(60)
};
// Create RpcClient with custom HttpClient
var client = new RpcClient(
new Uri("http://localhost:8545"),
httpClient: httpClient
);
var ethBlockNumber = new EthBlockNumber(client);
var blockNumber = await ethBlockNumber.SendRequestAsync();
Console.WriteLine($"Block: {blockNumber.Value}");
Example 5: Custom HttpClientHandler for Proxy Support
using Nethereum.JsonRpc.Client;
using System.Net;
using System.Net.Http;
// Configure proxy
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://proxy.example.com:8080")
{
Credentials = new NetworkCredential("proxyuser", "proxypass")
},
UseProxy = true,
MaxConnectionsPerServer = 20
};
// Create client with proxy handler
var client = new RpcClient(
new Uri("http://localhost:8545"),
httpClientHandler: handler
);
var ethChainId = new EthChainId(client);
var chainId = await ethChainId.SendRequestAsync();
Console.WriteLine($"Chain ID (via proxy): {chainId.Value}");
Example 6: Multiple Concurrent Requests (Thread Safety)
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth.DTOs;
using System.Threading.Tasks;
var client = new RpcClient(new Uri("http://localhost:8545"));
// RpcClient is thread-safe - can handle concurrent requests
var tasks = new List<Task>
{
Task.Run(async () =>
{
var ethBlockNumber = new EthBlockNumber(client);
var block = await ethBlockNumber.SendRequestAsync();
Console.WriteLine($"Task 1 - Block: {block.Value}");
}),
Task.Run(async () =>
{
var ethGasPrice = new EthGasPrice(client);
var gasPrice = await ethGasPrice.SendRequestAsync();
Console.WriteLine($"Task 2 - Gas Price: {gasPrice.Value}");
}),
Task.Run(async () =>
{
var ethChainId = new EthChainId(client);
var chainId = await ethChainId.SendRequestAsync();
Console.WriteLine($"Task 3 - Chain ID: {chainId.Value}");
})
};
await Task.WhenAll(tasks);
Console.WriteLine("All requests completed successfully");
Example 7: Error Handling and Retry Logic
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth;
using Polly;
var client = new RpcClient(new Uri("http://localhost:8545"));
client.ConnectionTimeout = TimeSpan.FromSeconds(10);
// Define retry policy with Polly
var retryPolicy = Policy
.Handle<RpcClientTimeoutException>()
.Or<RpcClientUnknownException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
onRetry: (exception, timeSpan, retryCount, context) =>
{
Console.WriteLine($"Retry {retryCount} after {timeSpan.TotalSeconds}s due to: {exception.Message}");
}
);
try
{
var blockNumber = await retryPolicy.ExecuteAsync(async () =>
{
var ethBlockNumber = new EthBlockNumber(client);
return await ethBlockNumber.SendRequestAsync();
});
Console.WriteLine($"Success! Block: {blockNumber.Value}");
}
catch (RpcResponseException ex)
{
Console.WriteLine($"RPC Error {ex.RpcError.Code}: {ex.RpcError.Message}");
}
catch (RpcClientTimeoutException ex)
{
Console.WriteLine($"Timeout after retries: {ex.Message}");
}
catch (RpcClientUnknownException ex)
{
Console.WriteLine($"Network error: {ex.Message}");
}
Example 8: Load Balancing Across Multiple Nodes
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Eth;
public class LoadBalancedRpcClient
{
private readonly List<RpcClient> _clients;
private int _currentIndex = 0;
private readonly object _lock = new object();
public LoadBalancedRpcClient(params string[] nodeUrls)
{
_clients = nodeUrls.Select(url => new RpcClient(new Uri(url))).ToList();
}
public RpcClient GetNextClient()
{
lock (_lock)
{
var client = _clients[_currentIndex];
_currentIndex = (_currentIndex + 1) % _clients.Count;
return client;
}
}
}
// Usage
var loadBalancer = new LoadBalancedRpcClient(
"http://node1.example.com:8545",
"http://node2.example.com:8545",
"http://node3.example.com:8545"
);
// Round-robin requests
for (int i = 0; i < 10; i++)
{
var client = loadBalancer.GetNextClient();
var ethBlockNumber = new EthBlockNumber(client);
var block = await ethBlockNumber.SendRequestAsync();
Console.WriteLine($"Request {i + 1} - Block: {block.Value}");
}
Example 9: Using with Nethereum.Web3
using Nethereum.Web3;
using Nethereum.JsonRpc.Client;
// Option 1: Web3 creates RpcClient internally (simplest)
var web3 = new Web3("https://mainnet.infura.io/v3/YOUR_PROJECT_ID");
// Option 2: Create custom RpcClient first
var client = new RpcClient(new Uri("http://localhost:8545"));
client.ConnectionTimeout = TimeSpan.FromSeconds(60);
var web3WithCustomClient = new Web3(client);
// Use Web3 normally
var balance = await web3WithCustomClient.Eth.GetBalance.SendRequestAsync("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb");
Console.WriteLine($"Balance: {Web3.Convert.FromWei(balance)} ETH");
var blockNumber = await web3WithCustomClient.Eth.Blocks.GetBlockNumber.SendRequestAsync();
Console.WriteLine($"Block: {blockNumber.Value}");
API Reference
RpcClient Constructor Overloads
// Basic constructor
public RpcClient(Uri baseUrl,
AuthenticationHeaderValue authHeaderValue = null,
JsonSerializerSettings jsonSerializerSettings = null,
HttpClientHandler httpClientHandler = null,
ILogger log = null)
// With custom HttpClient
public RpcClient(Uri baseUrl,
HttpClient httpClient,
AuthenticationHeaderValue authHeaderValue = null,
JsonSerializerSettings jsonSerializerSettings = null,
ILogger log = null)
Properties
public static int MaximumConnectionsPerServer { get; set; } = 20;
public TimeSpan ConnectionTimeout { get; set; } // Default: 120 seconds
public RequestInterceptor OverridingRequestInterceptor { get; set; }
Key Methods (Inherited from ClientBase)
Task<T> SendRequestAsync<T>(RpcRequest request, string route = null);
Task<T> SendRequestAsync<T>(string method, string route = null, params object[] paramList);
Task<RpcRequestResponseBatch> SendBatchRequestAsync(RpcRequestResponseBatch batch);
Task<RpcResponseMessage> SendAsync(RpcRequestMessage request, string route = null);
Important Notes
Connection Management
HttpClient Rotation:
- On older .NET Framework, RpcClient automatically rotates HttpClient instances every 60 seconds
- On .NET Core 2.1+, uses SocketsHttpHandler with connection pooling
- Connection lifetime: 10 minutes
- Idle timeout: 5 minutes
- Max connections per server: 20 (configurable via
MaximumConnectionsPerServer)
Best Practices:
- Reuse RpcClient instances (don't create per request)
- Set appropriate timeouts based on network conditions
- Use connection pooling for high-traffic applications
Thread Safety
- Thread-safe after initialization
- Safe to call from multiple threads concurrently
- Connection pooling handles concurrent requests efficiently
- Lock-free for read operations
Performance
| Operation | Latency | Notes |
|---|---|---|
| Local node | 1-10ms | Localhost Geth/Erigon |
| Cloud provider | 50-200ms | Infura, Alchemy, QuickNode |
| Slow network | 200-500ms | High latency regions |
Optimization Tips:
- Enable HTTP/2 with
EnableMultipleHttp2Connections - Use batch requests for multiple calls
- Tune
MaxConnectionsPerServerfor high throughput - Consider WebSocket client for subscriptions
Error Handling
| Exception | Cause | Retry? |
|---|---|---|
| RpcClientTimeoutException | Request exceeded ConnectionTimeout | Yes (with backoff) |
| RpcClientUnknownException | Network/HTTP errors | Yes (transient) |
| RpcResponseException | JSON-RPC error from node | Depends on error code |
| HttpRequestException | DNS, connection failures | Yes (with backoff) |
Authentication
Supports HTTP Basic Authentication:
- URL-based:
http://user:pass@localhost:8545 - Header-based:
AuthenticationHeaderValue("Basic", base64Credentials) - Automatically extracted from URI if present
JSON Serialization
Uses Newtonsoft.Json with default settings optimized for Ethereum:
- Proper handling of
0xhex prefixes - BigInteger serialization
- Block/transaction DTOs
For System.Text.Json, use Nethereum.JsonRpc.SystemTextJsonRpcClient instead.
Logging
Supports Microsoft.Extensions.Logging:
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<RpcClient>();
var client = new RpcClient(
new Uri("http://localhost:8545"),
log: logger
);
Logs include:
- Request JSON payloads
- Response JSON payloads
- Exception details
- Performance metrics
Related Packages
Alternative Transports
- Nethereum.JsonRpc.SystemTextJsonRpcClient - HTTP with System.Text.Json
- Nethereum.JsonRpc.WebSocketClient - WebSocket transport
- Nethereum.JsonRpc.IpcClient - IPC transport (Unix sockets, named pipes)
Higher-Level APIs
- Nethereum.Web3 - Complete Web3 API (uses RpcClient internally)
- Nethereum.RPC - Typed RPC services
Core Dependencies
- Nethereum.JsonRpc.Client - Abstraction layer
Common Ethereum Node Providers
| Provider | URL Format | Notes |
|---|---|---|
| Infura | https://mainnet.infura.io/v3/PROJECT_ID | Free tier available |
| Alchemy | https://eth-mainnet.g.alchemy.com/v2/API_KEY | Enhanced APIs |
| QuickNode | https://your-endpoint.quiknode.pro/token/ | Global network |
| Local Geth | http://localhost:8545 | Full node |
| Local Erigon | http://localhost:8545 | Archive node |