Division before multiplication, and how it silently returns zero
Integer division in Solidity truncates. Do it before multiplying and your reward calculation returns zero for nearly every user — with no revert, no warning, and no failing test.
Updated 2026-09-03
Solidity has no floating point. Every division truncates toward zero. That is well known — and yet the resulting bug ships constantly, because it produces no error of any kind. The function returns a number. The number is just wrong.
The vulnerable library
library PoolMath {
function shareOf(
uint256 userStake,
uint256 totalStaked,
uint256 rewardBalance
) internal pure returns (uint256) {
if (totalStaked == 0) return 0;
return (userStake / totalStaked) * rewardBalance;
}
}What it actually computes
Consider a staker with 10 tokens in a pool holding 1,000, with 500 tokens of rewards to distribute. The correct share is 5 tokens. What this returns:
(10 / 1000) * 500
= 0 * 500 <-- 10/1000 truncates to 0
= 0Any staker holding less than the entire pool gets zero. That is every staker in any pool with more than one participant — the overwhelmingly common case. The function does not revert, no event looks wrong, and a unit test written by the same person who wrote the bug will often use a single staker holding 100% of the pool, where 1000 / 1000 = 1 and the answer comes out right.
The fix
return (userStake * rewardBalance) / totalStaked;(10 * 500) / 1000 = 5. The rule is simply: always multiply before you divide, and where precision still matters, scale by a fixed-point factor (commonly 1e18) before dividing and scale back after. Watch for overflow when multiplying first — on Solidity 0.8+ that reverts rather than wrapping, which is a loud failure and therefore the good kind.