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.
Standard methods
Section titled “Standard methods”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.
eth_sendRawTransaction
Section titled “eth_sendRawTransaction”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.
Refused by policy
Section titled “Refused by policy”| 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).
Batched requests are rejected
Section titled “Batched requests are rejected”Send one JSON-RPC request per HTTP call. Batch arrays are refused.
Fee methods
Section titled “Fee methods”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.
The frost_ namespace
Section titled “The frost_ namespace”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.
frost_historyStatus
Section titled “frost_historyStatus”What history this node retains. Post-quantum signatures make storage expensive enough that nodes deliberately retain different amounts, so ask rather than assume.
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. |
frost_getStrippedTransaction
Section titled “frost_getStrippedTransaction”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.
frost_getSignatureSidecar
Section titled “frost_getSignatureSidecar”A block’s elided witness bytes, or null once pruned. Together with the
stripped record this reconstructs the original transaction exactly.
Node-local methods
Section titled “Node-local methods”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.
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).
The rest of the namespace
Section titled “The rest of the namespace”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.
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.
Submission validation
Section titled “Submission validation”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.
Which node to talk to
Section titled “Which node to talk to”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.