Skip to content

JSON-RPC

Frost speaks standard Ethereum JSON-RPC. The public endpoint at https://rpc.frostfi.net is a Frost ingress: it handles submissions itself and proxies reads to an execution layer behind it.

The full eth_, net_, and web3_ read surface is proxied and behaves exactly as you expect — eth_getBalance, eth_call, eth_estimateGas, eth_getLogs, eth_getBlockByNumber, eth_getTransactionReceipt, eth_getCode, eth_getStorageAt, eth_chainId, and the rest.

Any Ethereum library, explorer, or indexer works against it unmodified.

One nuance applies to history. Below the node’s witness-sidecar window, per-transaction and per-header reads — eth_getTransactionByHash, eth_getRawTransactionByHash, eth_getTransactionReceipt, headers and block objects — are served from the retained stripped store, with elided signature entries empty. On frost-reth, which serves the public endpoint, the block-scoped reads (eth_getBlockReceipts, eth_getBlockTransactionCountBy*, eth_get(Raw)TransactionByBlock*AndIndex, eth_getLogs) answer error code 4444 for those blocks; an 8141-geth node serves them from the same stripped store instead. Check frost_historyStatus for the window.

Handled by the ingress rather than proxied. It accepts every standard transaction type except EIP-4844 blob transactions (type 0x03, refused — blob sidecars have no data-availability path through Frost consensus), plus type 0x06 frame transactions. A raw transaction larger than 128 KiB is refused with -32602 (“oversized transaction”).

The returned hash is the keccak-256 of the raw bytes, as usual. But its meaning differs:

Before forwarding, the ingress validates the submission — see Submission validation below.

Method Why
eth_sendTransaction The endpoint holds no keys.
eth_sign ”
eth_signTransaction ”
eth_signTypedData ”

Anything else outside the allowlist returns -32601 (method not found).

Send one JSON-RPC request per HTTP call. Batch arrays are refused.

Frost is fee-blind, which shows up here:

Method Returns
eth_maxPriorityFeePerGas 0x0, always
eth_gasPrice The base fee

Tooling that reads these does the right thing automatically. Tooling with a hardcoded tip wastes gas for no benefit.

Three frost_ methods are proxied through to the execution layer, and they are the three documented first. The ingress also answers a few methods itself from node-local state — two observability methods and two bootstrap methods — and refuses the validation gate outright. See Node-local methods and The rest of the namespace.

What history this node retains. Post-quantum signatures make storage expensive enough that nodes deliberately retain different amounts, so ask rather than assume.

Terminal window
curl -s https://rpc.frostfi.net -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"frost_historyStatus","params":[]}'
{
"tier": "tx-history",
"headBlock": "0xdb703",
"earliestFullBody": "0xd8ff3",
"historyCutoff": "0x0",
"persistedState": "0xd8b18"
}
Field Meaning
tier The node’s retention tier.
headBlock Current head.
earliestFullBody Oldest block for which a full body is available.
historyCutoff Below this, history has been expired entirely.
persistedState The block whose state root is durably persisted on disk — the point a hard-kill recovery rewinds to and re-executes from, and the floor below which the consensus layer may prune its commits. 0x0 means unknown, and consumers must then prune nothing. It is not the historical-state window for eth_call.

The permanently retained, signature-stripped record of one transaction, with an inclusion proof against the block header’s strippedTransactionsRoot. This is how historical transactions stay verifiable after their multi-kilobyte witnesses have been pruned.

A block’s elided witness bytes, or null once pruned. Together with the stripped record this reconstructs the original transaction exactly.

Two observability methods are answered by the ingress process itself, without authentication, from state the node’s consensus driver publishes to it.

frost_getRecentCommits — a bounded tail of recent consensus commits, including the many that closed no block. Params are [since, limit]: since is a commit index to start after, or null for the newest; limit defaults to 64 and is capped at 128.

Terminal window
curl -s https://rpc.frostfi.net -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"frost_getRecentCommits","params":[null,1]}'
{
"commits": [
{
"index": 2454793,
"digest": "0x1937…a6de",
"leaderAuthority": 1,
"leaderRound": 2455266,
"leaderDigest": "0xce00…5ae3",
"timestampMs": 1790254264882,
"blocks": 4,
"transactions": 0,
"elBlocks": []
}
]
}

elBlocks lists the execution-layer blocks the commit closed; it is empty for most commits, because a block closes once per second of consensus time at ordinary load.

frost_dropStats — counters for forced transactions that consensus ordered but the execution layer skipped, grouped by where the drop was decided:

{
"driver": { "forced_txs_total": 5486433, "forced_txs_dropped_total": 3332,
"blocks_with_drops_total": 1611, "skip_all_dropped_total": 0,
"finalizer_rejected_skips_total": 0, "idle_heartbeat_blocks_total": 6 },
"ingress": { "txs_sequenced_total": 1443198, "own_blocks_gc_total": 14,
"txs_gc_dropped_total": 19, "status_lost_total": 0, "status_watchers": 1 },
"el": { "fee_cap_too_low": 0, "gas_overflow": 0, "insufficient_funds": 0,
"nonce_too_high": 3332, "nonce_too_low": 0, "other": 0,
"size_overflow": 0, "total": 3332 }
}

The el block breaks the skips down by reason; nonce_too_high is the usual one, a sender whose earlier transaction was itself skipped.

A third method, frost_nodeHealth, reports the node’s disk, chain and retention state to the fleet’s operators. It requires the fleet bearer token; without it the ingress answers as it would for any unknown method (-32601).

frost_validateTransaction is the method the ingress calls on its own execution layer to run the submission gate. It is deliberately not proxied: the gate simulation is expensive, and exposing it would hand an unauthenticated caller a free way to spend a node’s CPU.

Terminal window
curl -s https://rpc.frostfi.net -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"frost_validateTransaction","params":[]}'
# → {"error":{"code":-32601,
# "message":"method not supported by the ingress: frost_validateTransaction"}}

To find out whether a transaction would be admitted, submit it: a refusal comes back synchronously with a reason, which is the same verdict the method would have given. See Submission validation.

frost_getCheckpointChain and frost_getJournalTail are the opposite case: they are never proxied, and are answered by the ingress process itself from node-local state. Only a node explicitly configured as a bootstrap sync anchor serves them — rpc.frostfi.net is one, so both work there, but do not assume they work against an arbitrary endpoint. See Bootstrap and sync.

Before forwarding a frame transaction to consensus, the ingress simulates its VERIFY-frame prefix under the ERC-7562 validation rules: a bounded gas budget and a tracer that rejects reads of mutable global state. It also applies verdict caching, duplicate refusal, a nonce-gap bound, per-sender in-flight caps, and rate and size limits. Refusals come back as JSON-RPC errors:

Code Refusal
-32602 Oversized transaction (over 128 KiB).
-32005 Rate limited — each submitting source has its own token bucket, checked before the host-wide one, so one heavy submitter cannot drain the shared quota.
-32000 Already known: the same hash was submitted recently.
-32000 Rejected by validation, with the execution layer’s reason (a cached verdict is marked as such).
-32000 Nonce gap too large (by default more than 64 ahead of the state nonce), or too many in-flight transactions for the sender.
-32603 Validation unavailable — the ingress could not reach its execution layer, and fails closed.

A transaction with a bad post-quantum signature is therefore refused at submission with a reason, rather than being ordered and then silently skipped.

This is a courtesy, not a safety property. The actual guarantee is underneath: consensus orders bytes without validating them, and every execution layer skips invalid transactions identically and deterministically. The gate saves block space; the determinism prevents forks.

Validation is stateful in one way worth knowing: the account need not exist yet (that is the whole point of counterfactual onboarding), but its address must already be funded, because admission reads the payer’s balance from head state. Make sure a funding transaction is mined, not merely accepted, before submitting a first send.

Submissions must reach a validator. The public endpoint routes correctly; a follower node’s ingress will not accept submissions.

No live node keeps full witnesses indefinitely: every fleet node prunes its sidecar to a window and serves stripped records beneath it. For history older than that window the source is the cold archive, restored onto an archive node with frost-archiver; the verifiable path for any single transaction is the stripped record plus its inclusion proof from frost_getStrippedTransaction. Check frost_historyStatus before relying on a node for deep history. See Node types and History and storage.