← Back to the library
Web3Applied · 8 min

Sending fails at gas estimation with "execution reverted"

“The wallet or backend refuses to send: "execution reverted" or "cannot estimate gas", sometimes with only a hex blob, while the same call works in a test or for another account.”

ERROR CODES YOU MAY SEE

A code is a clue. Use its meaning and surrounding evidence to narrow the cause.

CALL_EXCEPTIONethers v6 · library

Execution reverted during a contract call, gas estimation, or transaction processing.

Inspect the action, revert data, and receipt when available. A failed simulation does not mean a transaction was submitted or mined.

Official reference for CALL_EXCEPTION (opens in new tab)Code definition reviewed
INSUFFICIENT_FUNDSethers v6 · library

The sending account cannot cover the full transaction cost (gas limit times fee per gas, plus value).

Check the native balance of the account that actually signs, on the chain the provider is connected to. During estimation this is not a contract revert, so decoding revert data will not help.

Official reference for INSUFFICIENT_FUNDS (opens in new tab)Code definition reviewed

THE PRINCIPLE

An estimation revert is a simulated failure, not a sent transaction. The answer is in the revert data and the state you simulated against.

FIRST MOVES

  1. Capture the revert data, not just the message. Error(string) has selector 0x08c379a0, Panic(uint256) comes from assert and arithmetic checks, and anything else is a custom error: a 4-byte selector plus ABI-encoded arguments.
  2. Decode it with an ABI that includes the errors of every contract on the call path, such as the token behind a vault. OpenZeppelin 5.x tokens throw ERC20InsufficientAllowance and ERC20InsufficientBalance.
  3. Re-run the exact request with eth_call: same from, value, and calldata, against latest and an explicit block number. Missing approvals, wrong msg.sender, or zero value explain most reverts.
  4. Explain differences between simulation and submission: state that changes between blocks (prices, deadlines, nonces, paused flags), block.timestamp, and providers that lag the head.
  5. Separate non-revert failures. INSUFFICIENT_FUNDS means the signer cannot cover gas plus value; it is not a contract error.

TOOLS: THEN → NOW

Sending with a manual gas limit to see what happenssimulateContract / staticCall first, then submit the returned request
Reading reason strings from block explorersDecoding custom errors locally (viem decodeErrorResult, ethers Interface.parseError) and tracing with cast run or debug_traceCall
Forking mainnet to test a hypothesisStill useful; eth_call state overrides can also test a missing approval or balance directly

PATTERN SNAPSHOT

import { BaseError, ContractFunctionRevertedError } from "viem";

try {
  const { request } = await client.simulateContract({
    address: vault, abi: [...vaultAbi, ...erc20Abi], // include errors from inner calls
    functionName: "deposit", args: [amount, receiver], account,
  });
  await wallet.writeContract(request);
} catch (err) {
  const revert = err instanceof BaseError
    ? err.walk((e) => e instanceof ContractFunctionRevertedError) : null;
  if (revert instanceof ContractFunctionRevertedError) {
    log.warn("would revert", revert.data?.errorName, revert.data?.args);
  } else throw err;
}

CLOSE THE AI. EXPLAIN THIS.

Your estimate reverts with data 0xfb8f41b2... and no message. What do you need to turn it into a readable error, and why might the vault's ABI alone not be enough?

HOW IT WORKS UNDERNEATH

SOURCES

Guide reviewed