stake.link

stake.link
DeFiHardhatBridge
27,500 USDC
View results
Submission Details
Severity: low
Invalid

SDLPoolCCIPController (Primary, Secondary) lack of LINK balance check before sending CCIP message

Summary

SDLPoolCCIPController (Primary, Secondary) lack of LINK balance check before sending CCIP message

Vulnerability Details

Sending CCIP message in SDLPoolCCIPController, to be precise, when _distributeRewards and _ccipSendUpdate in PrimaryController, and _initiateUpdate in SecondaryController, the fee for sending message are using LINK instead of Native token. But there is no check of existing balance LINK is enough to send the message.

Chainlink CCIP fee can be paid using two token, Native token and LINK token. When user initiate the message (token) transfer through this CCIP in the protocol, they have this two option. But the functions mentioned only use LINK, and the contract doesn't check if LINK token to send the CCIP message is enough to cover the fee.

There is no revert condition to catch this issue.

The only revert is if (fees > maxLINKFee) revert FeeExceedsLimit(fees); which is to make sure fee is under the max fee.

Thus, when the LINK token balance is less than fee, there will still be a revert (CCIP reject the trx due to LINK token is less than required fee).

If the user initiate the CCIP message and intend to pay fee using LINK token, then the revert would be from linkToken.safeTransferFrom(msg.sender, address(sdlPoolCCIPController), fees); due to their balance is less than fees amount. But, in those SDLPoolCCIPController functions, it is still revert, but not being catch explicitly by the current code.

File: SDLPoolCCIPControllerPrimary.sol
244: function _distributeRewards(
245: uint64 _destinationChainSelector,
246: address[] memory _rewardTokens,
247: uint256[] memory _rewardTokenAmounts
248: ) internal {
...
281: uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
282:
283: if (fees > maxLINKFee) revert FeeExceedsLimit(fees);
284: bytes32 messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
285:
286: emit DistributeRewards(messageId, _destinationChainSelector, fees);
287: }
...
317: function _ccipSendUpdate(uint64 _destinationChainSelector, uint256 _mintStartIndex) internal {
...
327: uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
328:
329: if (fees > maxLINKFee) revert FeeExceedsLimit(fees);
330: bytes32 messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
331:
332: emit MessageSent(messageId, _destinationChainSelector, fees);
333: }
File: SDLPoolCCIPControllerSecondary.sol
119: function _initiateUpdate(
120: uint64 _destinationChainSelector,
121: address _destination,
122: bytes memory _extraArgs
123: ) internal {
...
134: uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
135:
136: if (fees > maxLINKFee) revert FeeExceedsLimit(fees);
137: bytes32 messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
138:
139: emit MessageSent(messageId, _destinationChainSelector, fees);
140: }

Impact

In the case where LINK token is below fees, there is no explicit revert

Tools Used

Manual analysis

Recommendations

Consider to check the balance of LINK token before sending CCIP message, and revert if it's not enough

Updates

Lead Judging Commences

0kage Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.