Skip to content

SC07:2026 Arithmetic Errors (Rounding & Precision)

Vulnerability: Arithmetic Errors (Rounding & Precision Loss)

Description

Arithmetic errors (rounding and precision loss) describe any situation where a smart contract performs integer-based calculations that produce incorrect or exploitable results due to truncation, scaling, or unit conversion. Smart contracts are limited to integer arithmetic; any division, fixed-point scaling, or conversion between units can lose precision, introduce asymmetric rounding, or—when combined with unchecked blocks or non-EVM semantics—cause overflow/underflow (see SC09).

This affects all contract types that compute numeric values: DeFi (share minting/burning, LP tokens, interest accrual, swap outputs, AMM invariant updates), yield vaults and ERC-4626 (asset/share conversions), rebasing tokens, reward distribution, and NFT/token economics. On non-EVM chains (e.g., Move, Rust-based), integer semantics and available precision differ; similar risks apply wherever arithmetic drives economic outcomes.

Few areas to focus on:

  • Share and LP token calculations (deposit/withdraw formulas, rounding direction)
  • Interest and reward accrual (compounding, time-weighted averages)
  • Swap and AMM math (constant product, concentrated liquidity, output calculations)
  • Fixed-point and scaling (1e18, 1e8 conventions, cross-token conversions)
  • Rebasing and proportional distribution (per-user vs. global accounting)

Attackers exploit:

  • Rounding bias (e.g., rounding that favors depositor or protocol under adversarial sequences)
  • Repeated small gains via flash loans (SC04) or high-frequency interactions
  • Edge cases (zero total supply, first depositor, extreme ratios) where formulas break down
  • Precision loss in multi-step computations that accumulates across operations

When combined with flash loans (SC04) or business logic flaws (SC02), arithmetic errors can be amplified into protocol-draining exploits.

Example (Vulnerable Share Calculation)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VulnerableShares {
    uint256 public totalAssets;
    uint256 public totalShares;

    mapping(address => uint256) public balanceOf;

    function deposit(uint256 assets) external {
        require(assets > 0, "zero");

        uint256 shares;
        if (totalShares == 0) {
            shares = assets;
        } else {
            // Rounds down and may favor the depositor under certain edge states
            shares = (assets * totalShares) / totalAssets;
        }

        totalAssets += assets;
        totalShares += shares;
        balanceOf[msg.sender] += shares;
    }
}

Issues:

  • Rounding always truncates; under adversarial sequences of deposits/withdrawals, this can be gamed.
  • No invariant tests to ensure that total share value remains consistent across edge cases.

Example (More Robust Arithmetic & Invariant-Aware Design)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SaferShares {
    uint256 public totalAssets;
    uint256 public totalShares;

    mapping(address => uint256) public balanceOf;

    error ZeroAmount();
    error InvalidState();

    function deposit(uint256 assets) external {
        if (assets == 0) revert ZeroAmount();

        uint256 shares;
        if (totalShares == 0 || totalAssets == 0) {
            // Explicitly define initial conditions
            shares = assets;
        } else {
            // Use rounding strategy intentionally (up or down) and test it
            shares = (assets * totalShares + totalAssets - 1) / totalAssets; // round up
        }

        uint256 newTotalAssets = totalAssets + assets;
        if (newTotalAssets < totalAssets) revert InvalidState(); // overflow guard

        totalAssets = newTotalAssets;
        totalShares += shares;
        balanceOf[msg.sender] += shares;
    }
}

Security Improvements:

  • Explicitly handles initial states and avoids division by zero.
  • Uses a clearly documented rounding strategy (round up in this example).
  • Adds overflow checks and custom errors for clarity.

Real-world protocols should pair such logic with formal reasoning and invariants (e.g., through formal verification or property-based tests).

2025 Case Studies

Best Practices & Mitigations

  • Use safe math patterns (Solidity 0.8+ has built-in checks, but logic around them still matters).
  • Clearly document and test your rounding strategy:
  • Decide whether rounding should favor the protocol or users.
  • Prove that repeated interactions cannot create “free value”.
  • Rely on well-reviewed math libraries for complex operations:
  • Fixed-point math (e.g., 1e18 scaling)
  • High-precision exponentiation or logarithms
  • Incorporate invariant checks:
  • E.g., totalAssets vs. sum of user balances, or share/value consistency after operations.
  • Use fuzz testing and differential testing to discover edge cases around small/large values and repeated operations.