Last updated: July 03, 2025
SCWE-088: Improper Decimal Normalization in Price-Based Calculations
Stable Version v0.0.1
This content is in the version-(v0.0.1) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).
Send Feedback
Relationships
Description
This issue arises when smart contracts perform arithmetic involving token values and price feeds without properly aligning decimal places across different sources (e.g. ERC-20 tokens with varying decimals and Chainlink oracles with fixed 8-decimal prices). Failing to normalize decimals before calculations can lead to severely inflated or deflated results, causing users to be overcharged, underpaid, or otherwise economically exploited.
Impact
Smart contracts often rely on cross-asset conversions, such as paying fees in tokens or using collateralization logic involving price feeds. Improper decimal handling in these calculations may:
- Lead to gross overcharging or underpayment.
- Introduce systemic financial imbalances in vaults or accounting logic.
- Enable economic exploits by arbitraging rounding errors or precision gaps.
- Remain undetected during tests if decimals coincidentally align.
This issue is especially critical when:
- ERC-20 tokens with non-18 decimals (e.g., USDC = 6) are involved.
- Chainlink oracles return 8-decimal fixed-point prices.
- Arithmetic mixes native ETH values (in 18 decimals) with token values or prices.
- Always normalize decimals across all involved assets and feeds.
- Use centralized utility functions for all price/token conversions.
- Add sanity checks to detect outlier results (e.g., revert if output is >1000x expected range).
- When possible, emit intermediate calculation steps for auditing.
Examples
Realistic Exploit Example
Assumptions:
Registrar fee = 0.01 ETH = 1e16 wei
ETH/USD price = 3000 * 1e8 = 300000000000
USDC/USD price = 1 * 1e8 = 100000000
USDC has 6 decimals
Faulty Calculation:
result = (1e16 * 300000000000) / 100000000 = 3e19
return result / 1e6 = 3e13 (30 trillion base units = 30 million USDC)
Expected:
0.01 ETH * $3,000 = $30
→ 30 * 1e6 = 30,000,000 USDC units
Overcharge:
Actual charged: 30,000,000 USDC
Expected: 30 USDC
Overcharge: 999,900x