diff --git a/script/DeployChildContracts.s.sol b/script/DeployChildContracts.s.sol index 33c9e15d..d3003331 100644 --- a/script/DeployChildContracts.s.sol +++ b/script/DeployChildContracts.s.sol @@ -38,7 +38,9 @@ contract DeployChildContracts is Script { }); ChildERC20Bridge childERC20BridgeImplementation = new ChildERC20Bridge(); - childERC20BridgeImplementation.initialize(roles, address(1), "0x123", address(1), "root", address(1), address(wrappedIMX)); + childERC20BridgeImplementation.initialize( + roles, address(1), "0x123", address(1), "root", address(1), address(wrappedIMX) + ); TransparentUpgradeableProxy childERC20BridgeProxy = new TransparentUpgradeableProxy( address(childERC20BridgeImplementation), diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index e301510d..6da0848c 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -66,11 +66,8 @@ contract ChildERC20Bridge is /** * @notice Fallback function on recieving native IMX. - * @dev Will revert as direct native IMX transfers are not supported. */ - receive() external payable { - revert DirectNativeTransfer(); - } + receive() external payable {} /** * @notice Initialization function for ChildERC20Bridge. @@ -95,7 +92,8 @@ contract ChildERC20Bridge is if ( newBridgeAdaptor == address(0) || newChildTokenTemplate == address(0) || newRootIMXToken == address(0) || newRoles.defaultAdmin == address(0) || newRoles.pauser == address(0) || newRoles.unpauser == address(0) - || newRoles.variableManager == address(0) || newRoles.adaptorManager == address(0) || newWIMXToken == address(0) + || newRoles.variableManager == address(0) || newRoles.adaptorManager == address(0) + || newWIMXToken == address(0) ) { revert ZeroAddress(); } @@ -194,7 +192,7 @@ contract ChildERC20Bridge is _withdraw(NATIVE_IMX, msg.sender, amount); } - /** + /** * @inheritdoc IChildERC20Bridge */ function withdrawIMXTo(address receiver, uint256 amount) external payable { @@ -343,7 +341,7 @@ contract ChildERC20Bridge is // Deploy child chain token IChildERC20 childToken = - IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(rootToken)))); + IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(rootToken)))); // Map token rootTokenToChildToken[rootToken] = address(childToken); @@ -388,7 +386,7 @@ contract ChildERC20Bridge is revert EmptyTokenContract(); } - if (childToken.mint(receiver, amount)) { + if (!childToken.mint(receiver, amount)) { revert MintFailed(); } diff --git a/src/interfaces/child/IChildERC20.sol b/src/interfaces/child/IChildERC20.sol index 86dfa890..e0d91eb2 100644 --- a/src/interfaces/child/IChildERC20.sol +++ b/src/interfaces/child/IChildERC20.sol @@ -3,7 +3,8 @@ pragma solidity 0.8.19; -import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; +import {IERC20MetadataUpgradeable} from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; /** * @dev Interface of IChildERC20 diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index b79e286f..5c3360b6 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -36,7 +36,6 @@ interface IChildERC20Bridge { */ function withdraw(IChildERC20 childToken, uint256 amount) external payable; - /** * @notice Withdraws `amount` of `childToken` to `receiver` on the rootchain. * @param childToken The address of the child token to withdraw. @@ -45,7 +44,7 @@ interface IChildERC20Bridge { */ function withdrawTo(IChildERC20 childToken, address receiver, uint256 amount) external payable; - /** + /** * @notice Withdraws `amount` of IMX to `msg.sender` on the rootchain. * @param amount The amount of IMX to withdraw. */ @@ -161,6 +160,4 @@ interface IChildERC20BridgeErrors { error TransferWIMXFailed(); /// @notice Error when token balance invariant check fails. error BalanceInvariantCheckFailed(uint256 actualBalance, uint256 expectedBalance); - /// @notice Error when native IMX is directly transferred to the bridge. - error DirectNativeTransfer(); } diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index bcc1d5ab..56fdd3c1 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -151,7 +151,10 @@ contract RootERC20Bridge is * @dev Can only be called by VARIABLE_MANAGER_ROLE. * @dev The limit can decrease, but it can never decrease to below the contract's IMX balance. */ - function updateImxCumulativeDepositLimit(uint256 newImxCumulativeDepositLimit) external onlyRole(VARIABLE_MANAGER_ROLE) { + function updateImxCumulativeDepositLimit(uint256 newImxCumulativeDepositLimit) + external + onlyRole(VARIABLE_MANAGER_ROLE) + { if ( newImxCumulativeDepositLimit != UNLIMITED_DEPOSIT && newImxCumulativeDepositLimit < IERC20Metadata(rootIMXToken).balanceOf(address(this)) diff --git a/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol b/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol index e06b0d4b..114b715d 100644 --- a/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol +++ b/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol @@ -23,7 +23,10 @@ contract ChildERC20BridgeWithdrawIntegrationTest is IChildAxelarBridgeAdaptorEvents, IChildAxelarBridgeAdaptorErrors, Utils -{ +{ + // Define here to avoid error collisions between IChildERC20BridgeErrors and IChildAxelarBridgeAdaptorErrors + error ZeroValue(); + address constant CHILD_BRIDGE = address(3); address constant CHILD_BRIDGE_ADAPTOR = address(4); string constant CHILD_CHAIN_NAME = "test"; @@ -133,9 +136,10 @@ contract ChildERC20BridgeWithdrawIntegrationTest is } function test_RevertIf_WithdrawWithNoGas() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); - vm.expectRevert(NoGas.selector); + vm.expectRevert(ZeroValue.selector); childBridge.withdraw(childToken, withdrawAmount); } } diff --git a/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawWIMXTo.t.sol b/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawWIMXTo.t.sol index 68ef0343..24f73bf0 100644 --- a/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawWIMXTo.t.sol +++ b/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawWIMXTo.t.sol @@ -51,7 +51,7 @@ contract ChildERC20BridgeWithdrawWIMXToIntegrationTest is } function test_WithdrawWIMXTo_CallsBridgeAdaptor() public { - uint256 withdrawFee = 300; + uint256 withdrawFee = 1; uint256 withdrawAmount = 7 ether; wIMXToken.approve(address(childBridge), withdrawAmount); diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index ff985f17..df78ef6b 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.19; import {Test, console2} from "forge-std/Test.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import {ERC20PresetMinterPauser} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; @@ -17,6 +18,8 @@ import {ChildERC20} from "../../../src/child/ChildERC20.sol"; import {Utils} from "../../utils.t.sol"; contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20BridgeErrors, Utils { + // TODO: move me to roles contract + bytes32 constant ADAPTOR_MANAGER_ROLE = keccak256("ADAPTOR_MANAGER_ROLE"); address constant ROOT_BRIDGE = address(3); string public ROOT_BRIDGE_ADAPTOR = Strings.toHexString(address(4)); string constant ROOT_CHAIN_NAME = "test"; @@ -44,7 +47,13 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B adaptorManager: address(this) }); childBridge.initialize( - roles, address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN + roles, + address(this), + ROOT_BRIDGE_ADAPTOR, + address(childTokenTemplate), + ROOT_CHAIN_NAME, + ROOT_IMX_TOKEN, + CHILD_WIMX_TOKEN ); } @@ -69,7 +78,13 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B }); vm.expectRevert("Initializable: contract is already initialized"); childBridge.initialize( - roles, address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN + roles, + address(this), + ROOT_BRIDGE_ADAPTOR, + address(childTokenTemplate), + ROOT_CHAIN_NAME, + ROOT_IMX_TOKEN, + CHILD_WIMX_TOKEN ); } @@ -210,7 +225,9 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B }); vm.expectRevert(InvalidRootERC20BridgeAdaptor.selector); - bridge.initialize(roles, address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN); + bridge.initialize( + roles, address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN + ); } function test_RevertIf_InitializeWithAnEmptyChainNameString() public { @@ -224,7 +241,9 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B }); vm.expectRevert(InvalidRootChain.selector); - bridge.initialize(roles, address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN); + bridge.initialize( + roles, address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", ROOT_IMX_TOKEN, CHILD_WIMX_TOKEN + ); } function test_onMessageReceive_EmitsTokenMappedEvent() public { @@ -354,9 +373,16 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B } function test_RevertIf_updateBridgeAdaptorCalledByNotAdaptorManager() public { - vm.prank(address(0xf00f00)); - // FIXME:!!! - // vm.expectRevert(abi.encodeWithSelector(NotVariableManager.selector, 0xf00f00)); + address caller = address(0xf00f00); + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(caller), + " is missing role ", + StringsUpgradeable.toHexString(uint256(ADAPTOR_MANAGER_ROLE), 32) + ) + ); childBridge.updateBridgeAdaptor(address(0x11111)); } diff --git a/test/unit/child/WIMX.t.sol b/test/unit/child/WIMX.t.sol index 2223566c..bdcbbae1 100644 --- a/test/unit/child/WIMX.t.sol +++ b/test/unit/child/WIMX.t.sol @@ -189,6 +189,8 @@ contract WIMXTest is Test { "Token supply should be 0.55 after 2nd deposit" ); + vm.stopPrank(); + // Withdraw 0.5 IMX uint256 withdrawlAmt2 = 0.5 ether; vm.startPrank(user2); diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol index fde587cd..0022ac3c 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdraw.t.sol @@ -72,14 +72,14 @@ contract ChildERC20BridgeWithdrawUnitTest is Test, IChildERC20BridgeEvents, IChi function test_RevertsIf_WithdrawCalledWithEmptyChildToken() public { vm.expectRevert(EmptyTokenContract.selector); - childBridge.withdraw(IChildERC20(address(2222222)), 100); + childBridge.withdraw{value: 1 ether}(IChildERC20(address(2222222)), 100); } function test_RevertsIf_WithdrawCalledWithUnmappedToken() public { ChildERC20 newToken = new ChildERC20(); newToken.initialize(address(123), "Test", "TST", 18); vm.expectRevert(NotMapped.selector); - childBridge.withdraw(IChildERC20(address(newToken)), 100); + childBridge.withdraw{value: 1 ether}(IChildERC20(address(newToken)), 100); } function test_RevertsIf_WithdrawCalledWithAChildTokenWithUnsetRootToken() public { @@ -101,7 +101,7 @@ contract ChildERC20BridgeWithdrawUnitTest is Test, IChildERC20BridgeEvents, IChi vm.store(address(childBridge), slot, data); vm.expectRevert(ZeroAddressRootToken.selector); - childBridge.withdraw(IChildERC20(address(childToken)), 100); + childBridge.withdraw{value: 1 ether}(IChildERC20(address(childToken)), 100); } function test_RevertsIf_WithdrawCalledWithAChildTokenThatHasWrongBridge() public { @@ -111,7 +111,7 @@ contract ChildERC20BridgeWithdrawUnitTest is Test, IChildERC20BridgeEvents, IChi vm.store(address(childToken), bridgeSlotBytes32, bytes32(uint256(uint160(address(0x123))))); vm.expectRevert(IncorrectBridgeAddress.selector); - childBridge.withdraw(IChildERC20(address(childToken)), 100); + childBridge.withdraw{value: 1 ether}(IChildERC20(address(childToken)), 100); } function test_RevertsIf_WithdrawWhenBurnFails() public { @@ -119,7 +119,7 @@ contract ChildERC20BridgeWithdrawUnitTest is Test, IChildERC20BridgeEvents, IChi deployCodeTo("ChildERC20FailOnBurn.sol", address(childToken)); vm.expectRevert(BurnFailed.selector); - childBridge.withdraw(IChildERC20(address(childToken)), 100); + childBridge.withdraw{value: 1 ether}(IChildERC20(address(childToken)), 100); } function test_withdraw_CallsBridgeAdaptor() public { diff --git a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol index 7cc44731..2912a3f3 100644 --- a/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol +++ b/test/unit/child/withdrawals/ChildERC20BridgeWithdrawTo.t.sol @@ -72,14 +72,14 @@ contract ChildERC20BridgeWithdrawToUnitTest is Test, IChildERC20BridgeEvents, IC function test_RevertsIf_WithdrawToCalledWithEmptyChildToken() public { vm.expectRevert(EmptyTokenContract.selector); - childBridge.withdrawTo(IChildERC20(address(2222222)), address(this), 100); + childBridge.withdrawTo{value: 1 ether}(IChildERC20(address(2222222)), address(this), 100); } function test_RevertsIf_WithdrawToCalledWithUnmappedToken() public { ChildERC20 newToken = new ChildERC20(); newToken.initialize(address(123), "Test", "TST", 18); vm.expectRevert(NotMapped.selector); - childBridge.withdrawTo(IChildERC20(address(newToken)), address(this), 100); + childBridge.withdrawTo{value: 1 ether}(IChildERC20(address(newToken)), address(this), 100); } function test_RevertsIf_WithdrawToCalledWithAChildTokenWithUnsetRootToken() public { @@ -100,7 +100,7 @@ contract ChildERC20BridgeWithdrawToUnitTest is Test, IChildERC20BridgeEvents, IC vm.store(address(childBridge), slot, data); vm.expectRevert(ZeroAddressRootToken.selector); - childBridge.withdrawTo(IChildERC20(address(childToken)), address(this), 100); + childBridge.withdrawTo{value: 1 ether}(IChildERC20(address(childToken)), address(this), 100); } function test_RevertsIf_WithdrawToCalledWithAChildTokenThatHasWrongBridge() public { @@ -110,7 +110,7 @@ contract ChildERC20BridgeWithdrawToUnitTest is Test, IChildERC20BridgeEvents, IC vm.store(address(childToken), bridgeSlotBytes32, bytes32(uint256(uint160(address(0x123))))); vm.expectRevert(IncorrectBridgeAddress.selector); - childBridge.withdrawTo(IChildERC20(address(childToken)), address(this), 100); + childBridge.withdrawTo{value: 1 ether}(IChildERC20(address(childToken)), address(this), 100); } function test_RevertsIf_WithdrawToWhenBurnFails() public { @@ -118,7 +118,7 @@ contract ChildERC20BridgeWithdrawToUnitTest is Test, IChildERC20BridgeEvents, IC deployCodeTo("ChildERC20FailOnBurn.sol", address(childToken)); vm.expectRevert(BurnFailed.selector); - childBridge.withdrawTo(IChildERC20(address(childToken)), address(this), 100); + childBridge.withdrawTo{value: 1 ether}(IChildERC20(address(childToken)), address(this), 100); } function test_withdrawTo_CallsBridgeAdaptor() public { diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 14c01bd2..c4e1af44 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.19; import {Test, console2} from "forge-std/Test.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import {ERC20PresetMinterPauser} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; @@ -19,6 +20,8 @@ import {Utils} from "../../utils.t.sol"; import {WETH} from "../../../src/test/root/WETH.sol"; contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20BridgeErrors, Utils { + // TODO: move me to roles contract + bytes32 constant ADAPTOR_MANAGER_ROLE = keccak256("ADAPTOR_MANAGER_ROLE"); address constant CHILD_BRIDGE = address(3); address constant CHILD_BRIDGE_ADAPTOR = address(4); string CHILD_BRIDGE_ADAPTOR_STRING = Strings.toHexString(CHILD_BRIDGE_ADAPTOR); @@ -525,9 +528,16 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid } function test_RevertIf_updateRootBridgeAdaptorCalledByNonOwner() public { - vm.prank(address(0xf00f00)); - // FIXME: !!! - // vm.expectRevert(abi.encodeWithSelector(NotVariableManager.selector, 0xf00f00)); + address caller = address(0xf00f00); + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(caller), + " is missing role ", + StringsUpgradeable.toHexString(uint256(ADAPTOR_MANAGER_ROLE), 32) + ) + ); rootBridge.updateRootBridgeAdaptor(address(0x11111)); }