// 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"; /** * @title BatchOperationsTest * @notice Test suite for batch processing operations */ contract BatchOperationsTest is Test { BondManager public bondManager; ChallengeManager public challengeManager; InboxETH public inbox; LiquidityPoolETH public liquidityPool; // Make contract payable to receive ETH from bond releases receive() external payable {} address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address public relayer = address(0x1111); address public recipient = address(0x2222); uint256 public constant BOND_MULTIPLIER = 11000; uint256 public constant MIN_BOND = 1 ether; uint256 public constant CHALLENGE_WINDOW = 30 minutes; function setUp() public { bondManager = new BondManager(BOND_MULTIPLIER, MIN_BOND); challengeManager = new ChallengeManager(address(bondManager), CHALLENGE_WINDOW); 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_BatchClaimSubmission() public { uint256[] memory depositIds = new uint256[](3); address[] memory assets = new address[](3); uint256[] memory amounts = new uint256[](3); address[] memory recipients = new address[](3); bytes[] memory proofs = new bytes[](3); for (uint256 i = 0; i < 3; i++) { depositIds[i] = 1000 + i; assets[i] = address(0); // ETH amounts[i] = 1 ether; recipients[i] = recipient; proofs[i] = ""; } uint256 totalBond = bondManager.getRequiredBond(1 ether) * 3; // Advance time to ensure no cooldown issues vm.warp(block.timestamp + 1); vm.prank(relayer); uint256 bondAmount = inbox.submitClaimsBatch{value: totalBond}( depositIds, assets, amounts, recipients, proofs ); assertEq(bondAmount, totalBond, "Total bond should match"); // Verify all claims registered for (uint256 i = 0; i < 3; i++) { ChallengeManager.Claim memory claim = challengeManager.getClaim(depositIds[i]); assertEq(claim.depositId, depositIds[i], "Claim should be registered"); } } function test_BatchFinalization() public { // Submit claims first - start from timestamp 1000, first claim at 1001 uint256 currentTime = 1001; for (uint256 i = 0; i < 3; i++) { vm.deal(relayer, 100 ether); // Advance time to respect cooldown period (61 seconds between claims) vm.warp(currentTime); vm.prank(relayer); inbox.submitClaim{value: bondManager.getRequiredBond(1 ether)}( 2000 + i, address(0), 1 ether, recipient, "" ); currentTime += 61 seconds; } // Wait for challenge window vm.warp(block.timestamp + CHALLENGE_WINDOW + 1); // Finalize in batch uint256[] memory depositIds = new uint256[](3); for (uint256 i = 0; i < 3; i++) { depositIds[i] = 2000 + i; } challengeManager.finalizeClaimsBatch(depositIds); // Verify all finalized for (uint256 i = 0; i < 3; i++) { ChallengeManager.Claim memory claim = challengeManager.getClaim(depositIds[i]); assertTrue(claim.finalized, "Claim should be finalized"); } } function test_BatchBondRelease() public { // Submit and finalize claims - start from timestamp 1000, first claim at 1001 uint256 currentTime = 1001; for (uint256 i = 0; i < 3; i++) { vm.deal(relayer, 100 ether); // Advance time to respect cooldown period (61 seconds between claims) vm.warp(currentTime); vm.prank(relayer); inbox.submitClaim{value: bondManager.getRequiredBond(1 ether)}( 3000 + i, address(0), 1 ether, recipient, "" ); currentTime += 61 seconds; } vm.warp(block.timestamp + CHALLENGE_WINDOW + 1); uint256[] memory depositIds = new uint256[](3); for (uint256 i = 0; i < 3; i++) { depositIds[i] = 3000 + i; challengeManager.finalizeClaim(depositIds[i]); } // Release bonds in batch // Note: Due to vm.prank behavior, the relayer stored might be the test contract // The test contract needs to be able to receive ETH for this to work uint256 totalReleased = bondManager.releaseBondsBatch(depositIds); assertGt(totalReleased, 0, "Should release bonds"); // Verify bonds released for (uint256 i = 0; i < 3; i++) { (address bondRelayer, , bool slashed, bool released) = bondManager.getBond(depositIds[i]); assertTrue(released, "Bond should be released"); assertFalse(slashed, "Bond should not be slashed"); } } function test_BatchTooLarge() public { uint256[] memory depositIds = new uint256[](51); address[] memory assets = new address[](51); uint256[] memory amounts = new uint256[](51); address[] memory recipients = new address[](51); bytes[] memory proofs = new bytes[](51); for (uint256 i = 0; i < 51; i++) { depositIds[i] = 4000 + i; assets[i] = address(0); amounts[i] = 1 ether; recipients[i] = recipient; proofs[i] = ""; } vm.prank(relayer); vm.expectRevert("InboxETH: batch too large"); inbox.submitClaimsBatch{value: 100 ether}( depositIds, assets, amounts, recipients, proofs ); } }