Weakspot

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

PoolMath.sol — shareOf() computes each staker's reward share
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
  = 0

Any 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

Multiply first, divide last
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.

Keep reading