> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leafage.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sending RPC requests

> Send JSON-RPC requests through nodex-proxy or directly to leafage-evm: endpoints, request headers, and copy-and-paste examples for eth_call, batch calls, blockCtx, gas estimation, simulation, and tracing.

Leafage exposes exactly one protocol: JSON-RPC 2.0 over HTTP. Any client that can send an HTTP POST can use it directly; no SDK is required. This page starts with the first request and works up to batch calls, block context, gas estimation, simulation, and tracing.

## Endpoints

| Scenario                | Endpoint                           | Notes                                                                           |
| ----------------------- | ---------------------------------- | ------------------------------------------------------------------------------- |
| Production              | `POST http://proxy:8663/{chainId}` | Routed by nodex-proxy to a suitable query node; the chain is chosen by the path |
| Development / debugging | `POST http://127.0.0.1:8659`       | Talks to a single leafage-evm directly, with no routing or fallback             |

`chainId` is decimal; ETH mainnet is `/1`. A hex form is normalized to decimal by the proxy.

The examples below use the production endpoint; drop the path when talking to a node directly.

## Request headers

| Header                                                        | Required    | Purpose                                                                       |
| ------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------- |
| `Content-Type: application/json`                              | Yes         | JSON-RPC request body                                                         |
| `client-id`                                                   | Recommended | Aggregation key for usage reporting; missing values are recorded as `unknown` |
| `x-dbk-biz`, `x-dbk-source`, `x-dbk-source-host`, `x-dbk-env` | No          | Source identification; appears in the proxy's logs and traces                 |
| `x-nodex-node-type: archive`                                  | No          | Forces routing to the Archive node pool                                       |

## First request: `eth_call`

Read `totalSupply()` of the USDT contract; the function selector is `0x18160ddd`:

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST http://proxy:8663/1 \
    -H 'Content-Type: application/json' \
    -H 'client-id: my-service' \
    -d '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "eth_call",
      "params": [
        { "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "data": "0x18160ddd" },
        "latest"
      ]
    }'
  ```

  ```python Python theme={null}
  import requests

  RPC_URL = "http://proxy:8663/1"


  def rpc(method, params):
      resp = requests.post(
          RPC_URL,
          json={"jsonrpc": "2.0", "id": 1, "method": method, "params": params},
          headers={"client-id": "my-service"},
          timeout=10,
      )
      resp.raise_for_status()
      body = resp.json()
      if "error" in body:
          raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
      return body["result"]


  total_supply = rpc(
      "eth_call",
      [{"to": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "data": "0x18160ddd"}, "latest"],
  )
  print(int(total_supply, 16))
  ```

  ```javascript Node.js theme={null}
  const RPC_URL = "http://proxy:8663/1";

  async function rpc(method, params) {
    const res = await fetch(RPC_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json", "client-id": "my-service" },
      body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
    });
    const body = await res.json();
    if (body.error) {
      throw new Error(`${body.error.code}: ${body.error.message}`);
    }
    return body.result;
  }

  const totalSupply = await rpc("eth_call", [
    { to: "0xdAC17F958D2ee523a2206206994597C13D831ec7", data: "0x18160ddd" },
    "latest",
  ]);
  console.log(BigInt(totalSupply));
  ```

  ```go Go theme={null}
  package main

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"net/http"
  )

  const rpcURL = "http://proxy:8663/1"

  type rpcResponse struct {
  	Result json.RawMessage `json:"result"`
  	Error  *struct {
  		Code    int    `json:"code"`
  		Message string `json:"message"`
  	} `json:"error"`
  }

  func call(method string, params ...any) (json.RawMessage, error) {
  	payload, err := json.Marshal(map[string]any{
  		"jsonrpc": "2.0", "id": 1, "method": method, "params": params,
  	})
  	if err != nil {
  		return nil, err
  	}
  	req, err := http.NewRequest(http.MethodPost, rpcURL, bytes.NewReader(payload))
  	if err != nil {
  		return nil, err
  	}
  	req.Header.Set("Content-Type", "application/json")
  	req.Header.Set("client-id", "my-service")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return nil, err
  	}
  	defer resp.Body.Close()

  	var body rpcResponse
  	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
  		return nil, err
  	}
  	if body.Error != nil {
  		return nil, fmt.Errorf("rpc error %d: %s", body.Error.Code, body.Error.Message)
  	}
  	return body.Result, nil
  }

  func main() {
  	result, err := call("eth_call", map[string]string{
  		"to":   "0xdAC17F958D2ee523a2206206994597C13D831ec7",
  		"data": "0x18160ddd",
  	}, "latest")
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println(string(result))
  }
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x00000000000000000000000000000000000000000000000000012a0ff6a2ff40"
}
```

The parameters of `eth_call` are, in order, the call object, the block parameter, and the optional `stateOverride` and `blockOverrides`. The call object has the same fields as Ethereum's `eth_call`: `from`, `to`, `data` / `input`, `value`, `gas`, `gasPrice`, and so on.

<Tip>
  The `eth_*` methods are compatible with standard Ethereum, so libraries such as ethers, viem, and web3.py can use `http://proxy:8663/1` as an ordinary RPC endpoint. Send DeBank namespace methods through their raw call interface, for example ethers' `provider.send(method, params)`.
</Tip>

## Batch requests

A request body that is an array is processed as a JSON-RPC batch, returning several results in one round trip:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '[
    { "jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber", "params": [] },
    { "jsonrpc": "2.0", "id": 2, "method": "eth_chainId", "params": [] },
    { "jsonrpc": "2.0", "id": 3, "method": "eth_getBalance",
      "params": ["0xdAC17F958D2ee523a2206206994597C13D831ec7", "latest"] }
  ]'
```

A batch is routed as a whole to one node. To run a set of contract calls against one block, `contractMultiCall` below is a better fit than a batch of `eth_call`: it guarantees that all calls see the same state.

## Choosing the block: `blockCtx`

Methods in the DeBank namespace have no prefix and declare their block context with a `blockCtx` object. Read a balance at a historical height:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getAddressBalance",
    "params": [
      "0xdAC17F958D2ee523a2206206994597C13D831ec7",
      { "block_id": "0x1406f40", "type": "Equals" }
    ]
  }'
```

| `type`     | Meaning                            | Routing                                                           |
| ---------- | ---------------------------------- | ----------------------------------------------------------------- |
| `Equals`   | Exactly the state of `block_id`    | State node within 64 blocks of the head, Archive node beyond that |
| `Contains` | Any state no older than `block_id` | Always a State node, executed on `latest`                         |

Omitting `blockCtx` means `latest`. See [Block context and routing](/en/concepts/block-context) for the semantics.

## Calling several contracts at once: `contractMultiCall`

Runs a set of read-only calls on one state and returns each call's result plus the block that state belongs to:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "contractMultiCall",
    "params": [
      [
        { "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "data": "0x18160ddd" },
        { "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "data": "0x18160ddd" }
      ],
      { "block_id": "latest", "type": "Contains" }
    ]
  }'
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "results": [
      { "code": 0, "err": "", "from_cache": false, "result": "0x…", "gas_used": 23675, "time_cost": 0.31 },
      { "code": 0, "err": "", "from_cache": false, "result": "0x…", "gas_used": 30411, "time_cost": 0.28 }
    ],
    "stats": {
      "block_num": 21000000,
      "block_hash": "0x…",
      "block_time": 1735689600,
      "success": true,
      "cache_enabled": false
    }
  }
}
```

The parameter order is `requests`, `blockCtx`, `blockOverrides`, `stateOverride`, `fastFail`, `useParallel`, `disableCache`. Optional parameters are positional; use `null` for the ones you skip.

| Optional parameter            | Effect                                                   |
| ----------------------------- | -------------------------------------------------------- |
| `fastFail`                    | Stops executing the remaining calls after one call fails |
| `useParallel`, `disableCache` | Accepted but ignored by the current implementation       |

A per-call `code` of `0` means success; on revert it is `-39000` and `err` carries the decoded revert reason. The request as a whole returns a JSON-RPC level error only when parameter or block resolution fails.

<Note>
  `eth_multiCall` is the `eth` namespace version of the same capability: its block parameter uses the standard form, its response fields are camelCase (`fromCache`, `gasUsed`, `blockNum`), and its per-call revert code is `-40014`. Do not share one parser between the two.
</Note>

## Estimating gas: `estimateGas`

The method name has no `eth_` prefix. Estimate a USDT transfer:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "estimateGas",
    "params": [
      {
        "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
        "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "data": "0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000005f5e100"
      }
    ]
  }'
```

Returns the gas used as a hex string. The second parameter is an optional `blockCtx`, the third an optional `blockOverrides`.

## Simulating a transaction sequence: `simulateTransactions`

Executes a set of transactions in order, each seeing the state changes of the previous one, and returns the call traces and events of each:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "simulateTransactions",
    "params": [
      [
        {
          "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
          "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
          "data": "0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000005f5e100"
        }
      ],
      { "block_id": "latest", "type": "Equals" }
    ]
  }'
```

Each `results[i]` in the response contains `traces`, `events`, `code`, `err`, and `gas_used`; `stats` names the block the simulation was based on.

## Tracing one call: `pre_traceCall`

Returns an instruction-level struct log without needing the write node's `debug` API:

```bash theme={null}
curl -s -X POST http://proxy:8663/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "pre_traceCall",
    "params": [
      { "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "data": "0x18160ddd" },
      "latest"
    ]
  }'
```

The second parameter is an optional block parameter in the same form as `eth_call`. The batch version is `pre_traceMany`.

## Handling errors

JSON-RPC level errors appear in the response's `error` field:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -39006, "message": "block … not found for state node" }
}
```

| Code                 | Meaning                                                    | What the client should do                                                                              |
| -------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `-39000`             | Execution reverted                                         | Business error; `message` carries the revert reason                                                    |
| `-39001` \~ `-39004` | Out of gas, insufficient balance, nonce error, EVM failure | Business error; check the call parameters                                                              |
| `-39006`             | State node does not have that height                       | Already retried on Archive when going through the proxy; when direct, re-send to an Archive node       |
| `-39007`             | Invalid block identifier                                   | Check whether the height is beyond the chain head or the hash is off the canonical chain               |
| `-39008`             | Unsupported precompile                                     | Already retried on Native when going through the proxy; when direct, re-send to an original chain node |
| `-41002`             | Execution timed out                                        | Reduce the call size or split the batch                                                                |
| `-32601` / `-32602`  | Method not found / invalid params                          | Check the method name (`estimateGas` has no prefix) and parameter order                                |

See the [RPC reference](/en/reference/rpc#error-codes) for the full error code table.

## Next steps

<Columns cols={2}>
  <Card title="RPC reference" icon="terminal" href="/en/reference/rpc">
    All methods, parameters, and return structures.
  </Card>

  <Card title="Block context and routing" icon="route" href="/en/concepts/block-context">
    The semantics of `Equals` / `Contains` and the proxy's routing rules.
  </Card>
</Columns>
