EVM / Solidity Error 0xe450d38c: ERC20InsufficientBalance
An OpenZeppelin v5 ERC-20 transfer or burn needed more tokens than the sender holds. The data decodes to (sender, balance, needed); top up the balance or lower the amount, minding decimals.
Identify: does your log match?
Error: execution reverted (unknown custom error) (action="estimateGas", data="0xe450d38c00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000d8d726b7177a8000000000000000000000000000000000000000000000000003635c9adc5dea00000", reason=null, transaction={ "data": "0xa9059cbb...", "from": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "to": "0x5FbDB2315678afecb367f032d93F642f64180aa3" }, invocation=null, revert=null, code=CALL_EXCEPTION, version=6.13.4)- High
Amount not scaled by decimals
Passing 1000 * 10**18 to a 6-decimal token (USDC) asks for 10^12 times too much. Always read decimals().
- Medium
Wrong sender in transferFrom
transferFrom checks the from address's balance, not the caller's. The vault or user you pull from is empty.
- Low
Fee-on-transfer or rebasing token
The received amount was less than expected, so a follow-up transfer of the full amount exceeds the actual balance.
Fix
Decode the custom error with the ABI (ethers v6)
await token.transfer(to, amount); // "unknown custom error"
import { Contract, Interface, parseUnits } from 'ethers'; const iface = new Interface([ 'error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)', ]); const decimals = await token.decimals(); const amount = parseUnits('250', decimals); try { await token.transfer.staticCall(to, amount); } catch (e: any) { const parsed = iface.parseError(e.data); if (parsed?.name === 'ERC20InsufficientBalance') console.log(parsed.args.balance, parsed.args.needed); }
Including the error in the ABI turns "unknown custom error" into named args. parseUnits with on-chain decimals avoids scale bugs.
Verify: reproduce it on purpose
$ cast decode-error 0xe450d38c00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000d8d726b7177a8000000000000000000000000000000000000000000000000003635c9adc5dea00000 --sig "ERC20InsufficientBalance(address,uint256,uint256)"Run it against a dev environment to trigger 0xe450d38c on purpose and test your error handler.
Prevent
- Add OZ IERC20Errors to your frontend ABI.
- Never hardcode 18 decimals.
- Show balance vs needed in the UI before signing.
FAQ
How do I decode 0xe450d38c with cast?
cast decode-error <data> --sig "ERC20InsufficientBalance(address,uint256,uint256)" prints sender, balance and needed.
Which OpenZeppelin version uses this error?
OpenZeppelin Contracts v5.0+ (IERC20Errors, ERC-6093). v4 reverted with "ERC20: transfer amount exceeds balance".
Is this the same as insufficient allowance?
No. Allowance failures use ERC20InsufficientAllowance (0xfb8f41b2) and are fixed with approve().
Related errors
Last verified 2026-10-22 · Source: 4byte/OpenChain signatures + OpenZeppelin v5 custom errors + Panic codes · Official docs ↗ · ~3,600 searches/mo