Package rpc implements the JSON-RPC 2.0 client transport layer. ethclient is built on top of it. Use the rpc package directly when you need to call methods that ethclient does not expose — for example, Geth-specific debug_*, admin_*, or txpool_* namespaces.
import "github.com/ethereum/go-ethereum/rpc"
Connecting
func Dial(rawurl string) (*Client, error)
func DialContext(ctx context.Context, rawurl string) (*Client, error)
func DialOptions(ctx context.Context, rawurl string, options ...ClientOption) (*Client, error)
Supported URL schemes:
| Scheme | Transport |
|---|
http / https | HTTP (request/response only; no subscriptions) |
ws / wss | WebSocket (supports subscriptions) |
| (no scheme / bare path) | Unix domain socket / Windows named pipe (IPC) |
// HTTP
client, err := rpc.Dial("http://localhost:8545")
// WebSocket
client, err := rpc.Dial("ws://localhost:8546")
// IPC
client, err := rpc.Dial("/var/run/geth.ipc")
Making a single call
// Call sends a request and unmarshals the result into `result`.
// Pass nil to discard the result.
func (c *Client) Call(result interface{}, method string, args ...interface{}) error
// CallContext is the same but respects context cancellation and deadlines.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
var blockNumber hexutil.Uint64
if err := client.Call(&blockNumber, "eth_blockNumber"); err != nil {
log.Fatal(err)
}
fmt.Println("block:", uint64(blockNumber))
result must be a pointer (or nil). Any value accepted by encoding/json.Unmarshal works.
Batch calls
Send multiple requests in a single round-trip:
// BatchElem is one element in a batch.
type BatchElem struct {
Method string
Args []interface{}
Result interface{} // must be a non-nil pointer
Error error // set by BatchCall if the server returned an error for this element
}
func (c *Client) BatchCall(b []BatchElem) error
func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error
var (
balance hexutil.Big
nonce hexutil.Uint64
)
batch := []rpc.BatchElem{
{
Method: "eth_getBalance",
Args: []interface{}{addr, "latest"},
Result: &balance,
},
{
Method: "eth_getTransactionCount",
Args: []interface{}{addr, "latest"},
Result: &nonce,
},
}
if err := client.BatchCall(batch); err != nil {
log.Fatal(err) // I/O-level error
}
// Check per-element errors
for _, elem := range batch {
if elem.Error != nil {
log.Printf("%s error: %v", elem.Method, elem.Error)
}
}
fmt.Println("balance:", balance.ToInt())
fmt.Println("nonce:", uint64(nonce))
BatchCall returns only transport-level errors. Application-level errors (e.g. method not found) are set on BatchElem.Error for each element individually.
Subscriptions
Subscriptions are only supported over WebSocket and IPC connections. Calling Subscribe on an HTTP client returns ErrNotificationsUnsupported.
// Subscribe calls "<namespace>_subscribe" with the given args.
// Notifications are delivered to channel.
func (c *Client) Subscribe(
ctx context.Context,
namespace string,
channel interface{},
args ...interface{},
) (*ClientSubscription, error)
// EthSubscribe is a convenience wrapper for the "eth" namespace.
func (c *Client) EthSubscribe(
ctx context.Context,
channel interface{},
args ...interface{},
) (*ClientSubscription, error)
headers := make(chan map[string]interface{})
sub, err := client.EthSubscribe(ctx, headers, "newHeads")
if err != nil {
log.Fatal(err)
}
defer sub.Unsubscribe()
for {
select {
case h := <-headers:
fmt.Println("new head:", h["number"])
case err := <-sub.Err():
log.Fatal(err)
}
}
ClientSubscription methods:
func (sub *ClientSubscription) Unsubscribe()
func (sub *ClientSubscription) Err() <-chan error
Checking available methods
func (c *Client) SupportedModules() (map[string]string, error)
Returns a map of "namespace" → "version" for all API namespaces the node exposes.
When to use rpc vs ethclient
| Use case | Package |
|---|
Standard eth_* methods with typed return values | ethclient |
Custom or Geth-specific namespaces (debug, admin, txpool, …) | rpc |
Batch multiple eth_* calls in one round-trip | rpc |
| Need access to the underlying transport | rpc (retrieve via ethclient.Client.Client()) |
You can also mix both: get an *ethclient.Client for normal usage and call client.Client() to retrieve the underlying *rpc.Client when you need to call an unlisted method.
Example: call a Geth admin method
package main
import (
"context"
"fmt"
"log"
"github.com/ethereum/go-ethereum/rpc"
)
func main() {
// IPC gives access to admin/debug/txpool namespaces
client, err := rpc.Dial("/var/run/geth.ipc")
if err != nil {
log.Fatal(err)
}
defer client.Close()
// admin_nodeInfo — not available in ethclient
var info map[string]interface{}
if err := client.CallContext(context.Background(), &info, "admin_nodeInfo"); err != nil {
log.Fatal(err)
}
fmt.Println("enode:", info["enode"])
// txpool_status
var status struct {
Pending string `json:"pending"`
Queued string `json:"queued"`
}
if err := client.Call(&status, "txpool_status"); err != nil {
log.Fatal(err)
}
fmt.Printf("pending: %s queued: %s\n", status.Pending, status.Queued)
}
Error types
var (
ErrClientQuit = errors.New("client is closed")
ErrNoResult = errors.New("JSON-RPC response has no result")
ErrMissingBatchResponse = errors.New("response batch did not contain a response to this call")
ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow")
)
The client also exposes the rpc.Error and rpc.DataError interfaces so callers can inspect JSON-RPC error codes and data fields. ethclient.RevertErrorData is a convenience wrapper that extracts the ABI-encoded revert reason from a call error.