Remediating Decimal Mismatch
How to normalize token decimal precision before arithmetic to prevent over/under-payment vulnerabilities.
Remediating Decimal Mismatch
Overview
Related Detector: Decimal Mismatch
Always normalize values to a common decimal base before performing arithmetic across different tokens or price feeds.
Recommended Fix
Before (Vulnerable)
function getValueInUsdc(uint256 ethAmount) public view returns (uint256) {
(, int256 price,,,) = ethUsdFeed.latestRoundData(); // 8 decimals
return ethAmount * uint256(price); // 18 + 8 = 26 decimals — not USDC's 6
}
After (Fixed)
function getValueInUsdc(uint256 ethAmount) public view returns (uint256) {
(, int256 price,,,) = ethUsdFeed.latestRoundData(); // 8 decimals
// Normalize: 18 (ETH) + 8 (price) - 6 (USDC) = 20 excess decimals
return (ethAmount * uint256(price)) / 1e20;
}
Alternative Mitigations
- Internal precision: Use a high-precision internal representation (e.g., 27 decimals like RAY in MakerDAO) and convert at boundaries.
- Token decimals lookup: Query
token.decimals()at initialization and store the scaling factor.
Common Mistakes
- Hardcoding decimal assumptions: Assuming all tokens use 18 decimals. USDC/USDT use 6, WBTC uses 8.
- Ignoring oracle decimals: Chainlink feeds return 8 decimals for USD pairs but 18 for ETH pairs.