// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../contracts/bridge/UniversalCCIPBridge.sol"; import "../../contracts/liquidity/LiquidityManager.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; /** * @title Reentrancy Security Tests * @notice Tests for reentrancy protection */ contract ReentrancySecurityTest is Test { UniversalCCIPBridge public bridge; LiquidityManager public liquidityManager; address public admin; address public attacker; function setUp() public { admin = makeAddr("admin"); attacker = makeAddr("attacker"); vm.startPrank(admin); UniversalCCIPBridge bridgeImpl = new UniversalCCIPBridge(); bytes memory bridgeInitData = abi.encodeWithSelector( UniversalCCIPBridge.initialize.selector, makeAddr("registry"), makeAddr("router"), admin ); ERC1967Proxy bridgeProxy = new ERC1967Proxy(address(bridgeImpl), bridgeInitData); bridge = UniversalCCIPBridge(payable(address(bridgeProxy))); LiquidityManager lmImpl = new LiquidityManager(); bytes memory lmInitData = abi.encodeWithSelector(LiquidityManager.initialize.selector, admin); ERC1967Proxy lmProxy = new ERC1967Proxy(address(lmImpl), lmInitData); liquidityManager = LiquidityManager(address(lmProxy)); vm.stopPrank(); } function testBridgeReentrancyProtection() public { // Deploy malicious token that attempts reentrancy MaliciousToken maliciousToken = new MaliciousToken(payable(address(bridge))); vm.startPrank(attacker); UniversalCCIPBridge.BridgeOperation memory op = UniversalCCIPBridge.BridgeOperation({ token: address(maliciousToken), amount: 1000e18, destinationChain: 1, recipient: attacker, assetType: bytes32(0), usePMM: false, useVault: false, complianceProof: "", vaultInstructions: "" }); // Should fail due to reentrancy guard vm.expectRevert(); bridge.bridge(op); vm.stopPrank(); } function testLiquidityManagerReentrancy() public { // Test that liquidity manager has nonReentrant and returns safely for unconfigured token vm.startPrank(attacker); uint256 result = liquidityManager.provideLiquidity(makeAddr("token"), 1000e18, ""); assertEq(result, 0, "Unconfigured token should return 0"); vm.stopPrank(); } } /** * @title MaliciousToken * @notice Mock token that attempts reentrancy attack */ contract MaliciousToken { address payable public bridge; constructor(address payable _bridge) { bridge = _bridge; } function transferFrom(address, address, uint256) external returns (bool) { // Attempt reentrancy UniversalCCIPBridge.BridgeOperation memory op = UniversalCCIPBridge.BridgeOperation({ token: address(this), amount: 1000e18, destinationChain: 1, recipient: msg.sender, assetType: bytes32(0), usePMM: false, useVault: false, complianceProof: "", vaultInstructions: "" }); UniversalCCIPBridge(bridge).bridge(op); return true; } }