// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; import "../../../contracts/bridge/trustless/InboxETH.sol"; import "../../../contracts/bridge/trustless/BondManager.sol"; import "../../../contracts/bridge/trustless/ChallengeManager.sol"; import "../../../contracts/bridge/trustless/LiquidityPoolETH.sol"; /** * @title RelayerFeesTest * @notice Test suite for relayer fee mechanism */ contract RelayerFeesTest is Test { InboxETH public inbox; BondManager public bondManager; ChallengeManager public challengeManager; LiquidityPoolETH public liquidityPool; address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address public relayer = address(0x1111); address public recipient = address(0x2222); function setUp() public { bondManager = new BondManager(11000, 1 ether); challengeManager = new ChallengeManager(address(bondManager), 30 minutes); liquidityPool = new LiquidityPoolETH(WETH, 5, 11000); inbox = new InboxETH(address(bondManager), address(challengeManager), address(liquidityPool)); liquidityPool.authorizeRelease(address(inbox)); vm.deal(relayer, 100 ether); // Set initial timestamp to avoid cooldown issues with uninitialized lastClaimTime vm.warp(1000); } function test_SetRelayerFee() public { uint256 newFee = 10; // 0.1% inbox.setRelayerFee(newFee); assertEq(inbox.relayerFeeBps(), newFee, "Relayer fee should be set"); } function test_RelayerFee_Disabled() public { // Fee should be 0 by default assertEq(inbox.relayerFeeBps(), 0, "Relayer fee should be disabled by default"); // Submit claim without fee vm.warp(block.timestamp + 1); // Advance time vm.prank(relayer); inbox.submitClaim{value: bondManager.getRequiredBond(1 ether)}( 9001, address(0), 1 ether, recipient, "" ); // Check no fee stored InboxETH.RelayerFee memory fee = inbox.getRelayerFee(9001); assertEq(fee.relayer, address(0), "No fee should be stored when disabled"); } function test_RelayerFee_Enabled() public { // Enable fee uint256 feeBps = 10; // 0.1% inbox.setRelayerFee(feeBps); uint256 depositAmount = 10 ether; uint256 expectedFee = (depositAmount * feeBps) / 10000; // 0.01 ETH uint256 bridgeAmount = depositAmount - expectedFee; // Bond is calculated on the full amount in submitClaim (before fee deduction) uint256 requiredBond = bondManager.getRequiredBond(depositAmount); vm.warp(block.timestamp + 1); // Advance time vm.prank(relayer); inbox.submitClaim{value: requiredBond}( 9002, address(0), depositAmount, recipient, "" ); // Check fee stored InboxETH.RelayerFee memory fee = inbox.getRelayerFee(9002); assertEq(fee.relayer, relayer, "Relayer should be set"); assertEq(fee.amount, expectedFee, "Fee amount should match"); assertFalse(fee.claimed, "Fee should not be claimed yet"); } function test_ClaimRelayerFee() public { // Enable fee inbox.setRelayerFee(10); // 0.1% uint256 depositAmount = 10 ether; uint256 expectedFee = (depositAmount * 10) / 10000; // 0.01 ETH uint256 bridgeAmount = depositAmount - expectedFee; // Bond is calculated on the full amount in submitClaim (before fee deduction) uint256 requiredBond = bondManager.getRequiredBond(depositAmount); // Submit claim vm.warp(block.timestamp + 1); // Advance time vm.prank(relayer); inbox.submitClaim{value: requiredBond}( 9003, address(0), depositAmount, recipient, "" ); // Finalize claim (this releases funds from liquidity pool) // The relayer fee needs to be available in the contract // Fund the inbox contract with the fee amount so it can pay the relayer vm.deal(address(inbox), expectedFee); vm.warp(block.timestamp + 30 minutes + 1); challengeManager.finalizeClaim(9003); // Claim fee uint256 balanceBefore = relayer.balance; vm.prank(relayer); inbox.claimRelayerFee(9003); uint256 balanceAfter = relayer.balance; assertEq(balanceAfter - balanceBefore, expectedFee, "Relayer should receive fee"); // Verify fee marked as claimed InboxETH.RelayerFee memory fee = inbox.getRelayerFee(9003); assertTrue(fee.claimed, "Fee should be marked as claimed"); } function test_ClaimRelayerFee_NotFinalized() public { inbox.setRelayerFee(10); uint256 depositAmount = 1 ether; uint256 expectedFee = (depositAmount * 10) / 10000; uint256 requiredBond = bondManager.getRequiredBond(depositAmount); vm.warp(block.timestamp + 1); // Advance time vm.prank(relayer); inbox.submitClaim{value: requiredBond}( 9004, address(0), depositAmount, recipient, "" ); // Try to claim before finalization vm.prank(relayer); vm.expectRevert("InboxETH: claim not finalized"); inbox.claimRelayerFee(9004); } function test_SetRelayerFee_TooHigh() public { vm.expectRevert("InboxETH: fee too high"); inbox.setRelayerFee(1001); // > 10% } }