First Flight #12: Kitty Connect

First Flight #12: Kitty Connect
Beginner FriendlyFoundryNFTGameFi
100 EXP
View results
Submission Details
Severity: high
Valid

Missing `LINK` token approval in `KittyBridge::bridgeNftWithData`

Summary

KittyBridge::bridgeNftWithData should approve the router to transfer LINK tokens on contract's behalf, but this approval is missing. Consequently, the

Vulnerability Details

In the second step of the bridging process, KittyBridge::bridgeNftWithData is supposed to use Chainlink's Cross-Chain Interoperability Protocol (CCIP) to send the encoded NFT data to the destination chain. Sending a cross-chain message via CCIP incurs an execution fee for the message, which is properly calculated here

uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);

and there is even a conditional to check that the KittyBridge contract does have enough balance to cover said fees:

if (fees > s_linkToken.balanceOf(address(this))) {
revert KittyBridge__NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
}

Before actually sending the message via

messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);

In order to pay the fees, KittyBridge is supposed to approve the router to transfer LINK tokens on behalf of KittyBridge. However, this approval is missing.

Impact

The router will not be able to collect the execution fee from KittyBridge, and the bridging process will revert with "ERC20: insufficient allowance".

Tools Used

Manual review, Foundry.

Recommendations

Approve the router to transfer LINK tokens on behalf of KittyBridge as follows:

function bridgeNftWithData(uint64 _destinationChainSelector, address _receiver, bytes memory _data)
external
onlyAllowlistedDestinationChain(_destinationChainSelector)
validateReceiver(_receiver)
returns (bytes32 messageId)
{
// Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(_receiver, _data, address(s_linkToken));
// Initialize a router client instance to interact with cross-chain router
IRouterClient router = IRouterClient(this.getRouter());
// Get the fee required to send the CCIP message
uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
if (fees > s_linkToken.balanceOf(address(this))) {
revert KittyBridge__NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
}
+ s_linkToken.approve(address(router), fees);
messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
emit MessageSent(messageId, _destinationChainSelector, _receiver, _data, address(s_linkToken), fees);
return messageId;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Missing fee token approval

Support

FAQs

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