Across Linea CCTP Diff Audit

Table of Contents

Summary

Type
DeFi
Timeline
From 2025-03-13
To 2025-03-18

Languages:
         Solidity

Total Issues
4 (3 resolved, 1 partially resolved)
Critical Severity Issues
0 (0 resolved)
High Severity Issues
0 (0 resolved)
Medium Severity Issues
0 (0 resolved)
Low Severity Issues
2 (1 resolved, 1 partially resolved)
Notes & Additional Information
2 (2 resolved)
Client Reported Issues
0 (0 resolved)

Scope

We audited the across-protocol/contracts repository.

In scope were the changes made to the following files:

 contracts
├── Linea_SpokePool.sol
├── chain-adapters/
│   └── Linea_Adapter.sol
├── libraries/
│   └── CircleCCTPAdapter.sol
└── external/
    └── interfaces/
        └── CCTPInterfaces.sol

in the pull request #921 with the final commit 0720878.

We have additionally reviewed the e5310e0 commit.

System Overview

The Across protocol is an intent-based bridging protocol which enables fast token transfers between different blockcahins. For more details on how the protocol works, please refer to one of our previous audit reports.

Summary of Changes

In order to bridge USDC tokens between Ethereum and another blockchains supporting the Circle CCTP protocol, both the HubPool and SpokePools were utilizing the CircleCCTPAdapter contract, which implements the USDC bridging logic using the first version (V1) of the CCTP protocol.

However, the Linea blockchain does not support the V1 version of CCTP and will only support the V2 version, which uses a different set of contracts and it results in a different API being exposed to the users. As a result, in order to support USDC transfers to and from the Linea blockchain, it has been necessary to adjust the existing contracts in Across so that they are able to interact with CCTP through the new API.

The changes involved small modifications of the CircleCCTPAdapter, Linea_Adapter and Linea_SpokePool contracts and an introduction of the new ITokenMessengerV2 interface in order to facilitate communication with the second version of CCTP TokenMessenger contract.

Security Model and Trust Assumptions

The audit has been focused on the specific changes made to the contracts in order to integrate with CCTP V2 on Linea. As such, it has been restricted only to a small part of the codebase and has been conducted under the following trust assumptions.

Throughout the audit we assumed that all on-chain and off-chain components the in-scope code integrates with behave correctly and as expected. In particular, that the CCTP protocol works correctly and reliably transfers desired USDC amounts between Ethereum and Linea blockchains according to its documentation.

Furthermore, we assumed that the in-scope contracts will be correctly deployed and initialized with the correct parameters, which is crucial for the entire protocol to operate correctly.

Finally, the Linea CCTP Domain ID has not been officially announced at the time of the audit, although it was very likely that it would be equal to 11. Hence, it has been assumed that the constant reflecting it in the code is initialized correctly.

Privileged Roles

There have not been any new privileged roles added to the protocol in the pull request being reviewed.

Throughout the audit we assumed that all privileged entities already existing in the Across protocol will behave honestly and in the best interest of the protocol and its users.

 

Low Severity

Check For CCTP Version Is Not Fully Reliable

In order to determine the CCTP version which will be used for bridging USDC, the constructor of the CircleCCTPAdapter contract performs a low level call to the feeRecipient function of a CCTP TokenMessenger. The check relies on the fact that this function is not present in V1 contracts, but is present in V2 contracts and returns a non-zero address.

However, the check being performed is not fully reliable for the reasons enumerated below.

The values returned by the functions in Solidity are abi.encoded, which means that the address returned by the feeRecipient function will be padded to 32 bytes with 0s at the beginning. Furthermore, casting a bytes object to bytes20 will return the first 20 bytes of that object, which will include 12 bytes of padding and as a result, only the first 8 bytes of the returned address will be taken into account in this check. In case when the first 8 bytes of the returned feeRecipient are equal to 0, the contract will incorrectly assume that it should use CCTP V1.

Furthermore, while it is checked that the call succeeded, there is no verification that the return data is of correct size. As a result, the check can succeed in case when the return data is of a different size. In particular, for the return data of 0 length, the correctness of this check relies on the cast from an empty bytes object to bytes20 always returning 0.

Finally, a contract to be called could still have a fallback function which returns 32 bytes of data. In case when at least 1 of the last 20 bytes of that data is nonzero, the check will still succeed even if the target contract does not implement the feeRecipient function.

Consider casting the feeRecipient to uint160 before casting it to address in order to retrieve the full address. Moreover, consider validating the length of the data returned by the low level call in order to better handle the situations when data with unexpected size is returned. Furthermore, consider implementing a more robust mechanism of determining the CCTP version to be used.

Update: Partially resolved in pull request #921 at commit d9d4707. The casting of the data returned from a low level call has been fixed and additional verification has been added to the length of the returned data. It is still possible that the check for the CCTP version will not determine the correct version, but this scenario is very unlikely and the risk has been accepted by the Risk Labs team.

Insufficient Documentation

The _transferUsdc function of the CircleCCTPAdapter contract is responsible for bridging USDC between different blockchains using the CCTP protocol. In order to do this, it queries the CCTPTokenMessenger contract for the TokenMinter's address, determines the current burn limit per message and splits USDC deposits if necessary.

However, the cctpTokenMessenger variable used to retrieve the minter's address, is of type ITokenMessenger, which represents the version 1 of the TokenMessenger contract, but it is possible that in reality it points to the TokenMessengerV2 contract. Since both TokenMessenger and TokenMessengerV2 contracts define the localMinter function being used, the function call will succeed for both cases, but this implicit behaviour could be reflected in the comments. Furthermore, the localMinter being returned by this function is either of type ITokenMinter or ITokenMinterV2, depending on the version of TokenMessenger which is called. While both versions of the TokenMinter contract expose the burnLimitsPerMessage function, this fact is not obvious and could be documented as well.

Consider improving the documentation inside the _transferUsdc function in order to improve readability and maintainability of the codebase.

Update: Resolved in pull request #921 at commit d9d4707.

Notes & Additional Information

Misleading Comments

Throughout the codebase, several instances of misleading comments were identified: - The comment states that the minFinalityThreshold parameter can be set to 20 000 for a standard transfer, while the correct value is 2 000. - The comment refers to an l1Token, however the code checks whether l2TokenAddress is the address of usdcToken. Therefore, in the comment, l1Token should be changed to l2Token.

Consider correcting the aforementioned comments to improve the overall clarity and readability of the codebase.

Update: Resolved in pull request #921 at commit b135917.

Typographical Errors

Throughout the codebase, several instances of typographical errors were identified:

In the CircleCCTPAdapter.sol file: - In line 70, "its" should be "it is". - In line 116, "to bridged" should be "to bridge". - In line 124, there is an extra space that could be removed at the beginning of the line.

Consider fixing all instances of typographical errors in order to improve the readability of the codebase.

Update: Resolved in pull request #921 at commit 63231d9.

Conclusion

The changes added to the code enable the contracts to bridge USDC to and from the Linea blockchain utilizing the second version of the CCTP protocol, while still allowing to use the first version of the protocol for another blockchains.

Throughout the audit, there have not been any significant issues identified.

The Risk Labs team has been very helpful throughout the engagement, answering all our questions promptly and in a great detail.

Request Audit