This EIP introduces four new opcodes — SAFEADD (0x0c), SAFESUB (0x0d), SAFEMUL (0x0e), and SAFEDIV (0x0f) — that perform unsigned 256-bit arithmetic with built-in overflow, underflow, and division-by-zero checking. On error, these opcodes revert the current call frame with empty returndata, equivalent to REVERT(0, 0). A single SAFEADD instruction replaces the 11-instruction compiler-generated overflow check pattern, reducing checked addition cost from ~79 gas to 5 gas and from 22 bytes to 1 byte of bytecode.
Motivation
The Checked Arithmetic Overhead
High-level EVM languages emit overflow checks by default: Solidity since v0.8.0, Vyper since inception. These checks are essential for correctness, but the EVM provides no native support for them, forcing compilers to synthesize multi-instruction check patterns around every arithmetic operation.
A typical checked addition compiles to something like:
(Jump to safe add)
Duplicate operand
Perform Addition
Check for overflow
(Jump back)
This contains at least one conditional jump and potentially multiple more jumps. Benchmarks conducted with Solidity 0.8.33 (optimizer enabled, 200 runs) and Vyper 0.4.3 on equivalent ERC-20 contracts isolating a single checked addition yield the following overhead:
Metric
Solidity 0.8.33
Vyper 0.4.3
With SAFEADD
Execution gas per checked add
~79 (3 + 76)
~41 (3 + 38)
5
Bytecode per checked add
22 bytes
26 bytes
1 byte
Deployment gas per checked add
+4,704
+5,208
~200
Gas reduction
93.7%
87.8%
—
The raw ADD opcode costs 3 gas. A compiler-checked addition costs approximately 79 gas — a 25x multiplier purely for safety. This overhead grows across every arithmetic operation in every function in every contract on the network.
The Dangerous Trade-off
Because checked arithmetic is expensive, developers and auditors face constant pressure to bypass it. Solidity provides unchecked {} blocks; Vyper provides unsafe_add(), unsafe_sub(), and related builtins. While these are sometimes appropriate (e.g., loop counters that provably cannot overflow), their availability creates a dangerous trade-off: developers use unchecked arithmetic to save gas even when safety is not proven.
This incentive has leads to:
Less readable/auditable code, full of unchecked/unsafe.
Real exploits where unchecked subtraction was exploited.
Native checked arithmetic opcodes eliminate this trade-off. When a safe addition costs only 2 gas more than a raw addition (5 vs 3) and the bytecode size does not increase, there is no meaningful reason to opt out of overflow protection.
Comparison with Prior EIPs
Two prior EIPs have proposed overflow detection at the EVM level:
EIP-1051: Overflow Checking for the EVM — Introduces ovf and sovf flags set by existing arithmetic opcodes, with OFV (0x0c) and SOVF (0x0d) opcodes to check and clear them.
EIP-6888: Arithmetic Verification at EVM Level — Introduces carry and overflow flags with JUMPC and JUMPO opcodes for conditional jumps.
Both proposals share the same fundamental limitation: they still require multi-instruction patterns. After each arithmetic operation, the contract must execute a flag-read opcode and a conditional jump — at minimum 3 instructions instead of 1. Additionally, both introduce implicit EVM state (flags) that persists across instructions, complicating static analysis, formal verification, and compiler optimization. Flags also create subtle ordering dependencies: if a contract performs two additions and only checks the flag once, the first overflow is silently lost.
This EIP takes a different approach: each safe opcode is a self-contained, stateless instruction that either produces a correct result or reverts. No flags, no implicit state, no multi-instruction patterns. One opcode does the job of many.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Parameters
Constant
Value
FORK_BLOCK
TBD
Overview
Four new opcodes are introduced in the range 0x0c–0x0f, immediately following the existing arithmetic opcode group (ADD through SIGNEXTEND, 0x01–0x0b). These slots are currently unassigned.
Opcode
Mnemonic
Gas
Description
0x0c
SAFEADD
5
Checked unsigned addition; reverts on overflow
0x0d
SAFESUB
5
Checked unsigned subtraction; reverts on underflow
0x0e
SAFEMUL
7
Checked unsigned multiplication; reverts on overflow
0x0f
SAFEDIV
7
Checked unsigned division; reverts on division by zero
Revert Behavior
When any safe arithmetic opcode detects an error condition (overflow, underflow, or division by zero), execution MUST revert the current call frame with empty returndata. This is semantically equivalent to executing REVERT(0, 0).
SAFEADD (0x0c)
Input stack
Stack
Value
top - 0
a
top - 1
b
Output stack
Stack
Value
top - 0
a + b
Pseudocode
result = a + b (mod 2^256)
if result < a:
revert(0, 0)
push result
Error condition
Reverts if a + b > 2^256 - 1 (unsigned overflow).
SAFESUB (0x0d)
Input stack
Stack
Value
top - 0
a
top - 1
b
Output stack
Stack
Value
top - 0
a - b
Stack ordering matches the existing SUB opcode: a is on top, b is below, and the result is a - b.
Pseudocode
if b > a:
revert(0, 0)
push a - b
Error condition
Reverts if b > a (unsigned underflow).
SAFEMUL (0x0e)
Input stack
Stack
Value
top - 0
a
top - 1
b
Output stack
Stack
Value
top - 0
a * b
Pseudocode
if a == 0:
push 0
else:
result = a * b (mod 2^256)
if result / a != b:
revert(0, 0)
push result
Error condition
Reverts if a != 0 and a * b > 2^256 - 1 (unsigned overflow). When a == 0, the result is 0 regardless of b, with no revert.
SAFEDIV (0x0f)
Input stack
Stack
Value
top - 0
a
top - 1
b
Output stack
Stack
Value
top - 0
a / b
Stack ordering matches the existing DIV opcode: a is the dividend (on top), b is the divisor.
Pseudocode
if b == 0:
revert(0, 0)
push a / b (unsigned integer division, truncating)
Error condition
Reverts if b == 0 (division by zero). Unsigned integer division cannot overflow, so overflow is not checked.
Rationale
Why Direct-Revert Over Flags
Both EIP-1051 and EIP-6888 proposed flag-based approaches where existing arithmetic opcodes set implicit flags that must be checked by separate opcodes. This design has several drawbacks:
Multi-instruction overhead. Checking a flag still requires at least two additional instructions (read flag + conditional jump), reducing but not eliminating the checked arithmetic overhead.
Implicit state. Flags introduce hidden state that persists between instructions. This complicates static analysis, formal verification, and optimization passes in compilers.
Silent flag loss. If multiple arithmetic operations execute between flag checks, earlier overflow conditions are silently overwritten. This creates a subtle class of bugs in both hand-written assembly and compiler output.
Direct-revert opcodes are stateless, self-contained, and cannot silently lose error conditions. They also provide the maximum possible gas savings, since no additional instructions are needed.
Revert Instead of Exceptional Halt
Exceptional halts inside the EVM consume all remaining gas. These opcodes instead trigger a revert, which does not consume all gas. This distinction is important because checked arithmetic is commonly used for slippage checks and similar guards where the revert path is a normal, expected outcome. Consuming all gas in those cases would be unnecessarily punitive.
Why Use Up Four Opcodes?
To achieve the goal of performing basic arithmetic operations in a single opcode, unfortunately, four new opcodes are required. While this is very significant, it is justified by the anticipated, heavy use of the four new opcodes by newly deployed contracts. Our analysis before Devcon VII (see Devcon Presentation: “EVM Charts 2024: What’s hot? What’s not? by Dominic Bruetsch
Devcon SEA”) found that the existing four opcodes were among the Top-35 most-used opcodes, which together with the fact that checked arithmetic operations are now the default in Solidity and vyper provides an indication for this anticipated, heavy use.
Opcode Placement
The opcodes 0x0c–0x0f are placed immediately after the existing arithmetic group (0x01ADD through 0x0bSIGNEXTEND). This contiguous placement reflects their semantic relationship to existing arithmetic opcodes and simplifies implementation in EVM interpreters that use jump tables or opcode-range checks. All four slots are currently unassigned.
Gas Cost Justification
SAFEADD and SAFESUB are priced at 5 gas: the base cost of the underlying arithmetic operation (3 gas, the Gverylow group) plus 2 gas for the comparison and conditional revert check. The 2 gas premium conservatively accounts for the additional execution overhead.
SAFEMUL and SAFEDIV are priced at 7 gas: the base cost (5 gas, the Glow group) plus 2 gas for the check. SAFEMUL requires an internal division to verify the result; SAFEDIV requires a zero-comparison on the divisor. Both checks are computationally inexpensive relative to the underlying operation. Note that the existing MUL and DIV opcodes cost 5 gas, so the 2 gas premium is consistent with SAFEADD/SAFESUB.
Why Unsigned 256-bit Only
This EIP defines only unsigned safe arithmetic opcodes. Signed overflow detection (two’s complement) involves different boundary conditions and edge cases (e.g., INT256_MIN / -1 overflows). Rather than overloading this proposal, signed variants (SSAFEADD, SSAFESUB, SSAFEMUL, SSAFEDIV) are deferred to a companion EIP. Unsigned arithmetic covers the vast majority of smart contract operations, since Solidity’s uint256 and Vyper’s uint256 are the dominant numeric types. This is also the reason that smaller data types are not considered.
Why Include SAFEDIV
The existing DIV opcode silently returns 0 when the divisor is zero. While this is well-defined behavior, it is almost never the desired semantics — a division by zero virtually always indicates a logic error. Compilers today emit explicit zero-checks before DIV to revert on division by zero. SAFEDIV eliminates this pattern, providing the same gas and bytecode savings as the other safe opcodes.
Revert with Empty Returndata
Safe arithmetic opcodes revert with empty returndata (zero-length return buffer) rather than encoding a specific error message. This design is compiler-neutral: the EVM specification does not mandate ABI encoding, and different languages use different error encoding schemes. Empty returndata is also consistent with how compilers currently implement overflow reverts.
Backwards Compatibility
The opcodes 0x0c, 0x0d, 0x0e, and 0x0f are currently unassigned and behave as INVALID, consuming all gas if executed.
All existing arithmetic opcodes (ADD, SUB, MUL, DIV, and others) are entirely unchanged. Contracts using unchecked {} (Solidity) or unsafe_add() (Vyper) continue to emit raw ADD, SUB, MUL, and DIV opcodes with their existing semantics.
Compilers opt in to the new opcodes by targeting the post-fork EVM version. Contracts compiled for earlier EVM versions continue to function identically.
Test Cases
All test cases operate on unsigned 256-bit integers. MAX denotes 2^256 - 1 (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff). “Reverts” means the opcode triggers a revert with empty returndata and does not produce a stack output.
SAFEADD
#
a
b
Expected
Gas
1
1
2
3
5
2
0
100
100
5
3
100
0
100
5
4
MAX - 1
1
MAX
5
5
MAX
1
Reverts
5
6
1
MAX
Reverts
5
7
MAX
MAX
Reverts
5
8
2^128
2^128 - 1
2^129 - 1
5
9
0
0
0
5
SAFESUB
#
a
b
Expected
Gas
1
5
3
2
5
2
100
100
0
5
3
100
0
100
5
4
0
1
Reverts
5
5
0
MAX
Reverts
5
6
1
2
Reverts
5
7
MAX
MAX
0
5
8
MAX
0
MAX
5
SAFEMUL
#
a
b
Expected
Gas
1
3
4
12
7
2
0
MAX
0
7
3
MAX
0
0
7
4
1
MAX
MAX
7
5
MAX
1
MAX
7
6
2^128
2^128
Reverts
7
7
2^128
2^128 - 1
2^256 - 2^128
7
8
MAX
2
Reverts
7
9
0
0
0
7
SAFEDIV
#
a
b
Expected
Gas
1
10
3
3
7
2
10
10
1
7
3
10
0
Reverts
7
4
0
5
0
7
5
0
0
Reverts
7
6
MAX
1
MAX
7
7
MAX
MAX
1
7
8
1
MAX
0
7
Security Considerations
Gas Costs
The gas costs for these opcodes are conservatively set higher than their unchecked counterparts to avoid underpricing and prevent resource exhaustion attack vectors.
More Readable, Safer Smart Contracts
Once adopted, these opcodes allow developers to write more readable, safer code without the trade-off that checked arithmetic currently introduces.