- 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
231 lines
9.4 KiB
Solidity
231 lines
9.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import {Test} from "forge-std/Test.sol";
|
|
import "../../../contracts/bridge/trustless/EnhancedSwapRouterV2.sol";
|
|
import "../../../contracts/bridge/trustless/RouteTypesV2.sol";
|
|
import "../../../contracts/bridge/trustless/adapters/UniswapV3RouteExecutorAdapter.sol";
|
|
import "../../../contracts/bridge/trustless/adapters/BalancerRouteExecutorAdapter.sol";
|
|
import "../../../contracts/bridge/trustless/adapters/CurveRouteExecutorAdapter.sol";
|
|
import "../../../contracts/bridge/trustless/adapters/OneInchRouteExecutorAdapter.sol";
|
|
import "../../../contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
|
|
contract PilotVenueToken is ERC20 {
|
|
constructor(string memory name_, string memory symbol_, uint256 supply) ERC20(name_, symbol_) {
|
|
_mint(msg.sender, supply);
|
|
}
|
|
|
|
function mint(address to, uint256 amount) external {
|
|
_mint(to, amount);
|
|
}
|
|
}
|
|
|
|
contract Chain138PilotDexVenuesTest is Test {
|
|
PilotVenueToken internal weth;
|
|
PilotVenueToken internal usdt;
|
|
PilotVenueToken internal usdc;
|
|
PilotVenueToken internal dai;
|
|
|
|
Chain138PilotUniswapV3Router internal uniswapRouter;
|
|
Chain138PilotBalancerVault internal balancerVault;
|
|
Chain138PilotCurve3Pool internal curvePool;
|
|
Chain138PilotOneInchAggregationRouter internal oneInchRouter;
|
|
EnhancedSwapRouterV2 internal router;
|
|
|
|
address internal user = address(0x1234);
|
|
bytes32 internal constant BALANCER_POOL_ID = keccak256("chain138-pilot-balancer-weth-usdc");
|
|
|
|
function setUp() public {
|
|
weth = new PilotVenueToken("Wrapped Ether", "WETH", 10_000 ether);
|
|
usdt = new PilotVenueToken("Tether", "USDT", 50_000_000 * 1e6);
|
|
usdc = new PilotVenueToken("USD Coin", "USDC", 50_000_000 * 1e6);
|
|
dai = new PilotVenueToken("Dai", "DAI", 10_000_000 ether);
|
|
|
|
uniswapRouter = new Chain138PilotUniswapV3Router();
|
|
balancerVault = new Chain138PilotBalancerVault();
|
|
curvePool = new Chain138PilotCurve3Pool(address(usdt), address(usdc), address(0), 4);
|
|
oneInchRouter = new Chain138PilotOneInchAggregationRouter();
|
|
|
|
router = new EnhancedSwapRouterV2(address(weth), address(usdt), address(usdc), address(dai));
|
|
router.setProviderAdapter(RouteTypesV2.Provider.UniswapV3, address(new UniswapV3RouteExecutorAdapter()));
|
|
router.setProviderAdapter(RouteTypesV2.Provider.Balancer, address(new BalancerRouteExecutorAdapter()));
|
|
router.setProviderAdapter(RouteTypesV2.Provider.Curve, address(new CurveRouteExecutorAdapter()));
|
|
router.setProviderAdapter(RouteTypesV2.Provider.OneInch, address(new OneInchRouteExecutorAdapter()));
|
|
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, true);
|
|
|
|
weth.approve(address(uniswapRouter), type(uint256).max);
|
|
usdt.approve(address(uniswapRouter), type(uint256).max);
|
|
usdc.approve(address(uniswapRouter), type(uint256).max);
|
|
weth.approve(address(balancerVault), type(uint256).max);
|
|
usdt.approve(address(balancerVault), type(uint256).max);
|
|
usdc.approve(address(balancerVault), type(uint256).max);
|
|
usdt.approve(address(curvePool), type(uint256).max);
|
|
usdc.approve(address(curvePool), type(uint256).max);
|
|
weth.approve(address(oneInchRouter), type(uint256).max);
|
|
usdt.approve(address(oneInchRouter), type(uint256).max);
|
|
usdc.approve(address(oneInchRouter), type(uint256).max);
|
|
|
|
uniswapRouter.seedPair(address(weth), address(usdt), 3000, 100 ether, 210_000 * 1e6);
|
|
uniswapRouter.seedPair(address(weth), address(usdc), 3000, 100 ether, 210_000 * 1e6);
|
|
balancerVault.seedPool(BALANCER_POOL_ID, address(weth), address(usdc), 100 ether, 210_000 * 1e6, 30);
|
|
curvePool.fund(500_000 * 1e6, 500_000 * 1e6, 0);
|
|
oneInchRouter.seedRoute(address(weth), address(usdt), 100 ether, 210_000 * 1e6, 35);
|
|
|
|
router.setProviderRoute(
|
|
address(weth),
|
|
address(usdt),
|
|
RouteTypesV2.Provider.UniswapV3,
|
|
address(uniswapRouter),
|
|
abi.encode(bytes(""), uint24(3000), address(uniswapRouter), false),
|
|
true
|
|
);
|
|
router.setProviderRoute(
|
|
address(weth),
|
|
address(usdc),
|
|
RouteTypesV2.Provider.Balancer,
|
|
address(balancerVault),
|
|
abi.encode(BALANCER_POOL_ID),
|
|
true
|
|
);
|
|
router.setProviderRoute(
|
|
address(usdt),
|
|
address(usdc),
|
|
RouteTypesV2.Provider.Curve,
|
|
address(curvePool),
|
|
abi.encode(int128(0), int128(1), false),
|
|
true
|
|
);
|
|
router.setProviderRoute(
|
|
address(weth),
|
|
address(usdt),
|
|
RouteTypesV2.Provider.OneInch,
|
|
address(oneInchRouter),
|
|
abi.encode(address(oneInchRouter), address(oneInchRouter), bytes("")),
|
|
true
|
|
);
|
|
|
|
weth.mint(user, 10 ether);
|
|
usdt.mint(user, 50_000 * 1e6);
|
|
}
|
|
|
|
function testUniswapPilotQuotesAndExecutes() public {
|
|
uint256 quote = uniswapRouter.quoteExactInputSingle(address(weth), address(usdt), 3000, 1 ether, 0);
|
|
assertGt(quote, 0);
|
|
|
|
vm.startPrank(user);
|
|
weth.approve(address(router), 1 ether);
|
|
RouteTypesV2.RouteLeg[] memory legs = new RouteTypesV2.RouteLeg[](1);
|
|
legs[0] = RouteTypesV2.RouteLeg({
|
|
provider: RouteTypesV2.Provider.UniswapV3,
|
|
tokenIn: address(weth),
|
|
tokenOut: address(usdt),
|
|
amountSource: RouteTypesV2.AmountSource.UserInput,
|
|
minAmountOut: quote - 1,
|
|
target: address(uniswapRouter),
|
|
providerData: abi.encode(bytes(""), uint24(3000), address(uniswapRouter), false)
|
|
});
|
|
RouteTypesV2.RoutePlan memory plan = RouteTypesV2.RoutePlan({
|
|
chainId: block.chainid,
|
|
inputToken: address(weth),
|
|
outputToken: address(usdt),
|
|
amountIn: 1 ether,
|
|
minAmountOut: quote - 1,
|
|
recipient: user,
|
|
deadline: block.timestamp + 300,
|
|
legs: legs
|
|
});
|
|
uint256 amountOut = router.executeRoute(plan);
|
|
vm.stopPrank();
|
|
|
|
assertEq(amountOut, quote);
|
|
}
|
|
|
|
function testBalancerPilotExecutes() public {
|
|
vm.startPrank(user);
|
|
weth.approve(address(router), 1 ether);
|
|
RouteTypesV2.RouteLeg[] memory legs = new RouteTypesV2.RouteLeg[](1);
|
|
legs[0] = RouteTypesV2.RouteLeg({
|
|
provider: RouteTypesV2.Provider.Balancer,
|
|
tokenIn: address(weth),
|
|
tokenOut: address(usdc),
|
|
amountSource: RouteTypesV2.AmountSource.UserInput,
|
|
minAmountOut: 1_000 * 1e6,
|
|
target: address(balancerVault),
|
|
providerData: abi.encode(BALANCER_POOL_ID)
|
|
});
|
|
RouteTypesV2.RoutePlan memory plan = RouteTypesV2.RoutePlan({
|
|
chainId: block.chainid,
|
|
inputToken: address(weth),
|
|
outputToken: address(usdc),
|
|
amountIn: 1 ether,
|
|
minAmountOut: 1_000 * 1e6,
|
|
recipient: user,
|
|
deadline: block.timestamp + 300,
|
|
legs: legs
|
|
});
|
|
uint256 amountOut = router.executeRoute(plan);
|
|
vm.stopPrank();
|
|
|
|
assertGt(amountOut, 0);
|
|
}
|
|
|
|
function testCurvePilotExecutes() public {
|
|
vm.startPrank(user);
|
|
usdt.approve(address(router), 10_000 * 1e6);
|
|
RouteTypesV2.RouteLeg[] memory legs = new RouteTypesV2.RouteLeg[](1);
|
|
legs[0] = RouteTypesV2.RouteLeg({
|
|
provider: RouteTypesV2.Provider.Curve,
|
|
tokenIn: address(usdt),
|
|
tokenOut: address(usdc),
|
|
amountSource: RouteTypesV2.AmountSource.UserInput,
|
|
minAmountOut: 9_900 * 1e6,
|
|
target: address(curvePool),
|
|
providerData: abi.encode(int128(0), int128(1), false)
|
|
});
|
|
RouteTypesV2.RoutePlan memory plan = RouteTypesV2.RoutePlan({
|
|
chainId: block.chainid,
|
|
inputToken: address(usdt),
|
|
outputToken: address(usdc),
|
|
amountIn: 10_000 * 1e6,
|
|
minAmountOut: 9_900 * 1e6,
|
|
recipient: user,
|
|
deadline: block.timestamp + 300,
|
|
legs: legs
|
|
});
|
|
uint256 amountOut = router.executeRoute(plan);
|
|
vm.stopPrank();
|
|
|
|
assertGt(amountOut, 0);
|
|
}
|
|
|
|
function testOneInchPilotExecutes() public {
|
|
vm.startPrank(user);
|
|
weth.approve(address(router), 1 ether);
|
|
RouteTypesV2.RouteLeg[] memory legs = new RouteTypesV2.RouteLeg[](1);
|
|
legs[0] = RouteTypesV2.RouteLeg({
|
|
provider: RouteTypesV2.Provider.OneInch,
|
|
tokenIn: address(weth),
|
|
tokenOut: address(usdt),
|
|
amountSource: RouteTypesV2.AmountSource.UserInput,
|
|
minAmountOut: 1_000 * 1e6,
|
|
target: address(oneInchRouter),
|
|
providerData: abi.encode(address(oneInchRouter), address(oneInchRouter), bytes(""))
|
|
});
|
|
RouteTypesV2.RoutePlan memory plan = RouteTypesV2.RoutePlan({
|
|
chainId: block.chainid,
|
|
inputToken: address(weth),
|
|
outputToken: address(usdt),
|
|
amountIn: 1 ether,
|
|
minAmountOut: 1_000 * 1e6,
|
|
recipient: user,
|
|
deadline: block.timestamp + 300,
|
|
legs: legs
|
|
});
|
|
uint256 amountOut = router.executeRoute(plan);
|
|
vm.stopPrank();
|
|
|
|
assertGt(amountOut, 0);
|
|
}
|
|
}
|