Liquid Staking

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

Critical Vulnerability in LSTRewardsSplitter::splitRewards: Incorrect Update of principalDeposits Leads to Potential Reward Distribution Discrepancies and Exploitation Risks

Summary

A critical bug was identified in the splitRewards function of the LSTRewardsSplitter contract. The function fails to correctly update the principalDeposits after splitting rewards, leading to potential discrepancies in reward distribution.

Vulnerability Details

The splitRewards function is intended to split new rewards between fee receivers. However, the function does not correctly update the principalDeposits after splitting the rewards, resulting in incorrect balances and potential exploitation.

The issue lies in the calculation and update of principalDeposits. The function does not correctly account for the new rewards, leading to incorrect updates of the principalDeposits.

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/lstRewardsSplitter/LSTRewardsSplitter.sol#L112C1-L125C6

function splitRewards() external {
int256 newRewards = int256(lst.balanceOf(address(this))) - int256(principalDeposits);
if (newRewards < 0) {
principalDeposits -= uint256(-1 * newRewards);
} else if (newRewards == 0) {
revert InsufficientRewards();
} else {
_splitRewards(uint256(newRewards));
}
}

POC

  • Past this poc into the existing test test/core/lst-rewards-splitter.test.ts

//@audit
it('splitRewards should work correctly', async () => {
const { accounts, controller, token, splitter0 } = await loadFixture(deployFixture)
await token.transferAndCall(controller.target, toEther(100), '0x')
await expect(splitter0.splitRewards()).to.be.revertedWithCustomError(
splitter0,
'InsufficientRewards()'
)
await token.transfer(splitter0.target, toEther(100))
await splitter0.splitRewards()
assert.equal(fromEther(await splitter0.principalDeposits()), 170)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 170)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 10)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 20)
await token.setMultiplierBasisPoints(2000)
await splitter0.splitRewards()
assert.equal(fromEther(await splitter0.principalDeposits()), 34)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 34)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 2)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 4)
//@audit Test for potential exploitation
console.log('Before large rewards transfer:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
await token.transfer(splitter0.target, toEther(1000))
console.log('Before splitRewards:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
await splitter0.splitRewards()
console.log('After splitRewards:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
assert.equal(fromEther(await splitter0.principalDeposits()), 1034)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 1034)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 102)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 204)
})
  • Output:
    The test case fails, indicating that the splitRewards function does not correctly update the principalDeposits:

Before large rewards transfer:
splitter0 principalDeposits: 34
splitter0 balance: 34
Before splitRewards:
splitter0 principalDeposits: 34
splitter0 balance: 234
After splitRewards:
splitter0 principalDeposits: 222
splitter0 balance: 222
AssertionError: expected 222 to equal 1034

Impact

The incorrect update of principalDeposits can lead to several issues:

  • Incorrect Reward Distribution: Users may not receive the correct rewards, resulting in financial discrepancies.

  • Potential Exploitation: Malicious actors could exploit the incorrect update to manipulate the reward distribution.

  • Financial Loss: The platform and its users may suffer financial losses due to incorrect reward calculations

Tools Used

  • Hardhat

Recommendations

To resolve the issue, update the splitRewards function to ensure that the principalDeposits variable is correctly updated after splitting the rewards. This will involve recalculating the principalDeposits based on the current balance after the rewards have been split. This change will help maintain accurate balances and prevent potential discrepancies in reward distribution.

Updates

Lead Judging Commences

inallhonesty Lead Judge
9 months ago
inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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