Updates from the `secondary pool` to the `primary pool` may not be sent because there are `no rewards` for the secondary pool
The SDLPoolCCIPControllerSecondary::performUpkeep() function is only available when there is a `message of rewards` from the `SDLPoolCCIPControllerPrimary`. That could be a problem if there are not rewards to distribute in a specific `secondary chain` causing that queue updates from the `secondarly chain` will not be informed to the `SDLPoolPrimary`.\\nThe `secondary chain` informs to the `primary chain` the new `numNewRESDLTokens` and `totalRESDLSupplyChange` using the SDLPoolCCIPControllerSecondary::performUpkeep function, then the `primary chain` receives the information and it calculates the new mintStartIndex. Note that the `primary chain` increments the `reSDLSupplyByChain` in the `code line 300`, this so that the `primary chain` has the information on how much supply of reSDL tokens there is in the secondary chain:\\n```\\nFile: SDLPoolCCIPControllerPrimary.sol\\n    function _ccipReceive(Client.Any2EVMMessage memory _message) internal override {\\n        uint64 sourceChainSelector = _message.sourceChainSelector;\\n\\n        (uint256 numNewRESDLTokens, int256 totalRESDLSupplyChange) = abi.decode(_message.data, (uint256, int256));\\n\\n        if (totalRESDLSupplyChange > 0) {\\n            reSDLSupplyByChain[sourceChainSelector] += uint256(totalRESDLSupplyChange);\\n        } else if (totalRESDLSupplyChange < 0) {\\n            reSDLSupplyByChain[sourceChainSelector] -= uint256(-1 * totalRESDLSupplyChange);\\n        }\\n\\n        uint256 mintStartIndex = ISDLPoolPrimary(sdlPool).handleIncomingUpdate(numNewRESDLTokens, totalRESDLSupplyChange);\\n\\n        _ccipSendUpdate(sourceChainSelector, mintStartIndex);\\n\\n        emit MessageReceived(_message.messageId, sourceChainSelector);\\n    }\\n```\\n\\nNow the mintStartIndex is send to the secondary chain code line 307 and the secondary chain receives the new mintStartIndex. This entire process helps to keep the information updated between the primary chain and the secondary chain.\\nOn the other hand, when a secondary chain receive rewards, the secondary chain can call the function SDLPoolCCIPControllerSecondary::performUpkeep since `shouldUpdate` is `true` at code line 157:\\n```\\nFile: SDLPoolCCIPControllerSecondary.sol\\n    function _ccipReceive(Client.Any2EVMMessage memory _message) internal override {\\n        if (_message.data.length == 0) {\\n            uint256 numRewardTokens = _message.destTokenAmounts.length;\\n            address[] memory rewardTokens = new address[](numRewardTokens);\\n            if (numRewardTokens != 0) {\\n                for (uint256 i = 0; i < numRewardTokens; ++i) {\\n                    rewardTokens[i] = _message.destTokenAmounts[i].token;\\n                    IERC20(rewardTokens[i]).safeTransfer(sdlPool, _message.destTokenAmounts[i].amount);\\n                }\\n                ISDLPoolSecondary(sdlPool).distributeTokens(rewardTokens);\\n                if (ISDLPoolSecondary(sdlPool).shouldUpdate()) shouldUpdate = true;\\n            }\\n        } else {\\n            uint256 mintStartIndex = abi.decode(_message.data, (uint256));\\n            ISDLPoolSecondary(sdlPool).handleIncomingUpdate(mintStartIndex);\\n        }\\n\\n        emit MessageReceived(_message.messageId, _message.sourceChainSelector);\\n    }\\n```\\n\\nOnce `shouldUpdate` is `true`, the function SDLPoolCCIPControllerSecondary::performUpkeep can be called in order to send the new information (numNewRESDLTokens and totalRESDLSupplyChange) to the primary chain:\\n```\\n    function performUpkeep(bytes calldata) external {\\n        if (!shouldUpdate) revert UpdateConditionsNotMet();\\n\\n        shouldUpdate = false;\\n        _initiateUpdate(primaryChainSelector, primaryChainDestination, extraArgs);\\n    }\\n```\\n\\nThe problem is that the `primary chain` needs to send rewards to the `secondary chain` so that `shouldUpdate` is true and the function SDLPoolCCIPControllerSecondary::performUpkeep can be called. However, in certain circumstances it is possible that the `secondary chain` may never be able to send information to the `primary chain` since there may not be any rewards for the `secondary chain`. Please consider the next scenario:\\n`UserA` stakes directly in the `secondary chain` and the queuedRESDLSupplyChange increments\\nThe increase in supply CANNOT be reported to the `primary chain` since `shouldUpdate = false` and the function SDLPoolCCIPControllerSecondary::performUpkeep will be reverted.\\nRewards are calculated on the `primary chain`, however because the `secondary chain` has not been able to send the new supply information, zero rewards reSDLSupplyByChain will be calculated for the `secondary chain` since `reSDLSupplyByChain[chainSelector]` has not been increased with the new information from `step 1`.\\nSince there are NO rewards assigned for the `secondary chain`, it is not possible to set `shouldUpdate=True`, therefore the function SDLPoolCCIPControllerSecondary::performUpkeep will be reverted.\\nThe following test shows that a user can send `sdl` tokens to the `secondary pool` however SDLPoolCCIPControllerSecondary::performUpkeep cannot be called since there are no rewards assigned to the secondary pool:\\n```\\n// File: test/core/ccip/sdl-pool-ccip-controller-secondary.test.ts\\n// $ yarn test --grep "codehawks performUpkeep reverts"\\n// \\n  it('codehawks performUpkeep reverts', async () => {\\n    await token1.transfer(tokenPool.address, toEther(1000))\\n    let rewardsPool1 = await deploy('RewardsPool', [sdlPool.address, token1.address])\\n    await sdlPool.addToken(token1.address, rewardsPool1.address)\\n    assert.equal(fromEther(await sdlPool.totalEffectiveBalance()), 400)\\n    assert.equal((await controller.checkUpkeep('0x'))[0], false)\\n    assert.equal(await controller.shouldUpdate(), false)\\n    //\\n    // 1. Mint in the secondary pool\\n    await sdlToken.transferAndCall(\\n      sdlPool.address,\\n      toEther(100),\\n      ethers.utils.defaultAbiCoder.encode(['uint256', 'uint64'], [0, 0])\\n    )\\n    //\\n    // 2. The secondary pool needs to update data to the primary chain but the `controller.shouldUpdate` is false so `performUpkeep` reverts the transaction\\n    assert.equal(await sdlPool.shouldUpdate(), true)\\n    assert.equal((await controller.checkUpkeep('0x'))[0], false)\\n    assert.equal(await controller.shouldUpdate(), false)\\n    await expect(controller.performUpkeep('0x')).to.be.revertedWith('UpdateConditionsNotMet()')\\n  })\\n```\\n
Updates from the `secondary pool` to the `primary pool` may not be sent because there are `no rewards` for the `secondary pool`\\nThe SDLPoolCCIPControllerSecondary::performUpkeep function may check if the `secondary pool` has new information and so do not wait for rewards to be available for the secondary pool:\\n```\\n    function performUpkeep(bytes calldata) external {\\n// Remove the line below\\n// Remove the line below\\n      if (!shouldUpdate) revert UpdateConditionsNotMet();\\n// Add the line below\\n// Add the line below\\n      if (!shouldUpdate && !ISDLPoolSecondary(sdlPool).shouldUpdate()) revert UpdateConditionsNotMet();\\n\\n        shouldUpdate = false;\\n        _initiateUpdate(primaryChainSelector, primaryChainDestination, extraArgs);\\n    }\\n```\\n
`numNewRESDLTokens` and `totalRESDLSupplyChange` updates from the `secondary pool` to the `primary pool` may not be executed, causing the rewards calculation to be incorrect for each chain.\\nTools used\\nManual review
```\\nFile: SDLPoolCCIPControllerPrimary.sol\\n    function _ccipReceive(Client.Any2EVMMessage memory _message) internal override {\\n        uint64 sourceChainSelector = _message.sourceChainSelector;\\n\\n        (uint256 numNewRESDLTokens, int256 totalRESDLSupplyChange) = abi.decode(_message.data, (uint256, int256));\\n\\n        if (totalRESDLSupplyChange > 0) {\\n            reSDLSupplyByChain[sourceChainSelector] += uint256(totalRESDLSupplyChange);\\n        } else if (totalRESDLSupplyChange < 0) {\\n            reSDLSupplyByChain[sourceChainSelector] -= uint256(-1 * totalRESDLSupplyChange);\\n        }\\n\\n        uint256 mintStartIndex = ISDLPoolPrimary(sdlPool).handleIncomingUpdate(numNewRESDLTokens, totalRESDLSupplyChange);\\n\\n        _ccipSendUpdate(sourceChainSelector, mintStartIndex);\\n\\n        emit MessageReceived(_message.messageId, sourceChainSelector);\\n    }\\n```\\n