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:
87
test/flash/SwapFlashWorkflowBorrower.t.sol
Normal file
87
test/flash/SwapFlashWorkflowBorrower.t.sol
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 {SwapFlashWorkflowBorrower, IDODOStyleSwapExactIn} from "../../contracts/flash/SwapFlashWorkflowBorrower.sol";
|
||||
|
||||
/// @notice 1:1 swap router for tests (ignores pool).
|
||||
contract MockSwapRouter1to1 is IDODOStyleSwapExactIn {
|
||||
ERC20 public immutable tokenA;
|
||||
ERC20 public immutable tokenB;
|
||||
|
||||
constructor(ERC20 a, ERC20 b) {
|
||||
tokenA = a;
|
||||
tokenB = b;
|
||||
}
|
||||
|
||||
function swapExactIn(address, address tokenIn, uint256 amountIn, uint256 minAmountOut)
|
||||
external
|
||||
override
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
|
||||
address tokenOut = tokenIn == address(tokenA) ? address(tokenB) : address(tokenA);
|
||||
amountOut = amountIn;
|
||||
require(amountOut >= minAmountOut, "min");
|
||||
IERC20(tokenOut).transfer(msg.sender, amountOut);
|
||||
}
|
||||
}
|
||||
|
||||
contract MockERC20Mint is ERC20 {
|
||||
constructor(string memory n, string memory s) ERC20(n, s) {}
|
||||
|
||||
function mint(address to, uint256 v) external {
|
||||
_mint(to, v);
|
||||
}
|
||||
}
|
||||
|
||||
contract SwapFlashWorkflowBorrowerTest is Test {
|
||||
SimpleERC3156FlashVault internal vault;
|
||||
MockERC20Mint internal tokenA;
|
||||
MockERC20Mint internal tokenB;
|
||||
MockSwapRouter1to1 internal router;
|
||||
SwapFlashWorkflowBorrower internal borrower;
|
||||
|
||||
address internal owner = address(0xA11);
|
||||
address internal user = address(0xB22);
|
||||
|
||||
function setUp() public {
|
||||
vm.startPrank(owner);
|
||||
vault = new SimpleERC3156FlashVault(owner, 5);
|
||||
tokenA = new MockERC20Mint("A", "A");
|
||||
tokenB = new MockERC20Mint("B", "B");
|
||||
tokenA.mint(address(vault), 1_000_000e18);
|
||||
vault.setTokenSupported(address(tokenA), true);
|
||||
vm.stopPrank();
|
||||
|
||||
router = new MockSwapRouter1to1(tokenA, tokenB);
|
||||
tokenA.mint(address(router), 10_000_000e18);
|
||||
tokenB.mint(address(router), 10_000_000e18);
|
||||
|
||||
borrower = new SwapFlashWorkflowBorrower(address(vault));
|
||||
}
|
||||
|
||||
function test_roundTripSwap_prefundFeeInBorrowedToken() public {
|
||||
uint256 amount = 50_000e18;
|
||||
uint256 fee = vault.flashFee(address(tokenA), amount);
|
||||
tokenA.mint(address(borrower), fee);
|
||||
|
||||
SwapFlashWorkflowBorrower.SwapFlashParams memory p = SwapFlashWorkflowBorrower.SwapFlashParams({
|
||||
integration: address(router),
|
||||
pool: address(0xdead),
|
||||
midToken: address(tokenB),
|
||||
minOutFirst: amount,
|
||||
minOutSecond: amount
|
||||
});
|
||||
bytes memory data = abi.encode(p);
|
||||
|
||||
vm.prank(user);
|
||||
vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(tokenA), amount, data);
|
||||
|
||||
assertEq(vault.totalFeesCollected(address(tokenA)), fee);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user