feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
This commit is contained in:
79
test/flash/CrossChainFlashBorrower.t.sol
Normal file
79
test/flash/CrossChainFlashBorrower.t.sol
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user