// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; import "../../../contracts/bridge/trustless/EnhancedSwapRouter.sol"; import "../../../contracts/bridge/trustless/LiquidityPoolETH.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { _mint(msg.sender, 1000000 ether); } } contract MockLiquidityProvider { mapping(address => mapping(address => bool)) public supported; mapping(address => mapping(address => uint256)) public quotes; function setSupport(address tokenIn, address tokenOut, bool isSupported) external { supported[tokenIn][tokenOut] = isSupported; } function setQuote(address tokenIn, address tokenOut, uint256 amountOut) external { quotes[tokenIn][tokenOut] = amountOut; } function getQuote( address tokenIn, address tokenOut, uint256 ) external view returns (uint256 amountOut, uint256 slippageBps) { if (!supported[tokenIn][tokenOut]) { return (0, 10000); } return (quotes[tokenIn][tokenOut], 30); } function executeSwap( address tokenIn, address tokenOut, uint256 amountIn, uint256 ) external returns (uint256 amountOut) { require(supported[tokenIn][tokenOut], "unsupported"); amountOut = quotes[tokenIn][tokenOut]; ERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn); ERC20(tokenOut).transfer(msg.sender, amountOut); } function supportsTokenPair(address tokenIn, address tokenOut) external view returns (bool) { return supported[tokenIn][tokenOut]; } function providerName() external pure returns (string memory) { return "Mock"; } function estimateGas(address, address, uint256) external pure returns (uint256) { return 150000; } } contract EnhancedSwapRouterTest is Test { EnhancedSwapRouter public router; LiquidityPoolETH public liquidityPool; MockLiquidityProvider public liquidityProvider; MockERC20 public weth; MockERC20 public usdt; MockERC20 public usdc; MockERC20 public dai; address public deployer = address(0xDE0001); address public user = address(0x1111); // Mock protocol addresses address public uniswapV3Router = address(0x1111111111111111111111111111111111111111); address public curve3Pool = address(0x2222222222222222222222222222222222222222); address public dodoexRouter = address(0x3333333333333333333333333333333333333333); address public balancerVault = address(0x4444444444444444444444444444444444444444); address public oneInchRouter = address(0x5555555555555555555555555555555555555555); function setUp() public { vm.startPrank(deployer); // Deploy mock tokens weth = new MockERC20("Wrapped Ether", "WETH"); usdt = new MockERC20("Tether USD", "USDT"); usdc = new MockERC20("USD Coin", "USDC"); dai = new MockERC20("Dai Stablecoin", "DAI"); liquidityProvider = new MockLiquidityProvider(); // Deploy liquidity pool liquidityPool = new LiquidityPoolETH(address(weth), 5, 11000); // Deploy enhanced swap router router = new EnhancedSwapRouter( uniswapV3Router, curve3Pool, dodoexRouter, balancerVault, oneInchRouter, address(weth), address(usdt), address(usdc), address(dai) ); // Grant ROUTING_MANAGER_ROLE to deployer for tests router.grantRole(router.ROUTING_MANAGER_ROLE(), deployer); // Fund user vm.deal(user, 100 ether); vm.stopPrank(); } function testInitialization() public { assertEq(address(router.weth()), address(weth)); assertEq(address(router.usdt()), address(usdt)); assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3)); assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.Dodoex)); } function testSetProviderEnabled() public { // Deployer already has ROUTING_MANAGER_ROLE from setUp vm.prank(deployer); router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, false); assertFalse(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3)); // Re-enable for other tests vm.prank(deployer); router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, true); assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3)); } function testSetRoutingConfig() public { // Deployer already has ROUTING_MANAGER_ROLE from setUp EnhancedSwapRouter.SwapProvider[] memory providers = new EnhancedSwapRouter.SwapProvider[](2); providers[0] = EnhancedSwapRouter.SwapProvider.Dodoex; providers[1] = EnhancedSwapRouter.SwapProvider.Balancer; vm.prank(deployer); router.setRoutingConfig(0, providers); // Small swaps // Verify config was set (would need getter function) // For now, just verify no revert assertTrue(true); } function testSetBalancerPoolId() public { // Deployer already has ROUTING_MANAGER_ROLE from setUp bytes32 poolId = keccak256("test-pool"); vm.prank(deployer); router.setBalancerPoolId(address(weth), address(usdt), poolId); assertEq(router.balancerPoolIds(address(weth), address(usdt)), poolId); } function testGetQuotes() public view { // This would require actual protocol integration // For now, just verify function exists (EnhancedSwapRouter.SwapProvider[] memory providers, uint256[] memory amounts) = router.getQuotes(address(usdt), 1 ether); // In production, would verify quotes are returned assertTrue(providers.length >= 0); } function testSetDodoPoolAddress() public { address mockPool = address(0xDD00); vm.prank(deployer); router.setDodoPoolAddress(address(weth), address(usdt), mockPool); assertEq(router.dodoPoolAddresses(address(weth), address(usdt)), mockPool); vm.prank(deployer); router.setDodoPoolAddress(address(usdt), address(weth), mockPool); assertEq(router.dodoPoolAddresses(address(usdt), address(weth)), mockPool); } function testSwapTokenToTokenRevertsWhenPoolNotSet() public { vm.prank(user); weth.approve(address(router), 1 ether); vm.expectRevert(EnhancedSwapRouter.DodoRouteNotConfigured.selector); router.swapTokenToToken(address(weth), address(usdt), 1 ether, 0); } function testSetDodoLiquidityProvider() public { vm.prank(deployer); router.setDodoLiquidityProvider(address(liquidityProvider)); assertEq(router.dodoLiquidityProvider(), address(liquidityProvider)); } function testGetQuotesUsesDodoLiquidityProviderWhenRouteSupported() public { vm.startPrank(deployer); router.setDodoLiquidityProvider(address(liquidityProvider)); router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, false); router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.Balancer, false); vm.stopPrank(); liquidityProvider.setSupport(address(weth), address(usdt), true); liquidityProvider.setQuote(address(weth), address(usdt), 1234); (EnhancedSwapRouter.SwapProvider[] memory providers, uint256[] memory amounts) = router.getQuotes(address(usdt), 1 ether); assertEq(providers.length, 1); assertEq(uint256(providers[0]), uint256(EnhancedSwapRouter.SwapProvider.Dodoex)); assertEq(amounts[0], 1234); } function testSwapTokenToTokenRevertsWhenProviderRouteMissing() public { address mockPool = address(0xDD00); vm.startPrank(deployer); router.setDodoPoolAddress(address(weth), address(usdt), mockPool); router.setDodoLiquidityProvider(address(liquidityProvider)); vm.stopPrank(); vm.prank(user); vm.expectRevert(EnhancedSwapRouter.DodoRouteNotConfigured.selector); router.swapTokenToToken(address(weth), address(usdt), 1 ether, 0); } }