The protocol relies on SDLPoolPrimary (deployed on Ethereum) to be informed about effective balance changes on secondary chains. This is essential for keeping track of the rewards it needs to distribute to stakers on those chains. However, the protocol can get stuck in a situation where it's never informed about these changes due to a lack of rewards to distribute.
The process begins with a call to SDLCCIPControllerPrimary.distributeRewards()
made by the RewardsInitiator
, which is triggered by Chainlink automation. This function calculates the total amount of tokens to distribute to each chain:
Note how uint256 rewards is calculated based on reSDLSupplyByChain for a specific chain. If reSDLSupplyByChain[chainSelector] is 0 and the chain is not the last one in whitelistedChains, the rewards will be zero. Additionally, SDLCCIPControllerPrimary._distributeRewards has a check where it returns early if there are no rewards:
In this scenario, the protocol will not send the ccip message to the secondary chain, thus reSDLSupplyByChain will not be updated, keeping the rewards at zero.
The impact is high as this can cause the secondary chain to be in an eternal waiting state for the update process, not informing the primary chain of changes on the reSDL supply. However, this can be easily fixed by transferring some reSDL tokens to that secondary chain using the bridge. This action makes SDLCCIPControllerPrimary.handleOutgoingRESDL modify the reSDLSupplyByChain, thereby starting the update process:
Manual Review
It is recommended to check if an update is needed or not in the SDLPoolCCIPControllerSecondary.checkUpkeep(bytes calldata) function:
Since the underlying call to the pool only checks for flags to be set appropriately, it shouldn't incur a huge expenditure of gas and would allow the updates to be performed as expected.
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.