Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: high
Invalid

contracts/core/test/chainlink/CCIPOffRampMock.sol

The provided Solidity code for the CCIPOffRampMock contract contains several potential vulnerabilities and design considerations. Below is a breakdown of possible issues:

  1. Reentrancy Vulnerability:

    • The function executeSingleMessage calls releaseOrMint on ITokenPool, which could potentially trigger external contract calls. If these external calls modify state that can affect the execution flow of the calling contract, it may lead to reentrancy attacks.

    • Mitigation: Use a reentrancy guard or consider following the checks-effects-interactions pattern. For example, you can accumulate the amounts to be released or minted before calling external functions.

  2. Token Pool Mapping Control:

    • The setTokenPool function allows anyone to change the token pool associated with any token. This can be dangerous if not properly controlled, as malicious actors could redirect tokens to an unintended or malicious contract.

    • Mitigation: Add access control mechanisms (e.g., Ownable) to restrict who can set the token pools.

  3. Lack of Input Validation:

    • The constructor initializes the tokenPools mapping without verifying that the length of _tokens and _tokenPools arrays are equal. This can lead to indexing issues or unexpected behavior if the arrays are of different lengths.

    • Mitigation: Add a require statement to ensure both arrays have the same length.

  4. Gas Limit Hardcoding:

    • The GAS_FOR_CALL_EXACT_CHECK constant is hardcoded. If the expected gas limit is exceeded during execution, the transaction will fail. This could potentially cause issues if the functions called require more gas due to changes in the future.

    • Mitigation: Consider allowing the gas limit to be specified as a parameter, or monitor and adjust based on actual gas usage during deployment.

  5. Event Emission:

    • There are no events emitted for critical state changes, such as when a token pool is set or when tokens are minted or released. This could make it difficult to track actions taken on the contract for off-chain systems.

    • Mitigation: Emit events for significant state changes to facilitate better tracking and debugging.

  6. Function Visibility:

    • The setTokenPool function does not have visibility explicitly defined. Although Solidity defaults to public, it's best practice to declare function visibility explicitly for better readability and understanding of the contract's API.

  7. Potential Overflows (for older Solidity versions):

    • Although not a direct issue in this version (since Solidity 0.8 and above has built-in overflow checks), it's good to be aware of arithmetic operations that may lead to overflows or underflows in prior versions. Ensure that if using math libraries, they're compatible with the Solidity version used.

Suggested Code Improvements:

Here are some potential improvements based on the points above:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouter} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouter.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface ITokenPool {
function token() external view returns (address);
function releaseOrMint(address _receiver, uint256 _amount) external;
}
contract CCIPOffRampMock is Ownable {
uint16 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000;
IRouter public router;
mapping(address => ITokenPool) public tokenPools;
event TokenPoolSet(address indexed token, address indexed pool);
event TokensReleased(address indexed receiver, address indexed token, uint256 amount);
constructor(address _router, address[] memory _tokens, address[] memory _tokenPools) {
require(_tokens.length == _tokenPools.length, "Mismatched lengths");
router = IRouter(_router);
for (uint256 i = 0; i < _tokens.length; ++i) {
tokenPools[_tokens[i]] = ITokenPool(_tokenPools[i]);
}
}
function executeSingleMessage(
bytes32 _messageId,
uint64 _sourceChainSelector,
bytes calldata _data,
address _receiver,
Client.EVMTokenAmount[] calldata _tokenAmounts
) external returns (bool) {
for (uint256 i = 0; i < _tokenAmounts.length; ++i) {
tokenPools[_tokenAmounts[i].token].releaseOrMint(_receiver, _tokenAmounts[i].amount);
emit TokensReleased(_receiver, _tokenAmounts[i].token, _tokenAmounts[i].amount);
}
(bool success, , ) = router.routeMessage(
Client.Any2EVMMessage(
_messageId,
_sourceChainSelector,
abi.encode(msg.sender),
_data,
_tokenAmounts
),
GAS_FOR_CALL_EXACT_CHECK,
1000000,
_receiver
);
return success;
}
function setTokenPool(address _token, address _pool) external onlyOwner {
tokenPools[_token] = ITokenPool(_pool);
emit TokenPoolSet(_token, _pool);
}
}

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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