DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Unvalidated Zero-Amount Stakes Lead to Potential Division by Zero in Reward Calculation

Summary

The FjordPoints smart contract contains a critical vulnerability in the onStaked function due to a lack of input validation for stake amounts. This oversight allows for zero-amount stakes, which can potentially manipulate the totalStaked value to zero. As a result, the pointsPerToken calculation in the reward distribution mechanism is susceptible to a division by zero error, which could render the entire contract non-functional.

Vulnerability Details

The vulnerability stems from two key issues in the smart contract:

1: The onStaked function does not validate that the staked amount is greater than zero:

function onStaked(address user, uint256 amount) external onlyStaking checkDistribution updatePendingPoints(user) {
UserInfo storage userInfo = users[user];
userInfo.stakedAmount = userInfo.stakedAmount.add(amount);
totalStaked = totalStaked.add(amount);
emit Staked(user, amount);
}

2: The pointsPerToken calculation in the distributePoints function relies on totalStaked as a denominator:

pointsPerToken = pointsPerToken.add(weeksPending * (pointsPerEpoch.mul(PRECISION_18).div(totalStaked)));

Impact

The exploitation of this vulnerability could lead to:

  • Complete failure of the reward distribution mechanism

  • Inability to calculate or distribute rewards to any users

  • Potential permanent loss of accumulated rewards

  • Necessity for contract migration or upgrade, eroding user trust

  • Possible crash in the value of associated tokens

  • Cascading failures in interconnected contracts or systems relying on this contract

Tools Used

Manual review

Recommendations

1: Implement input validation in the onStaked function:

function onStaked(address user, uint256 amount) external onlyStaking checkDistribution updatePendingPoints(user) {
require(amount > 0, "Stake amount must be greater than zero");
// ... rest of the function
}

2: Add a safety check in the distributePoints function to prevent division by zero:

function distributePoints() public {
// ... existing code
require(totalStaked > 0, "Total staked amount must be greater than zero");
pointsPerToken = pointsPerToken.add(weeksPending * (pointsPerEpoch.mul(PRECISION_18).div(totalStaked)));
// ... rest of the function
}

3: Implement a minimum stake amount to prevent small stakes that could lead to precision issues:

uint256 public constant MIN_STAKE_AMOUNT = 1e6; // Example: 1 token with 6 decimals
function onStaked(address user, uint256 amount) external onlyStaking checkDistribution updatePendingPoints(user) {
require(amount >= MIN_STAKE_AMOUNT, "Stake amount must be at least MIN_STAKE_AMOUNT");
// ... rest of the function
}

4: Consider implementing a two-step process for critical state changes, such as large unstaking operations that could significantly impact totalStaked.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.