Skip to content

Commit

Permalink
[SMR-1908] Setters (#43)
Browse files Browse the repository at this point in the history
* rebase and fix prank in tests

* readd tests

* move function to rootbridge interface

* add adaptor roles

* make rootchain tests pass with adaptorroles

* add root adaptor functions

* add rootchain tests

* integrate adaptor roles into child bridge adaptor

* integrate update rootbridge adaptor for child chain

* forge fmt

* PR fixes

* clean up PR for review

* fmt

* fix test name
  • Loading branch information
tsnewnami authored Nov 20, 2023
1 parent a5a9f06 commit 421221e
Show file tree
Hide file tree
Showing 22 changed files with 1,007 additions and 106 deletions.
10 changes: 2 additions & 8 deletions script/DeployRootContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -35,21 +35,15 @@ 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),
""
);

RootAxelarBridgeAdaptor rootBridgeAdaptorImplementation = new RootAxelarBridgeAdaptor(rootGateway);
rootBridgeAdaptorImplementation.initialize(address(rootERC20BridgeImplementation), "Filler", address(1));

TransparentUpgradeableProxy rootBridgeAdaptorProxy = new TransparentUpgradeableProxy(
address(rootBridgeAdaptorImplementation),
Expand Down
18 changes: 16 additions & 2 deletions script/InitializeChildContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +13,9 @@ struct InitializeChildContractsParams {
address childAdminAddress;
address childPauserAddress;
address childUnpauserAddress;
address childBridgeManager;
address childGasServiceManager;
address childTargetManager;
ChildERC20Bridge childERC20Bridge;
ChildAxelarBridgeAdaptor childAxelarBridgeAdaptor;
address childTokenTemplate;
Expand All @@ -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"),
Expand Down Expand Up @@ -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();
Expand Down
19 changes: 16 additions & 3 deletions script/InitializeRootContracts.s.sol
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -17,6 +16,9 @@ struct InitializeRootContractsParams {
address rootAdminAddress;
address rootPauserAddress;
address rootUnpauserAddress;
address rootBridgeManagerAddress;
address rootGasManagerAddress;
address rootTargetManagerAddress;
RootERC20Bridge rootERC20Bridge;
RootAxelarBridgeAdaptor rootBridgeAdaptor;
address rootChainChildTokenTemplate;
Expand All @@ -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"),
Expand Down Expand Up @@ -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
Expand Down
73 changes: 65 additions & 8 deletions src/child/ChildAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
17 changes: 15 additions & 2 deletions src/child/ChildERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
66 changes: 66 additions & 0 deletions src/common/AdaptorRoles.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
39 changes: 39 additions & 0 deletions src/interfaces/child/IChildAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,57 @@
// 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();
/// @notice Error when a message is sent with no gas payment.
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 {
/// @notice Emitted when an Axelar message is sent to the root chain.
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);
}
Loading

0 comments on commit 421221e

Please sign in to comment.