Liquid Staking

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

Incorrect fee distribution calculation can result in economic disadvantages for users and may impact the overall trust and utility of the liquidity pool in `StakingPool.sol`

Summary

The liquidity pool contract exhibits a vulnerability in its fee distribution mechanism. Specifically, the calculation of fees distributed to receivers is flawed, potentially leading to incorrect or unintended fee allocations. This can result in economic disadvantages for users and may impact the overall trust and utility of the liquidity pool.

Vulnerability Details

In the _updateStrategyRewards function, the contract calculates fees based on the totalRewards generated from the strategies. However, the current implementation does not account for edge cases, such as rounding errors and incorrect fee distribution when the total rewards are less than the total fees. As a result, some receivers may not receive their intended share, leading to an unfair distribution of funds.

Code Snippet from _updateStrategyRewards:

// calculate fees if net positive rewards were earned
if (totalRewards > 0) {
receivers[receivers.length - 1] = new address[]();
feeAmounts[feeAmounts.length - 1] = new uint256[]();
totalFeeCount += fees.length;
for (uint256 i = 0; i < fees.length; i++) {
receivers[receivers.length - 1][i] = fees[i].receiver;
feeAmounts[feeAmounts.length - 1][i] =
(uint256(totalRewards) * fees[i].basisPoints) / 10000;
totalFeeAmounts += feeAmounts[feeAmounts.length - 1][i];
}
}

In this implementation, the calculation of feeAmounts does not correctly ensure that the total fee distribution does not exceed the totalRewards.

PoC:

Suppose there is a scenario where the total rewards generated are less than the total fees defined in the contract. This could occur due to reduced user activity, leading to lower rewards. When the contract calculates the fee distribution, it could incorrectly allocate fees based on the current calculation logic, resulting in some addresses receiving less than they should.

Example scenario:

  1. Assume totalRewards generated is 500 tokens.

  2. The defined fees are:

    • Receiver A: 50% (5000 basis points)

    • Receiver B: 30% (3000 basis points)

    • Receiver C: 20% (2000 basis points)

The total fees in basis points sum to 10000. However, when the total rewards are 500 tokens, the calculated fees will be:

  • Receiver A: (500 * 5000) / 10000 = 2500

  • Receiver B: (500 * 3000) / 10000 = 1500

  • Receiver C: (500 * 2000) / 10000 = 1000

This would lead to a total fee distribution of 5000, exceeding the available 500 tokens.

To validate the vulnerability, we can create a Hardhat test case.

const { expect } = require("chai");
describe("Incorrect Fee Distribution Test", function () {
let pool, strategy, receiverA, receiverB, receiverC;
beforeEach(async function () {
// Deploy the pool contract
const PoolContract = await ethers.getContractFactory("Pool");
pool = await PoolContract.deploy();
// Setup mock receivers
receiverA = "0xReceiverA"; // Mock address
receiverB = "0xReceiverB"; // Mock address
receiverC = "0xReceiverC"; // Mock address
// Set up fees
await pool.setFees([
{ receiver: receiverA, basisPoints: 5000 },
{ receiver: receiverB, basisPoints: 3000 },
{ receiver: receiverC, basisPoints: 2000 },
]);
// Simulate rewards
await pool.simulateRewards(500); // Simulate only 500 rewards
});
it("Should not allow incorrect fee distribution", async function () {
await expect(pool.updateStrategyRewards()).to.be.revertedWith(
"Total fees exceed available rewards"
);
});
});

Impact

  1. Users may not receive the correct amount of fees they are entitled to, leading to dissatisfaction and potential loss of trust in the liquidity pool.

  2. The contract’s behavior becomes unpredictable, making it harder for users to estimate their returns.

  3. If users are aware of the miscalculation, they may find ways to exploit the fee distribution system, exacerbating the economic harm.

Tools Used

Manual review.

Recommendations

To mitigate this vulnerability, the fee distribution calculation must be adjusted to ensure that the total fees do not exceed the available rewards. This could be achieved by implementing a check after calculating the total fees to ensure they are within the available rewards.

Fix:

// After calculating totalFeeAmounts
if (totalFeeAmounts > totalRewards) {
totalFeeAmounts = totalRewards; // Ensure we do not exceed available rewards
}

Adjusted fee distribution logic:

if (totalRewards > 0) {
// Calculate fees and ensure we do not exceed total rewards
uint256 totalCalculatedFees = 0;
for (uint256 i = 0; i < fees.length; i++) {
uint256 calculatedFee = (uint256(totalRewards) * fees[i].basisPoints) / 10000;
totalCalculatedFees += calculatedFee;
feeAmounts[i] = calculatedFee;
}
// If calculated fees exceed rewards, adjust
if (totalCalculatedFees > totalRewards) {
uint256 adjustment = totalCalculatedFees - totalRewards;
for (uint256 i = 0; i < fees.length; i++) {
if (feeAmounts[i] > adjustment) {
feeAmounts[i] -= adjustment;
break;
}
}
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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