109 lines
3.4 KiB
Solidity
109 lines
3.4 KiB
Solidity
// 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;
|
|
}
|
|
}
|