This report investigates a specific vulnerability within the checkUpkeep functions of the LSTRewardsSplitter contract. The primary issue revolves around handling reward calculations when new rewards are negative, which can cause incorrect upkeep triggers.
The logic in the checkUpkeep function incorrectly handles cases where new rewards are negative. This leads the function to return true (indicating that upkeep is needed) even when it should not, resulting in unnecessary and potentially costly invocations of the performUpkeep function.
it('checkUpkeep should work correctly2', async () => {
const { controller, token, splitter0, splitter1 } = await loadFixture(deployFixture)
await token.transfer(splitter0.target, toEther(90))
await token.transfer(splitter1.target, toEther(80))
assert.deepEqual(await controller.checkUpkeep('0x'), [
false,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[false, false]]),
])
await token.transfer(splitter1.target, toEther(20))
assert.deepEqual(await controller.checkUpkeep('0x'), [
true,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[false, true]]),
])
await token.transfer(splitter0.target, toEther(10))
assert.deepEqual(await controller.checkUpkeep('0x'), [
true,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]]),
])
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
assert.deepEqual(await controller.checkUpkeep('0x'), [
false,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[false, false]]),
])
await token.setMultiplierBasisPoints(5000)
assert.deepEqual(await controller.checkUpkeep('0x'), [
true,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]]),
])
await token.transfer(splitter0.target, toEther(100))
console.log('After transfer:')
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
console.log('splitter1 balance:', fromEther(await token.balanceOf(splitter1.target)))
console.log('principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('controller balance:', fromEther(await token.balanceOf(controller.target)))
const checkUpkeepResult = await controller.checkUpkeep('0x')
console.log('checkUpkeep result:', checkUpkeepResult)
const controllerBalance = await token.balanceOf(controller.target)
const principalDeposits = await splitter0.principalDeposits()
const newRewards = controllerBalance - principalDeposits
const rewardThreshold = await controller.rewardThreshold()
console.log('controllerBalance:', fromEther(controllerBalance))
console.log('principalDeposits:', fromEther(principalDeposits))
console.log('newRewards:', fromEther(newRewards))
console.log('rewardThreshold:', fromEther(rewardThreshold))
const expectedNewRewards = controllerBalance - principalDeposits
assert.deepEqual(checkUpkeepResult, [
expectedNewRewards >= rewardThreshold,
ethers.AbiCoder.defaultAbiCoder().encode(
['bool[]'],
[[expectedNewRewards >= rewardThreshold, expectedNewRewards >= rewardThreshold]]
),
])
await token.setMultiplierBasisPoints(20000)
assert.deepEqual(await controller.checkUpkeep('0x'), [
false,
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[false, false]]),
])
})
The function checkUpkeep returned true incorrectly when newRewards was negative. The correct behavior should involve a more explicit check to avoid triggering upkeep without valid positive rewards.
Combining these factors, the severity of this finding is classified as Medium. It is crucial to address this to prevent unnecessary costs and inefficiencies.
Update checkUpkeep Logic: Explicitly handle negative newRewards values to avoid unnecessary upkeep calls.