Failing to update rewards before executing the handleIncomingUpdate
function in SDLPoolPrimary
, while adjusting the effectiveBalance
of the ccipController
, results in miscalculated rewards. This oversight can obstruct the distribution of rewards for the secondary chain.
Actions taken in the secondary pool are queued and then communicated to the primary pool. The primary pool must acknowledge these changes before they are executed. The message sent to the primary pool includes the number of new queued locks to be minted (numNewQueuedLocks
) and the change in the reSDL supply (reSDLSupplyChange
).
Upon receiving the message, the SDLPoolCCIPControllerPrimary
contract updates the reSDLSupplyByChain
and forwards the message to SDLPoolPrimary
. The SDLPoolPrimary
contract then processes the message, returning the mintStartIndex
for the secondary chain to use when minting new locks. It also updates the effectiveBalances
for the ccipController
and the totalEffectiveBalance
.
The issue arises because the handleIncomingUpdate
function does not update the rewards before altering these values. Since these values directly affect reward accounting, failing to update them leads to incorrect calculations. This could result in a scenario where the total rewards accrued by all stakers exceed the available balance in the rewardsPool
.
For example, consider Alice has staked 500 sdl
tokens, and there is an outgoing 1000 reSdl
. The state would be as follows:
effectiveBalance[alice]
= 500
effectiveBalance[ccipController]
= 1000
totalEffectiveBalance
= 1500
Now, assume 1500 reward
tokens are distributed this will update the rewardPerToken = 1
(rewards/totalStaked), and Alice will withdraw her rewards. The amount of rewards Alice receives is calculated using the withdrawableRewards
function, which relies on her effectiveBalance
(controller.staked()). With a rewardPerToken
of 1
and Alice's userRewardPerTokenPaid
at 0
, Alice would receive 500 rewards
.
now, someone stakes another 1000 sdl
on the secondary chain, an incoming update with a supply change of 1000
is received on the primary chain. This update changes the effectiveBalance[ccipController]
to 2000
without a prior reward update which will keep the userRewardPerTokenPaid
for ccipController 0.
Consequently, when the RewardsInitiator
contract calls the distributeRewards
function in SDLPoolCCIPControllerPrimary
, attempting to withdrawRewards
from the rewardPool
the call will perpetually fail. The rewards for the ccipController
would be calculated as 2000 * (1 - 0) = 2000 rewards
, while the actual balance of the rewardsPool
is only 1000 rewards
.
notice that the increase of 1000
will never be solved .
here a poc that shows , that not updating reward in incomingUpdates , will cause the distributeReward function to revert , cause of insufficient balance in the reward pool , i used the repo setup :
Incorrect reward calculations could prevent rightful stakers from receiving their due rewards or leave unclaimable rewards in the pool (in the case of negative supply change), thereby compromising the protocol's credibility.
manual review
Implement an updateReward(ccipController)
call within the handleIncomingUpdate
function to ensure rewards are recalculated whenever effectiveBalance
changes. This will prevent miscalculations and maintain reward distribution accuracy.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.