A bug was identified in the performUpkeep function IN LSTRewardsSplitter.sol. The function does not correctly update the principalDeposits
after a large rewards transfer, leading to incorrect balances and failed assertions in the unit tests.
The performUpkeep function is expected to update the principalDeposits correctly after processing rewards. However, the function fails to do so, resulting in incorrect values for principalDeposits and token balances. This discrepancy was observed during the unit tests, where the expected value of principalDeposits did not match the actual value.
it('performUpkeep should work correctly2', async () => {
const { signers, accounts, controller, token, splitter0, splitter1 } = await loadFixture(deployFixture)
await token.transferAndCall(controller.target, toEther(100), '0x')
await token.connect(signers[1]).transferAndCall(controller.target, toEther(200), '0x')
await token.transfer(splitter0.target, toEther(100))
await token.transfer(splitter1.target, toEther(80))
await expect(
controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[false, false]])
)
).to.be.revertedWithCustomError(controller, 'InvalidPerformData()')
await expect(
controller.performUpkeep(ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]]))
).to.be.revertedWithCustomError(splitter0, 'InsufficientRewards()')
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, false]])
)
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)
assert.equal(fromEther(await splitter1.principalDeposits()), 200)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 280)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 0)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 0)
await token.transfer(splitter0.target, toEther(200))
await token.transfer(splitter1.target, toEther(20))
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
assert.equal(fromEther(await splitter0.principalDeposits()), 310)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 310)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 30)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 60)
assert.equal(fromEther(await splitter1.principalDeposits()), 240)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 240)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 20)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 40)
await token.setMultiplierBasisPoints(1000)
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
assert.equal(fromEther(await splitter0.principalDeposits()), 31)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 31)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 3)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 6)
assert.equal(fromEther(await splitter1.principalDeposits()), 24)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 24)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 2)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 4)
console.log('Before large rewards transfer:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
console.log('splitter1 principalDeposits:', fromEther(await splitter1.principalDeposits()))
console.log('splitter1 balance:', fromEther(await token.balanceOf(splitter1.target)))
await token.transfer(splitter0.target, toEther(1000))
await token.transfer(splitter1.target, toEther(1000))
console.log('Before performUpkeep:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
console.log('splitter1 principalDeposits:', fromEther(await splitter1.principalDeposits()))
console.log('splitter1 balance:', fromEther(await token.balanceOf(splitter1.target)))
console.log('controller balance:', fromEther(await token.balanceOf(controller.target)))
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
console.log('After performUpkeep:')
console.log('splitter0 principalDeposits:', fromEther(await splitter0.principalDeposits()))
console.log('splitter0 balance:', fromEther(await token.balanceOf(splitter0.target)))
console.log('splitter1 principalDeposits:', fromEther(await splitter1.principalDeposits()))
console.log('splitter1 balance:', fromEther(await token.balanceOf(splitter1.target)))
console.log('controller balance:', fromEther(await token.balanceOf(controller.target)))
assert.equal(fromEther(await splitter0.principalDeposits()), 1031)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 1031)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 130)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 260)
assert.equal(fromEther(await splitter1.principalDeposits()), 1024)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 1024)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 120)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 240)
await token.setMultiplierBasisPoints(1000)
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
assert.equal(fromEther(await splitter0.principalDeposits()), 131)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 131)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 13)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 26)
assert.equal(fromEther(await splitter1.principalDeposits()), 124)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 124)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 12)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 24)
await token.transfer(splitter0.target, toEther(100))
await token.transfer(splitter1.target, toEther(100))
await controller.performUpkeep(
ethers.AbiCoder.defaultAbiCoder().encode(['bool[]'], [[true, true]])
)
assert.equal(fromEther(await splitter0.principalDeposits()), 231)
assert.equal(fromEther(await token.balanceOf(splitter0.target)), 231)
assert.equal(fromEther(await token.balanceOf(accounts[5])), 23)
assert.equal(fromEther(await token.balanceOf(accounts[6])), 46)
assert.equal(fromEther(await splitter1.principalDeposits()), 224)
assert.equal(fromEther(await token.balanceOf(splitter1.target)), 224)
assert.equal(fromEther(await token.balanceOf(accounts[7])), 22)
assert.equal(fromEther(await token.balanceOf(accounts[8])), 44)
})
The expected value for splitter0.principalDeposits() was 1031, but the actual value was 128. This indicates that the performUpkeep function did not correctly update the principalDeposits.
The incorrect update of principalDeposits can lead to inaccurate accounting of rewards and balances, potentially causing financial discrepancies and affecting the integrity of the staking mechanism. This bug can undermine user trust and lead to financial losses if not addressed promptly.
Ensure that the performUpkeep function correctly calculates and updates the principalDeposits based on the new rewards.