Skip to content

feat: implement CCTPModule for cross-chain transfers#162

Draft
jashmehta-qds wants to merge 5 commits into
devfrom
feat/cctp-module
Draft

feat: implement CCTPModule for cross-chain transfers#162
jashmehta-qds wants to merge 5 commits into
devfrom
feat/cctp-module

Conversation

@jashmehta-qds

@jashmehta-qds jashmehta-qds commented Jul 1, 2026

Copy link
Copy Markdown

Add the CCTPModule contract, which facilitates delayed bridge operations by burning USDC via Circle CCTP for cross-chain transfers. The module includes admin-configurable parameters for asset management, finality thresholds, and fee ceilings. Additionally, a comprehensive test suite is introduced to validate the module's functionality and security.


Note

High Risk
New on-chain path that holds and burns user USDC through external Circle contracts and CashModule withdrawals, with fee and config policies that directly affect delivered amounts.

Overview
Adds a new CCTP bridge module so EtherFi safes can queue and execute USDC cross-chain transfers by burning via Circle CCTP, following the same delayed-withdrawal pattern as other bridge modules (requestBridge → CashModule delay → executeBridge / cancelBridge).

Safes sign bridge params (destination domain, asset, amount, recipient, and Fast vs Standard finality). Admins configure per-asset TokenMessenger, relay-fee ceilings, provider fee bps, allowed domains, and fee recipient. At request time the module snapshots messenger, fees, and finality into the queued withdrawal so later admin changes cannot redirect or alter an in-flight bridge; execution intentionally does not re-check domain allowlists so queued bridges are not stranded.

Deploy (DeployCCTPModule) wires OP mainnet USDC + TokenMessengerV2 with initial fee caps; ConfigureCCTPModuleOptimismDev runs post-deploy governance (default module registration, CashModule withdraw permission, admin role, ETH/Base domains, provider fee recipient). A large test suite covers signatures, fees, snapshots, cancel paths, and CashModule integration.

Reviewed by Cursor Bugbot for commit 53ff392. Bugbot is set up for automated code reviews on this repo. Configure here.

Add the CCTPModule contract, which facilitates delayed bridge operations by burning USDC via Circle CCTP for cross-chain transfers. The module includes admin-configurable parameters for asset management, finality thresholds, and fee ceilings. Additionally, a comprehensive test suite is introduced to validate the module's functionality and security.
@jashmehta-qds
jashmehta-qds changed the base branch from master to dev July 1, 2026 16:12

@certora-run certora-run Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification Results

  • Group ID: aa7be30a-037a-4ea4-bfc1-2e737f1736d7
  • Commit: 0acdccd
Job Result VERIFIED Link
EtherFiSafe.conf 14 Link
CashModuleCore.conf --rule "p03" 2 Link

Add etherFi fee configuration to the CCTPModule, allowing for a service fee to be deducted from bridge amounts. Update the AssetConfig structure to include etherFiFeeBps and implement related functions for setting the fee recipient and calculating fees. Modify tests to validate the new fee logic and ensure proper handling of etherFi fees during cross-chain transfers.
Update the CCTPModule contract to replace all instances of etherFi fee with provider fee, including related variables, events, and functions. Adjust tests accordingly to ensure consistency and validate the new provider fee logic for cross-chain transfers.
Comment thread src/modules/cctp/CCTPModule.sol Outdated
*/
struct AssetConfig {
address tokenMessenger;
uint32 finalityThreshold;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we make it where the BE decides whether we use standard or fast mode. What if we add a feature in the future where the user selects if they want to use fast mode or standard? We could just leave the option open for that so we don't require a SC upgrade in the future

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, I moved so the BE can decide.

@jtfirek

jtfirek commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

my AI found this bug, could you check?

  1. cancelBridge fires a garbage "cancelled" event (the one real bug to fix)
    What happens: When a user cancels a queued bridge, the system logs the cancellation twice — once correctly, and once with all the details blanked out (amount 0, token 0x0, recipient 0x0).

Why it happens: cancelBridge grabs a live reference to the withdrawal record, then calls the CashModule to unwind it. The CashModule calls back into the CCTP module and deletes that record. When control comes back, cancelBridge tries to read the record it was holding — but it's already been wiped, so it reads zeros and logs them.

Who triggers it: Any normal user cancelling their own bridge. No attacker needed — it happens every single cancel.

Impact: No money is lost. But anything that reads these logs (your indexer, transaction history, accounting) sees a duplicate + a corrupted entry. It's a data-integrity bug, cheap to fix.

Revise the CCTPModule contract to clarify the handling of finality thresholds and fee configurations. The finality threshold is now specified per request, while the max fee is capped based on admin settings. Adjust related structures and functions to reflect these changes, ensuring that standard transfers enforce a zero-fee policy. Update tests to validate the new logic and ensure proper functionality across scenarios.
@jashmehta-qds

Copy link
Copy Markdown
Author

my AI found this bug, could you check?

  1. cancelBridge fires a garbage "cancelled" event (the one real bug to fix)
    What happens: When a user cancels a queued bridge, the system logs the cancellation twice — once correctly, and once with all the details blanked out (amount 0, token 0x0, recipient 0x0).

Why it happens: cancelBridge grabs a live reference to the withdrawal record, then calls the CashModule to unwind it. The CashModule calls back into the CCTP module and deletes that record. When control comes back, cancelBridge tries to read the record it was holding — but it's already been wiped, so it reads zeros and logs them.

Who triggers it: Any normal user cancelling their own bridge. No attacker needed — it happens every single cancel.

Impact: No money is lost. But anything that reads these logs (your indexer, transaction history, accounting) sees a duplicate + a corrupted entry. It's a data-integrity bug, cheap to fix.

This was correctly picked up, updated the sequence of events.

…mism

Introduce two new scripts: DeployCCTPModule for deploying the CCTPModule contract on Optimism, and ConfigureCCTPModuleOptimismDev for post-deployment configuration. The configuration script automates governance steps including module registration, role granting, domain allowance, and setting the provider fee recipient, ensuring a streamlined setup for cross-chain operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants