// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test, console} from "forge-std/Test.sol"; import "../../../contracts/bridge/trustless/BondManager.sol"; import "../../../contracts/bridge/trustless/ChallengeManager.sol"; import "../../../contracts/bridge/trustless/InboxETH.sol"; import "../../../contracts/bridge/trustless/LiquidityPoolETH.sol"; import "../../../contracts/bridge/trustless/EnhancedSwapRouter.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 PerformanceBenchmarkTest is Test { BondManager public bondManager; ChallengeManager public challengeManager; LiquidityPoolETH public liquidityPool; InboxETH public inbox; EnhancedSwapRouter public swapRouter; MockERC20 public weth; MockERC20 public usdt; MockERC20 public usdc; MockERC20 public dai; address public deployer = address(0xDE0001); address public relayer = address(0x1111); address public lp = address(0x2222); uint256 public constant BOND_MULTIPLIER = 1.1e18; uint256 public constant MIN_BOND = 1 ether; uint256 public constant CHALLENGE_WINDOW = 30 minutes; // 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); weth = new MockERC20("Wrapped Ether", "WETH"); usdt = new MockERC20("Tether USD", "USDT"); usdc = new MockERC20("USD Coin", "USDC"); dai = new MockERC20("Dai Stablecoin", "DAI"); bondManager = new BondManager(BOND_MULTIPLIER, MIN_BOND); challengeManager = new ChallengeManager(address(bondManager), CHALLENGE_WINDOW); liquidityPool = new LiquidityPoolETH(address(weth), 5, 11000); inbox = new InboxETH(address(bondManager), address(challengeManager), address(liquidityPool)); swapRouter = new EnhancedSwapRouter( uniswapV3Router, curve3Pool, dodoexRouter, balancerVault, oneInchRouter, address(weth), address(usdt), address(usdc), address(dai) ); liquidityPool.authorizeRelease(address(inbox)); swapRouter.grantRole(swapRouter.ROUTING_MANAGER_ROLE(), deployer); vm.deal(relayer, 1000 ether); vm.deal(lp, 10000 ether); vm.warp(1000); // Provide liquidity vm.stopPrank(); vm.prank(lp); liquidityPool.provideLiquidity{value: 1000 ether}(LiquidityPoolETH.AssetType.ETH); vm.startPrank(deployer); } function testBenchmark_SubmitClaim() public { uint256 amount = 1 ether; uint256 bond = bondManager.getRequiredBond(amount); uint256 gasStart = gasleft(); vm.prank(relayer); inbox.submitClaim{value: bond}(1, address(0), amount, address(0x3333), ""); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for submitClaim:", gasUsed); // Benchmark: Should be under 200k gas assertLt(gasUsed, 200000, "submitClaim gas too high"); } function testBenchmark_BatchSubmitClaims() public { uint256 amount = 1 ether; uint256 bond = bondManager.getRequiredBond(amount); uint256 batchSize = 10; uint256 gasStart = gasleft(); for (uint256 i = 1; i <= batchSize; i++) { vm.prank(relayer); inbox.submitClaim{value: bond}(i, address(0), amount, address(0x3333), ""); } uint256 gasUsed = gasStart - gasleft(); uint256 avgGasPerClaim = gasUsed / batchSize; console.log("Total gas for", batchSize, "claims:", gasUsed); console.log("Average gas per claim:", avgGasPerClaim); // Average should be reasonable assertLt(avgGasPerClaim, 150000, "Average gas per claim too high"); } function testBenchmark_GetQuotes() public view { uint256 gasStart = gasleft(); swapRouter.getQuotes(address(usdt), 1 ether); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for getQuotes:", gasUsed); // Should be relatively cheap (view function) assertLt(gasUsed, 100000, "getQuotes gas too high"); } function testBenchmark_RoutingConfigUpdate() public { EnhancedSwapRouter.SwapProvider[] memory providers = new EnhancedSwapRouter.SwapProvider[](3); providers[0] = EnhancedSwapRouter.SwapProvider.Dodoex; providers[1] = EnhancedSwapRouter.SwapProvider.Balancer; providers[2] = EnhancedSwapRouter.SwapProvider.UniswapV3; uint256 gasStart = gasleft(); vm.prank(deployer); swapRouter.setRoutingConfig(0, providers); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for setRoutingConfig:", gasUsed); // Should be cheap assertLt(gasUsed, 100000, "setRoutingConfig gas too high"); } function testBenchmark_ProviderToggle() public { uint256 gasStart = gasleft(); vm.prank(deployer); swapRouter.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, false); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for setProviderEnabled:", gasUsed); // Should be very cheap (SSTORE) assertLt(gasUsed, 50000, "setProviderEnabled gas too high"); } function testBenchmark_BondRelease() public { uint256 depositId = 1; uint256 amount = 1 ether; uint256 bond = bondManager.getRequiredBond(amount); // Submit and finalize claim vm.prank(relayer); inbox.submitClaim{value: bond}(depositId, address(0), amount, address(0x3333), ""); vm.warp(block.timestamp + CHALLENGE_WINDOW + 1); challengeManager.finalizeClaim(depositId); uint256 gasStart = gasleft(); bondManager.releaseBond(depositId); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for releaseBond:", gasUsed); // Should be reasonable assertLt(gasUsed, 100000, "releaseBond gas too high"); } function testBenchmark_BatchBondRelease() public { uint256 batchSize = 10; uint256 amount = 1 ether; uint256 bond = bondManager.getRequiredBond(amount); // Submit multiple claims for (uint256 i = 1; i <= batchSize; i++) { vm.prank(relayer); inbox.submitClaim{value: bond}(i, address(0), amount, address(0x3333), ""); } vm.warp(block.timestamp + CHALLENGE_WINDOW + 1); // Finalize all for (uint256 i = 1; i <= batchSize; i++) { challengeManager.finalizeClaim(i); } // Prepare batch array uint256[] memory depositIds = new uint256[](batchSize); for (uint256 i = 0; i < batchSize; i++) { depositIds[i] = i + 1; } uint256 gasStart = gasleft(); bondManager.releaseBondsBatch(depositIds); uint256 gasUsed = gasStart - gasleft(); uint256 avgGasPerRelease = gasUsed / batchSize; console.log("Total gas for batch release of", batchSize, "bonds:", gasUsed); console.log("Average gas per release:", avgGasPerRelease); // Batch should be more efficient assertLt(avgGasPerRelease, 80000, "Average gas per batch release too high"); } function testBenchmark_LiquidityProvision() public { uint256 gasStart = gasleft(); vm.prank(lp); liquidityPool.provideLiquidity{value: 10 ether}(LiquidityPoolETH.AssetType.ETH); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for provideLiquidity:", gasUsed); // Should be reasonable assertLt(gasUsed, 150000, "provideLiquidity gas too high"); } function testBenchmark_GetRequiredBond() public view { uint256 gasStart = gasleft(); bondManager.getRequiredBond(1 ether); uint256 gasUsed = gasStart - gasleft(); console.log("Gas used for getRequiredBond:", gasUsed); // Should be very cheap (view function with simple math) assertLt(gasUsed, 10000, "getRequiredBond gas too high"); } }