Skip to content

feat: OFT/CCTP same chain deposits#1407

Open
fusmanii wants to merge 7 commits intomasterfrom
faisal/oft-cctp-same-chain-deposit
Open

feat: OFT/CCTP same chain deposits#1407
fusmanii wants to merge 7 commits intomasterfrom
faisal/oft-cctp-same-chain-deposit

Conversation

@fusmanii
Copy link
Copy Markdown
Contributor

@fusmanii fusmanii commented Apr 8, 2026

No description provided.

Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
Comment thread contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol Outdated

address burnToken = quote.burnToken.toAddress();
IERC20(burnToken).safeTransferFrom(msg.sender, destinationHandler, quote.amount);
ISponsoredCCTPDstPeriphery(destinationHandler).directReceiveMessage(quote, signature);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We push tokens into the dst handler, this is why we require a new DIRECT_CALLER_ROLE.

I understand that this is a gas optimization over pulling two times (depositor -> srcPeriphery -> dstHandler.

Hmm, on OFT side, this also lands nicely with authorizedSrcPeripheryContracts, so that doesn't need a special role for direct deposits. We can just reuse authorizedSrcPeripheryContracts for same-chain deposit authorizations

using AddressToBytes32 for address;
using SafeERC20 for IERC20;

bytes32 public constant DIRECT_CALLER_ROLE = keccak256("DIRECT_CALLER_ROLE");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we reuse authorizedSrcPeripheryContracts instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I moved DIRECT_CALLER_ROLE to BaseModuleHandler.sol so both contracts get access to DIRECT_CALLER_ROLE
c5284cf

Comment on lines +17 to +21
import { Address } from "@openzeppelin/contracts-v4/utils/Address.sol";

interface IDirectDstOFTHandler {
function executeDirect(address tokenSent, uint256 amountLD, bytes calldata composeMsg) external;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here with the interface, can we e.g. reuse DstOFTHandler definition?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done
63062f2

/// @notice Source endpoint id
uint32 public immutable SRC_EID;
/// @notice OFT shared decimals used by this deployment.
uint8 public immutable SHARED_DECIMALS;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This looks unused

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@grasphoper
Copy link
Copy Markdown
Collaborator

@codex please review this one. It adds the ability to deposit funds directly from the same chain, rather than using OFT/CCTP bridge to deposit and mint tokens cross-chain.

It allows us to reuse signature checking and binding functionality (e.g. Arbitrary EVM actions are bound to some received token amount via a signature)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83f0ba0ab7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +104 to +105
if (quote.signedParams.dstEid == SRC_EID) {
address destinationHandler = address(uint160(uint256(quote.signedParams.destinationHandler)));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject or refund msg.value for direct OFT execution

In SponsoredOFTSrcPeriphery.deposit, the same-chain branch (dstEid == SRC_EID) skips all native-fee handling and never refunds msg.value, so any ETH sent with a direct quote is left in this contract permanently. This is a user-funds loss path if callers keep sending the bridge fee they use for cross-chain deposits (the previous flow always refunded surplus), and this contract has no withdrawal path for stuck ETH.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@fusmanii hm, maybe force msg.value == 0 here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good point, yeah forcing msg.value == 0 makes sense here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 63062f2

Comment on lines +101 to +103
address burnToken = quote.burnToken.toAddress();
IERC20(burnToken).safeTransferFrom(msg.sender, destinationHandler, quote.amount);
ISponsoredCCTPDstPeriphery(destinationHandler).directReceiveMessage(quote, signature);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enforce base token for CCTP direct deposits

The direct CCTP source path transfers quote.burnToken to the destination handler and immediately calls directReceiveMessage, but the destination execution path does not validate that burnToken matches the handler’s baseToken and proceeds with base-token accounting. If a signed quote contains a mismatched burnToken and the handler already holds baseToken, the flow can consume that baseToken while accepting an unrelated token from the caller, which creates a drain/mis-accounting path under that input condition.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 63062f2

fusmanii added 3 commits April 8, 2026 15:56
Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
@fusmanii
Copy link
Copy Markdown
Contributor Author

fusmanii commented Apr 8, 2026

@codex re-review pls

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
@fusmanii fusmanii requested review from grasphoper and tbwebb22 April 9, 2026 17:30
@fusmanii fusmanii marked this pull request as ready for review April 9, 2026 17:30
bytes memory signature
) external nonReentrant authorizeFundedFlow onlyRole(DIRECT_CALLER_ROLE) {
if (quote.burnToken.toAddress() != baseToken) revert InvalidBurnToken();
_validateQuoteOrRevert(quote, signature);
Copy link
Copy Markdown
Contributor

@tbwebb22 tbwebb22 Apr 9, 2026

Choose a reason for hiding this comment

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

nit, but we could remove the _validateQuoteOrRevert call here, and copy the logic from _validateQuoteOrRevert into here, minus the signature check, since we already did the signature check on SrcPeriphery. Right now we're checking the signature twice

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

great point, I could remove the sig param all together: f37c45b

Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
@tbwebb22 tbwebb22 self-requested a review April 10, 2026 22:11
@fusmanii fusmanii added the need to audit This PR needs to be audited externally before deploying label Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

need to audit This PR needs to be audited externally before deploying

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants