diff --git a/script/DeployRootContracts.s.sol b/script/DeployRootContracts.s.sol index 65fe92af..5e304ed6 100644 --- a/script/DeployRootContracts.s.sol +++ b/script/DeployRootContracts.s.sol @@ -7,7 +7,7 @@ import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.s import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {ChildERC20} from "../src/child/ChildERC20.sol"; -import {RootERC20Bridge, IRootERC20Bridge} from "../src/root/RootERC20Bridge.sol"; +import {RootERC20Bridge} from "../src/root/RootERC20Bridge.sol"; import {RootAxelarBridgeAdaptor} from "../src/root/RootAxelarBridgeAdaptor.sol"; import {ChildERC20Bridge} from "../src/child/ChildERC20Bridge.sol"; import {ChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; @@ -35,13 +35,8 @@ contract DeployRootContracts is Script { ChildERC20 rootChainChildTokenTemplate = new ChildERC20(); rootChainChildTokenTemplate.initialize(address(123), "TEMPLATE", "TPT", 18); - IRootERC20Bridge.InitializationRoles memory fillerRoles = - IRootERC20Bridge.InitializationRoles(address(1), address(1), address(1), address(1), address(1)); - RootERC20Bridge rootERC20BridgeImplementation = new RootERC20Bridge(); - rootERC20BridgeImplementation.initialize( - fillerRoles, address(1), address(1), "filler", address(1), address(1), address(1), "filler_child_name", 1 - ); + TransparentUpgradeableProxy rootERC20BridgeProxy = new TransparentUpgradeableProxy( address(rootERC20BridgeImplementation), address(proxyAdmin), @@ -49,7 +44,6 @@ contract DeployRootContracts is Script { ); RootAxelarBridgeAdaptor rootBridgeAdaptorImplementation = new RootAxelarBridgeAdaptor(rootGateway); - rootBridgeAdaptorImplementation.initialize(address(rootERC20BridgeImplementation), "Filler", address(1)); TransparentUpgradeableProxy rootBridgeAdaptorProxy = new TransparentUpgradeableProxy( address(rootBridgeAdaptorImplementation), diff --git a/script/InitializeChildContracts.s.sol b/script/InitializeChildContracts.s.sol index daa92cba..db14d495 100644 --- a/script/InitializeChildContracts.s.sol +++ b/script/InitializeChildContracts.s.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.19; import {Script, console2} from "forge-std/Script.sol"; import {ChildERC20Bridge, IChildERC20Bridge} from "../src/child/ChildERC20Bridge.sol"; -import {ChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; +import {ChildAxelarBridgeAdaptor, IChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; import {Utils} from "./Utils.sol"; // TODO update private key usage to be more secure: https://book.getfoundry.sh/reference/forge/forge-script#wallet-options---raw @@ -13,6 +13,9 @@ struct InitializeChildContractsParams { address childAdminAddress; address childPauserAddress; address childUnpauserAddress; + address childBridgeManager; + address childGasServiceManager; + address childTargetManager; ChildERC20Bridge childERC20Bridge; ChildAxelarBridgeAdaptor childAxelarBridgeAdaptor; address childTokenTemplate; @@ -31,6 +34,9 @@ contract InitializeChildContracts is Script { childAdminAddress: vm.envAddress("CHILD_ADMIN_ADDRESS"), childPauserAddress: vm.envAddress("CHILD_PAUSER_ADDRESS"), childUnpauserAddress: vm.envAddress("CHILD_UNPAUSER_ADDRESS"), + childBridgeManager: vm.envAddress("CHILD_BRIDGE_MANAGER"), + childGasServiceManager: vm.envAddress("CHILD_GAS_SERVICE_MANAGER"), + childTargetManager: vm.envAddress("CHILD_TARGET_MANAGER"), childERC20Bridge: ChildERC20Bridge(payable(vm.envAddress("CHILD_ERC20_BRIDGE"))), childAxelarBridgeAdaptor: ChildAxelarBridgeAdaptor(vm.envAddress("CHILD_BRIDGE_ADAPTOR")), childTokenTemplate: vm.envAddress("CHILDCHAIN_CHILD_TOKEN_TEMPLATE"), @@ -70,8 +76,16 @@ contract InitializeChildContracts is Script { params.wIMXToken ); + IChildAxelarBridgeAdaptor.InitializationRoles memory adaptorRoles = IChildAxelarBridgeAdaptor + .InitializationRoles({ + defaultAdmin: params.childAdminAddress, + bridgeManager: params.childBridgeManager, + gasServiceManager: params.childGasServiceManager, + targetManager: params.childTargetManager + }); + params.childAxelarBridgeAdaptor.initialize( - params.rootChainName, address(params.childERC20Bridge), params.childGasService + adaptorRoles, params.rootChainName, address(params.childERC20Bridge), params.childGasService ); vm.stopBroadcast(); diff --git a/script/InitializeRootContracts.s.sol b/script/InitializeRootContracts.s.sol index ee7d0fc7..c58e324f 100644 --- a/script/InitializeRootContracts.s.sol +++ b/script/InitializeRootContracts.s.sol @@ -1,13 +1,12 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity 0.8.19; -import {Script, console2} from "forge-std/Script.sol"; - +import {Script} from "forge-std/Script.sol"; import {ChildERC20} from "../src/child/ChildERC20.sol"; import {RootERC20Bridge, IRootERC20Bridge} from "../src/root/RootERC20Bridge.sol"; +import {IRootAxelarBridgeAdaptor} from "../src/interfaces/root/IRootAxelarBridgeAdaptor.sol"; import {RootAxelarBridgeAdaptor} from "../src/root/RootAxelarBridgeAdaptor.sol"; import {ChildERC20Bridge} from "../src/child/ChildERC20Bridge.sol"; -import {ChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; import {AddressToString} from "@axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol"; import {Utils} from "./Utils.sol"; @@ -17,6 +16,9 @@ struct InitializeRootContractsParams { address rootAdminAddress; address rootPauserAddress; address rootUnpauserAddress; + address rootBridgeManagerAddress; + address rootGasManagerAddress; + address rootTargetManagerAddress; RootERC20Bridge rootERC20Bridge; RootAxelarBridgeAdaptor rootBridgeAdaptor; address rootChainChildTokenTemplate; @@ -37,6 +39,9 @@ contract InitializeRootContracts is Script { rootAdminAddress: vm.envAddress("ROOT_ADMIN_ADDRESS"), rootPauserAddress: vm.envAddress("ROOT_PAUSER_ADDRESS"), rootUnpauserAddress: vm.envAddress("ROOT_UNPAUSER_ADDRESS"), + rootBridgeManagerAddress: vm.envAddress("ROOT_BRIDGE_MANAGER_ADDRESS"), + rootGasManagerAddress: vm.envAddress("ROOT_GAS_MANAGER_ADDRESS"), + rootTargetManagerAddress: vm.envAddress("ROOT_TARGET_MANAGER_ADDRESS"), rootERC20Bridge: RootERC20Bridge(payable(vm.envAddress("ROOT_ERC20_BRIDGE"))), rootBridgeAdaptor: RootAxelarBridgeAdaptor(vm.envAddress("ROOT_BRIDGE_ADAPTOR")), rootChainChildTokenTemplate: vm.envAddress("ROOTCHAIN_CHILD_TOKEN_TEMPLATE"), @@ -82,7 +87,15 @@ contract InitializeRootContracts is Script { params.initialIMXCumulativeDepositLimit ); + IRootAxelarBridgeAdaptor.InitializationRoles memory adaptorRoles = IRootAxelarBridgeAdaptor.InitializationRoles({ + defaultAdmin: params.rootAdminAddress, + bridgeManager: params.rootBridgeManagerAddress, + gasServiceManager: params.rootGasManagerAddress, + targetManager: params.rootTargetManagerAddress + }); + params.rootBridgeAdaptor.initialize( + adaptorRoles, address(params.rootERC20Bridge), // root bridge params.childChainName, // child chain name params.rootGasService // axelar gas service diff --git a/src/child/ChildAxelarBridgeAdaptor.sol b/src/child/ChildAxelarBridgeAdaptor.sol index ee0b92c7..e85d7316 100644 --- a/src/child/ChildAxelarBridgeAdaptor.sol +++ b/src/child/ChildAxelarBridgeAdaptor.sol @@ -3,21 +3,22 @@ pragma solidity 0.8.19; import {AxelarExecutable} from "@axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; import {IAxelarGasService} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol"; -import {IAxelarGateway} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol"; -import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import {IChildERC20Bridge} from "../interfaces/child/IChildERC20Bridge.sol"; import { IChildAxelarBridgeAdaptorErrors, - IChildAxelarBridgeAdaptorEvents + IChildAxelarBridgeAdaptorEvents, + IChildAxelarBridgeAdaptor } from "../interfaces/child/IChildAxelarBridgeAdaptor.sol"; import {IChildERC20BridgeAdaptor} from "../interfaces/child/IChildERC20BridgeAdaptor.sol"; +import {AdaptorRoles} from "../common/AdaptorRoles.sol"; contract ChildAxelarBridgeAdaptor is AxelarExecutable, IChildERC20BridgeAdaptor, - Initializable, IChildAxelarBridgeAdaptorErrors, - IChildAxelarBridgeAdaptorEvents + IChildAxelarBridgeAdaptorEvents, + IChildAxelarBridgeAdaptor, + AdaptorRoles { /// @notice Address of bridge to relay messages to. IChildERC20Bridge public childBridge; @@ -30,18 +31,74 @@ contract ChildAxelarBridgeAdaptor is * @notice Initializes the contract. * @param _childBridge Address of the child bridge contract. */ - function initialize(string memory _rootChain, address _childBridge, address _gasService) external initializer { - if (_childBridge == address(0)) { + function initialize( + InitializationRoles memory newRoles, + string memory _rootChain, + address _childBridge, + address _gasService + ) external initializer { + if ( + _childBridge == address(0) || _gasService == address(0) || newRoles.defaultAdmin == address(0) + || newRoles.bridgeManager == address(0) || newRoles.gasServiceManager == address(0) + || newRoles.targetManager == address(0) + ) { revert ZeroAddress(); } + if (bytes(_rootChain).length == 0) { + revert InvalidRootChain(); + } + + __AccessControl_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, newRoles.defaultAdmin); + _grantRole(BRIDGE_MANAGER_ROLE, newRoles.bridgeManager); + _grantRole(GAS_SERVICE_MANAGER_ROLE, newRoles.gasServiceManager); + _grantRole(TARGET_MANAGER_ROLE, newRoles.targetManager); + childBridge = IChildERC20Bridge(_childBridge); rootChain = _rootChain; gasService = IAxelarGasService(_gasService); } /** - * TODO + * @inheritdoc IChildAxelarBridgeAdaptor + */ + function updateChildBridge(address newChildBridge) external onlyRole(BRIDGE_MANAGER_ROLE) { + if (newChildBridge == address(0)) { + revert ZeroAddress(); + } + + emit ChildBridgeUpdated(address(childBridge), newChildBridge); + childBridge = IChildERC20Bridge(newChildBridge); + } + + /** + * @inheritdoc IChildAxelarBridgeAdaptor + */ + function updateRootChain(string memory newRootChain) external onlyRole(TARGET_MANAGER_ROLE) { + if (bytes(newRootChain).length == 0) { + revert InvalidRootChain(); + } + + emit RootChainUpdated(rootChain, newRootChain); + rootChain = newRootChain; + } + + /** + * @inheritdoc IChildAxelarBridgeAdaptor + */ + function updateGasService(address newGasService) external onlyRole(GAS_SERVICE_MANAGER_ROLE) { + if (newGasService == address(0)) { + revert ZeroAddress(); + } + + emit GasServiceUpdated(address(gasService), newGasService); + gasService = IAxelarGasService(newGasService); + } + + /** + * @inheritdoc IChildERC20BridgeAdaptor */ function sendMessage(bytes calldata payload, address refundRecipient) external payable override { if (msg.value == 0) { diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index bfa7df62..8b8cb93b 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -112,14 +112,27 @@ contract ChildERC20Bridge is IChildERC20BridgeErrors, IChildERC20Bridge, IChildE /** * @inheritdoc IChildERC20Bridge */ - function updateBridgeAdaptor(address newBridgeAdaptor) external override onlyRole(ADAPTOR_MANAGER_ROLE) { + function updateChildBridgeAdaptor(address newBridgeAdaptor) external onlyRole(ADAPTOR_MANAGER_ROLE) { if (newBridgeAdaptor == address(0)) { revert ZeroAddress(); } - emit BridgeAdaptorUpdated(address(bridgeAdaptor), newBridgeAdaptor); + + emit ChildBridgeAdaptorUpdated(address(bridgeAdaptor), newBridgeAdaptor); bridgeAdaptor = IChildERC20BridgeAdaptor(newBridgeAdaptor); } + /** + * @inheritdoc IChildERC20Bridge + */ + function updateRootBridgeAdaptor(string memory newRootBridgeAdaptor) external onlyRole(ADAPTOR_MANAGER_ROLE) { + if (bytes(newRootBridgeAdaptor).length == 0) { + revert InvalidRootERC20BridgeAdaptor(); + } + + emit RootBridgeAdaptorUpdated(rootERC20BridgeAdaptor, newRootBridgeAdaptor); + rootERC20BridgeAdaptor = newRootBridgeAdaptor; + } + /** * @notice Fallback function on receiving native IMX from WIMX contract. */ diff --git a/src/common/AdaptorRoles.sol b/src/common/AdaptorRoles.sol new file mode 100644 index 00000000..290e34cc --- /dev/null +++ b/src/common/AdaptorRoles.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache 2.0 +pragma solidity 0.8.19; + +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; + +/** + * + * @title AdaptorRoles.sol + * @notice AdaptorRoles.sol is an abstract contract that defines the roles and permissions across the root and child chain adaptor contracts. + * @dev This contract uses OpenZeppelin's AccessControl contract. This contract is abstract and is intended to be inherited by the root and child chain adaptor contracts. + */ +abstract contract AdaptorRoles is AccessControlUpgradeable { + // Roles + /// @notice Role identifier for those who can update the bridge used by the adaptor. + bytes32 public constant BRIDGE_MANAGER_ROLE = keccak256("BRIDGE_MANAGER"); + + /// @notice Role identifier for those who can update the gas service used by the adaptor. + bytes32 public constant GAS_SERVICE_MANAGER_ROLE = keccak256("GAS_SERVICE_MANAGER"); + + /// @notice Role identifier for those who can update targeted bridge used by the adpator (e.g. target is child chain on root adaptors). + bytes32 public constant TARGET_MANAGER_ROLE = keccak256("TARGET_MANAGER"); + + // Role granting functions + /** + * @notice Function to grant bridge manager role to an address + */ + function grantBridgeManager(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + grantRole(BRIDGE_MANAGER_ROLE, account); + } + + /** + * @notice Function to grant gas service manager role to an address + */ + function grantGasServiceManager(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + grantRole(GAS_SERVICE_MANAGER_ROLE, account); + } + + /** + * @notice Function to grant target manager role to an address + */ + function grantTargetManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + grantRole(TARGET_MANAGER_ROLE, account); + } + + // Role revoking functions + /** + * @notice Function to revoke bridge manager role from an address + */ + function revokeBridgeManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + revokeRole(BRIDGE_MANAGER_ROLE, account); + } + + /** + * @notice Function to revoke gas service manager role from an address + */ + function revokeGasServiceManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + revokeRole(GAS_SERVICE_MANAGER_ROLE, account); + } + + /** + * @notice Function to target manager role from an address + */ + function revokeTargetMangaerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + revokeRole(TARGET_MANAGER_ROLE, account); + } +} diff --git a/src/interfaces/child/IChildAxelarBridgeAdaptor.sol b/src/interfaces/child/IChildAxelarBridgeAdaptor.sol index 2aa22e0d..1a63bfd6 100644 --- a/src/interfaces/child/IChildAxelarBridgeAdaptor.sol +++ b/src/interfaces/child/IChildAxelarBridgeAdaptor.sol @@ -1,6 +1,37 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity 0.8.19; +interface IChildAxelarBridgeAdaptor { + /// @notice Initialization roles used by the adaptor. + struct InitializationRoles { + address defaultAdmin; // The address which will inherit `DEFAULT_ADMIN_ROLE`. + address bridgeManager; // The address which will inherit `BRIDGE_MANAGER_ROLE`. + address gasServiceManager; // The address which will inherit `GAS_SERVICE_MANAGER_ROLE`. + address targetManager; // The address which will inherit `TARGET_MANAGER_ROLE`. + } + + /** + * @notice Update the child bridge + * @param newChildBridge Address of new root bridge. + * @dev Can only be called by BRIDGE_MANAGER_ROLE. + */ + function updateChildBridge(address newChildBridge) external; + + /** + * @notice Update the root chain name. + * @param newRootChain Name of new root chain. + * @dev Can only be called by TARGET_MANAGER_ROLE. + */ + function updateRootChain(string memory newRootChain) external; + + /** + * @notice Update the gas service. + * @param newGasService Address of new gas service. + * @dev Can only be called by GAS_SERVICE_MANAGER_ROLE. + */ + function updateGasService(address newGasService) external; +} + interface IChildAxelarBridgeAdaptorErrors { /// @notice Error when a zero address is given when not valid. error ZeroAddress(); @@ -8,6 +39,8 @@ interface IChildAxelarBridgeAdaptorErrors { error NoGas(); /// @notice Error when the caller is not the bridge. error CallerNotBridge(); + /// @notice Error when the root chain name is invalid. + error InvalidRootChain(); } interface IChildAxelarBridgeAdaptorEvents { @@ -15,4 +48,10 @@ interface IChildAxelarBridgeAdaptorEvents { event AxelarMessageSent(string indexed rootChain, string indexed rootBridgeAdaptor, bytes indexed payload); /// @notice Emitted when an Axelar message is received from the root chain. event AdaptorExecute(string sourceChain, string sourceAddress_, bytes payload_); + /// @notice Emitted when the child bridge is updated. + event ChildBridgeUpdated(address indexed oldRootBridge, address indexed newRootBridge); + /// @notice Emitted when the root chain name is updated. + event RootChainUpdated(string indexed oldChildChain, string indexed newChildChain); + /// @notice Emitted when the gas service is updated. + event GasServiceUpdated(address indexed oldGasService, address indexed newGasService); } diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index b3af5347..75f48ac3 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -25,7 +25,13 @@ interface IChildERC20Bridge { * @notice Sets a new bridge adaptor address to receive and send function calls for L1 messages * @param newBridgeAdaptor The new child chain bridge adaptor address. */ - function updateBridgeAdaptor(address newBridgeAdaptor) external; + function updateChildBridgeAdaptor(address newBridgeAdaptor) external; + + /** + * @notice Sets a new root chain bridge adaptor address to receive and send function calls for L2 messages + * @param newRootBridgeAdaptor The new root chain bridge adaptor address. + */ + function updateRootBridgeAdaptor(string memory newRootBridgeAdaptor) external; /** * @notice Withdraws `amount` of `childToken` to `msg.sender` on the rootchain. @@ -121,8 +127,10 @@ interface IChildERC20BridgeEvents { address indexed receiver, uint256 amount ); - /// @notice Emitted when a new bridge adaptor is set. - event BridgeAdaptorUpdated(address oldBridgeAdaptor, address newBridgeAdaptor); + /// @notice Emitted when the child chain bridge adaptor is updated. + event ChildBridgeAdaptorUpdated(address oldChildBridgeAdaptor, address newChildBridgeAdaptor); + /// @notice Emitted when the root chain bridge adaptor is updated. + event RootBridgeAdaptorUpdated(string oldRootBridgeAdaptor, string newRootBridgeAdaptor); } // TODO add parameters to errors if it makes sense diff --git a/src/interfaces/root/IRootAxelarBridgeAdaptor.sol b/src/interfaces/root/IRootAxelarBridgeAdaptor.sol index d009aa04..0bd02080 100644 --- a/src/interfaces/root/IRootAxelarBridgeAdaptor.sol +++ b/src/interfaces/root/IRootAxelarBridgeAdaptor.sol @@ -1,6 +1,37 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity 0.8.19; +interface IRootAxelarBridgeAdaptor { + /// @notice Initialization roles used by the adaptor. + struct InitializationRoles { + address defaultAdmin; // The address which will inherit `DEFAULT_ADMIN_ROLE`. + address bridgeManager; // The address which will inherit `BRIDGE_MANAGER_ROLE`. + address gasServiceManager; // The address which will inherit `GAS_SERVICE_MANAGER_ROLE`. + address targetManager; // The address which will inherit `TARGET_MANAGER_ROLE`. + } + + /** + * @notice Update the root bridge + * @param newRootBridge Address of new root bridge. + * @dev Can only be called by BRIDGE_MANAGER_ROLE. + */ + function updateRootBridge(address newRootBridge) external; + + /** + * @notice Update the child chain name. + * @param newChildChain Name of new child chain. + * @dev Can only be called by TARGET_MANAGER_ROLE. + */ + function updateChildChain(string memory newChildChain) external; + + /** + * @notice Update the gas service. + * @param newGasService Address of new gas service. + * @dev Can only be called by GAS_SERVICE_MANAGER_ROLE. + */ + function updateGasService(address newGasService) external; +} + interface IRootAxelarBridgeAdaptorErrors { /// @notice Error when a zero address is given when not valid. error ZeroAddresses(); @@ -17,4 +48,10 @@ interface IRootAxelarBridgeAdaptorEvents { event AxelarMessageSent(string indexed childChain, string indexed childBridgeAdaptor, bytes indexed payload); /// @notice Emitted when an Axelar message is received from the child chain. event AdaptorExecute(string sourceChain, string sourceAddress_, bytes payload_); + /// @notice Emitted when the root bridge is updated. + event RootBridgeUpdated(address indexed oldRootBridge, address indexed newRootBridge); + /// @notice Emitted when the child chain name is updated. + event ChildChainUpdated(string indexed oldChildChain, string indexed newChildChain); + /// @notice Emitted when the gas service is updated. + event GasServiceUpdated(address indexed oldGasService, address indexed newGasService); } diff --git a/src/interfaces/root/IRootERC20Bridge.sol b/src/interfaces/root/IRootERC20Bridge.sol index e10320ac..f7cdeb2f 100644 --- a/src/interfaces/root/IRootERC20Bridge.sol +++ b/src/interfaces/root/IRootERC20Bridge.sol @@ -22,6 +22,20 @@ interface IRootERC20Bridge { */ function grantVariableManagerRole(address account) external; + /** + * @notice Updates the root bridge adaptor. + * @param newRootBridgeAdaptor Address of new root bridge adaptor. + * @dev Can only be called by ADAPTOR_MANAGER_ROLE. + */ + function updateRootBridgeAdaptor(address newRootBridgeAdaptor) external; + + /** + * @notice Updates the child bridge adaptor. + * @param newChildBridgeAdaptor String checksum address of new root bridge adaptor. + * @dev Can only be called by ADAPTOR_MANAGER_ROLE. + */ + function updateChildBridgeAdaptor(string memory newChildBridgeAdaptor) external; + function childBridgeAdaptor() external view returns (string memory); /** * @notice Receives a bridge message from child chain, parsing the message type then executing. @@ -43,6 +57,19 @@ interface IRootERC20Bridge { */ function mapToken(IERC20Metadata rootToken) external payable returns (address); + /** + * @notice Deposits `amount` of ETH to `msg.sender` on the child chain. + * @param amount The amount of ETH to deposit. + */ + function depositETH(uint256 amount) external payable; + + /** + * @notice Deposits `amount` of ETH to `receiver` on the child chain. + * @param receiver The address to deposit the ETH to. + * @param amount The amount of ETH to deposit. + */ + function depositToETH(address receiver, uint256 amount) external payable; + /** * @notice Initiate sending a deposit message to the child chain. * @custom:requires `rootToken` to already be mapped with `mapToken`. @@ -58,23 +85,13 @@ interface IRootERC20Bridge { * @param amount The amount of tokens to deposit. */ function depositTo(IERC20Metadata rootToken, address receiver, uint256 amount) external payable; - - /** - * @notice Initiate sending an ETH deposit message to the child chain. - * @param amount The amount of tokens to deposit. - */ - function depositETH(uint256 amount) external payable; - /** - * @notice Initiate sending an ETH deposit message to the child chain, with a specified receiver. - * @param receiver The address of the receiver on the child chain. - * @param amount The amount of tokens to deposit. - */ - function depositToETH(address receiver, uint256 amount) external payable; } interface IRootERC20BridgeEvents { + /// @notice Emitted when the root chain bridge adaptor is updated. + event RootBridgeAdaptorUpdated(address oldRootBridgeAdaptor, address newRootBridgeAdaptor); /// @notice Emitted when the child chain bridge adaptor is updated. - event NewRootBridgeAdaptor(address oldRootBridgeAdaptor, address newRootBridgeAdaptor); + event ChildBridgeAdaptorUpdated(string oldChildBridgeAdaptor, string newChildBridgeAdaptor); /// @notice Emitted when the IMX deposit limit is updated. event NewImxDepositLimit(uint256 oldImxDepositLimit, uint256 newImxDepositLimit); /// @notice Emitted when a map token message is sent to the child chain. diff --git a/src/root/RootAxelarBridgeAdaptor.sol b/src/root/RootAxelarBridgeAdaptor.sol index cdbc038d..78061547 100644 --- a/src/root/RootAxelarBridgeAdaptor.sol +++ b/src/root/RootAxelarBridgeAdaptor.sol @@ -2,31 +2,27 @@ pragma solidity 0.8.19; import {AxelarExecutable} from "@axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; -import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {IAxelarGateway} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol"; import {IAxelarGasService} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol"; import {IRootERC20BridgeAdaptor} from "../interfaces/root/IRootERC20BridgeAdaptor.sol"; import { IRootAxelarBridgeAdaptorEvents, - IRootAxelarBridgeAdaptorErrors + IRootAxelarBridgeAdaptorErrors, + IRootAxelarBridgeAdaptor } from "../interfaces/root/IRootAxelarBridgeAdaptor.sol"; import {IRootERC20Bridge} from "../interfaces/root/IRootERC20Bridge.sol"; +import {AdaptorRoles} from "../common/AdaptorRoles.sol"; /** * @notice RootAxelarBridgeAdaptor is a bridge adaptor that allows the RootERC20Bridge to communicate with the Axelar Gateway. */ contract RootAxelarBridgeAdaptor is AxelarExecutable, - Initializable, IRootERC20BridgeAdaptor, IRootAxelarBridgeAdaptorEvents, - IRootAxelarBridgeAdaptorErrors + IRootAxelarBridgeAdaptorErrors, + IRootAxelarBridgeAdaptor, + AdaptorRoles { - using SafeERC20 for IERC20Metadata; - IRootERC20Bridge public rootBridge; string public childChain; IAxelarGasService public gasService; @@ -35,23 +31,77 @@ contract RootAxelarBridgeAdaptor is /** * @notice Initilization function for RootAxelarBridgeAdaptor. + * @param newRoles Struct containing addresses of roles. * @param _rootBridge Address of root bridge contract. * @param _childChain Name of child chain. * @param _gasService Address of Axelar Gas Service contract. */ - function initialize(address _rootBridge, string memory _childChain, address _gasService) public initializer { - if (_rootBridge == address(0) || _gasService == address(0)) { + function initialize( + InitializationRoles memory newRoles, + address _rootBridge, + string memory _childChain, + address _gasService + ) public initializer { + if ( + _rootBridge == address(0) || _gasService == address(0) || newRoles.defaultAdmin == address(0) + || newRoles.bridgeManager == address(0) || newRoles.gasServiceManager == address(0) + || newRoles.targetManager == address(0) + ) { revert ZeroAddresses(); } if (bytes(_childChain).length == 0) { revert InvalidChildChain(); } + + __AccessControl_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, newRoles.defaultAdmin); + _grantRole(BRIDGE_MANAGER_ROLE, newRoles.bridgeManager); + _grantRole(GAS_SERVICE_MANAGER_ROLE, newRoles.gasServiceManager); + _grantRole(TARGET_MANAGER_ROLE, newRoles.targetManager); + rootBridge = IRootERC20Bridge(_rootBridge); childChain = _childChain; gasService = IAxelarGasService(_gasService); } + /** + * @inheritdoc IRootAxelarBridgeAdaptor + */ + function updateRootBridge(address newRootBridge) external override onlyRole(BRIDGE_MANAGER_ROLE) { + if (newRootBridge == address(0)) { + revert ZeroAddresses(); + } + + emit RootBridgeUpdated(address(rootBridge), newRootBridge); + rootBridge = IRootERC20Bridge(newRootBridge); + } + + /** + * @inheritdoc IRootAxelarBridgeAdaptor + */ + function updateChildChain(string memory newChildChain) external override onlyRole(TARGET_MANAGER_ROLE) { + if (bytes(newChildChain).length == 0) { + revert InvalidChildChain(); + } + + emit ChildChainUpdated(childChain, newChildChain); + childChain = newChildChain; + } + + /** + * @inheritdoc IRootAxelarBridgeAdaptor + */ + function updateGasService(address newGasService) external override onlyRole(GAS_SERVICE_MANAGER_ROLE) { + if (newGasService == address(0)) { + revert ZeroAddresses(); + } + + emit GasServiceUpdated(address(gasService), newGasService); + gasService = IAxelarGasService(newGasService); + } + /** * @inheritdoc IRootERC20BridgeAdaptor * @notice Sends an arbitrary message to the child chain, via the Axelar network. diff --git a/src/root/RootERC20Bridge.sol b/src/root/RootERC20Bridge.sol index 9c8422e9..75909b5e 100644 --- a/src/root/RootERC20Bridge.sol +++ b/src/root/RootERC20Bridge.sol @@ -175,19 +175,26 @@ contract RootERC20Bridge is IRootERC20Bridge, IRootERC20BridgeEvents, IRootERC20 } /** - * @notice Updates the root bridge adaptor. - * @param newRootBridgeAdaptor Address of new root bridge adaptor. - * @dev Can only be called by ADAPTOR_MANAGER_ROLE. + * @inheritdoc IRootERC20Bridge */ function updateRootBridgeAdaptor(address newRootBridgeAdaptor) external onlyRole(ADAPTOR_MANAGER_ROLE) { if (newRootBridgeAdaptor == address(0)) { revert ZeroAddress(); } - emit NewRootBridgeAdaptor(address(rootBridgeAdaptor), newRootBridgeAdaptor); + emit RootBridgeAdaptorUpdated(address(rootBridgeAdaptor), newRootBridgeAdaptor); rootBridgeAdaptor = IRootERC20BridgeAdaptor(newRootBridgeAdaptor); } - // TODO add updating of child bridge adaptor. Part of SMR-1908 + /** + * @inheritdoc IRootERC20Bridge + */ + function updateChildBridgeAdaptor(string memory newChildBridgeAdaptor) external onlyRole(ADAPTOR_MANAGER_ROLE) { + if (bytes(newChildBridgeAdaptor).length == 0) { + revert InvalidChildERC20BridgeAdaptor(); + } + emit ChildBridgeAdaptorUpdated(childBridgeAdaptor, newChildBridgeAdaptor); + childBridgeAdaptor = newChildBridgeAdaptor; + } /** * @notice Updates the IMX deposit limit. diff --git a/test/integration/child/ChildAxelarBridge.t.sol b/test/integration/child/ChildAxelarBridge.t.sol index b9959237..942d2025 100644 --- a/test/integration/child/ChildAxelarBridge.t.sol +++ b/test/integration/child/ChildAxelarBridge.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {ChildAxelarBridgeAdaptor} from "../../../src/child/ChildAxelarBridgeAdaptor.sol"; +import {ChildAxelarBridgeAdaptor, IChildAxelarBridgeAdaptor} from "../../../src/child/ChildAxelarBridgeAdaptor.sol"; import { ChildERC20Bridge, IChildERC20Bridge, @@ -54,8 +54,16 @@ contract ChildERC20BridgeIntegrationTest is Test, IChildERC20BridgeEvents, IChil WIMX_TOKEN_ADDRESS ); + IChildAxelarBridgeAdaptor.InitializationRoles memory adaptorRoles = IChildAxelarBridgeAdaptor + .InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: address(this), + gasServiceManager: address(this), + targetManager: address(this) + }); + childAxelarBridgeAdaptor.initialize( - ROOT_CHAIN_NAME, address(childERC20Bridge), address(mockChildAxelarGasService) + adaptorRoles, ROOT_CHAIN_NAME, address(childERC20Bridge), address(mockChildAxelarGasService) ); } diff --git a/test/unit/child/ChildAxelarBridgeAdaptor.t.sol b/test/unit/child/ChildAxelarBridgeAdaptor.t.sol index b69850d5..af0463fe 100644 --- a/test/unit/child/ChildAxelarBridgeAdaptor.t.sol +++ b/test/unit/child/ChildAxelarBridgeAdaptor.t.sol @@ -10,8 +10,10 @@ import {MockChildAxelarGateway} from "../../../src/test/child/MockChildAxelarGat import {MockChildAxelarGasService} from "../../../src/test/child/MockChildAxelarGasService.sol"; import { IChildAxelarBridgeAdaptorErrors, - IChildAxelarBridgeAdaptorEvents + IChildAxelarBridgeAdaptorEvents, + IChildAxelarBridgeAdaptor } from "../../../src/interfaces/child/IChildAxelarBridgeAdaptor.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErrors, IChildAxelarBridgeAdaptorEvents { address public GATEWAY_ADDRESS = address(1); @@ -24,28 +26,100 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro MockChildAxelarGateway public mockChildAxelarGateway; MockChildAxelarGasService public mockChildAxelarGasService; + address bridgeManager = makeAddr("bridgeManager"); + address gasServiceManager = makeAddr("gasServiceManager"); + address targetManager = makeAddr("targetManager"); + + IChildAxelarBridgeAdaptor.InitializationRoles roles = IChildAxelarBridgeAdaptor.InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: bridgeManager, + gasServiceManager: gasServiceManager, + targetManager: targetManager + }); + function setUp() public { token = new ERC20PresetMinterPauser("Test", "TST"); mockChildERC20Bridge = new MockChildERC20Bridge(); mockChildAxelarGateway = new MockChildAxelarGateway(); mockChildAxelarGasService = new MockChildAxelarGasService(); axelarAdaptor = new ChildAxelarBridgeAdaptor(address(mockChildAxelarGateway)); - axelarAdaptor.initialize(ROOT_CHAIN_NAME, address(mockChildERC20Bridge), address(mockChildAxelarGasService)); + axelarAdaptor.initialize( + roles, ROOT_CHAIN_NAME, address(mockChildERC20Bridge), address(mockChildAxelarGasService) + ); } + /** + * INITIALIZE + */ + function test_Initialize() public { assertEq(address(axelarAdaptor.childBridge()), address(mockChildERC20Bridge), "childBridge not set"); assertEq(address(axelarAdaptor.gateway()), address(mockChildAxelarGateway), "gateway not set"); assertEq(axelarAdaptor.rootChain(), ROOT_CHAIN_NAME, "rootChain not set"); + assertEq(address(axelarAdaptor.gasService()), address(mockChildAxelarGasService), "gasService not set"); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.BRIDGE_MANAGER_ROLE(), bridgeManager), true, "bridgeManager not set" + ); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.GAS_SERVICE_MANAGER_ROLE(), gasServiceManager), + true, + "gasServiceManager not set" + ); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.TARGET_MANAGER_ROLE(), targetManager), true, "targetManager not set" + ); + } + + function test_RevertIf_InitializeWithZeroAdmin() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(ZeroAddress.selector); + roles.defaultAdmin = address(0); + newAdaptor.initialize(roles, "root", address(mockChildERC20Bridge), address(mockChildAxelarGasService)); + } + + function test_RevertIf_InitializeWithZeroBridgeManager() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(ZeroAddress.selector); + roles.bridgeManager = address(0); + newAdaptor.initialize(roles, "root", address(mockChildERC20Bridge), address(mockChildAxelarGasService)); + } + + function test_RevertIf_InitializeWithZeroGasServiceManager() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(ZeroAddress.selector); + roles.gasServiceManager = address(0); + newAdaptor.initialize(roles, "root", address(mockChildERC20Bridge), address(mockChildAxelarGasService)); + } + + function test_RevertIf_InitializeWithZeroTargetManager() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(ZeroAddress.selector); + roles.targetManager = address(0); + newAdaptor.initialize(roles, "root", address(mockChildERC20Bridge), address(mockChildAxelarGasService)); } - // TODO add more initialize tests function test_RevertIf_InitializeGivenZeroAddress() public { ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); vm.expectRevert(ZeroAddress.selector); - newAdaptor.initialize("root", address(0), address(mockChildAxelarGasService)); + newAdaptor.initialize(roles, "root", address(0), address(mockChildAxelarGasService)); } + function test_RevertIf_InitializeGivenEmptyRootChain() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(InvalidRootChain.selector); + newAdaptor.initialize(roles, "", address(mockChildERC20Bridge), address(mockChildAxelarGasService)); + } + + function test_RevertIf_InitializeGivenZeroGasService() public { + ChildAxelarBridgeAdaptor newAdaptor = new ChildAxelarBridgeAdaptor(GATEWAY_ADDRESS); + vm.expectRevert(ZeroAddress.selector); + newAdaptor.initialize(roles, "root", address(mockChildERC20Bridge), address(0)); + } + + /** + * EXECUTE + */ + function test_Execute_CallsBridge() public { bytes32 commandId = bytes32("testCommandId"); string memory sourceChain = "test"; @@ -72,6 +146,10 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro axelarAdaptor.execute(commandId, sourceChain, sourceAddress, payload); } + /** + * SEND MESSAGE + */ + function test_sendMessage_CallsGasService() public { address refundRecipient = address(123); bytes memory payload = abi.encode(WITHDRAW_SIG, address(token), address(this), address(999), 11111); @@ -188,4 +266,128 @@ contract ChildAxelarBridgeAdaptorUnitTest is Test, IChildAxelarBridgeAdaptorErro vm.prank(address(mockChildERC20Bridge)); axelarAdaptor.sendMessage{value: 0}(payload, address(123)); } + + /** + * UPDATE CHILD BRIDGE + */ + + function test_updateChildBridge_UpdatesChildBridge() public { + vm.startPrank(bridgeManager); + address newChildBridge = address(0x123); + axelarAdaptor.updateChildBridge(newChildBridge); + assertEq(address(axelarAdaptor.childBridge()), newChildBridge, "childBridge not updated"); + vm.stopPrank(); + } + + function test_updateChildBridge_EmitsChildBridgeUpdatedEvent() public { + vm.startPrank(bridgeManager); + address newChildBridge = address(0x123); + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit ChildBridgeUpdated(address(mockChildERC20Bridge), newChildBridge); + axelarAdaptor.updateChildBridge(newChildBridge); + vm.stopPrank(); + } + + function test_RevertIf_updateChildBridgeCalledByNonBridgeManager() public { + bytes32 role = axelarAdaptor.BRIDGE_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + address newChildBridge = address(0x123); + axelarAdaptor.updateChildBridge(newChildBridge); + } + + function test_RevertIf_updateChildBridgeCalledWithZeroAddress() public { + vm.startPrank(bridgeManager); + vm.expectRevert(ZeroAddress.selector); + axelarAdaptor.updateChildBridge(address(0)); + vm.stopPrank(); + } + + /** + * UPDATE ROOT CHAIN + */ + + function test_updateRootChain_UpdatesRootChain() public { + vm.startPrank(targetManager); + string memory newRootChain = "newRoot"; + axelarAdaptor.updateRootChain(newRootChain); + assertEq(axelarAdaptor.rootChain(), newRootChain, "rootChain not updated"); + vm.stopPrank(); + } + + function test_updateRootChain_EmitsRootChainUpdatedEvent() public { + vm.startPrank(targetManager); + string memory newRootChain = "newRoot"; + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit RootChainUpdated(ROOT_CHAIN_NAME, newRootChain); + axelarAdaptor.updateRootChain(newRootChain); + vm.stopPrank(); + } + + function test_RevertIf_updateRootChainCalledByNonTargetManager() public { + bytes32 role = axelarAdaptor.TARGET_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + axelarAdaptor.updateRootChain("newRoot"); + } + + function test_RevertIf_updateRootChainCalledWithEmptyRootChain() public { + vm.startPrank(targetManager); + vm.expectRevert(InvalidRootChain.selector); + axelarAdaptor.updateRootChain(""); + vm.stopPrank(); + } + + /** + * UPDATE GAS SERVICE + */ + + function test_updateGasService_UpdatesGasService() public { + vm.startPrank(gasServiceManager); + address newGasService = address(0x123); + axelarAdaptor.updateGasService(newGasService); + assertEq(address(axelarAdaptor.gasService()), newGasService, "gasService not updated"); + vm.stopPrank(); + } + + function test_updateGasService_EmitsGasServiceUpdatedEvent() public { + vm.startPrank(gasServiceManager); + address newGasService = address(0x123); + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit GasServiceUpdated(address(mockChildAxelarGasService), newGasService); + axelarAdaptor.updateGasService(newGasService); + vm.stopPrank(); + } + + function test_RevertIf_updateGasServiceCalledByNonGasServiceManager() public { + bytes32 role = axelarAdaptor.GAS_SERVICE_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + axelarAdaptor.updateGasService(address(0)); + } + + function test_RevertIf_updateGasServiceCalledWithZeroAddress() public { + vm.startPrank(gasServiceManager); + vm.expectRevert(ZeroAddress.selector); + axelarAdaptor.updateGasService(address(0)); + vm.stopPrank(); + } } diff --git a/test/unit/child/ChildERC20Bridge.t.sol b/test/unit/child/ChildERC20Bridge.t.sol index 28f1befe..eaf1e83a 100644 --- a/test/unit/child/ChildERC20Bridge.t.sol +++ b/test/unit/child/ChildERC20Bridge.t.sol @@ -101,6 +101,10 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B 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"); + assertTrue(childBridge.hasRole(childBridge.ADAPTOR_MANAGER_ROLE(), address(this)), "adaptorManager not set"); + assertTrue(childBridge.hasRole(childBridge.PAUSER_ROLE(), pauser), "pauser not set"); + assertTrue(childBridge.hasRole(childBridge.UNPAUSER_ROLE(), unpauser), "unpauser not set"); + assertTrue(childBridge.hasRole(childBridge.ADAPTOR_MANAGER_ROLE(), address(this)), "adaptorManager not set"); assertFalse(address(childBridge.childETHToken()) == address(0), "childETHToken not set"); assertFalse(address(childBridge.childETHToken()).code.length == 0, "childETHToken contract empty"); } @@ -260,6 +264,92 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); } + /** + * UPDATE CHILD BRIDGE ADAPTOR + */ + + function test_updateChildBridgeAdaptor_UpdatesChildBridgeAdaptor() public { + address newAdaptorAddress = address(0x11111); + + assertEq(address(childBridge.bridgeAdaptor()), address(this), "bridgeAdaptor not set"); + childBridge.updateChildBridgeAdaptor(newAdaptorAddress); + assertEq(address(childBridge.bridgeAdaptor()), newAdaptorAddress, "bridgeAdaptor not updated"); + } + + function test_updateChildBridgeAdpator_EmitsEvent() public { + address newAdaptorAddress = address(0x11111); + + vm.expectEmit(true, true, false, false, address(childBridge)); + emit ChildBridgeAdaptorUpdated(address(childBridge.bridgeAdaptor()), newAdaptorAddress); + + childBridge.updateChildBridgeAdaptor(newAdaptorAddress); + } + + function test_RevertIf_updateChildBridgeAdaptorCalledByNotAdaptorManager() public { + address caller = address(0xf00f00); + bytes32 role = childBridge.ADAPTOR_MANAGER_ROLE(); + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(caller), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + childBridge.updateChildBridgeAdaptor(address(0x11111)); + } + + function test_RevertIf_updateChildBridgeAdaptorCalledWithZeroAddress() public { + vm.expectRevert(ZeroAddress.selector); + childBridge.updateChildBridgeAdaptor(address(0)); + } + + /** + * UPDATE ROOT BRIDGE ADAPTOR + */ + + function test_updateRootBridgeAdaptor_UpdatesRootBridgeAdaptor() public { + string memory newAdaptor = "newAdaptor"; + + assertEq(childBridge.rootERC20BridgeAdaptor(), ROOT_BRIDGE_ADAPTOR, "rootERC20BridgeAdaptor not set"); + childBridge.updateRootBridgeAdaptor(newAdaptor); + assertEq(childBridge.rootERC20BridgeAdaptor(), newAdaptor, "rootERC20BridgeAdaptor not updated"); + } + + function test_updateRootBridgeAdaptor_EmitsEvent() public { + string memory newAdaptor = "newAdaptor"; + + vm.expectEmit(true, true, false, false, address(childBridge)); + emit RootBridgeAdaptorUpdated(childBridge.rootERC20BridgeAdaptor(), newAdaptor); + + childBridge.updateRootBridgeAdaptor(newAdaptor); + } + + function test_RevertIf_updateRootBridgeAdaptorCalledByNotAdaptorManager() public { + address caller = address(0xf00f00); + bytes32 role = childBridge.ADAPTOR_MANAGER_ROLE(); + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(caller), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + childBridge.updateRootBridgeAdaptor("newAdaptor"); + } + + function test_RevertIf_updateRootBridgeAdaptorCalledWithEmptyString() public { + vm.expectRevert(InvalidRootERC20BridgeAdaptor.selector); + childBridge.updateRootBridgeAdaptor(""); + } + + /** + * ON MESSAGE RECIEVE + */ + function test_onMessageReceive_SetsTokenMapping() public { address predictedChildToken = Clones.predictDeterministicAddress( address(childTokenTemplate), keccak256(abi.encodePacked(rootToken)), address(childBridge) @@ -363,36 +453,6 @@ contract ChildERC20BridgeUnitTest is Test, IChildERC20BridgeEvents, IChildERC20B childBridge.onMessageReceive(ROOT_CHAIN_NAME, ROOT_BRIDGE_ADAPTOR, data); } - function test_updateBridgeAdaptor() public { - address newAdaptorAddress = address(0x11111); - - assertEq(address(childBridge.bridgeAdaptor()), address(this), "bridgeAdaptor not set"); - vm.expectEmit(true, true, true, true); - emit BridgeAdaptorUpdated(address(childBridge.bridgeAdaptor()), newAdaptorAddress); - childBridge.updateBridgeAdaptor(newAdaptorAddress); - assertEq(address(childBridge.bridgeAdaptor()), newAdaptorAddress, "bridgeAdaptor not updated"); - } - - function test_RevertIf_updateBridgeAdaptorCalledByNotAdaptorManager() public { - address caller = address(0xf00f00); - bytes32 role = childBridge.ADAPTOR_MANAGER_ROLE(); - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - StringsUpgradeable.toHexString(caller), - " is missing role ", - StringsUpgradeable.toHexString(uint256(role), 32) - ) - ); - childBridge.updateBridgeAdaptor(address(0x11111)); - } - - function test_RevertIf_updateBridgeAdaptorCalledWithZeroAddress() public { - vm.expectRevert(ZeroAddress.selector); - childBridge.updateBridgeAdaptor(address(0)); - } - /** * DEPOSIT ETH */ diff --git a/test/unit/common/AdaptorRoles.t.sol b/test/unit/common/AdaptorRoles.t.sol new file mode 100644 index 00000000..5cc11985 --- /dev/null +++ b/test/unit/common/AdaptorRoles.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "forge-std/Test.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; +import {MockAdaptorRoles} from "./MockAdaptorRoles.sol"; + +contract Setup is Test { + address admin = makeAddr("admin"); + address bridgeManager = makeAddr("bridgeManager"); + address gasServiceManager = makeAddr("gasServiceManager"); + address targetManager = makeAddr("targetManager"); + + MockAdaptorRoles mockAdaptorRoles; + + function setUp() public { + mockAdaptorRoles = new MockAdaptorRoles(admin); + } +} + +contract AdaptorRoles is Setup { + function grantRoles() internal { + vm.startPrank(admin); + mockAdaptorRoles.grantBridgeManager(bridgeManager); + mockAdaptorRoles.grantGasServiceManager(gasServiceManager); + mockAdaptorRoles.grantTargetManagerRole(targetManager); + vm.stopPrank(); + } + + function revokeRoles() internal { + vm.startPrank(admin); + mockAdaptorRoles.revokeBridgeManagerRole(bridgeManager); + mockAdaptorRoles.revokeGasServiceManagerRole(gasServiceManager); + mockAdaptorRoles.revokeTargetMangaerRole(targetManager); + vm.stopPrank(); + } + + function test_grantRoles() public { + grantRoles(); + assert(mockAdaptorRoles.hasRole(mockAdaptorRoles.BRIDGE_MANAGER_ROLE(), bridgeManager)); + assert(mockAdaptorRoles.hasRole(mockAdaptorRoles.GAS_SERVICE_MANAGER_ROLE(), gasServiceManager)); + assert(mockAdaptorRoles.hasRole(mockAdaptorRoles.TARGET_MANAGER_ROLE(), targetManager)); + } + + function test_revokeRoles() public { + grantRoles(); + revokeRoles(); + assert(!mockAdaptorRoles.hasRole(mockAdaptorRoles.BRIDGE_MANAGER_ROLE(), bridgeManager)); + assert(!mockAdaptorRoles.hasRole(mockAdaptorRoles.GAS_SERVICE_MANAGER_ROLE(), gasServiceManager)); + assert(!mockAdaptorRoles.hasRole(mockAdaptorRoles.TARGET_MANAGER_ROLE(), targetManager)); + } +} diff --git a/test/unit/common/MockAdaptorRoles.sol b/test/unit/common/MockAdaptorRoles.sol new file mode 100644 index 00000000..ebd2da28 --- /dev/null +++ b/test/unit/common/MockAdaptorRoles.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "../../../src/common/AdaptorRoles.sol"; + +contract MockAdaptorRoles is AdaptorRoles { + constructor(address admin) { + _setupRole(DEFAULT_ADMIN_ROLE, admin); + } +} diff --git a/test/unit/root/RootAxelarBridgeAdaptor.t.sol b/test/unit/root/RootAxelarBridgeAdaptor.t.sol index d5744f41..f9827f80 100644 --- a/test/unit/root/RootAxelarBridgeAdaptor.t.sol +++ b/test/unit/root/RootAxelarBridgeAdaptor.t.sol @@ -10,9 +10,11 @@ import {MockAxelarGasService} from "../../../src/test/root/MockAxelarGasService. import { RootAxelarBridgeAdaptor, IRootAxelarBridgeAdaptorEvents, - IRootAxelarBridgeAdaptorErrors + IRootAxelarBridgeAdaptorErrors, + IRootAxelarBridgeAdaptor } from "../../../src/root/RootAxelarBridgeAdaptor.sol"; import {StubRootBridge} from "../../../src/test/root/StubRootBridge.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IRootAxelarBridgeAdaptorErrors { address constant CHILD_BRIDGE = address(3); @@ -26,6 +28,17 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR MockAxelarGasService public axelarGasService; StubRootBridge public stubRootBridge; + address bridgeManager = makeAddr("bridgeManager"); + address gasServiceManager = makeAddr("gasServiceManager"); + address targetManager = makeAddr("targetManager"); + + IRootAxelarBridgeAdaptor.InitializationRoles public roles = IRootAxelarBridgeAdaptor.InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: bridgeManager, + gasServiceManager: gasServiceManager, + targetManager: targetManager + }); + function setUp() public { token = new ERC20PresetMinterPauser("Test", "TST"); mockAxelarGateway = new MockAxelarGateway(); @@ -34,29 +47,77 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR childBridgeAdaptor = stubRootBridge.childBridgeAdaptor(); axelarAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); - axelarAdaptor.initialize(address(stubRootBridge), CHILD_CHAIN_NAME, address(axelarGasService)); + axelarAdaptor.initialize(roles, address(stubRootBridge), CHILD_CHAIN_NAME, address(axelarGasService)); vm.deal(address(stubRootBridge), 99999999999); } + /** + * INITIALIZE + */ + function test_Initialize() public { assertEq(address(axelarAdaptor.rootBridge()), address(stubRootBridge), "rootBridge not set"); assertEq(axelarAdaptor.childChain(), CHILD_CHAIN_NAME, "childChain not set"); assertEq(address(axelarAdaptor.gateway()), address(mockAxelarGateway), "axelarGateway not set"); assertEq(address(axelarAdaptor.gasService()), address(axelarGasService), "axelarGasService not set"); + assertEq(axelarAdaptor.hasRole(axelarAdaptor.DEFAULT_ADMIN_ROLE(), address(this)), true, "defaultAdmin not set"); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.BRIDGE_MANAGER_ROLE(), bridgeManager), true, "bridgeManager not set" + ); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.GAS_SERVICE_MANAGER_ROLE(), gasServiceManager), + true, + "gasServiceManager not set" + ); + assertEq( + axelarAdaptor.hasRole(axelarAdaptor.TARGET_MANAGER_ROLE(), targetManager), true, "targetManager not set" + ); + } + + function test_RevertIf_InitializeWithZeroAdmin() public { + RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); + vm.expectRevert(ZeroAddresses.selector); + roles.defaultAdmin = address(0); + newAdaptor.initialize(roles, address(this), CHILD_CHAIN_NAME, address(axelarGasService)); + } + + function test_RevertIf_InitializeWithZeroBridgeManager() public { + RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); + vm.expectRevert(ZeroAddresses.selector); + roles.bridgeManager = address(0); + newAdaptor.initialize(roles, address(this), CHILD_CHAIN_NAME, address(axelarGasService)); } - function test_RevertWhen_InitializeGivenZeroAddress() public { + function test_RevertIf_InitializeWithZeroGasServiceManager() public { RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); vm.expectRevert(ZeroAddresses.selector); - newAdaptor.initialize(address(0), CHILD_CHAIN_NAME, address(axelarGasService)); + roles.gasServiceManager = address(0); + newAdaptor.initialize(roles, address(this), CHILD_CHAIN_NAME, address(axelarGasService)); } - function test_RevertWhen_InitializeGivenEmptyChildChainName() public { + function test_RevertIf_InitializeWithZeroTargetManager() public { + RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); + vm.expectRevert(ZeroAddresses.selector); + roles.targetManager = address(0); + newAdaptor.initialize(roles, address(this), CHILD_CHAIN_NAME, address(axelarGasService)); + } + + function test_RevertIf_InitializeGivenZeroAddress() public { + RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); + vm.expectRevert(ZeroAddresses.selector); + newAdaptor.initialize(roles, address(0), CHILD_CHAIN_NAME, address(axelarGasService)); + } + + function test_RevertIf_InitializeGivenEmptyChildChainName() public { RootAxelarBridgeAdaptor newAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); vm.expectRevert(InvalidChildChain.selector); - newAdaptor.initialize(address(this), "", address(axelarGasService)); + newAdaptor.initialize(roles, address(this), "", address(axelarGasService)); } + /** + * EXECUTE + */ + function test_Execute_CallsBridge() public { bytes32 commandId = bytes32("testCommandId"); string memory sourceChain = "test"; @@ -83,6 +144,10 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR axelarAdaptor.execute(commandId, sourceChain, sourceAddress, payload); } + /** + * SEND MESSAGE + */ + /// @dev For this unit test we just want to make sure the correct functions are called on the Axelar Gateway and Gas Service. function test_sendMessage_CallsGasService() public { address refundRecipient = address(123); @@ -171,6 +236,10 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR axelarAdaptor.sendMessage{value: callValue}(payload, refundRecipient); } + /** + * MAP TOKEN + */ + function test_RevertIf_mapTokenCalledByNonRootBridge() public { address payable prankster = payable(address(0x33)); uint256 value = 300; @@ -189,4 +258,124 @@ contract RootAxelarBridgeAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IR vm.prank(address(stubRootBridge)); axelarAdaptor.sendMessage{value: 0}(payload, address(123)); } + + /** + * UPDATE ROOT BRIDGE + */ + + function test_updateRootBridge_UpdatesRootBridge() public { + vm.startPrank(bridgeManager); + address newRootBridge = address(0x3333); + axelarAdaptor.updateRootBridge(newRootBridge); + assertEq(address(axelarAdaptor.rootBridge()), newRootBridge, "rootBridge not updated"); + vm.stopPrank(); + } + + function test_updateRootBridge_EmitsEvent() public { + vm.startPrank(bridgeManager); + address newRootBridge = address(0x3333); + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit RootBridgeUpdated(address(stubRootBridge), newRootBridge); + axelarAdaptor.updateRootBridge(newRootBridge); + vm.stopPrank(); + } + + function test_RevertsIf_updateRootBridgeCalledByNonBridgeManager() public { + bytes32 role = axelarAdaptor.BRIDGE_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + axelarAdaptor.updateRootBridge(address(0x3333)); + } + + function test_RevertsIf_updateRootBridgeCalledWithZeroAddress() public { + vm.startPrank(bridgeManager); + vm.expectRevert(ZeroAddresses.selector); + axelarAdaptor.updateRootBridge(address(0)); + } + + /** + * UPDATE CHILD CHAIN + */ + + function test_updateChildChain_UpdatesChildChain() public { + vm.startPrank(targetManager); + string memory newChildChain = "newChildChain"; + axelarAdaptor.updateChildChain(newChildChain); + assertEq(axelarAdaptor.childChain(), newChildChain, "childChain not updated"); + vm.stopPrank(); + } + + function test_updateChildChain_EmitsEvent() public { + vm.startPrank(targetManager); + string memory newChildChain = "newChildChain"; + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit ChildChainUpdated(CHILD_CHAIN_NAME, newChildChain); + axelarAdaptor.updateChildChain(newChildChain); + vm.stopPrank(); + } + + function test_RevertsIf_updateChildChainCalledByNonTargetManager() public { + bytes32 role = axelarAdaptor.TARGET_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + axelarAdaptor.updateChildChain("newChildChain"); + } + + function test_RevertsIf_updateChildChainCalledWithEmptyString() public { + vm.startPrank(targetManager); + vm.expectRevert(InvalidChildChain.selector); + axelarAdaptor.updateChildChain(""); + } + + /** + * UPDATE GAS SERVICE + */ + + function test_updateGasService_UpdatesGasService() public { + vm.startPrank(gasServiceManager); + address newGasService = address(0x3333); + axelarAdaptor.updateGasService(newGasService); + assertEq(address(axelarAdaptor.gasService()), newGasService, "gasService not updated"); + vm.stopPrank(); + } + + function test_updateGasService_EmitsEvent() public { + vm.startPrank(gasServiceManager); + address newGasService = address(0x3333); + vm.expectEmit(true, true, false, false, address(axelarAdaptor)); + emit GasServiceUpdated(address(axelarGasService), newGasService); + axelarAdaptor.updateGasService(newGasService); + vm.stopPrank(); + } + + function test_RevertsIf_updateGasServiceCalledByNonGasServiceManager() public { + bytes32 role = axelarAdaptor.GAS_SERVICE_MANAGER_ROLE(); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(address(this)), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + axelarAdaptor.updateGasService(address(0x3333)); + } + + function test_RevertsIf_updateGasServiceCalledWithZeroAddress() public { + vm.startPrank(gasServiceManager); + vm.expectRevert(ZeroAddresses.selector); + axelarAdaptor.updateGasService(address(0)); + } } diff --git a/test/unit/root/RootERC20Bridge.t.sol b/test/unit/root/RootERC20Bridge.t.sol index 254cbab5..7e061e54 100644 --- a/test/unit/root/RootERC20Bridge.t.sol +++ b/test/unit/root/RootERC20Bridge.t.sol @@ -597,11 +597,11 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid assertEq(address(rootBridge.rootBridgeAdaptor()), newAdaptorAddress, "bridgeAdaptor not updated"); } - function test_updateRootBridgeAdaptor_EmitsNewRootBridgeAdaptorEvent() public { + function test_updateRootBridgeAdaptor_EmitsRootBridgeAdaptorUpdatedEvent() public { address newAdaptorAddress = address(0x11111); vm.expectEmit(); - emit NewRootBridgeAdaptor(address(rootBridge.rootBridgeAdaptor()), newAdaptorAddress); + emit RootBridgeAdaptorUpdated(address(rootBridge.rootBridgeAdaptor()), newAdaptorAddress); rootBridge.updateRootBridgeAdaptor(newAdaptorAddress); } @@ -626,6 +626,43 @@ contract RootERC20BridgeUnitTest is Test, IRootERC20BridgeEvents, IRootERC20Brid rootBridge.updateRootBridgeAdaptor(address(0)); } + function test_updateChildBridgeAdaptor_UpdatesChildBridgeAdaptor() public { + string memory newAdaptorAddress = Strings.toHexString(address(0x11111)); + + assertEq(rootBridge.childBridgeAdaptor(), CHILD_BRIDGE_ADAPTOR_STRING, "bridgeAdaptor not set"); + rootBridge.updateChildBridgeAdaptor(newAdaptorAddress); + assertEq(rootBridge.childBridgeAdaptor(), newAdaptorAddress, "bridgeAdaptor not updated"); + } + + function test_updateChildBridgeAdaptor_EmitsEvent() public { + string memory newAdaptorAddress = Strings.toHexString(address(0x11111)); + + vm.expectEmit(true, true, false, false, address(rootBridge)); + emit ChildBridgeAdaptorUpdated(rootBridge.childBridgeAdaptor(), newAdaptorAddress); + + rootBridge.updateChildBridgeAdaptor(newAdaptorAddress); + } + + function test_RevertsIf_updateChildBridgeAdaptorCalledByNonAdaptorManager() public { + address caller = address(0xf00f00); + bytes32 role = rootBridge.ADAPTOR_MANAGER_ROLE(); + vm.prank(caller); + vm.expectRevert( + abi.encodePacked( + "AccessControl: account ", + StringsUpgradeable.toHexString(caller), + " is missing role ", + StringsUpgradeable.toHexString(uint256(role), 32) + ) + ); + rootBridge.updateChildBridgeAdaptor(Strings.toHexString(address(0x11111))); + } + + function test_RevertsIf_updatreChildBridgeAdaptorCalledWithEmptyString() public { + vm.expectRevert(InvalidChildERC20BridgeAdaptor.selector); + rootBridge.updateChildBridgeAdaptor(""); + } + /** * DEPOSIT ETH */ diff --git a/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol b/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol index 383f4fd1..0b03e983 100644 --- a/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol +++ b/test/unit/root/flowrate/RootERC20BridgeFlowRate.t.sol @@ -434,7 +434,7 @@ contract RootERC20BridgeFlowRateUnitTest is function testUnpauseBadAuth() public { vm.startPrank(pauseAdmin); rootBridgeFlowRate.pause(); - vm.startPrank(nonAdmin); + changePrank(nonAdmin); vm.expectRevert( abi.encodePacked( "AccessControl: account ", diff --git a/test/unit/root/withdrawals/RootAxelarBridgeAdaptorWithdraw.t.sol b/test/unit/root/withdrawals/RootAxelarBridgeAdaptorWithdraw.t.sol index 9f1cfdb2..3630d460 100644 --- a/test/unit/root/withdrawals/RootAxelarBridgeAdaptorWithdraw.t.sol +++ b/test/unit/root/withdrawals/RootAxelarBridgeAdaptorWithdraw.t.sol @@ -9,7 +9,8 @@ import {MockAxelarGasService} from "../../../../src/test/root/MockAxelarGasServi import { RootAxelarBridgeAdaptor, IRootAxelarBridgeAdaptorEvents, - IRootAxelarBridgeAdaptorErrors + IRootAxelarBridgeAdaptorErrors, + IRootAxelarBridgeAdaptor } from "../../../../src/root/RootAxelarBridgeAdaptor.sol"; import {StubRootBridge} from "../../../../src/test/root/StubRootBridge.sol"; @@ -25,6 +26,13 @@ contract RootAxelarBridgeWithdrawAdaptorTest is Test, IRootAxelarBridgeAdaptorEv MockAxelarGasService public axelarGasService; StubRootBridge public stubRootBridge; + IRootAxelarBridgeAdaptor.InitializationRoles public roles = IRootAxelarBridgeAdaptor.InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: address(this), + gasServiceManager: address(this), + targetManager: address(this) + }); + function setUp() public { token = new ERC20PresetMinterPauser("Test", "TST"); mockAxelarGateway = new MockAxelarGateway(); @@ -33,7 +41,7 @@ contract RootAxelarBridgeWithdrawAdaptorTest is Test, IRootAxelarBridgeAdaptorEv childBridgeAdaptor = stubRootBridge.childBridgeAdaptor(); axelarAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway)); - axelarAdaptor.initialize(address(stubRootBridge), CHILD_CHAIN_NAME, address(axelarGasService)); + axelarAdaptor.initialize(roles, address(stubRootBridge), CHILD_CHAIN_NAME, address(axelarGasService)); vm.deal(address(stubRootBridge), 99999999999); } diff --git a/test/utils.t.sol b/test/utils.t.sol index ca1de2e1..f730cb13 100644 --- a/test/utils.t.sol +++ b/test/utils.t.sol @@ -9,7 +9,7 @@ import {MockAxelarGasService} from "../src/test/root/MockAxelarGasService.sol"; import {RootERC20Bridge, IERC20Metadata} from "../src/root/RootERC20Bridge.sol"; import {RootERC20BridgeFlowRate} from "../src/root//flowrate/RootERC20BridgeFlowRate.sol"; import {ChildERC20Bridge, IChildERC20Bridge} from "../src/child/ChildERC20Bridge.sol"; -import {ChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; +import {ChildAxelarBridgeAdaptor, IChildAxelarBridgeAdaptor} from "../src/child/ChildAxelarBridgeAdaptor.sol"; import {WETH} from "../src/test/root/WETH.sol"; import {IWETH} from "../src/interfaces/root/IWETH.sol"; import {WIMX} from "../src/child/WIMX.sol"; @@ -17,6 +17,7 @@ import {WIMX} from "../src/child/WIMX.sol"; import {IChildERC20, ChildERC20} from "../src/child/ChildERC20.sol"; import {IRootERC20Bridge} from "../src/root/RootERC20Bridge.sol"; import {RootAxelarBridgeAdaptor} from "../src/root/RootAxelarBridgeAdaptor.sol"; +import {IRootAxelarBridgeAdaptor} from "../src/interfaces/root/IRootAxelarBridgeAdaptor.sol"; interface IPausable { function pause() external; @@ -77,7 +78,16 @@ contract Utils is Test { childBridge.initialize( roles, address(childBridgeAdaptor), rootAdaptor, address(childTokenTemplate), "ROOT", rootIMX, childWIMX ); - childBridgeAdaptor.initialize("ROOT", address(childBridge), address(axelarGasService)); + + IChildAxelarBridgeAdaptor.InitializationRoles memory adaptorRoles = IChildAxelarBridgeAdaptor + .InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: address(this), + gasServiceManager: address(this), + targetManager: address(this) + }); + + childBridgeAdaptor.initialize(adaptorRoles, "ROOT", address(childBridge), address(axelarGasService)); bytes memory mapTokenData = abi.encode(MAP_TOKEN_SIG, rootToken, "TEST NAME", "TNM", 18); vm.prank(address(childBridgeAdaptor)); @@ -140,8 +150,18 @@ contract Utils is Test { address(this) ); + IRootAxelarBridgeAdaptor.InitializationRoles memory adaptorRoles = IRootAxelarBridgeAdaptor.InitializationRoles({ + defaultAdmin: address(this), + bridgeManager: address(this), + gasServiceManager: address(this), + targetManager: address(this) + }); + integrationTest.axelarAdaptor.initialize( - address(integrationTest.rootBridgeFlowRate), childBridgeName, address(integrationTest.axelarGasService) + adaptorRoles, + address(integrationTest.rootBridgeFlowRate), + childBridgeName, + address(integrationTest.axelarGasService) ); }