Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract GMP specific logic out of bridge contracts #51

Merged
merged 8 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions script/InitializeChildContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct InitializeChildContractsParams {
ChildERC20Bridge childERC20Bridge;
ChildAxelarBridgeAdaptor childAxelarBridgeAdaptor;
address childTokenTemplate;
address rootERC20BridgeAdaptor;
address rootBridgeAdaptor;
string rootChainName;
address rootIMXToken;
address wIMXToken;
Expand All @@ -43,7 +43,7 @@ contract InitializeChildContracts is Script {
childERC20Bridge: ChildERC20Bridge(payable(vm.envAddress("CHILD_ERC20_BRIDGE"))),
childAxelarBridgeAdaptor: ChildAxelarBridgeAdaptor(vm.envAddress("CHILD_BRIDGE_ADAPTOR")),
childTokenTemplate: vm.envAddress("CHILDCHAIN_CHILD_TOKEN_TEMPLATE"),
rootERC20BridgeAdaptor: vm.envAddress("ROOT_BRIDGE_ADAPTOR"),
rootBridgeAdaptor: vm.envAddress("ROOT_BRIDGE_ADAPTOR"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QQ, with the introduction of @wcgcyx's scripts, what purpose do these forge scripts hold?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I believe @wcgcyx PR will remove these scripts once it is merged.

rootChainName: vm.envString("ROOT_CHAIN_NAME"),
rootIMXToken: vm.envAddress("ROOT_IMX_ADDRESS"),
wIMXToken: vm.envAddress("CHILD_WIMX_ADDRESS"),
Expand All @@ -55,7 +55,7 @@ contract InitializeChildContracts is Script {
/**
* INITIALIZE CHILD CONTRACTS
*/
string[] memory checksumInputs = Utils.getChecksumInputs(params.rootERC20BridgeAdaptor);
string[] memory checksumInputs = Utils.getChecksumInputs(params.rootBridgeAdaptor);
bytes memory checksumOutput = vm.ffi(checksumInputs);
string memory rootBridgeAdaptorString = string(Utils.removeZeroByteValues(checksumOutput));

Expand All @@ -72,9 +72,7 @@ contract InitializeChildContracts is Script {
params.childERC20Bridge.initialize(
roles,
address(params.childAxelarBridgeAdaptor),
rootBridgeAdaptorString,
params.childTokenTemplate,
params.rootChainName,
params.rootIMXToken,
params.wIMXToken
);
Expand All @@ -88,7 +86,11 @@ contract InitializeChildContracts is Script {
});

params.childAxelarBridgeAdaptor.initialize(
adaptorRoles, params.rootChainName, address(params.childERC20Bridge), params.childGasService
adaptorRoles,
address(params.childERC20Bridge),
params.rootChainName,
rootBridgeAdaptorString,
params.childGasService
);

vm.stopBroadcast();
Expand Down
3 changes: 1 addition & 2 deletions script/InitializeRootContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,9 @@ contract InitializeRootContracts is Script {
roles,
address(params.rootBridgeAdaptor),
params.childERC20Bridge,
childBridgeAdaptorChecksum,
params.rootChainChildTokenTemplate,
params.rootIMXToken,
params.rootWETHToken,
params.childChainName,
params.initialIMXCumulativeDepositLimit
);

Expand All @@ -97,6 +95,7 @@ contract InitializeRootContracts is Script {
adaptorRoles,
address(params.rootERC20Bridge), // root bridge
params.childChainName, // child chain name
childBridgeAdaptorChecksum,
params.rootGasService // axelar gas service
);

Expand Down
106 changes: 76 additions & 30 deletions src/child/ChildAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity 0.8.19;

import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {AxelarExecutable} from "@axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
import {IAxelarGasService} from "@axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol";
import {IChildERC20Bridge} from "../interfaces/child/IChildERC20Bridge.sol";
Expand All @@ -10,11 +11,12 @@ import {
IChildAxelarBridgeAdaptorEvents,
IChildAxelarBridgeAdaptor
} from "../interfaces/child/IChildAxelarBridgeAdaptor.sol";
import {IChildERC20BridgeAdaptor} from "../interfaces/child/IChildERC20BridgeAdaptor.sol";
import {IChildBridgeAdaptor} from "../interfaces/child/IChildBridgeAdaptor.sol";
import {AdaptorRoles} from "../common/AdaptorRoles.sol";

/**
* @notice Facilitates communication between the ChildERC20Bridge and the Axelar Gateway. It enables sending and receiving messages to and from the root chain.
* @notice Facilitates communication between the ChildERC20Bridge and the Axelar core contracts,to send and receive messages to and from the root chain.
* @dev The contract ensures that any delivered message originated from the registered root chain and bridge adapter contract on the root chain. It will reject all other messages.
* @dev Features:
* - Send messages to the root chain via the Axelar Gateway.
* - Receive messages from the root chain via the Axelar Gateway.
Expand All @@ -31,49 +33,64 @@ import {AdaptorRoles} from "../common/AdaptorRoles.sol";
contract ChildAxelarBridgeAdaptor is
AdaptorRoles,
AxelarExecutable,
IChildERC20BridgeAdaptor,
IChildBridgeAdaptor,
IChildAxelarBridgeAdaptorErrors,
IChildAxelarBridgeAdaptorEvents,
IChildAxelarBridgeAdaptor
{
/// @notice Address of bridge to relay messages to.
IChildERC20Bridge public childBridge;

/// @notice Axelar's ID for the child chain. Axelar uses the chain name as the chain ID.
string public rootChainId;

/// @notice Address of the bridge adaptor on the root chain, which this contract will communicate with.
string public rootBridgeAdaptor;

IAxelarGasService public gasService;
string public rootChain;

constructor(address _gateway) AxelarExecutable(_gateway) {}

/**
* @notice Initializes the contract.
* @param _childBridge Address of the child bridge contract.
* @notice Initialization function for ChildAxelarBridgeAdaptor.
* @param _roles Struct containing addresses of roles.
* @param _childBridge Address of child bridge contract.
* @param _rootChainId Axelar's string ID for the root chain.
* @param _rootBridgeAdaptor Address of the bridge adaptor on the root chain.
* @param _gasService Address of Axelar Gas Service contract.
*/
function initialize(
InitializationRoles memory newRoles,
string memory _rootChain,
InitializationRoles memory _roles,
address _childBridge,
string memory _rootChainId,
string memory _rootBridgeAdaptor,
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)
_childBridge == address(0) || _gasService == address(0) || _roles.defaultAdmin == address(0)
|| _roles.bridgeManager == address(0) || _roles.gasServiceManager == address(0)
|| _roles.targetManager == address(0)
) {
revert ZeroAddress();
}

if (bytes(_rootChain).length == 0) {
if (bytes(_rootChainId).length == 0) {
revert InvalidRootChain();
}

if (bytes(_rootBridgeAdaptor).length == 0) {
revert InvalidRootBridgeAdaptor();
}
__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);
_grantRole(DEFAULT_ADMIN_ROLE, _roles.defaultAdmin);
_grantRole(BRIDGE_MANAGER_ROLE, _roles.bridgeManager);
_grantRole(GAS_SERVICE_MANAGER_ROLE, _roles.gasServiceManager);
_grantRole(TARGET_MANAGER_ROLE, _roles.targetManager);

childBridge = IChildERC20Bridge(_childBridge);
rootChain = _rootChain;
rootChainId = _rootChainId;
rootBridgeAdaptor = _rootBridgeAdaptor;
gasService = IAxelarGasService(_gasService);
}

Expand All @@ -97,8 +114,20 @@ contract ChildAxelarBridgeAdaptor is
revert InvalidRootChain();
}

emit RootChainUpdated(rootChain, newRootChain);
rootChain = newRootChain;
emit RootChainUpdated(rootChainId, newRootChain);
rootChainId = newRootChain;
}

/**
* @inheritdoc IChildAxelarBridgeAdaptor
*/
function updateRootBridgeAdaptor(string memory newRootBridgeAdaptor) external onlyRole(TARGET_MANAGER_ROLE) {
if (bytes(newRootBridgeAdaptor).length == 0) {
revert InvalidRootBridgeAdaptor();
}

emit RootBridgeAdaptorUpdated(rootBridgeAdaptor, newRootBridgeAdaptor);
rootBridgeAdaptor = newRootBridgeAdaptor;
}

/**
Expand All @@ -114,7 +143,7 @@ contract ChildAxelarBridgeAdaptor is
}

/**
* @inheritdoc IChildERC20BridgeAdaptor
* @inheritdoc IChildBridgeAdaptor
*/
function sendMessage(bytes calldata payload, address refundRecipient) external payable override {
if (msg.value == 0) {
Expand All @@ -125,8 +154,8 @@ contract ChildAxelarBridgeAdaptor is
}

// Load from storage.
string memory _rootBridgeAdaptor = childBridge.rootERC20BridgeAdaptor();
string memory _rootChain = rootChain;
string memory _rootBridgeAdaptor = rootBridgeAdaptor;
string memory _rootChain = rootChainId;

gasService.payNativeGasForContractCall{value: msg.value}(
address(this), _rootChain, _rootBridgeAdaptor, payload, refundRecipient
Expand All @@ -137,18 +166,35 @@ contract ChildAxelarBridgeAdaptor is
}

/**
* @dev This function is called by the parent `AxelarExecutable` contract to execute the payload.
* @param sourceChain_ The chain id that the message originated from.
* @param sourceAddress_ The contract address that sent the message on the source chain.
* @param payload_ The message payload.
* @custom:assumes `sourceAddress_` is a 20 byte address.
* @dev This function is called by the parent `AxelarExecutable` contract to execute a message payload sent from the root chain.
* It is only called after the message has been validated by the Axelar core contracts.
* Validations include, ensuring that the Axelar validator set has signed the message and that the message has not been executed before.
* For more details see:
* - [AxelarExecutable.sol](https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/executable/AxelarExecutable.sol#L17),
* - [AxelarGateway.sol](https://github.com/axelarnetwork/axelar-cgp-solidity/blob/d4536599321774927bf9716178a9e360f8e0efac/contracts/AxelarGateway.sol#L233)
*
* @dev The function first validates the message by checking that it originated from the registered
* root chain and bridge adaptor contract on the root chain. If not, the message is rejected.
* If a message is valid, it calls the child bridge contract's `onMessageReceive` function.
* @param _sourceChain The chain id that the message originated from.
* @param _sourceAddress The contract address that sent the message on the source chain.
* @param _payload The message payload.
* @custom:assumes `_sourceAddress` is a 20 byte address.
*/
function _execute(string calldata sourceChain_, string calldata sourceAddress_, bytes calldata payload_)
function _execute(string calldata _sourceChain, string calldata _sourceAddress, bytes calldata _payload)
internal
override
{
emit AdaptorExecute(sourceChain_, sourceAddress_, payload_);
childBridge.onMessageReceive(sourceChain_, sourceAddress_, payload_);
if (!Strings.equal(_sourceChain, rootChainId)) {
revert InvalidSourceChain();
}

if (!Strings.equal(_sourceAddress, rootBridgeAdaptor)) {
revert InvalidSourceAddress();
}

emit AdaptorExecute(_sourceChain, _sourceAddress, _payload);
childBridge.onMessageReceive(_payload);
}

// slither-disable-next-line unused-state,naming-convention
Expand Down
Loading
Loading