nautechsystems/nautilus_trader · error
eth_call execution reverted
Error message
eth_call execution reverted
What it means
Raised by `call_at` in the blockchain HTTP RPC adapter when an explicit-height `eth_call` is recognized as having reverted on-chain. The underlying `call_result_at` preserves the revert as data via `RpcCallResult::Reverted`, but the plain `call_at` API discards the revert data and converts it into this anyhow error. It means the contract executed the calldata and deliberately reverted (e.g. require/assert failed), not that the RPC itself failed.
Source
Thrown at crates/adapters/blockchain/src/rpc/http.rs:504
/// # Errors
///
/// Returns an error if the RPC call fails or the result is missing or malformed.
#[cfg(feature = "hypersync")]
#[allow(
dead_code,
reason = "Used by the independent verification read inventory"
)]
pub(crate) async fn call_at(
&self,
from: Option<&Address>,
to: &Address,
value: U256,
data: &[u8],
block: u64,
) -> anyhow::Result<Bytes> {
match self.call_result_at(from, to, value, data, block).await? {
RpcCallResult::Success(bytes) => Ok(bytes),
RpcCallResult::Reverted => anyhow::bail!("eth_call execution reverted"),
}
}
/// Executes an explicit-height `eth_call`, preserving a recognized EVM revert as data.
#[cfg(feature = "hypersync")]
pub(crate) async fn call_result_at(
&self,
from: Option<&Address>,
to: &Address,
value: U256,
data: &[u8],
block: u64,
) -> anyhow::Result<RpcCallResult> {
let mut call = serde_json::json!({
"to": to,
"value": format!("0x{value:x}"),
"data": hex::encode_prefixed(data),
});View on GitHub (pinned to 18893faf8b)
Solutions
- Decode the revert reason instead: use `call_result_at` which returns `RpcCallResult::Reverted` and preserves the revert data.
- Check preconditions on-chain before the call (allowance, balance, role, state at `block`).
- Verify the calldata encoding (function selector and ABI arguments) — malformed calldata often reverts.
- Confirm the target contract is deployed at the queried `block` height.
Example fix
// before
let out = rpc.call_at(from, to, value, data, block).await?;
// after
match rpc.call_result_at(from, to, value, data, block).await? {
RpcCallResult::Success(bytes) => bytes,
RpcCallResult::Reverted => decode_revert_reason(data) /* handle revert */,
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check state preconditions before the call let allowance: U256 = erc20.allowance(owner, spender).call().await?; anyhow::ensure!(allowance >= amount, "insufficient allowance, eth_call would revert");
Type guard
fn is_reverted(err: &anyhow::Error) -> bool {
err.to_string().contains("eth_call execution reverted")
} Try / catch
match rpc.call_at(from, to, value, data, block).await {
Ok(bytes) => process(bytes),
Err(e) if is_reverted(&e) => handle_revert(decode_revert_data(&e)),
Err(e) => return Err(e),
} Prevention
- Simulate or static-check state (allowance, balance, roles) before eth_call
- Prefer `call_result_at` when you need the revert payload
- Unit-test calldata encoding against the contract ABI
- Verify contract deployment height relative to the queried block
When it happens
Trigger: Calling `call_at(from, to, value, data, block)` where the contract at `to` reverts when executed at the given historical block: failed `require`/`assert`, custom error, or explicit `revert()` in the contract logic.
Common situations: Simulating a token transfer that would fail (insufficient allowance/balance); calling a view function guarded by state checks that don't hold at the queried block; probing historical state where the contract wasn't deployed or a check like `onlyOwner` fails.
Related errors
- Finalized block {} does not contain transaction {}
- eth_call RPC error {code}
- eth_call RPC error {}
- eth_getTransactionReceipt returned a receipt with a mismatch
- eth_getTransactionByHash returned a mismatched transaction h
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/250ae0abea822b72.
Report an issue: GitHub.