diff --git a/test/invariant/InvariantBridge.t.sol b/test/invariant/InvariantBridge.t.sol index 9b5ab6cd..ee52ffaa 100644 --- a/test/invariant/InvariantBridge.t.sol +++ b/test/invariant/InvariantBridge.t.sol @@ -201,9 +201,15 @@ contract InvariantBridge is Test { childSelectors[7] = childBridgeHandler.withdrawETHTo.selector; targetSelector(FuzzSelector({addr: address(childBridgeHandler), selectors: childSelectors})); - bytes4[] memory rootSelectors = new bytes4[](2); + bytes4[] memory rootSelectors = new bytes4[](8); rootSelectors[0] = rootBridgeHandler.deposit.selector; rootSelectors[1] = rootBridgeHandler.depositTo.selector; + rootSelectors[2] = rootBridgeHandler.depositIMX.selector; + rootSelectors[3] = rootBridgeHandler.depositIMXTo.selector; + rootSelectors[4] = rootBridgeHandler.depositETH.selector; + rootSelectors[5] = rootBridgeHandler.depositETHTo.selector; + rootSelectors[6] = rootBridgeHandler.depositWETH.selector; + rootSelectors[7] = rootBridgeHandler.depositWETHTo.selector; targetSelector(FuzzSelector({addr: address(rootBridgeHandler), selectors: rootSelectors})); targetContract(address(childBridgeHandler)); diff --git a/test/invariant/root/RootERC20BridgeFlowRateHandler.sol b/test/invariant/root/RootERC20BridgeFlowRateHandler.sol index 65e30bca..a4334d6d 100644 --- a/test/invariant/root/RootERC20BridgeFlowRateHandler.sol +++ b/test/invariant/root/RootERC20BridgeFlowRateHandler.sol @@ -5,6 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {ChildERC20} from "../../../src/child/ChildERC20.sol"; import {ChildHelper} from "../child/ChildHelper.sol"; import {RootHelper} from "./RootHelper.sol"; +import {WIMX as WETH} from "../../../src/child/WIMX.sol"; contract RootERC20BridgeFlowRateHandler is Test { uint256 public constant MAX_AMOUNT = 10000; @@ -111,4 +112,211 @@ contract RootERC20BridgeFlowRateHandler is Test { vm.selectFork(original); } + + function depositIMX(uint256 userIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = ChildERC20(rootHelper.rootBridge().rootIMXToken()).balanceOf(user); + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawIMX(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + rootHelper.depositIMX(user, amount, gasAmt); + + vm.selectFork(original); + } + + function depositIMXTo(uint256 userIndexSeed, uint256 recipientIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + address recipient = users[bound(recipientIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = ChildERC20(rootHelper.rootBridge().rootIMXToken()).balanceOf(user); + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawIMX(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + rootHelper.depositIMXTo(user, recipient, amount, gasAmt); + + // If recipient is different, transfer back + if (user != recipient) { + vm.selectFork(childId); + vm.prank(recipient); + user.call{value: amount}(""); + vm.selectFork(rootId); + } + + vm.selectFork(original); + } + + function depositETH(uint256 userIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = user.balance; + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawETH(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + rootHelper.depositETH(user, amount, gasAmt); + + vm.selectFork(original); + } + + function depositETHTo(uint256 userIndexSeed, uint256 recipientIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + address recipient = users[bound(recipientIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = user.balance; + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawETH(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + rootHelper.depositETHTo(user, recipient, amount, gasAmt); + + // If recipient is different, transfer back + if (user != recipient) { + vm.selectFork(childId); + address eth = childHelper.childBridge().childETHToken(); + vm.prank(recipient); + ChildERC20(eth).transfer(user, amount); + } + vm.selectFork(childId); + + vm.selectFork(original); + } + + function depositWETH(uint256 userIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = user.balance; + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawETH(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + // Wrap ETH + address payable wETH = payable(rootHelper.rootBridge().rootWETHToken()); + vm.prank(user); + WETH(wETH).deposit{value: amount}(); + + rootHelper.depositWETH(user, amount, gasAmt); + + vm.selectFork(original); + } + + function depositWETHTo(uint256 userIndexSeed, uint256 recipientIndexSeed, uint256 amount, uint256 gasAmt) public { + uint256 original = vm.activeFork(); + + // Switch to root chain + vm.selectFork(rootId); + + // Bound + address user = users[bound(userIndexSeed, 0, users.length - 1)]; + address recipient = users[bound(recipientIndexSeed, 0, users.length - 1)]; + amount = bound(amount, 1, MAX_AMOUNT); + gasAmt = bound(gasAmt, 1, MAX_GAS); + + // Get current balance + uint256 currentBalance = user.balance; + + if (currentBalance < amount) { + // Fund difference + uint256 previousLen = rootHelper.getQueueSize(user); + vm.selectFork(childId); + childHelper.withdrawETH(user, amount - currentBalance, gasAmt); + vm.selectFork(rootId); + rootHelper.finaliseWithdrawal(user, previousLen); + } + + // Wrap ETH + address payable wETH = payable(rootHelper.rootBridge().rootWETHToken()); + vm.prank(user); + WETH(wETH).deposit{value: amount}(); + + rootHelper.depositWETHTo(user, recipient, amount, gasAmt); + + // If recipient is different, transfer back + if (user != recipient) { + vm.selectFork(childId); + address eth = childHelper.childBridge().childETHToken(); + vm.prank(recipient); + ChildERC20(eth).transfer(user, amount); + vm.selectFork(rootId); + } + + vm.selectFork(original); + } }