From 82d032c6706fbd757cbb221374288eae04e0c6ae Mon Sep 17 00:00:00 2001 From: Craig M Date: Thu, 19 Oct 2023 17:57:03 +1300 Subject: [PATCH 01/14] initialising with child eth token implemented and tested --- script/InitializeChildContracts.s.sol | 4 +- src/child/ChildERC20Bridge.sol | 37 +++++++++++++------ src/root/RootERC20Bridge.sol | 5 ++- .../integration/child/ChildAxelarBridge.t.sol | 9 +++-- test/unit/child/ChildERC20Bridge.t.sol | 30 +++++++++------ 5 files changed, 55 insertions(+), 30 deletions(-) diff --git a/script/InitializeChildContracts.s.sol b/script/InitializeChildContracts.s.sol index 513d0b2e..fd7e7b5f 100644 --- a/script/InitializeChildContracts.s.sol +++ b/script/InitializeChildContracts.s.sol @@ -19,6 +19,7 @@ contract InitializeChildContracts is Script { string memory childRpcUrl = vm.envString("CHILD_RPC_URL"); string memory rootChainName = vm.envString("ROOT_CHAIN_NAME"); address rootIMXToken = vm.envAddress("ROOT_IMX_ADDRESS"); + address childETHToken = vm.envAddress("CHILD_ETH_ADDRESS"); /** * INITIALIZE CHILD CONTRACTS @@ -31,7 +32,8 @@ contract InitializeChildContracts is Script { Strings.toHexString(rootERC20BridgeAdaptor), childTokenTemplate, rootChainName, - rootIMXToken + rootIMXToken, + childETHToken ); childAxelarBridgeAdaptor.setRootBridgeAdaptor(); diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index ec91db47..c52c3a93 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -33,6 +33,13 @@ contract ChildERC20Bridge is { using SafeERC20 for IERC20Metadata; + /// @dev leave this as the first param for the integration tests + mapping(address => address) public rootTokenToChildToken; + + bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); + bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); + address public constant NATIVE_TOKEN = address(0xeee); + IChildERC20BridgeAdaptor public bridgeAdaptor; /// @dev The address that will be sending messages to, and receiving messages from, the child chain. string public rootERC20BridgeAdaptor; @@ -40,11 +47,11 @@ contract ChildERC20Bridge is address public childTokenTemplate; /// @dev The name of the chain that this bridge is connected to. string public rootChain; - mapping(address => address) public rootTokenToChildToken; - - bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); - bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); - address public imxToken; + /// @dev The address of the IMX ERC20 token on L1. + address public rootIMXToken; + /// @dev The address of the ETH ERC20 token on L2. + address public childETHToken; + /// @dev The address of the token template that will be cloned to create tokens on the child chain. /** * @notice Initilization function for RootERC20Bridge. @@ -52,7 +59,8 @@ contract ChildERC20Bridge is * @param newRootERC20BridgeAdaptor Stringified address of root ERC20 bridge adaptor to communicate with. * @param newChildTokenTemplate Address of child token template to clone. * @param newRootChain A stringified representation of the chain that this bridge is connected to. Used for validation. - * @param newIMXToken Address of ECR20 IMX on the root chain. + * @param newRootIMXToken Address of ECR20 IMX on the root chain. + * @param newChildETHToken Address of ERC20 ETH on the child chain. * @dev Can only be called once. */ function initialize( @@ -60,11 +68,14 @@ contract ChildERC20Bridge is string memory newRootERC20BridgeAdaptor, address newChildTokenTemplate, string memory newRootChain, - address newIMXToken - ) public initializer { + address newRootIMXToken, + address newChildETHToken) + public initializer + { if (newBridgeAdaptor == address(0) || newChildTokenTemplate == address(0) - || newIMXToken == address(0)) { + || newRootIMXToken == address(0) + || newChildETHToken == address(0)) { revert ZeroAddress(); } @@ -80,7 +91,9 @@ contract ChildERC20Bridge is childTokenTemplate = newChildTokenTemplate; bridgeAdaptor = IChildERC20BridgeAdaptor(newBridgeAdaptor); rootChain = newRootChain; - imxToken = newIMXToken; + rootIMXToken = newRootIMXToken; + childETHToken = newChildETHToken; + } /** @@ -122,7 +135,7 @@ contract ChildERC20Bridge is revert ZeroAddress(); } - if (address(rootToken) == imxToken) { + if (address(rootToken) == rootIMXToken) { revert CantMapIMX(); } @@ -149,7 +162,7 @@ contract ChildERC20Bridge is address childToken; - if (address(rootToken) != imxToken) { + if (address(rootToken) != rootIMXToken) { childToken = rootTokenToChildToken[address(rootToken)]; if (childToken == address(0)) { revert NotMapped(); diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index ddb8f287..1fb146b9 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -29,19 +29,20 @@ contract RootERC20Bridge is { using SafeERC20 for IERC20Metadata; + /// @dev leave this as the first param for the integration tests + mapping(address => address) public rootTokenToChildToken; + bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); address public constant NATIVE_TOKEN = address(0xeee); IRootERC20BridgeAdaptor public rootBridgeAdaptor; /// @dev Used to verify source address in messages sent from child chain. - /// @dev Stringified version of address. string public childBridgeAdaptor; /// @dev The address that will be minting tokens on the child chain. address public childERC20Bridge; /// @dev The address of the token template that will be cloned to create tokens on the child chain. address public childTokenTemplate; - mapping(address => address) public rootTokenToChildToken; /// @dev The address of the IMX ERC20 token on L1. address public rootIMXToken; /// @dev The address of the ETH ERC20 token on L2. diff --git a/test/integration/child/ChildAxelarBridge.t.sol b/test/integration/child/ChildAxelarBridge.t.sol index 54d530a9..850bfa2b 100644 --- a/test/integration/child/ChildAxelarBridge.t.sol +++ b/test/integration/child/ChildAxelarBridge.t.sol @@ -18,7 +18,8 @@ import {Utils} from "../../utils.t.sol"; contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChildERC20BridgeErrors, Utils { string public ROOT_ADAPTOR_ADDRESS = Strings.toHexString(address(1)); string public ROOT_CHAIN_NAME = "ROOT_CHAIN"; - address constant IMX_TOKEN = address(9); + address constant IMX_TOKEN_ADDRESS = address(0xccc); + address constant CHILD_ETH_TOKEN = address(0xddd); ChildERC20Bridge public childERC20Bridge; ChildERC20 public childERC20; @@ -35,7 +36,7 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil new ChildAxelarBridgeAdaptor(address(mockChildAxelarGateway), address(childERC20Bridge)); childERC20Bridge.initialize( - address(childAxelarBridgeAdaptor), ROOT_ADAPTOR_ADDRESS, address(childERC20), ROOT_CHAIN_NAME, IMX_TOKEN + address(childAxelarBridgeAdaptor), ROOT_ADAPTOR_ADDRESS, address(childERC20), ROOT_CHAIN_NAME, IMX_TOKEN_ADDRESS, CHILD_ETH_TOKEN ); } @@ -246,8 +247,8 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil bytes32 depositSig = childERC20Bridge.DEPOSIT_SIG(); address rootAddress = address(0x123); { - // Slot is 6 because of the Ownable, Initializable contracts coming first. - uint256 rootTokenToChildTokenMappingSlot = 6; + // Slot is 2 because of the Ownable, Initializable contracts coming first. + uint256 rootTokenToChildTokenMappingSlot = 2; address childAddress = address(444444); bytes32 slot = getMappingStorageSlotFor(rootAddress, rootTokenToChildTokenMappingSlot); bytes32 data = bytes32(uint256(uint160(childAddress))); diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index aa7a2aeb..b2d11942 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -19,7 +19,9 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B string public ROOT_BRIDGE_ADAPTOR = Strings.toHexString(address(4)); string constant ROOT_CHAIN_NAME = "test"; - address constant IMX_TOKEN = address(99); + address constant IMX_TOKEN = address(0xccc); + address constant CHILD_ETH_TOKEN = address(0xddd); + address constant NATIVE_TOKEN = address(0xeee); ChildERC20 public childTokenTemplate; ChildERC20 public rootToken; ChildERC20Bridge public childBridge; @@ -33,7 +35,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childBridge = new ChildERC20Bridge(); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); } function test_Initialize() public { @@ -45,43 +47,49 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); } function test_RevertIf_InitializeWithAZeroAddressAdapter() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1)); + bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1), address(1)); } function test_RevertIf_InitializeWithAZeroAddressChildTemplate() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(1)); + bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(1), address(1)); } function test_RevertIf_InitializeWithAZeroAddressIMXToken() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(0)); + bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(0), address(1)); + } + + function test_RevertIf_InitializeWithAZeroAddressEthChildToken() public { + ChildERC20Bridge bridge = new ChildERC20Bridge(); + vm.expectRevert(ZeroAddress.selector); + bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1), address(0)); } function test_RevertIf_InitializeWithAZeroAddressAll() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(0)); + bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(0), address(0)); } function test_RevertIf_InitializeWithAnEmptyBridgeAdaptorString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootERC20BridgeAdaptor.selector); - bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN); + bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); } function test_RevertIf_InitializeWithAnEmptyChainNameString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootChain.selector); - bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", IMX_TOKEN); + bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", IMX_TOKEN, CHILD_ETH_TOKEN); } function test_onMessageReceive_EmitsTokenMappedEvent() public { @@ -357,8 +365,8 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address rootAddress = address(0x123); { - // Slot is 6 because of the Ownable, Initializable contracts coming first. - uint256 rootTokenToChildTokenMappingSlot = 6; + // Slot is 2 because of the Ownable, Initializable contracts coming first. + uint256 rootTokenToChildTokenMappingSlot = 2; address childAddress = address(444444); bytes32 slot = getMappingStorageSlotFor(rootAddress, rootTokenToChildTokenMappingSlot); bytes32 data = bytes32(uint256(uint160(childAddress))); From ee559c705a3c3dd0684f1e04299dc84c3a2ff7ad Mon Sep 17 00:00:00 2001 From: Craig M Date: Thu, 19 Oct 2023 18:10:36 +1300 Subject: [PATCH 02/14] Update RootERC20Bridge.t.sol --- test/unit/root/RootERC20Bridge.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 90c5c5a3..8acf4883 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -20,7 +20,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid address constant CHILD_BRIDGE = address(3); address constant CHILD_BRIDGE_ADAPTOR = address(4); string constant CHILD_CHAIN_NAME = "test"; - address constant IMX_TOKEN = address(99); + address constant IMX_TOKEN = address(0xccc); address constant CHILD_ETH_TOKEN = address(0xddd); address constant NATIVE_TOKEN = address(0xeee); uint256 constant mapTokenFee = 300; From c4c59a4e7fa52fa08e7550e9ab34ae895c11c9fe Mon Sep 17 00:00:00 2001 From: Craig M Date: Fri, 20 Oct 2023 17:53:24 +1300 Subject: [PATCH 03/14] first draft --- script/DeployChildContracts.s.sol | 2 +- src/child/ChildERC20Bridge.sol | 32 +++++--- src/interfaces/child/IChildERC20Bridge.sol | 2 +- src/interfaces/root/IRootERC20Bridge.sol | 2 +- src/root/RootERC20Bridge.sol | 10 +-- test/unit/child/ChildERC20Bridge.t.sol | 88 ++++++++++++++++++---- test/unit/root/RootERC20Bridge.t.sol | 8 +- 7 files changed, 107 insertions(+), 37 deletions(-) diff --git a/script/DeployChildContracts.s.sol b/script/DeployChildContracts.s.sol index 9bf20df8..45a0cae0 100644 --- a/script/DeployChildContracts.s.sol +++ b/script/DeployChildContracts.s.sol @@ -14,7 +14,7 @@ contract DeployChildContracts is Script { function run() public { uint256 deployerPrivateKey = vm.envUint("CHILD_PRIVATE_KEY"); address childGateway = vm.envAddress("CHILD_GATEWAY_ADDRESS"); - address childGasService = vm.envAddress("CHILD_GAS_SERVICE_ADDRESS"); // Not yet used. + //address childGasService = vm.envAddress("CHILD_GAS_SERVICE_ADDRESS"); // Not yet used. string memory childRpcUrl = vm.envString("CHILD_RPC_URL"); vm.createSelectFork(childRpcUrl); diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index c52c3a93..9b191539 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -15,6 +15,7 @@ import { } from "../interfaces/child/IChildERC20Bridge.sol"; import {IChildERC20BridgeAdaptor} from "../interfaces/child/IChildERC20BridgeAdaptor.sol"; import {IChildERC20} from "../interfaces/child/IChildERC20.sol"; +import {console2} from "forge-std/Test.sol"; /** * @notice RootERC20Bridge is a bridge that allows ERC20 tokens to be transferred from the root chain to the child chain. @@ -38,7 +39,7 @@ contract ChildERC20Bridge is bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); - address public constant NATIVE_TOKEN = address(0xeee); + address public constant NATIVE_ETH = address(0xeee); IChildERC20BridgeAdaptor public bridgeAdaptor; /// @dev The address that will be sending messages to, and receiving messages from, the child chain. @@ -60,7 +61,7 @@ contract ChildERC20Bridge is * @param newChildTokenTemplate Address of child token template to clone. * @param newRootChain A stringified representation of the chain that this bridge is connected to. Used for validation. * @param newRootIMXToken Address of ECR20 IMX on the root chain. - * @param newChildETHToken Address of ERC20 ETH on the child chain. + * @param newRootETHToken Address used to denote ETH on the root chain. * @dev Can only be called once. */ function initialize( @@ -69,13 +70,13 @@ contract ChildERC20Bridge is address newChildTokenTemplate, string memory newRootChain, address newRootIMXToken, - address newChildETHToken) + address newRootETHToken) public initializer { if (newBridgeAdaptor == address(0) || newChildTokenTemplate == address(0) || newRootIMXToken == address(0) - || newChildETHToken == address(0)) { + || newRootETHToken == address(0)) { revert ZeroAddress(); } @@ -92,8 +93,11 @@ contract ChildERC20Bridge is bridgeAdaptor = IChildERC20BridgeAdaptor(newBridgeAdaptor); rootChain = newRootChain; rootIMXToken = newRootIMXToken; - childETHToken = newChildETHToken; + IChildERC20 clonedETHToken = + IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(newRootETHToken)))); + clonedETHToken.initialize(newRootETHToken, "Ethereum", "ETH", 18); + childETHToken = address(clonedETHToken); } /** @@ -163,10 +167,15 @@ contract ChildERC20Bridge is address childToken; if (address(rootToken) != rootIMXToken) { - childToken = rootTokenToChildToken[address(rootToken)]; - if (childToken == address(0)) { - revert NotMapped(); + if (address(rootToken) == NATIVE_ETH) { + childToken = childETHToken; + } else { + childToken = rootTokenToChildToken[address(rootToken)]; + if (childToken == address(0)) { + revert NotMapped(); + } } + if (address(childToken).code.length == 0) { revert EmptyTokenContract(); } @@ -174,7 +183,12 @@ contract ChildERC20Bridge is if (!IChildERC20(childToken).mint(receiver, amount)) { revert MintFailed(); } - emit ERC20Deposit(address(rootToken), childToken, sender, receiver, amount); + + if (address(rootToken) == NATIVE_ETH) { + emit NativeEthDeposit(address(rootToken), childToken, sender, receiver, amount); + } else { + emit ERC20Deposit(address(rootToken), childToken, sender, receiver, amount); + } } else { Address.sendValue(payable(receiver), amount); emit IMXDeposit(address(rootToken), sender, receiver, amount); diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index e20e1f92..302c37e5 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -38,7 +38,7 @@ interface IChildERC20BridgeEvents { address indexed receiver, uint256 amount ); - event NativeDeposit( + event NativeEthDeposit( address indexed rootToken, address indexed childToken, address depositor, diff --git a/src/interfaces/root/IRootERC20Bridge.sol b/src/interfaces/root/IRootERC20Bridge.sol index 0752b2cb..08ee6634 100644 --- a/src/interfaces/root/IRootERC20Bridge.sol +++ b/src/interfaces/root/IRootERC20Bridge.sol @@ -51,7 +51,7 @@ interface IRootERC20BridgeEvents { address indexed receiver, uint256 amount ); - event NativeDeposit( + event NativeEthDeposit( address indexed rootToken, address indexed childToken, address depositor, diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index 1fb146b9..75a0b8f1 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -34,7 +34,7 @@ contract RootERC20Bridge is bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); - address public constant NATIVE_TOKEN = address(0xeee); + address public constant NATIVE_ETH = address(0xeee); IRootERC20BridgeAdaptor public rootBridgeAdaptor; /// @dev Used to verify source address in messages sent from child chain. @@ -111,7 +111,7 @@ contract RootERC20Bridge is uint256 expectedBalance = address(this).balance - (msg.value - amount); - _deposit(IERC20Metadata(NATIVE_TOKEN), receiver, amount); + _deposit(IERC20Metadata(NATIVE_ETH), receiver, amount); // invariant check to ensure that the root native balance has increased by the amount deposited if (address(this).balance != expectedBalance) { @@ -187,7 +187,7 @@ contract RootERC20Bridge is // TODO We can call _mapToken here, but ordering in the GMP is not guaranteed. // Therefore, we need to decide how to handle this and it may be a UI decision to wait until map token message is executed on child chain. // Discuss this, and add this decision to the design doc. - if (address(rootToken) != NATIVE_TOKEN) { + if (address(rootToken) != NATIVE_ETH) { if (address(rootToken) != rootIMXToken) { childToken = rootTokenToChildToken[address(rootToken)]; if (childToken == address(0)) { @@ -207,8 +207,8 @@ contract RootERC20Bridge is rootBridgeAdaptor.sendMessage{value: feeAmount}(payload, msg.sender); - if (address(rootToken) == NATIVE_TOKEN) { - emit NativeDeposit(address(rootToken), childETHToken, msg.sender, receiver, amount); + if (address(rootToken) == NATIVE_ETH) { + emit NativeEthDeposit(address(rootToken), childETHToken, msg.sender, receiver, amount); } else if (address(rootToken) == rootIMXToken) { emit IMXDeposit(address(rootToken), msg.sender, receiver, amount); } else { diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index b2d11942..0218b9a7 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import {Test, console2} from "forge-std/Test.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"; import { @@ -10,6 +11,7 @@ import { IERC20Metadata, IChildERC20BridgeErrors } from "../../../src/child/ChildERC20Bridge.sol"; +import {IChildERC20} from "../../../src/interfaces/child/IChildERC20.sol"; import {ChildERC20} from "../../../src/child/ChildERC20.sol"; import {MockAdaptor} from "../../../src/test/root/MockAdaptor.sol"; import {Utils} from "../../utils.t.sol"; @@ -18,12 +20,11 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address constant ROOT_BRIDGE = address(3); string public ROOT_BRIDGE_ADAPTOR = Strings.toHexString(address(4)); string constant ROOT_CHAIN_NAME = "test"; - - address constant IMX_TOKEN = address(0xccc); - address constant CHILD_ETH_TOKEN = address(0xddd); - address constant NATIVE_TOKEN = address(0xeee); + address constant ROOT_IMX_TOKEN = address(0xccc); + address constant NATIVE_ETH = address(0xeee); ChildERC20 public childTokenTemplate; ChildERC20 public rootToken; + address public childETHToken; ChildERC20Bridge public childBridge; function setUp() public { @@ -32,10 +33,9 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childTokenTemplate = new ChildERC20(); childTokenTemplate.initialize(address(123), "Test", "TST", 18); - + childBridge = new ChildERC20Bridge(); - - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); } function test_Initialize() public { @@ -43,11 +43,14 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B assertEq(childBridge.rootERC20BridgeAdaptor(), ROOT_BRIDGE_ADAPTOR, "rootERC20BridgeAdaptor not set"); assertEq(childBridge.childTokenTemplate(), address(childTokenTemplate), "childTokenTemplate not set"); assertEq(childBridge.rootChain(), ROOT_CHAIN_NAME, "rootChain not set"); + assertEq(childBridge.rootIMXToken(), ROOT_IMX_TOKEN, "rootIMXToken not set"); + assertNotEq(childBridge.childETHToken(), address(0), "childETHToken not set"); + assertNotEq(address(childBridge.childETHToken()).code.length, 0, "childETHToken contract empty"); } function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); } function test_RevertIf_InitializeWithAZeroAddressAdapter() public { @@ -68,7 +71,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(0), address(1)); } - function test_RevertIf_InitializeWithAZeroAddressEthChildToken() public { + function test_RevertIf_InitializeWithAZeroAddressEthRootToken() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1), address(0)); @@ -83,13 +86,13 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B function test_RevertIf_InitializeWithAnEmptyBridgeAdaptorString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootERC20BridgeAdaptor.selector); - bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, IMX_TOKEN, CHILD_ETH_TOKEN); + bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); } function test_RevertIf_InitializeWithAnEmptyChainNameString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootChain.selector); - bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", IMX_TOKEN, CHILD_ETH_TOKEN); + bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", ROOT_IMX_TOKEN, NATIVE_ETH); } function test_onMessageReceive_EmitsTokenMappedEvent() public { @@ -191,7 +194,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B function test_RevertIf_mapTokenCalledWithIMXAddress() public { bytes memory data = abi.encode( - childBridge.MAP_TOKEN_SIG(), IMX_TOKEN, "ImmutableX", "IMX", 18 + childBridge.MAP_TOKEN_SIG(), ROOT_IMX_TOKEN, "ImmutableX", "IMX", 18 ); vm.expectRevert(CantMapIMX.selector); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); @@ -225,6 +228,59 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childBridge.updateBridgeAdaptor(address(0)); } + //Deposit ETH + + function test_onMessageReceive_DepositETH_EmitsETHDepositEvent() public { + address sender = address(100); + address receiver = address(200); + uint256 amount = 1000; + + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), address(NATIVE_ETH), sender, receiver, amount); + + address predictedChildETHToken = Clones.predictDeterministicAddress( + address(childTokenTemplate), keccak256(abi.encodePacked(NATIVE_ETH)), address(childBridge) + ); + + vm.expectEmit(address(childBridge)); + emit NativeEthDeposit(address(NATIVE_ETH), predictedChildETHToken, sender, receiver, amount); + childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); + } + + function test_onMessageReceive_DepositETH_TransfersTokensToReceiver() public { + address sender = address(100); + address receiver = address(200); + uint256 amount = 1000; + + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), address(NATIVE_ETH), sender, receiver, amount); + + address predictedChildETHToken = Clones.predictDeterministicAddress( + address(childTokenTemplate), keccak256(abi.encodePacked(NATIVE_ETH)), address(childBridge) + ); + + uint256 receiverPreBal = ChildERC20(predictedChildETHToken).balanceOf(receiver); + + childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); + + assertEq(ChildERC20(predictedChildETHToken).balanceOf(receiver), receiverPreBal + amount, "receiver balance not increased"); + } + + function test_onMessageReceive_DepositETH_IncreasesTotalSupply() public { + address sender = address(100); + address receiver = address(200); + uint256 amount = 1000; + + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), address(NATIVE_ETH), sender, receiver, amount); + + address predictedChildETHToken = Clones.predictDeterministicAddress( + address(childTokenTemplate), keccak256(abi.encodePacked(NATIVE_ETH)), address(childBridge) + ); + uint256 totalSupplyPre = ChildERC20(predictedChildETHToken).totalSupply(); + + childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); + + assertEq(ChildERC20(predictedChildETHToken).totalSupply(), totalSupplyPre + amount, "totalSupply not increased"); + } + //Deposit function test_onMessageReceive_DepositIMX_EmitsIMXDepositEvent() public { @@ -236,10 +292,10 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address receiver = address(200); uint256 amount = 1 ether; - bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), IMX_TOKEN, sender, receiver, amount); + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), ROOT_IMX_TOKEN, sender, receiver, amount); vm.expectEmit(address(childBridge)); - emit IMXDeposit(IMX_TOKEN, sender, receiver, amount); + emit IMXDeposit(ROOT_IMX_TOKEN, sender, receiver, amount); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); } @@ -251,7 +307,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address receiver = address(200); uint256 amount = 1 ether; - bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), IMX_TOKEN, sender, receiver, amount); + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), ROOT_IMX_TOKEN, sender, receiver, amount); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); @@ -267,7 +323,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address receiver = address(200); uint256 amount = 10 ether; - bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), IMX_TOKEN, sender, receiver, amount); + bytes memory depositData = abi.encode(childBridge.DEPOSIT_SIG(), ROOT_IMX_TOKEN, sender, receiver, amount); vm.expectRevert("Address: insufficient balance"); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 8acf4883..950cc6b8 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -212,12 +212,12 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid rootBridge.depositETH{value: amount+depositFee}(amount); } - function test_depositETHEmitsNativeDepositEvent() public { + function test_depositETHEmitsNativeEthDepositEvent() public { uint256 amount = 1000; setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectEmit(); - emit NativeDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), address(this), amount); + emit NativeEthDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), address(this), amount); rootBridge.depositETH{value: amount+depositFee}(amount); } @@ -246,13 +246,13 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid rootBridge.depositToETH{value: amount+depositFee}(receiver, amount); } - function test_depositToETHEmitsNativeDepositEvent() public { + function test_depositToETHEmitsNativeEthDepositEvent() public { uint256 amount = 1000; address receiver = address(12345); setupDepositTo(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, receiver, false); vm.expectEmit(); - emit NativeDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), receiver, amount); + emit NativeEthDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), receiver, amount); rootBridge.depositToETH{value: amount+depositFee}(receiver, amount); } From 22a0f27436018feac683ad1a5c6ab0ad8bb53c97 Mon Sep 17 00:00:00 2001 From: Craig M Date: Fri, 20 Oct 2023 17:56:21 +1300 Subject: [PATCH 04/14] Update InitializeChildContracts.s.sol --- script/InitializeChildContracts.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/InitializeChildContracts.s.sol b/script/InitializeChildContracts.s.sol index fd7e7b5f..8f0a6ba2 100644 --- a/script/InitializeChildContracts.s.sol +++ b/script/InitializeChildContracts.s.sol @@ -19,7 +19,7 @@ contract InitializeChildContracts is Script { string memory childRpcUrl = vm.envString("CHILD_RPC_URL"); string memory rootChainName = vm.envString("ROOT_CHAIN_NAME"); address rootIMXToken = vm.envAddress("ROOT_IMX_ADDRESS"); - address childETHToken = vm.envAddress("CHILD_ETH_ADDRESS"); + address rootETHToken = vm.envAddress("ROOT_ETH_ADDRESS"); /** * INITIALIZE CHILD CONTRACTS @@ -33,7 +33,7 @@ contract InitializeChildContracts is Script { childTokenTemplate, rootChainName, rootIMXToken, - childETHToken + rootETHToken ); childAxelarBridgeAdaptor.setRootBridgeAdaptor(); From 932ad87744d9ce31b4bf56570aa7fbac2275067d Mon Sep 17 00:00:00 2001 From: Craig M Date: Fri, 20 Oct 2023 17:56:42 +1300 Subject: [PATCH 05/14] Update ChildERC20Bridge.sol --- src/child/ChildERC20Bridge.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index 9b191539..d8b15681 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -15,7 +15,6 @@ import { } from "../interfaces/child/IChildERC20Bridge.sol"; import {IChildERC20BridgeAdaptor} from "../interfaces/child/IChildERC20BridgeAdaptor.sol"; import {IChildERC20} from "../interfaces/child/IChildERC20.sol"; -import {console2} from "forge-std/Test.sol"; /** * @notice RootERC20Bridge is a bridge that allows ERC20 tokens to be transferred from the root chain to the child chain. From 6a060775aef9d81407c22c11e6bff105dc86a1f0 Mon Sep 17 00:00:00 2001 From: Craig M Date: Fri, 20 Oct 2023 18:06:12 +1300 Subject: [PATCH 06/14] Update ChildERC20Bridge.sol --- src/child/ChildERC20Bridge.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index d8b15681..f0817a49 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -41,6 +41,7 @@ contract ChildERC20Bridge is address public constant NATIVE_ETH = address(0xeee); IChildERC20BridgeAdaptor public bridgeAdaptor; + /// @dev The address that will be sending messages to, and receiving messages from, the child chain. string public rootERC20BridgeAdaptor; /// @dev The address of the token template that will be cloned to create tokens. @@ -51,7 +52,6 @@ contract ChildERC20Bridge is address public rootIMXToken; /// @dev The address of the ETH ERC20 token on L2. address public childETHToken; - /// @dev The address of the token template that will be cloned to create tokens on the child chain. /** * @notice Initilization function for RootERC20Bridge. From fc2eaae00a2b3bd4668f8930838899badf4e28ec Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 14:29:45 +1300 Subject: [PATCH 07/14] Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5680b282..dc2e9557 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ docs/ .vscode/ # Contract addresses -addresses.json \ No newline at end of file +addresses.json + +/node_modules \ No newline at end of file From e4de38e11e98f0c662efc82b1b4afff07403650a Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 14:48:22 +1300 Subject: [PATCH 08/14] remove ETH_TOKEN address from child initialise --- script/InitializeChildContracts.s.sol | 4 +-- src/child/ChildERC20Bridge.sol | 15 ++++----- src/interfaces/child/IChildERC20Bridge.sol | 2 ++ src/interfaces/root/IRootERC20Bridge.sol | 2 ++ src/root/RootERC20Bridge.sol | 5 +++ .../integration/child/ChildAxelarBridge.t.sol | 3 +- test/unit/child/ChildERC20Bridge.t.sol | 30 +++++++++--------- test/unit/root/RootERC20Bridge.t.sol | 31 +++++++++++-------- 8 files changed, 53 insertions(+), 39 deletions(-) diff --git a/script/InitializeChildContracts.s.sol b/script/InitializeChildContracts.s.sol index 8f0a6ba2..513d0b2e 100644 --- a/script/InitializeChildContracts.s.sol +++ b/script/InitializeChildContracts.s.sol @@ -19,7 +19,6 @@ contract InitializeChildContracts is Script { string memory childRpcUrl = vm.envString("CHILD_RPC_URL"); string memory rootChainName = vm.envString("ROOT_CHAIN_NAME"); address rootIMXToken = vm.envAddress("ROOT_IMX_ADDRESS"); - address rootETHToken = vm.envAddress("ROOT_ETH_ADDRESS"); /** * INITIALIZE CHILD CONTRACTS @@ -32,8 +31,7 @@ contract InitializeChildContracts is Script { Strings.toHexString(rootERC20BridgeAdaptor), childTokenTemplate, rootChainName, - rootIMXToken, - rootETHToken + rootIMXToken ); childAxelarBridgeAdaptor.setRootBridgeAdaptor(); diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index f0817a49..105d9787 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -60,7 +60,6 @@ contract ChildERC20Bridge is * @param newChildTokenTemplate Address of child token template to clone. * @param newRootChain A stringified representation of the chain that this bridge is connected to. Used for validation. * @param newRootIMXToken Address of ECR20 IMX on the root chain. - * @param newRootETHToken Address used to denote ETH on the root chain. * @dev Can only be called once. */ function initialize( @@ -68,14 +67,12 @@ contract ChildERC20Bridge is string memory newRootERC20BridgeAdaptor, address newChildTokenTemplate, string memory newRootChain, - address newRootIMXToken, - address newRootETHToken) + address newRootIMXToken) public initializer { if (newBridgeAdaptor == address(0) || newChildTokenTemplate == address(0) - || newRootIMXToken == address(0) - || newRootETHToken == address(0)) { + || newRootIMXToken == address(0)) { revert ZeroAddress(); } @@ -94,8 +91,8 @@ contract ChildERC20Bridge is rootIMXToken = newRootIMXToken; IChildERC20 clonedETHToken = - IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(newRootETHToken)))); - clonedETHToken.initialize(newRootETHToken, "Ethereum", "ETH", 18); + IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(NATIVE_ETH)))); + clonedETHToken.initialize(NATIVE_ETH, "Ethereum", "ETH", 18); childETHToken = address(clonedETHToken); } @@ -142,6 +139,10 @@ contract ChildERC20Bridge is revert CantMapIMX(); } + if (address(rootToken) == NATIVE_ETH) { + revert CantMapETH(); + } + if (rootTokenToChildToken[rootToken] != address(0)) { revert AlreadyMapped(); } diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index 302c37e5..94945b5b 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -63,6 +63,8 @@ interface IChildERC20BridgeErrors { error NotMapped(); /// @notice Error when attempting to map IMX. error CantMapIMX(); + /// @notice Error when attempting to map ETH. + error CantMapETH(); /// @notice Error when a token is already mapped. error AlreadyMapped(); /// @notice Error when a message is given to the bridge from an address not the designated bridge adaptor. diff --git a/src/interfaces/root/IRootERC20Bridge.sol b/src/interfaces/root/IRootERC20Bridge.sol index 08ee6634..1c104563 100644 --- a/src/interfaces/root/IRootERC20Bridge.sol +++ b/src/interfaces/root/IRootERC20Bridge.sol @@ -73,6 +73,8 @@ interface IRootERC20BridgeErrors { error NotMapped(); /// @notice Error when attempting to map IMX. error CantMapIMX(); + /// @notice Error when attempting to map ETH. + error CantMapETH(); /// @notice Error when token balance invariant check fails. error BalanceInvariantCheckFailed(uint256 actualBalance, uint256 expectedBalance); } diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index 75a0b8f1..71eebc9b 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -150,6 +150,11 @@ contract RootERC20Bridge is if (address(rootToken) == rootIMXToken) { revert CantMapIMX(); } + + if (address(rootToken) == NATIVE_ETH) { + revert CantMapETH(); + } + if (rootTokenToChildToken[address(rootToken)] != address(0)) { revert AlreadyMapped(); } diff --git a/test/integration/child/ChildAxelarBridge.t.sol b/test/integration/child/ChildAxelarBridge.t.sol index 850bfa2b..01144366 100644 --- a/test/integration/child/ChildAxelarBridge.t.sol +++ b/test/integration/child/ChildAxelarBridge.t.sol @@ -19,7 +19,6 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil string public ROOT_ADAPTOR_ADDRESS = Strings.toHexString(address(1)); string public ROOT_CHAIN_NAME = "ROOT_CHAIN"; address constant IMX_TOKEN_ADDRESS = address(0xccc); - address constant CHILD_ETH_TOKEN = address(0xddd); ChildERC20Bridge public childERC20Bridge; ChildERC20 public childERC20; @@ -36,7 +35,7 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil new ChildAxelarBridgeAdaptor(address(mockChildAxelarGateway), address(childERC20Bridge)); childERC20Bridge.initialize( - address(childAxelarBridgeAdaptor), ROOT_ADAPTOR_ADDRESS, address(childERC20), ROOT_CHAIN_NAME, IMX_TOKEN_ADDRESS, CHILD_ETH_TOKEN + address(childAxelarBridgeAdaptor), ROOT_ADAPTOR_ADDRESS, address(childERC20), ROOT_CHAIN_NAME, IMX_TOKEN_ADDRESS ); } diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index 0218b9a7..0d6ed39d 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -35,7 +35,7 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childTokenTemplate.initialize(address(123), "Test", "TST", 18); childBridge = new ChildERC20Bridge(); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN); } function test_Initialize() public { @@ -50,49 +50,43 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); + childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN); } function test_RevertIf_InitializeWithAZeroAddressAdapter() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1), address(1)); + bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1)); } function test_RevertIf_InitializeWithAZeroAddressChildTemplate() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(1), address(1)); + bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(1)); } function test_RevertIf_InitializeWithAZeroAddressIMXToken() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(0), address(1)); - } - - function test_RevertIf_InitializeWithAZeroAddressEthRootToken() public { - ChildERC20Bridge bridge = new ChildERC20Bridge(); - vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(1), address(0)); + bridge.initialize(address(1), ROOT_BRIDGE_ADAPTOR, address(1), ROOT_CHAIN_NAME, address(0)); } function test_RevertIf_InitializeWithAZeroAddressAll() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(0), address(0)); + bridge.initialize(address(0), ROOT_BRIDGE_ADAPTOR, address(0), ROOT_CHAIN_NAME, address(0)); } function test_RevertIf_InitializeWithAnEmptyBridgeAdaptorString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootERC20BridgeAdaptor.selector); - bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN, NATIVE_ETH); + bridge.initialize(address(this), "", address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN); } function test_RevertIf_InitializeWithAnEmptyChainNameString() public { ChildERC20Bridge bridge = new ChildERC20Bridge(); vm.expectRevert(InvalidRootChain.selector); - bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", ROOT_IMX_TOKEN, NATIVE_ETH); + bridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), "", ROOT_IMX_TOKEN); } function test_onMessageReceive_EmitsTokenMappedEvent() public { @@ -200,6 +194,14 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); } + function test_RevertIf_mapTokenCalledWithETHAddress() public { + bytes memory data = abi.encode( + childBridge.MAP_TOKEN_SIG(), NATIVE_ETH, "Ethereum", "ETH", 18 + ); + vm.expectRevert(CantMapETH.selector); + childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); + } + function test_RevertIf_onMessageReceiveCalledTwice() public { bytes memory data = abi.encode( childBridge.MAP_TOKEN_SIG(), address(rootToken), rootToken.name(), rootToken.symbol(), rootToken.decimals() diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 950cc6b8..95c60a78 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -22,7 +22,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid string constant CHILD_CHAIN_NAME = "test"; address constant IMX_TOKEN = address(0xccc); address constant CHILD_ETH_TOKEN = address(0xddd); - address constant NATIVE_TOKEN = address(0xeee); + address constant NATIVE_ETH = address(0xeee); uint256 constant mapTokenFee = 300; uint256 constant depositFee = 200; @@ -176,6 +176,11 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid rootBridge.mapToken{value: 300}(IERC20Metadata(IMX_TOKEN)); } + function test_RevertIf_mapTokenCalledWithETHAddress() public { + vm.expectRevert(CantMapETH.selector); + rootBridge.mapToken{value: 300}(IERC20Metadata(NATIVE_ETH)); + } + function test_updateRootBridgeAdaptor() public { address newAdaptorAddress = address(0x11111); @@ -201,7 +206,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositETHCallsSendMessage() public { uint256 amount = 1000; - (, bytes memory predictedPayload) = setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + (, bytes memory predictedPayload) = setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectCall( address(mockAxelarAdaptor), @@ -214,16 +219,16 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositETHEmitsNativeEthDepositEvent() public { uint256 amount = 1000; - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectEmit(); - emit NativeEthDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), address(this), amount); + emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), address(this), amount); rootBridge.depositETH{value: amount+depositFee}(amount); } function test_RevertIf_depositETHInsufficientValue() public { uint256 amount = 1000; - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectRevert(InsufficientValue.selector); rootBridge.depositETH{value: (amount/2)+depositFee}(amount); @@ -236,7 +241,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositToETHCallsSendMessage() public { uint256 amount = 1000; address receiver = address(12345); - (, bytes memory predictedPayload) = setupDepositTo(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + (, bytes memory predictedPayload) = setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); vm.expectCall( address(mockAxelarAdaptor), depositFee, @@ -249,17 +254,17 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositToETHEmitsNativeEthDepositEvent() public { uint256 amount = 1000; address receiver = address(12345); - setupDepositTo(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); vm.expectEmit(); - emit NativeEthDeposit(NATIVE_TOKEN, CHILD_ETH_TOKEN, address(this), receiver, amount); + emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), receiver, amount); rootBridge.depositToETH{value: amount+depositFee}(receiver, amount); } function test_RevertIf_depositToETHInsufficientValue() public { uint256 amount = 1000; address receiver = address(12345); - setupDepositTo(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); vm.expectRevert(InsufficientValue.selector); rootBridge.depositToETH{value: (amount/2)+depositFee}(receiver, amount); @@ -271,7 +276,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_RevertIf_depositETHAmountIsZero() public { uint256 amount = 0; - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectRevert(ZeroAmount.selector); rootBridge.depositETH{value: amount+depositFee}(amount); @@ -281,7 +286,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid uint256 amount = 0; address receiver = address(12345); - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectRevert(ZeroAmount.selector); rootBridge.depositToETH{value: amount+depositFee}(receiver, amount); @@ -289,7 +294,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_RevertIf_depositAmountIsZero() public { uint256 amount = 0; - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectRevert(ZeroAmount.selector); rootBridge.deposit{value: depositFee}(token, amount); @@ -298,7 +303,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_RevertIf_depositToAmountIsZero() public { uint256 amount = 0; address receiver = address(12345); - setupDeposit(ERC20PresetMinterPauser(NATIVE_TOKEN), rootBridge, mapTokenFee, depositFee, amount, false); + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectRevert(ZeroAmount.selector); rootBridge.depositTo{value: depositFee}(token, receiver, amount); From c597a9f758e086eb0bf7fb176429e1aef537e3db Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 14:55:01 +1300 Subject: [PATCH 09/14] formatting --- src/child/ChildERC20Bridge.sol | 13 ++++------ src/interfaces/child/IChildERC20Bridge.sol | 7 +---- src/interfaces/root/IRootERC20Bridge.sol | 7 +---- src/root/RootERC20Bridge.sol | 4 +-- .../integration/child/ChildAxelarBridge.t.sol | 6 ++++- test/unit/child/ChildERC20Bridge.t.sol | 26 +++++++++++-------- test/unit/root/RootERC20Bridge.t.sol | 19 +++++++++----- 7 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index 251d4005..41b096e8 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -41,7 +41,7 @@ contract ChildERC20Bridge is address public constant NATIVE_ETH = address(0xeee); IChildERC20BridgeAdaptor public bridgeAdaptor; - + /// @dev The address that will be sending messages to, and receiving messages from, the child chain. string public rootERC20BridgeAdaptor; /// @dev The address of the token template that will be cloned to create tokens. @@ -67,12 +67,9 @@ contract ChildERC20Bridge is string memory newRootERC20BridgeAdaptor, address newChildTokenTemplate, string memory newRootChain, - address newRootIMXToken) - public initializer - { - if (newBridgeAdaptor == address(0) - || newChildTokenTemplate == address(0) - || newRootIMXToken == address(0)) { + address newRootIMXToken + ) public initializer { + if (newBridgeAdaptor == address(0) || newChildTokenTemplate == address(0) || newRootIMXToken == address(0)) { revert ZeroAddress(); } @@ -175,7 +172,7 @@ contract ChildERC20Bridge is revert NotMapped(); } } - + if (address(childToken).code.length == 0) { revert EmptyTokenContract(); } diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index 94945b5b..90bffc19 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -32,12 +32,7 @@ interface IChildERC20BridgeEvents { address indexed receiver, uint256 amount ); - event IMXDeposit( - address indexed rootToken, - address depositor, - address indexed receiver, - uint256 amount - ); + event IMXDeposit(address indexed rootToken, address depositor, address indexed receiver, uint256 amount); event NativeEthDeposit( address indexed rootToken, address indexed childToken, diff --git a/src/interfaces/root/IRootERC20Bridge.sol b/src/interfaces/root/IRootERC20Bridge.sol index 1c104563..6e09c715 100644 --- a/src/interfaces/root/IRootERC20Bridge.sol +++ b/src/interfaces/root/IRootERC20Bridge.sol @@ -45,12 +45,7 @@ interface IRootERC20BridgeEvents { address indexed receiver, uint256 amount ); - event IMXDeposit( - address indexed rootToken, - address depositor, - address indexed receiver, - uint256 amount - ); + event IMXDeposit(address indexed rootToken, address depositor, address indexed receiver, uint256 amount); event NativeEthDeposit( address indexed rootToken, address indexed childToken, diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index ec58af89..71789528 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -108,7 +108,7 @@ contract RootERC20Bridge is } uint256 expectedBalance = address(this).balance - (msg.value - amount); - + _deposit(IERC20Metadata(NATIVE_ETH), receiver, amount); // invariant check to ensure that the root native balance has increased by the amount deposited @@ -190,7 +190,7 @@ contract RootERC20Bridge is // TODO We can call _mapToken here, but ordering in the GMP is not guaranteed. // Therefore, we need to decide how to handle this and it may be a UI decision to wait until map token message is executed on child chain. // Discuss this, and add this decision to the design doc. - if (address(rootToken) != NATIVE_ETH) { + if (address(rootToken) != NATIVE_ETH) { if (address(rootToken) != rootIMXToken) { childToken = rootTokenToChildToken[address(rootToken)]; if (childToken == address(0)) { diff --git a/test/integration/child/ChildAxelarBridge.t.sol b/test/integration/child/ChildAxelarBridge.t.sol index 01144366..d5c660b7 100644 --- a/test/integration/child/ChildAxelarBridge.t.sol +++ b/test/integration/child/ChildAxelarBridge.t.sol @@ -35,7 +35,11 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil new ChildAxelarBridgeAdaptor(address(mockChildAxelarGateway), address(childERC20Bridge)); childERC20Bridge.initialize( - address(childAxelarBridgeAdaptor), ROOT_ADAPTOR_ADDRESS, address(childERC20), ROOT_CHAIN_NAME, IMX_TOKEN_ADDRESS + address(childAxelarBridgeAdaptor), + ROOT_ADAPTOR_ADDRESS, + address(childERC20), + ROOT_CHAIN_NAME, + IMX_TOKEN_ADDRESS ); } diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index d1b37edb..f8fb5505 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -33,9 +33,11 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childTokenTemplate = new ChildERC20(); childTokenTemplate.initialize(address(123), "Test", "TST", 18); - + childBridge = new ChildERC20Bridge(); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN); + childBridge.initialize( + address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN + ); } function test_Initialize() public { @@ -50,7 +52,9 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - childBridge.initialize(address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN); + childBridge.initialize( + address(this), ROOT_BRIDGE_ADAPTOR, address(childTokenTemplate), ROOT_CHAIN_NAME, ROOT_IMX_TOKEN + ); } function test_RevertIf_InitializeWithAZeroAddressAdapter() public { @@ -187,17 +191,13 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B } function test_RevertIf_mapTokenCalledWithIMXAddress() public { - bytes memory data = abi.encode( - childBridge.MAP_TOKEN_SIG(), ROOT_IMX_TOKEN, "ImmutableX", "IMX", 18 - ); + bytes memory data = abi.encode(childBridge.MAP_TOKEN_SIG(), ROOT_IMX_TOKEN, "ImmutableX", "IMX", 18); vm.expectRevert(CantMapIMX.selector); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); } function test_RevertIf_mapTokenCalledWithETHAddress() public { - bytes memory data = abi.encode( - childBridge.MAP_TOKEN_SIG(), NATIVE_ETH, "Ethereum", "ETH", 18 - ); + bytes memory data = abi.encode(childBridge.MAP_TOKEN_SIG(), NATIVE_ETH, "Ethereum", "ETH", 18); vm.expectRevert(CantMapETH.selector); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); } @@ -258,12 +258,16 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B address predictedChildETHToken = Clones.predictDeterministicAddress( address(childTokenTemplate), keccak256(abi.encodePacked(NATIVE_ETH)), address(childBridge) ); - + uint256 receiverPreBal = ChildERC20(predictedChildETHToken).balanceOf(receiver); childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, depositData); - assertEq(ChildERC20(predictedChildETHToken).balanceOf(receiver), receiverPreBal + amount, "receiver balance not increased"); + assertEq( + ChildERC20(predictedChildETHToken).balanceOf(receiver), + receiverPreBal + amount, + "receiver balance not increased" + ); } function test_onMessageReceive_DepositETH_IncreasesTotalSupply() public { diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 0ca62f8f..4dd5abdf 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -209,7 +209,8 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositETHCallsSendMessage() public { uint256 amount = 1000; - (, bytes memory predictedPayload) = setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); + (, bytes memory predictedPayload) = + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectCall( address(mockAxelarAdaptor), @@ -226,7 +227,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid vm.expectEmit(); emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), address(this), amount); - rootBridge.depositETH{value: amount+depositFee}(amount); + rootBridge.depositETH{value: amount + depositFee}(amount); } function test_RevertIf_depositETHInsufficientValue() public { @@ -244,7 +245,9 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositToETHCallsSendMessage() public { uint256 amount = 1000; address receiver = address(12345); - (, bytes memory predictedPayload) = setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + (, bytes memory predictedPayload) = setupDepositTo( + ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false + ); vm.expectCall( address(mockAxelarAdaptor), depositFee, @@ -257,17 +260,21 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_depositToETHEmitsNativeEthDepositEvent() public { uint256 amount = 1000; address receiver = address(12345); - setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + setupDepositTo( + ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false + ); vm.expectEmit(); emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), receiver, amount); - rootBridge.depositToETH{value: amount+depositFee}(receiver, amount); + rootBridge.depositToETH{value: amount + depositFee}(receiver, amount); } function test_RevertIf_depositToETHInsufficientValue() public { uint256 amount = 1000; address receiver = address(12345); - setupDepositTo(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false); + setupDepositTo( + ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, receiver, false + ); vm.expectRevert(InsufficientValue.selector); rootBridge.depositToETH{value: (amount / 2) + depositFee}(receiver, amount); From eb4134ed3d99d9d091ca38acfc75e365db5dc7ed Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 15:13:35 +1300 Subject: [PATCH 10/14] integration tests for IMX and Native events --- .../integration/child/ChildAxelarBridge.t.sol | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/integration/child/ChildAxelarBridge.t.sol b/test/integration/child/ChildAxelarBridge.t.sol index d5c660b7..1bdbd36b 100644 --- a/test/integration/child/ChildAxelarBridge.t.sol +++ b/test/integration/child/ChildAxelarBridge.t.sol @@ -19,6 +19,7 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil string public ROOT_ADAPTOR_ADDRESS = Strings.toHexString(address(1)); string public ROOT_CHAIN_NAME = "ROOT_CHAIN"; address constant IMX_TOKEN_ADDRESS = address(0xccc); + address constant NATIVE_ETH = address(0xeee); ChildERC20Bridge public childERC20Bridge; ChildERC20 public childERC20; @@ -147,6 +148,47 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil ); } + function test_deposit_EmitsNativeDeposit() public { + address sender = address(0xff); + address receiver = address(0xee); + uint256 amount = 100; + + address predictedChildETHToken = Clones.predictDeterministicAddress( + address(childERC20), keccak256(abi.encodePacked(NATIVE_ETH)), address(childERC20Bridge) + ); + + bytes32 commandId = bytes32("testCommandId"); + + vm.expectEmit(address(childERC20Bridge)); + emit NativeEthDeposit(NATIVE_ETH, predictedChildETHToken, sender, receiver, amount); + + childAxelarBridgeAdaptor.execute( + commandId, + ROOT_CHAIN_NAME, + ROOT_ADAPTOR_ADDRESS, + abi.encode(childERC20Bridge.DEPOSIT_SIG(), NATIVE_ETH, sender, receiver, amount) + ); + } + + function test_deposit_EmitsIMXDeposit() public { + address sender = address(0xff); + address receiver = address(0xee); + uint256 amount = 100; + bytes32 commandId = bytes32("testCommandId"); + + vm.deal(address(childERC20Bridge), 1 ether); + + vm.expectEmit(address(childERC20Bridge)); + emit IMXDeposit(IMX_TOKEN_ADDRESS, sender, receiver, amount); + + childAxelarBridgeAdaptor.execute( + commandId, + ROOT_CHAIN_NAME, + ROOT_ADAPTOR_ADDRESS, + abi.encode(childERC20Bridge.DEPOSIT_SIG(), IMX_TOKEN_ADDRESS, sender, receiver, amount) + ); + } + function test_deposit_TransfersTokenToReceiver() public { address rootTokenAddress = address(456); address sender = address(0xff); From 108abc4806e672769c16205581d50eccabd28709 Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 17:04:59 +1300 Subject: [PATCH 11/14] removed childETH from root and added integration tests --- script/InitializeRootContracts.s.sol | 3 +- src/root/RootERC20Bridge.sol | 11 +- test/integration/root/RootERC20Bridge.t.sol | 120 +++++++++++++++++++- test/unit/root/RootERC20Bridge.t.sol | 27 ++--- test/utils.t.sol | 12 +- 5 files changed, 143 insertions(+), 30 deletions(-) diff --git a/script/InitializeRootContracts.s.sol b/script/InitializeRootContracts.s.sol index 515f122d..b28ad6df 100644 --- a/script/InitializeRootContracts.s.sol +++ b/script/InitializeRootContracts.s.sol @@ -35,8 +35,7 @@ contract InitializeRootContracts is Script { childERC20Bridge, childBridgeAdaptor, rootChainChildTokenTemplate, - rootIMXToken, - childETHToken + rootIMXToken ); rootBridgeAdaptor.setChildBridgeAdaptor(); diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index 71789528..17aab64b 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -11,6 +11,7 @@ import {IAxelarGateway} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarG import {IRootERC20Bridge, IERC20Metadata} from "../interfaces/root/IRootERC20Bridge.sol"; import {IRootERC20BridgeEvents, IRootERC20BridgeErrors} from "../interfaces/root/IRootERC20Bridge.sol"; import {IRootERC20BridgeAdaptor} from "../interfaces/root/IRootERC20BridgeAdaptor.sol"; +import {IChildERC20} from "../interfaces/child/IChildERC20.sol"; /** * @notice RootERC20Bridge is a bridge that allows ERC20 tokens to be transferred from the root chain to the child chain. @@ -55,7 +56,6 @@ contract RootERC20Bridge is * @param newChildBridgeAdaptor Address of child bridge adaptor to communicate with. * @param newChildTokenTemplate Address of child token template to clone. * @param newRootIMXToken Address of ERC20 IMX on the root chain. - * @param newChildETHToken Address of ERC20 ETH on the child chain. * @dev Can only be called once. */ function initialize( @@ -63,20 +63,21 @@ contract RootERC20Bridge is address newChildERC20Bridge, address newChildBridgeAdaptor, address newChildTokenTemplate, - address newRootIMXToken, - address newChildETHToken + address newRootIMXToken ) public initializer { if ( newRootBridgeAdaptor == address(0) || newChildERC20Bridge == address(0) || newChildTokenTemplate == address(0) || newChildBridgeAdaptor == address(0) - || newRootIMXToken == address(0) || newChildETHToken == address(0) + || newRootIMXToken == address(0) ) { revert ZeroAddress(); } childERC20Bridge = newChildERC20Bridge; childTokenTemplate = newChildTokenTemplate; rootIMXToken = newRootIMXToken; - childETHToken = newChildETHToken; + IChildERC20 clonedETHToken = + IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(NATIVE_ETH)))); + childETHToken = address(clonedETHToken); rootBridgeAdaptor = IRootERC20BridgeAdaptor(newRootBridgeAdaptor); childBridgeAdaptor = Strings.toHexString(newChildBridgeAdaptor); } diff --git a/test/integration/root/RootERC20Bridge.t.sol b/test/integration/root/RootERC20Bridge.t.sol index 8b0c38af..4b5ba252 100644 --- a/test/integration/root/RootERC20Bridge.t.sol +++ b/test/integration/root/RootERC20Bridge.t.sol @@ -17,19 +17,21 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx string constant CHILD_CHAIN_NAME = "test"; bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); address constant IMX_TOKEN_ADDRESS = address(0xccc); - address constant CHILD_ETH_TOKEN = address(0xddd); + address constant NATIVE_ETH = address(0xeee); + uint256 constant mapTokenFee = 300; uint256 constant depositFee = 200; ERC20PresetMinterPauser public token; + ERC20PresetMinterPauser public imxToken; RootERC20Bridge public rootBridge; RootAxelarBridgeAdaptor public axelarAdaptor; MockAxelarGateway public mockAxelarGateway; MockAxelarGasService public axelarGasService; function setUp() public { - (token, rootBridge, axelarAdaptor, mockAxelarGateway, axelarGasService) = - integrationSetup(CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, CHILD_CHAIN_NAME, IMX_TOKEN_ADDRESS, CHILD_ETH_TOKEN); + (imxToken, token, rootBridge, axelarAdaptor, mockAxelarGateway, axelarGasService) = + integrationSetup(CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, CHILD_CHAIN_NAME, IMX_TOKEN_ADDRESS); } /** @@ -95,6 +97,118 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx assertEq(rootBridge.rootTokenToChildToken(address(token)), childToken, "childToken not set"); } + // TODO split into multiple tests + function test_depositETH() public { + uint256 tokenAmount = 300; + string memory childBridgeAdaptorString = Strings.toHexString(CHILD_BRIDGE_ADAPTOR); + + (, bytes memory predictedPayload) = + setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, tokenAmount, false); + + console2.logBytes(predictedPayload); + + vm.expectEmit(address(axelarAdaptor)); + emit MapTokenAxelarMessage(CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload); + vm.expectEmit(address(rootBridge)); + emit NativeEthDeposit(address(NATIVE_ETH), rootBridge.childETHToken(), address(this), address(this), tokenAmount); + + vm.expectCall( + address(axelarAdaptor), + depositFee, + abi.encodeWithSelector(axelarAdaptor.sendMessage.selector, predictedPayload, address(this)) + ); + + vm.expectCall( + address(axelarGasService), + depositFee, + abi.encodeWithSelector( + axelarGasService.payNativeGasForContractCall.selector, + address(axelarAdaptor), + CHILD_CHAIN_NAME, + childBridgeAdaptorString, + predictedPayload, + address(this) + ) + ); + + vm.expectCall( + address(mockAxelarGateway), + 0, + abi.encodeWithSelector( + mockAxelarGateway.callContract.selector, CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload + ) + ); + + uint256 bridgePreBal = address(rootBridge).balance; + + uint256 thisNativePreBal = address(this).balance; + uint256 gasServiceNativePreBal = address(axelarGasService).balance; + + rootBridge.depositETH{value: tokenAmount + depositFee}(tokenAmount); + + // Check that tokens are transferred + assertEq(bridgePreBal + tokenAmount, address(rootBridge).balance, "ETH not transferred to bridge"); + // Check that native asset transferred to gas service + assertEq(thisNativePreBal - (depositFee+tokenAmount), address(this).balance, "ETH not paid from user"); + assertEq(gasServiceNativePreBal + depositFee, address(axelarGasService).balance, "ETH not paid to adaptor"); + } + + // TODO split into multiple tests + function test_depositIMXToken() public { + uint256 tokenAmount = 300; + string memory childBridgeAdaptorString = Strings.toHexString(CHILD_BRIDGE_ADAPTOR); + + (, bytes memory predictedPayload) = + setupDeposit(ERC20PresetMinterPauser(IMX_TOKEN_ADDRESS), rootBridge, mapTokenFee, depositFee, tokenAmount, false); + + vm.expectEmit(address(axelarAdaptor)); + emit MapTokenAxelarMessage(CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload); + vm.expectEmit(address(rootBridge)); + emit IMXDeposit(address(IMX_TOKEN_ADDRESS), address(this), address(this), tokenAmount); + + vm.expectCall( + address(axelarAdaptor), + depositFee, + abi.encodeWithSelector(axelarAdaptor.sendMessage.selector, predictedPayload, address(this)) + ); + + vm.expectCall( + address(axelarGasService), + depositFee, + abi.encodeWithSelector( + axelarGasService.payNativeGasForContractCall.selector, + address(axelarAdaptor), + CHILD_CHAIN_NAME, + childBridgeAdaptorString, + predictedPayload, + address(this) + ) + ); + + vm.expectCall( + address(mockAxelarGateway), + 0, + abi.encodeWithSelector( + mockAxelarGateway.callContract.selector, CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload + ) + ); + + uint256 thisPreBal = imxToken.balanceOf(address(this)); + uint256 bridgePreBal = imxToken.balanceOf(address(rootBridge)); + + uint256 thisNativePreBal = address(this).balance; + uint256 gasServiceNativePreBal = address(axelarGasService).balance; + + rootBridge.deposit{value: depositFee}(IERC20Metadata(IMX_TOKEN_ADDRESS), tokenAmount); + + // Check that tokens are transferred + assertEq(thisPreBal - tokenAmount, imxToken.balanceOf(address(this)), "Tokens not transferred from user"); + assertEq(bridgePreBal + tokenAmount, imxToken.balanceOf(address(rootBridge)), "Tokens not transferred to bridge"); + // Check that native asset transferred to gas service + assertEq(thisNativePreBal - depositFee, address(this).balance, "ETH not paid from user"); + assertEq(gasServiceNativePreBal + depositFee, address(axelarGasService).balance, "ETH not paid to adaptor"); + } + // TODO split into multiple tests function test_depositToken() public { uint256 tokenAmount = 300; diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 4dd5abdf..86aa82e3 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -21,7 +21,6 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid address constant CHILD_BRIDGE_ADAPTOR = address(4); string constant CHILD_CHAIN_NAME = "test"; address constant IMX_TOKEN = address(0xccc); - address constant CHILD_ETH_TOKEN = address(0xddd); address constant NATIVE_ETH = address(0xeee); uint256 constant mapTokenFee = 300; uint256 constant depositFee = 200; @@ -44,7 +43,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid // The specific ERC20 token template does not matter for these unit tests rootBridge.initialize( - address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN, CHILD_ETH_TOKEN + address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN ); } @@ -61,50 +60,44 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); rootBridge.initialize( - address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN, CHILD_ETH_TOKEN + address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN ); } function test_RevertIf_InitializeWithAZeroAddressRootAdapter() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), address(1), address(1), address(1), address(1), address(1)); + bridge.initialize(address(0), address(1), address(1), address(1), address(1)); } function test_RevertIf_InitializeWithAZeroAddressChildBridge() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), address(0), address(1), address(1), address(1), address(1)); + bridge.initialize(address(1), address(0), address(1), address(1), address(1)); } function test_RevertIf_InitializeWithAZeroAddressChildAdapter() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), address(1), address(0), address(1), address(1), address(1)); + bridge.initialize(address(1), address(1), address(0), address(1), address(1)); } function test_RevertIf_InitializeWithAZeroAddressTokenTemplate() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), address(1), address(1), address(0), address(1), address(1)); + bridge.initialize(address(1), address(1), address(1), address(0), address(1)); } function test_RevertIf_InitializeWithAZeroAddressIMXToken() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), address(1), address(1), address(1), address(0), address(1)); - } - - function test_RevertIf_InitializeWithAZeroAddressETHToken() public { - RootERC20Bridge bridge = new RootERC20Bridge(); - vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(1), address(1), address(1), address(1), address(1), address(0)); + bridge.initialize(address(1), address(1), address(1), address(1), address(0)); } function test_RevertIf_InitializeWithAZeroAddressAll() public { RootERC20Bridge bridge = new RootERC20Bridge(); vm.expectRevert(ZeroAddress.selector); - bridge.initialize(address(0), address(0), address(0), address(0), address(0), address(0)); + bridge.initialize(address(0), address(0), address(0), address(0), address(0)); } /** @@ -226,7 +219,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, amount, false); vm.expectEmit(); - emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), address(this), amount); + emit NativeEthDeposit(NATIVE_ETH, rootBridge.childETHToken(), address(this), address(this), amount); rootBridge.depositETH{value: amount + depositFee}(amount); } @@ -265,7 +258,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid ); vm.expectEmit(); - emit NativeEthDeposit(NATIVE_ETH, CHILD_ETH_TOKEN, address(this), receiver, amount); + emit NativeEthDeposit(NATIVE_ETH, rootBridge.childETHToken(), address(this), receiver, amount); rootBridge.depositToETH{value: amount + depositFee}(receiver, amount); } diff --git a/test/utils.t.sol b/test/utils.t.sol index a7373846..f2556d03 100644 --- a/test/utils.t.sol +++ b/test/utils.t.sol @@ -16,11 +16,11 @@ contract Utils is Test { address childBridge, address childBridgeAdaptor, string memory childBridgeName, - address imxTokenAddress, - address ethTokenAddress + address imxTokenAddress ) public returns ( + ERC20PresetMinterPauser imxToken, ERC20PresetMinterPauser token, RootERC20Bridge rootBridge, RootAxelarBridgeAdaptor axelarAdaptor, @@ -31,6 +31,11 @@ contract Utils is Test { token = new ERC20PresetMinterPauser("Test", "TST"); token.mint(address(this), 1000000 ether); + + deployCodeTo("ERC20PresetMinterPauser.sol", abi.encode("ImmutableX", "IMX"), imxTokenAddress); + imxToken = ERC20PresetMinterPauser(imxTokenAddress); + imxToken.mint(address(this), 1000000 ether); + rootBridge = new RootERC20Bridge(); mockAxelarGateway = new MockAxelarGateway(); axelarGasService = new MockAxelarGasService(); @@ -43,7 +48,7 @@ contract Utils is Test { ); rootBridge.initialize( - address(axelarAdaptor), childBridge, childBridgeAdaptor, address(token), imxTokenAddress, ethTokenAddress + address(axelarAdaptor), childBridge, childBridgeAdaptor, address(token), imxTokenAddress ); axelarAdaptor.setChildBridgeAdaptor(); } @@ -84,6 +89,7 @@ contract Utils is Test { if (saveTokenMapping) { childToken = rootBridge.mapToken{value: mapTokenFee}(token); } + if (address(token) == address(0xeee)) { vm.deal(to, tokenAmount + depositFee); } else { From abc5c4f94914778d30ad6e566b4bba643f8b7e4a Mon Sep 17 00:00:00 2001 From: Craig M Date: Tue, 24 Oct 2023 17:08:55 +1300 Subject: [PATCH 12/14] formatting --- script/InitializeRootContracts.s.sol | 6 +----- test/integration/root/RootERC20Bridge.t.sol | 19 ++++++++++++------- test/unit/root/RootERC20Bridge.t.sol | 8 ++------ test/utils.t.sol | 5 +---- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/script/InitializeRootContracts.s.sol b/script/InitializeRootContracts.s.sol index b28ad6df..56dc7e35 100644 --- a/script/InitializeRootContracts.s.sol +++ b/script/InitializeRootContracts.s.sol @@ -31,11 +31,7 @@ contract InitializeRootContracts is Script { vm.startBroadcast(rootPrivateKey); rootERC20Bridge.initialize( - address(rootBridgeAdaptor), - childERC20Bridge, - childBridgeAdaptor, - rootChainChildTokenTemplate, - rootIMXToken + address(rootBridgeAdaptor), childERC20Bridge, childBridgeAdaptor, rootChainChildTokenTemplate, rootIMXToken ); rootBridgeAdaptor.setChildBridgeAdaptor(); diff --git a/test/integration/root/RootERC20Bridge.t.sol b/test/integration/root/RootERC20Bridge.t.sol index 4b5ba252..bab1f5c9 100644 --- a/test/integration/root/RootERC20Bridge.t.sol +++ b/test/integration/root/RootERC20Bridge.t.sol @@ -104,13 +104,15 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx (, bytes memory predictedPayload) = setupDeposit(ERC20PresetMinterPauser(NATIVE_ETH), rootBridge, mapTokenFee, depositFee, tokenAmount, false); - + console2.logBytes(predictedPayload); vm.expectEmit(address(axelarAdaptor)); emit MapTokenAxelarMessage(CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload); vm.expectEmit(address(rootBridge)); - emit NativeEthDeposit(address(NATIVE_ETH), rootBridge.childETHToken(), address(this), address(this), tokenAmount); + emit NativeEthDeposit( + address(NATIVE_ETH), rootBridge.childETHToken(), address(this), address(this), tokenAmount + ); vm.expectCall( address(axelarAdaptor), @@ -149,7 +151,7 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx // Check that tokens are transferred assertEq(bridgePreBal + tokenAmount, address(rootBridge).balance, "ETH not transferred to bridge"); // Check that native asset transferred to gas service - assertEq(thisNativePreBal - (depositFee+tokenAmount), address(this).balance, "ETH not paid from user"); + assertEq(thisNativePreBal - (depositFee + tokenAmount), address(this).balance, "ETH not paid from user"); assertEq(gasServiceNativePreBal + depositFee, address(axelarGasService).balance, "ETH not paid to adaptor"); } @@ -158,9 +160,10 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx uint256 tokenAmount = 300; string memory childBridgeAdaptorString = Strings.toHexString(CHILD_BRIDGE_ADAPTOR); - (, bytes memory predictedPayload) = - setupDeposit(ERC20PresetMinterPauser(IMX_TOKEN_ADDRESS), rootBridge, mapTokenFee, depositFee, tokenAmount, false); - + (, bytes memory predictedPayload) = setupDeposit( + ERC20PresetMinterPauser(IMX_TOKEN_ADDRESS), rootBridge, mapTokenFee, depositFee, tokenAmount, false + ); + vm.expectEmit(address(axelarAdaptor)); emit MapTokenAxelarMessage(CHILD_CHAIN_NAME, childBridgeAdaptorString, predictedPayload); vm.expectEmit(address(rootBridge)); @@ -203,7 +206,9 @@ contract RootERC20BridgeIntegrationTest is Test, IRootERC20BridgeEvents, IRootAx // Check that tokens are transferred assertEq(thisPreBal - tokenAmount, imxToken.balanceOf(address(this)), "Tokens not transferred from user"); - assertEq(bridgePreBal + tokenAmount, imxToken.balanceOf(address(rootBridge)), "Tokens not transferred to bridge"); + assertEq( + bridgePreBal + tokenAmount, imxToken.balanceOf(address(rootBridge)), "Tokens not transferred to bridge" + ); // Check that native asset transferred to gas service assertEq(thisNativePreBal - depositFee, address(this).balance, "ETH not paid from user"); assertEq(gasServiceNativePreBal + depositFee, address(axelarGasService).balance, "ETH not paid to adaptor"); diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 86aa82e3..cef2ad5d 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -42,9 +42,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid mockAxelarAdaptor = new MockAdaptor(); // The specific ERC20 token template does not matter for these unit tests - rootBridge.initialize( - address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN - ); + rootBridge.initialize(address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN); } /** @@ -59,9 +57,7 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid function test_RevertIfInitializeTwice() public { vm.expectRevert("Initializable: contract is already initialized"); - rootBridge.initialize( - address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN - ); + rootBridge.initialize(address(mockAxelarAdaptor), CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, address(token), IMX_TOKEN); } function test_RevertIf_InitializeWithAZeroAddressRootAdapter() public { diff --git a/test/utils.t.sol b/test/utils.t.sol index f2556d03..77585442 100644 --- a/test/utils.t.sol +++ b/test/utils.t.sol @@ -31,7 +31,6 @@ contract Utils is Test { token = new ERC20PresetMinterPauser("Test", "TST"); token.mint(address(this), 1000000 ether); - deployCodeTo("ERC20PresetMinterPauser.sol", abi.encode("ImmutableX", "IMX"), imxTokenAddress); imxToken = ERC20PresetMinterPauser(imxTokenAddress); imxToken.mint(address(this), 1000000 ether); @@ -47,9 +46,7 @@ contract Utils is Test { address(axelarGasService) ); - rootBridge.initialize( - address(axelarAdaptor), childBridge, childBridgeAdaptor, address(token), imxTokenAddress - ); + rootBridge.initialize(address(axelarAdaptor), childBridge, childBridgeAdaptor, address(token), imxTokenAddress); axelarAdaptor.setChildBridgeAdaptor(); } From 59e4a8c356e531ebce25678801ec8128f5aaee67 Mon Sep 17 00:00:00 2001 From: Benjamin Patch Date: Wed, 25 Oct 2023 09:58:24 +1100 Subject: [PATCH 13/14] Remove need for child eth token env var in deployment scripts --- .env.sample | 3 +-- script/InitializeRootContracts.s.sol | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index f1854b16..957e611b 100644 --- a/.env.sample +++ b/.env.sample @@ -10,5 +10,4 @@ ROOT_GAS_SERVICE_ADDRESS= CHILD_GAS_SERVICE_ADDRESS= ROOT_CHAIN_NAME= CHILD_CHAIN_NAME= -ROOT_IMX_ADDRESS= -CHILD_ETH_ADDRESS= \ No newline at end of file +ROOT_IMX_ADDRESS= \ No newline at end of file diff --git a/script/InitializeRootContracts.s.sol b/script/InitializeRootContracts.s.sol index 56dc7e35..ad935886 100644 --- a/script/InitializeRootContracts.s.sol +++ b/script/InitializeRootContracts.s.sol @@ -22,7 +22,6 @@ contract InitializeRootContracts is Script { string memory rootRpcUrl = vm.envString("ROOT_RPC_URL"); uint256 rootPrivateKey = vm.envUint("ROOT_PRIVATE_KEY"); address rootIMXToken = vm.envAddress("ROOT_IMX_ADDRESS"); - address childETHToken = vm.envAddress("CHILD_ETH_ADDRESS"); /** * INITIALIZE ROOT CHAIN CONTRACTS From 56737b439c0a65058ad241c32c0f442ebca58e82 Mon Sep 17 00:00:00 2001 From: Benjamin Patch Date: Wed, 25 Oct 2023 11:11:24 +1100 Subject: [PATCH 14/14] Predict the child ETH address properly --- src/root/RootERC20Bridge.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index 17aab64b..8a7486a1 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -75,9 +75,10 @@ contract RootERC20Bridge is childERC20Bridge = newChildERC20Bridge; childTokenTemplate = newChildTokenTemplate; rootIMXToken = newRootIMXToken; - IChildERC20 clonedETHToken = - IChildERC20(Clones.cloneDeterministic(childTokenTemplate, keccak256(abi.encodePacked(NATIVE_ETH)))); - childETHToken = address(clonedETHToken); + + childETHToken = Clones.predictDeterministicAddress( + childTokenTemplate, keccak256(abi.encodePacked(NATIVE_ETH)), childERC20Bridge + ); rootBridgeAdaptor = IRootERC20BridgeAdaptor(newRootBridgeAdaptor); childBridgeAdaptor = Strings.toHexString(newChildBridgeAdaptor); }