Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Inconsistent Fee Calculation Leading to Potential Fee Loss

Summary

A potential inconsistency exists in the StakingPool.sol contract's fee calculation logic. Specifically, when the aggregated totalFees exceed or equal the totalStaked, the contract erroneously resets totalFees to zero. This behavior can lead to inaccurate fee distribution, resulting in fee recipients not receiving their rightful rewards.

Vulnerability Details

Issue in Fee Calculation Logic

Code Snippet:

if (totalFees >= totalStaked) {
totalFees = 0;
}

Explanation:
In the getStrategyRewards function, after calculating the total fees from various strategies, the contract checks if totalFees is greater than or equal to totalStaked. If this condition is met, it resets totalFees to zero. This abrupt reset bypasses the intended fee distribution mechanism.

Incorrect Fee Distribution

for (uint256 i = 0; i < fees.length; i++) {
totalFees += (uint256(totalRewards) * fees[i].basisPoints) / 10000;
}

Explanation:
Fees are calculated based on the totalRewards using basis points. These fees are added to the existing totalFees. However, if the sum of these fees exceeds the totalStaked, the subsequent condition resets totalFees to zero, nullifying the fee distribution.

Proof of Concept (PoC)

1. Setting Up Mock Strategies

Code Snippet:

// Mock Strategy A with high pending fees
MockStrategy strategyA = new MockStrategy(500, 300);
// Mock Strategy B with high pending fees
MockStrategy strategyB = new MockStrategy(600, 700);

Explanation:
Deploy two mock strategies, strategyA and strategyB, with predefined depositChange and pendingFees. These values are set to simulate a scenario where totalFees may exceed totalStaked.

2. Adding Strategies to StakingPool

Code Snippet:

// Add strategies to stakingPool
stakingPool.addStrategy(address(strategyA));
stakingPool.addStrategy(address(strategyB));

Explanation:
Integrate the mock strategies into the StakingPool contract to participate in fee calculations.

3. Simulating Deposits

Code Snippet:

// Simulate deposit from priorityPool
stakingPool.deposit(owner, 1000 ether, data);

Explanation:
Deposit 1,000 LINK tokens into the staking pool, representing totalStaked. This sets the baseline for assessing whether totalFees exceeds totalStaked.

4. Calculating Strategy Rewards and Fees

Code Snippet:

// Call getStrategyRewards with both strategies
(uint256 totalRewards, uint256 totalFees) = stakingPool.getStrategyRewards([0, 1]);

Explanation:
Invoke the getStrategyRewards function with both strategies. The function aggregates rewards and fees based on the strategies' outputs.

5. Observing Fee Adjustment

Code Snippet:

// Assert that totalFees is correctly capped
assertEq(totalFees, 0);

Explanation:
After the fee calculation, verify that totalFees has been erroneously set to zero when it exceeds totalStaked, demonstrating the vulnerability.

Impact

  • Fee Loss: Recipients designated to receive fees may not receive any rewards when totalFees surpass totalStaked, leading to financial discrepancies.

  • Trust Erosion: Users and stakeholders may lose trust in the platform's fee distribution mechanism, potentially affecting user retention and platform credibility.

  • Operational Inefficiency: Incorrect fee handling can complicate financial tracking and reporting within the platform.

Tools Used

  • Manual Code Review: Thorough examination of the StakingPool.sol contract to identify logical inconsistencies.

  • Static Analysis: Utilization of static analysis tools to corroborate findings and ensure no additional vulnerabilities are present.

Recommendations

Mitigation Steps and Recommendations

  1. Adjust Fee Capping Logic:

    Code Snippet:

    if (totalFees >= totalStaked) {
    totalFees = totalStaked;
    }

    Explanation:
    Instead of resetting totalFees to zero when it exceeds totalStaked, cap it at totalStaked. This ensures that fees are distributed accurately without exceeding the available staked amount.

  2. Implement Overflow Checks:

    Code Snippet:

    require(totalFees <= type(uint256).max - additionalFees, "Fee overflow");

    Explanation:
    Incorporate checks to prevent arithmetic overflows during fee calculations, ensuring that totalFees remains within safe bounds.

  3. Emit Events on Fee Adjustment:

    Code Snippet:

    if (totalFees >= totalStaked) {
    totalFees = totalStaked;
    emit FeesCapped(totalFees);
    }

    Explanation:
    Emit an event whenever fees are capped to provide transparency and facilitate easier tracking of fee adjustments.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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