Developer guide
How to Decode a Failed Transaction and Its Revert Reason
"Execution reverted" tells you nothing. But the chain almost always returned a reason — a string, a custom error, or a panic code. Here is how each one is encoded and how to read it.
Three Kinds of Revert
1. Error(string) — a require message
0x08c379a0 // selector for Error(string) 0000...0020 // offset 0000...0014 // length = 20 4552433230... // "ERC20: bad allowance"
Decode it as a standard ABI-encoded string after the 4-byte selector.
2. Panic(uint256) — a language-level failure
0x4e487b71 + code 0x01 assert(false) 0x11 arithmetic overflow / underflow 0x12 division or modulo by zero 0x21 invalid enum conversion 0x31 pop() on an empty array 0x32 array index out of bounds 0x41 out of memory / too much allocated 0x51 call to an uninitialized internal function
3. A custom error
Since Solidity 0.8.4, contracts revert with typed errors that are far cheaper than strings:
error InsufficientBalance(uint256 available, uint256 required);
-> selector = first 4 bytes of keccak256("InsufficientBalance(uint256,uint256)")
-> followed by the two ABI-encoded argumentsYou need the ABI (or a signature database) to turn that selector back into a name. Explorers that only know Error(string) show a blank reason here.
Empty revert data
No data at all usually means: out of gas, a plain revert(), a call to an address with no code, or a failed native-value transfer to a contract without a payable fallback.
Reading It in Practice
- Open a contract page for the contract with its ABI loaded.
- Re-attempt the call from the Write tab. Before sending, the transaction is simulated — a revert is caught and decoded against the ABI, so custom errors come back with their name and arguments instead of "execution reverted".
- Already have the failed transaction? Paste its calldata into the Calldata Decoder on the Overview tab to confirm which function ran and with what arguments — half of all reverts are simply the wrong arguments.
- Use the Events tab with the transaction hash to see what did emit before the failure point.
The Usual Suspects
- Missing or too-small ERC-20 allowance — check allowance(owner, spender) before the call.
- Decimals: sending 1 instead of 1000000000000000000 (or the reverse) trips balance and slippage checks.
- Access control — onlyOwner / AccessControl reverts because the connected wallet is not the role holder.
- Deadline or slippage parameters already expired by the time the transaction is mined.
- Paused contracts: "Pausable: paused" is a require string and decodes cleanly.
- Calling an implementation directly instead of through its proxy, so all state reads as zero.
👉 Want the revert reason decoded for you?
Build a contract page