// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {UniversalCCIPBridge} from "../bridge/UniversalCCIPBridge.sol"; import {ICrossChainFlashBridge} from "./interfaces/ICrossChainFlashBridge.sol"; /** * @title UniversalCCIPFlashBridgeAdapter * @notice Pulls `token` from the caller (e.g. `CrossChainFlashBorrower`) and forwards a `UniversalCCIPBridge.bridge` call. * @dev `extraData` (see `ICrossChainFlashBridge`): if empty, uses `assetType = 0`, `usePMM/useVault = false`, empty proofs. * If non-empty: `abi.encode(bytes32 assetType, bool usePMM, bool useVault, bytes complianceProof, bytes vaultInstructions)`. * Native value is forwarded for CCIP fees on the underlying bridge. */ contract UniversalCCIPFlashBridgeAdapter is ICrossChainFlashBridge { using SafeERC20 for IERC20; UniversalCCIPBridge public immutable universalBridge; constructor(address universalBridge_) { require(universalBridge_ != address(0), "UniversalCCIPFlashBridgeAdapter: zero bridge"); universalBridge = UniversalCCIPBridge(payable(universalBridge_)); } function bridgeTokensFrom( address token, uint256 amount, uint64 destinationChainSelector, address recipientOnDestination, bytes calldata extraData ) external payable override returns (bytes32 messageId) { IERC20(token).safeTransferFrom(msg.sender, address(this), amount); IERC20(token).forceApprove(address(universalBridge), amount); bytes32 assetType; bool usePMM; bool useVault; bytes memory complianceProof; bytes memory vaultInstructions; if (extraData.length == 0) { assetType = bytes32(0); usePMM = false; useVault = false; complianceProof = ""; vaultInstructions = ""; } else { (assetType, usePMM, useVault, complianceProof, vaultInstructions) = abi.decode(extraData, (bytes32, bool, bool, bytes, bytes)); } UniversalCCIPBridge.BridgeOperation memory op = UniversalCCIPBridge.BridgeOperation({ token: token, amount: amount, destinationChain: destinationChainSelector, recipient: recipientOnDestination, assetType: assetType, usePMM: usePMM, useVault: useVault, complianceProof: complianceProof, vaultInstructions: vaultInstructions }); messageId = universalBridge.bridge{value: msg.value}(op); IERC20(token).forceApprove(address(universalBridge), 0); } receive() external payable {} }