Rounding 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.