// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {UniversalCCIPBridge} from "../../contracts/bridge/UniversalCCIPBridge.sol"; import {UniversalCCIPFlashBridgeAdapter} from "../../contracts/flash/UniversalCCIPFlashBridgeAdapter.sol"; /// @dev Minimal stand-in for `UniversalCCIPBridge.bridge` (pulls token, records op). contract FlashBridgeAdapterTestMock { event BridgeCalled(address token, uint256 amount, uint64 dest, address recipient, bytes32 assetType, bool pmm, bool vault); function bridge(UniversalCCIPBridge.BridgeOperation calldata op) external payable returns (bytes32) { IERC20(op.token).transferFrom(msg.sender, address(this), op.amount); emit BridgeCalled( op.token, op.amount, op.destinationChain, op.recipient, op.assetType, op.usePMM, op.useVault ); return keccak256(abi.encodePacked("mock", op.token, op.amount)); } receive() external payable {} } contract MockMintERC20 is ERC20 { constructor() ERC20("A", "A") {} function mint(address to, uint256 v) external { _mint(to, v); } } contract UniversalCCIPFlashBridgeAdapterTest is Test { FlashBridgeAdapterTestMock internal uni; UniversalCCIPFlashBridgeAdapter internal adapter; MockMintERC20 internal token; address internal alice = address(0xA11CE); function setUp() public { uni = new FlashBridgeAdapterTestMock(); adapter = new UniversalCCIPFlashBridgeAdapter(address(uni)); token = new MockMintERC20(); token.mint(alice, 500e18); vm.deal(alice, 10 ether); } function test_adapter_pullsAndCallsBridge_emptyExtraData() public { vm.startPrank(alice); token.approve(address(adapter), 100e18); bytes32 mid = adapter.bridgeTokensFrom{value: 1 wei}(address(token), 100e18, 7, address(0xBEEF), ""); vm.stopPrank(); assertTrue(mid != bytes32(0)); assertEq(token.balanceOf(address(uni)), 100e18); } function test_adapter_decodesExtraData() public { bytes memory extra = abi.encode(bytes32(uint256(1)), true, false, bytes("p"), bytes("v")); vm.startPrank(alice); token.approve(address(adapter), 50e18); adapter.bridgeTokensFrom(address(token), 50e18, 99, address(0xCAFE), extra); vm.stopPrank(); assertEq(token.balanceOf(address(uni)), 50e18); } }