rounding-issues-and-exploits
Scannednpx machina-cli add skill mnedelchev-vn/solidity-defi-claude-skills/rounding-issues-and-exploits --openclawRounding issues and exploits analysis
Detect vulnerabilities that allow attackers to harm protocol's funds or other user's funds. These attacks are most likely used to steal funds directly from the protocols.
When to Use
- Reviewing logic that includes yield bearing assets — conversion of assets to shares and vice versa
- Reviewing logic that includes interest calculation ( e.g. borrow or repay actions )
- Reviewing logic involving fees calculation and distribution
- Math operations calculating liquidations and collateral positions state update
- Any math logic that includes calculations of swapping or trading input or output amounts
When NOT to Use
- Contracts with no math calculations
Examples of rounding issues and exploits
Case 1: Solidity doesn't support Floating Point Arithmetic
Solidity doesn’t support floats by default which means that 10 / 3 * 5 and 10 * 5 / 3 in Solidity will never have the same output. In the first case the output is 15 and in the second the output is 16. To reduce precision loss we must always first multiply and then divide.
Case 2: Dividing to bigger number will result to 0
Check if there are scenarios where the divisor is actually bigger than the dividend. In Solidity this will be returned as 0 and not reverting. Example:
10 / 50 will equal to 0.
Case 3: Can't divide by 0
A division by zero will revert in Solidity. This case should never exist.Example:
10 / 0 will revert.
Case 4: Never leave the rounding direction unclear
The rounding dicrection has to be explicitly defined — to be up or down with proper comments why this decision has been made. The accepted approach is to always round up in favour of the protocol:
- When calculating amounts paid out to users → round down
- When calculating amounts paid in by users → round up
- This “protocol-first” approach is what actually OZ's ERC4626 does:
- When user is depositing though the
depositmethod then the shares are calculated with down rounding - When user is depositing through the
mintmethod then the assets are calculated with the up rounding - When user is withdrawing though the
withdrawmethod then the shares are calculated with up rounding - When user is withdrawing though the
redeemmethod then the assets are calculated with down rounding
- When user is depositing though the
- The “protocol-first” approach should also be considered when calculating trading fees or withdrawal fees. Round down in this example could lead to user exploiting actions with smaller amounts in order to skip paying any fees. This is why we have to round up here.
Very often mistake is that the developer has used the same rounding direction in opposite method e.g. deposit and withdraw.
Case 5: Wrong percentage calculation
All fees or interest calculations include multiply and division to values lesser than 1. However Solidity doesn't support floats thus making 0.3% fee charge or 3% borrow interest calculations not a straight forward thing to calculate. We need to scale up the percentage values and then divide. For example let's say that we have uint256 tradingFee = 3000. This means that we have to multiply amount with tradingFee and then divide by 10000. It's possible that protocol builder has missed a 0 which leads to false calculation — amount * 3000 / 1000.
Additional Analysis
Beyond the patterns above, apply your full security knowledge to identify any related issues not covered here.
Source
git clone https://github.com/mnedelchev-vn/solidity-defi-claude-skills/blob/master/skills/rounding-issues-and-exploits/SKILL.mdView on GitHub Overview
This skill analyzes Solidity smart contracts to detect rounding issues and exploits that could harm protocol or user funds. It’s essential for DeFi protocols dealing with assets-to-shares conversions, interest, fees, liquidations, or swap/trade math, where precision errors can lead to financial loss.
How This Skill Works
It examines math-heavy code paths such as asset-to-share conversions, interest calculations, fee distributions, and swap computations to identify precision loss and unsafe rounding. It promotes a protocol-first rounding approach, ensures consistent rounding across related methods, and verifies scaling of percentage-based calculations to prevent miscalculation.
When to Use It
- Reviewing logic that includes yield bearing assets — conversion of assets to shares and vice versa
- Reviewing logic that includes interest calculation ( e.g. borrow or repay actions )
- Reviewing logic involving fees calculation and distribution
- Math operations calculating liquidations and collateral positions state update
- Any math logic that includes calculations of swapping or trading input or output amounts
Quick Start
- Step 1: Identify math-heavy entry points (mint/deposit, redeem/withdraw, borrow/repay, fees, swaps)
- Step 2: Check rounding directions and ensure explicit, consistent policy across all related methods
- Step 3: Add unit tests that simulate edge cases and verify results with scaled integer arithmetic
Best Practices
- Multiply before divide for percentage calculations and scale values (e.g., use 10000 as denominator) to simulate decimals
- Clearly define rounding direction for each action (deposit vs. mint, withdraw vs. redeem) and document the protocol-first policy
- Ensure rounding is consistent across related methods to avoid gaps that could be exploited
- Test edge cases: division by zero, zero results, and extreme numeric values to surface precision issues
- Audit fee and interest formulas for correct scaling and avoid truncation that could enable exploitation
Example Use Cases
- Case 1: Solidity doesn’t support floats; 10 / 3 * 5 vs 10 * 5 / 3 yield different results due to precision
- Case 2: Dividing to a bigger number can yield 0 (e.g., 10 / 50 = 0) if not handled
- Case 3: Division by zero reverts; such cases must be guarded against early
- Case 4: Inconsistent rounding across methods (deposit vs withdraw) can enable subtle exploits
- Case 5: Wrong percentage calculations due to missing scaling (e.g., using 3000 with divisor 1000)