EIP-8219 Checked arithmetic opcodes

Hegota Declined for Inclusion · EVM & execution · EIP status Draft · created 2026-04-08

Adds four new EVM instructions (SAFEADD, SAFESUB, SAFEMUL, SAFEDIV) that automatically revert execution on overflow or division by zero in addition, subtraction, multiplication and division.

Why it is needed

Solidity has checked arithmetic overflow (a result exceeding the representable range) by default since version 0.8.0, and Vyper since the start. However, the EVM itself has no instruction that performs this check, so the compiler inserts a multi-instruction check for every operation. As a result, an addition that should cost 3 gas costs about 79 gas in Solidity, so safety costs 25 times as much. Because the cost is burdensome, developers are tempted to turn off checks with workarounds such as unchecked blocks or unsafe_add, and code that disables checks even where safety is not proven grows more common and harder to audit. There are real attacks that exploited unchecked subtraction. This proposal adds instructions with the check built in directly to the EVM, lowering the cost of a safe addition to 5 gas and removing the reason to bypass the check at all.

What changes

Today

After EIP-8219

Who is affected

In depth

Opcode placement and gas pricing

The new opcodes are 0x0c SAFEADD (5 gas), 0x0d SAFESUB (5 gas), 0x0e SAFEMUL (7 gas) and 0x0f SAFEDIV (7 gas). They use the four currently unassigned slots right after the existing arithmetic group (0x01 ADD through 0x0b SIGNEXTEND), and this contiguous placement simplifies interpreter implementations that use jump tables or opcode range checks. The price is the base operation cost plus 2 gas for the check. SAFEADD and SAFESUB add 2 gas to the 3 gas of the Gverylow group, and SAFEMUL and SAFEDIV add 2 gas to the 5 gas of the Glow group. The activation constant FORK_BLOCK is still TBD.

Behavior on error: revert with empty returndata

When overflow, underflow or division by zero is detected, the current call frame MUST be reverted with empty returndata. The semantics are the same as executing REVERT(0, 0). Revert was chosen over an exceptional halt that consumes all remaining gas because checked arithmetic is commonly used where a revert is a normal and expected outcome, such as slippage checks. Empty returndata is a compiler-neutral choice that matches Vyper's current overflow revert implementation (REVERT(0,0)), but gives up the Panic(0x11) encoding that Solidity uses in exchange for gas and bytecode savings.

Boundary conditions of the four operations

SAFEADD reverts if a + b > 2^256 - 1. SAFESUB reverts if b > a (underflow), with the same stack order as the existing SUB, a on top and the result a - b. SAFEMUL returns 0 without reverting if a == 0, regardless of b, and otherwise reverts if dividing the result by a does not give b (overflow). SAFEDIV has the same stack order as the existing DIV (dividend a on top) and reverts if the divisor b == 0. Unsigned integer division cannot overflow, so it is not checked separately. Per the test cases, dividing 0 by 0 (a=0, b=0 for SAFEDIV) also reverts.

Differences from the flag-based prior proposals (EIP-1051, EIP-6888)

EIP-1051 has existing arithmetic opcodes set ovf and sovf flags that are read with the OFV and SOVF opcodes, and EIP-6888 uses carry and overflow flags with the JUMPC and JUMPO conditional jumps. Both approaches need a flag read and a conditional jump after the operation, so they take at least 3 instructions, and they leave implicit state (the flags) between instructions, which complicates static analysis, formal verification and compiler optimization. There is also the problem that if the flag is checked only once across two additions, the earlier overflow silently disappears. This EIP is a self-contained stateless instruction in which each opcode either produces the correct result or reverts, so it has none of these problems, and since it needs no extra instructions the gas savings are maximal.

Benchmark figures

We measured the same ERC-20 contract with a single checked addition isolated, using Solidity 0.8.33 (optimizer, 200 runs) and Vyper 0.4.3. Execution gas per addition is about 79 gas for Solidity (3 + 76) and about 41 gas for Vyper (3 + 38), while SAFEADD costs 5 gas, savings of 93.7% and 87.8% respectively. Bytecode per addition drops from 12 bytes (Solidity) and 14 bytes (Vyper) to 1 byte, and deployment gas drops from +2,352 and +2,604 to about 200. The benchmark contract has one checked addition each in transfer and transferFrom, so the contract-wide total is twice these figures, and Solidity's overflow check helper is emitted only once per contract and shared by all checked additions.

Why only unsigned 256-bit is handled and the justification for four opcodes

This EIP defines only unsigned arithmetic. Signed (two's complement) overflow has other boundary conditions such as INT256_MIN / -1, so the signed variants (SSAFEADD, SSAFESUB, SSAFEMUL, SSAFEDIV) are deferred to a separate companion EIP. Because uint256 is the dominant numeric type in Solidity and Vyper, unsigned operations account for most use, and for the same reason smaller data types are not covered either. Using four opcode slots is costly, but an analysis done before the Devcon SEA talk ('EVM Charts 2024') found that the four existing arithmetic opcodes ranked within the top 35 by usage frequency, and the author argues that checked arithmetic being the default in both languages supports heavy use in new contracts.

Backward compatibility

0x0c to 0x0f are currently unassigned slots, and executing them today behaves like INVALID and consumes all gas. The existing ADD, SUB, MUL, DIV and others do not change at all, so contracts that use Solidity's unchecked blocks or Vyper's unsafe_add() keep using the raw opcodes with the same semantics as today. Compilers must target a post-fork EVM version to use the new opcodes, and contracts compiled for earlier EVM versions behave identically.

Status and call decisions

The source is in Draft status, so the specification may still change. In core developer calls, it was accepted as a Hegotá fork candidate (PFI) at 08.13 ACDE #243, but at 09.10 ACDE #245 it was declined (DFI) along with EIP-2488, EIP-8182 and EIP-7645, because every client rated it unanimously low (0.0 to 0.8).

Original EIP · ← EIPs