// 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 {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"; import {SimpleERC3156FlashVault} from "../../contracts/flash/SimpleERC3156FlashVault.sol"; import {CrossChainFlashBorrower} from "../../contracts/flash/CrossChainFlashBorrower.sol"; import {ICrossChainFlashBridge} from "../../contracts/flash/interfaces/ICrossChainFlashBridge.sol"; contract MockCrossChainBridge is ICrossChainFlashBridge { event BridgeCalled(address token, uint256 amount, uint64 dest, address recipient, bytes extra, uint256 value); function bridgeTokensFrom( address token, uint256 amount, uint64 destinationChainSelector, address recipientOnDestination, bytes calldata extraData ) external payable override returns (bytes32 messageId) { IERC20(token).transferFrom(msg.sender, address(this), amount); emit BridgeCalled(token, amount, destinationChainSelector, recipientOnDestination, extraData, msg.value); messageId = keccak256(abi.encodePacked(block.number, token, amount)); } } contract MockERC20Mint is ERC20 { constructor() ERC20("T", "T") {} function mint(address to, uint256 v) external { _mint(to, v); } } contract CrossChainFlashBorrowerTest is Test { SimpleERC3156FlashVault internal vault; MockERC20Mint internal token; MockCrossChainBridge internal bridge; CrossChainFlashBorrower internal borrower; address internal owner = address(0xA11); address internal user = address(0xB22); function setUp() public { vm.startPrank(owner); vault = new SimpleERC3156FlashVault(owner, 5); token = new MockERC20Mint(); token.mint(address(vault), 1_000_000e18); vault.setTokenSupported(address(token), true); vm.stopPrank(); bridge = new MockCrossChainBridge(); borrower = new CrossChainFlashBorrower(address(vault)); } function test_flashBridge_out_repaysFromPrefund() public { uint256 amount = 40_000e18; uint256 fee = vault.flashFee(address(token), amount); uint256 bridgeAmount = amount; token.mint(address(borrower), bridgeAmount + fee); CrossChainFlashBorrower.CrossChainFlashParams memory p = CrossChainFlashBorrower.CrossChainFlashParams({ bridge: address(bridge), bridgeAmount: bridgeAmount, destinationChainSelector: 123, recipientOnDestination: address(0xbeef), bridgeExtraData: hex"abcd", nativeBridgeFee: 0 }); vm.prank(user); vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(token), amount, abi.encode(p)); assertEq(token.balanceOf(address(bridge)), bridgeAmount); assertEq(vault.totalFeesCollected(address(token)), fee); } }