Skip to content

Commit

Permalink
Merge branch 'main' into SMR-1815-WIMX-withdraw
Browse files Browse the repository at this point in the history
  • Loading branch information
wcgcyx committed Nov 7, 2023
2 parents 0275ef8 + fabfe75 commit b5abb94
Show file tree
Hide file tree
Showing 27 changed files with 1,002 additions and 88 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,12 @@ CHILD_GAS_SERVICE_ADDRESS=
ROOT_CHAIN_NAME="ROOT"
CHILD_CHAIN_NAME="CHILD"
ROOT_IMX_ADDRESS=
INITIAL_IMX_CUMULATIVE_DEPOSIT_LIMIT="0"
```
where `{ROOT,CHILD}_{GATEWAY,GAS_SERVICE}_ADDRESS` refers to the gateway and gas service addresses used by Axelar.

`INITIAL_IMX_CUMULATIVE_DEPOSIT_LIMIT` refers to the cumulative amount of IMX that can be deposited. A value of `0` indicated unlimited.

We can just use dummy gateway/gas service addresses if we only want to test the deployment, and not bridging functionality. If wanting to use dummy addresses, any valid Ethereum address can be used here.

4. Run the deploy script.
Expand Down Expand Up @@ -138,6 +141,7 @@ ROOT_GATEWAY_ADDRESS="0x013459EC3E8Aeced878C5C4bFfe126A366cd19E9"
CHILD_GATEWAY_ADDRESS="0xc7B788E88BAaB770A6d4936cdcCcd5250E1bbAd8"
ROOT_GAS_SERVICE_ADDRESS="0x28f8B50E1Be6152da35e923602a2641491E71Ed8"
CHILD_GAS_SERVICE_ADDRESS="0xC573c722e21eD7fadD38A8f189818433e01Ae466"
INITIAL_IMX_CUMULATIVE_DEPOSIT_LIMIT="0"
ENVIRONMENT="local"
```
(Note that `{ROOT,CHILD}_PRIVATE_KEY` can be any of the standard localhost private keys that get funded)
Expand Down
12 changes: 6 additions & 6 deletions script/DeployRootContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ contract DeployRootContracts is Script {
uint256 rootPrivateKey = vm.envUint("ROOT_PRIVATE_KEY");
string memory rootRpcUrl = vm.envString("ROOT_RPC_URL");
string memory deployEnvironment = vm.envString("ENVIRONMENT");
address rootGateway = vm.envAddress("ROOT_GATEWAY_ADDRESS");

/**
* DEPLOY ROOT CHAIN CONTRACTS
Expand All @@ -35,19 +36,18 @@ contract DeployRootContracts is Script {
rootChainChildTokenTemplate.initialize(address(123), "TEMPLATE", "TPT", 18);

RootERC20Bridge rootERC20BridgeImplementation = new RootERC20Bridge();
rootERC20BridgeImplementation.initialize(address(1), address(1), "filler", address(1), address(1), address(1));
rootERC20BridgeImplementation.initialize(
address(1), address(1), "filler", address(1), address(1), address(1), "filler_child_name", 1
);
TransparentUpgradeableProxy rootERC20BridgeProxy = new TransparentUpgradeableProxy(
address(rootERC20BridgeImplementation),
address(proxyAdmin),
""
);

// TODO add dummy initialize of implementation contracts!
RootAxelarBridgeAdaptor rootBridgeAdaptorImplementation = new RootAxelarBridgeAdaptor(rootGateway);
rootBridgeAdaptorImplementation.initialize(address(rootERC20BridgeImplementation), "Filler", address(1));

RootAxelarBridgeAdaptor rootBridgeAdaptorImplementation = new RootAxelarBridgeAdaptor();
rootBridgeAdaptorImplementation.initialize(
address(rootERC20BridgeImplementation), "Filler", address(1), address(1)
);
TransparentUpgradeableProxy rootBridgeAdaptorProxy = new TransparentUpgradeableProxy(
address(rootBridgeAdaptorImplementation),
address(proxyAdmin),
Expand Down
11 changes: 6 additions & 5 deletions script/InitializeRootContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ struct InitializeRootContractsParams {
address rootIMXToken;
address rootWETHToken;
string childChainName;
address rootGateway;
address rootGasService;
uint256 initialIMXCumulativeDepositLimit;
}

contract InitializeRootContracts is Script {
Expand All @@ -41,8 +41,8 @@ contract InitializeRootContracts is Script {
rootIMXToken: vm.envAddress("ROOT_IMX_ADDRESS"),
rootWETHToken: vm.envAddress("ROOT_WETH_ADDRESS"),
childChainName: vm.envString("CHILD_CHAIN_NAME"),
rootGateway: vm.envAddress("ROOT_GATEWAY_ADDRESS"),
rootGasService: vm.envAddress("ROOT_GAS_SERVICE_ADDRESS")
rootGasService: vm.envAddress("ROOT_GAS_SERVICE_ADDRESS"),
initialIMXCumulativeDepositLimit: vm.envUint("INITIAL_IMX_CUMULATIVE_DEPOSIT_LIMIT")
});

string[] memory checksumInputs = Utils.getChecksumInputs(params.childBridgeAdaptor);
Expand All @@ -60,13 +60,14 @@ contract InitializeRootContracts is Script {
childBridgeAdaptorChecksum,
params.rootChainChildTokenTemplate,
params.rootIMXToken,
params.rootWETHToken
params.rootWETHToken,
params.childChainName,
params.initialIMXCumulativeDepositLimit
);

params.rootBridgeAdaptor.initialize(
address(params.rootERC20Bridge), // root bridge
params.childChainName, // child chain name
params.rootGateway, // axelar gateway
params.rootGasService // axelar gas service
);

Expand Down
7 changes: 3 additions & 4 deletions src/child/ChildAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ contract ChildAxelarBridgeAdaptor is
/// @notice Address of bridge to relay messages to.
IChildERC20Bridge public childBridge;
IAxelarGasService public gasService;
string public rootBridgeAdaptor;
string public rootChain;

constructor(address _gateway) AxelarExecutable(_gateway) {}
Expand All @@ -39,7 +38,6 @@ contract ChildAxelarBridgeAdaptor is
childBridge = IChildERC20Bridge(_childBridge);
rootChain = _rootChain;
gasService = IAxelarGasService(_gasService);
rootBridgeAdaptor = childBridge.rootERC20BridgeAdaptor();
}

/**
Expand All @@ -54,15 +52,15 @@ contract ChildAxelarBridgeAdaptor is
}

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

gasService.payNativeGasForContractCall{value: msg.value}(
address(this), _rootChain, _rootBridgeAdaptor, payload, refundRecipient
);

gateway.callContract(_rootChain, _rootBridgeAdaptor, payload);
emit AxelarMessage(_rootChain, _rootBridgeAdaptor, payload);
emit AxelarMessageSent(_rootChain, _rootBridgeAdaptor, payload);
}

/**
Expand All @@ -73,6 +71,7 @@ contract ChildAxelarBridgeAdaptor is
internal
override
{
emit AdaptorExecute(sourceChain_, sourceAddress_, payload_);
childBridge.onMessageReceive(sourceChain_, sourceAddress_, payload_);
}
}
9 changes: 5 additions & 4 deletions src/child/ChildERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,21 @@ contract ChildERC20Bridge is
if (!Strings.equal(messageSourceChain, rootChain)) {
revert InvalidSourceChain();
}

if (!Strings.equal(sourceAddress, rootERC20BridgeAdaptor)) {
revert InvalidSourceAddress();
}
if (data.length == 0) {
revert InvalidData();
if (data.length <= 32) {
// Data must always be greater than 32.
// 32 bytes for the signature, and at least some information for the payload
revert InvalidData("Data too short");
}

if (bytes32(data[:32]) == MAP_TOKEN_SIG) {
_mapToken(data);
} else if (bytes32(data[:32]) == DEPOSIT_SIG) {
_deposit(data[32:]);
} else {
revert InvalidData();
revert InvalidData("Unsupported action signature");
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/interfaces/child/IChildAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ interface IChildAxelarBridgeAdaptorErrors {

interface IChildAxelarBridgeAdaptorEvents {
/// @notice Emitted when an Axelar message is sent to the root chain.
event AxelarMessage(string indexed rootChain, string indexed rootBridgeAdaptor, bytes indexed payload);
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_);
}
2 changes: 1 addition & 1 deletion src/interfaces/child/IChildERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ interface IChildERC20BridgeErrors {
/// @notice Error when a message is given to the bridge from an address not the designated bridge adaptor.
error NotBridgeAdaptor();
/// @notice Error when the message's payload is not valid.
error InvalidData();
error InvalidData(string reason);
/// @notice Error when the message's source chain is not valid.
error InvalidSourceChain();
/// @notice Error when the source chain's message sender is not a recognised address.
Expand Down
4 changes: 3 additions & 1 deletion src/interfaces/root/IRootAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ interface IRootAxelarBridgeAdaptorErrors {

interface IRootAxelarBridgeAdaptorEvents {
/// @notice Emitted when an Axelar message is sent to the child chain.
event AxelarMessage(string indexed childChain, string indexed childBridgeAdaptor, bytes indexed payload);
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_);
}
34 changes: 34 additions & 0 deletions src/interfaces/root/IRootERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER

interface IRootERC20Bridge {
function childBridgeAdaptor() external view returns (string memory);
/**
* @notice Receives a bridge message from child chain, parsing the message type then executing.
* @param sourceChain The chain the message originated from.
* @param sourceAddress The address the message originated from.
* @param data The data payload of the message.
*/
function onMessageReceive(string calldata sourceChain, string calldata sourceAddress, bytes calldata data)
external;

/**
* @notice Initiate sending a mapToken message to the child chain.
Expand Down Expand Up @@ -35,6 +43,10 @@ interface IRootERC20Bridge {
}

interface IRootERC20BridgeEvents {
/// @notice Emitted when the child chain bridge adaptor is updated.
event NewRootBridgeAdaptor(address oldRootBridgeAdaptor, address newRootBridgeAdaptor);
/// @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.
event L1TokenMapped(address indexed rootToken, address indexed childToken);
/// @notice Emitted when an ERC20 deposit message is sent to the child chain.
Expand All @@ -60,6 +72,14 @@ interface IRootERC20BridgeEvents {
address indexed receiver,
uint256 amount
);

event RootChainERC20Withdraw(
address indexed rootToken,
address indexed childToken,
address withdrawer,
address indexed receiver,
uint256 amount
);
}

interface IRootERC20BridgeErrors {
Expand All @@ -69,6 +89,8 @@ interface IRootERC20BridgeErrors {
error ZeroAmount();
/// @notice Error when a zero address is given when not valid.
error ZeroAddress();
/// @notice Error when the child chain name is invalid.
error InvalidChildChain();
/// @notice Error when a token is already mapped.
error AlreadyMapped();
/// @notice Error when a token is not mapped when it should be.
Expand All @@ -83,4 +105,16 @@ interface IRootERC20BridgeErrors {
error BalanceInvariantCheckFailed(uint256 actualBalance, uint256 expectedBalance);
/// @notice Error when the given child chain bridge adaptor is invalid.
error InvalidChildERC20BridgeAdaptor();
/// @notice Error when a message received has invalid data.
error InvalidData(string reason);
/// @notice Error when a message received has invalid source address.
error InvalidSourceAddress();
/// @notice Error when a message received has invalid source chain.
error InvalidSourceChain();
/// @notice Error when caller is not the root bridge adaptor but should be.
error NotBridgeAdaptor();
/// @notice Error when the total IMX deposit limit is exceeded
error ImxDepositLimitExceeded();
/// @notice Error when the IMX deposit limit is set below the amount of IMX already deposited
error ImxDepositLimitTooLow();
}
35 changes: 22 additions & 13 deletions src/root/RootAxelarBridgeAdaptor.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.21;

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";
Expand All @@ -20,41 +21,38 @@ import {IRootERC20Bridge} from "../interfaces/root/IRootERC20Bridge.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
{
using SafeERC20 for IERC20Metadata;

address public rootBridge;
IRootERC20Bridge public rootBridge;
string public childBridgeAdaptor;
string public childChain;
IAxelarGateway public axelarGateway;
IAxelarGasService public gasService;
mapping(uint256 => string) public chainIdToChainName;

constructor(address _gateway) AxelarExecutable(_gateway) {}

/**
* @notice Initilization function for RootAxelarBridgeAdaptor.
* @param _rootBridge Address of root bridge contract.
* @param _childChain Name of child chain.
* @param _axelarGateway Address of Axelar Gateway contract.
* @param _gasService Address of Axelar Gas Service contract.
*/
function initialize(address _rootBridge, string memory _childChain, address _axelarGateway, address _gasService)
public
initializer
{
if (_rootBridge == address(0) || _axelarGateway == address(0) || _gasService == address(0)) {
function initialize(address _rootBridge, string memory _childChain, address _gasService) public initializer {
if (_rootBridge == address(0) || _gasService == address(0)) {
revert ZeroAddresses();
}

if (bytes(_childChain).length == 0) {
revert InvalidChildChain();
}
rootBridge = _rootBridge;
rootBridge = IRootERC20Bridge(_rootBridge);
childChain = _childChain;
axelarGateway = IAxelarGateway(_axelarGateway);
gasService = IAxelarGasService(_gasService);
}

Expand All @@ -66,7 +64,7 @@ contract RootAxelarBridgeAdaptor is
if (msg.value == 0) {
revert NoGas();
}
if (msg.sender != rootBridge) {
if (msg.sender != address(rootBridge)) {
revert CallerNotBridge();
}

Expand All @@ -79,7 +77,18 @@ contract RootAxelarBridgeAdaptor is
address(this), _childChain, _childBridgeAdaptor, payload, refundRecipient
);

axelarGateway.callContract(_childChain, _childBridgeAdaptor, payload);
emit AxelarMessage(_childChain, _childBridgeAdaptor, payload);
gateway.callContract(_childChain, _childBridgeAdaptor, payload);
emit AxelarMessageSent(_childChain, _childBridgeAdaptor, payload);
}

/**
* @dev This function is called by the parent `AxelarExecutable` contract to execute the payload.
*/
function _execute(string calldata sourceChain_, string calldata sourceAddress_, bytes calldata payload_)
internal
override
{
emit AdaptorExecute(sourceChain_, sourceAddress_, payload_);
rootBridge.onMessageReceive(sourceChain_, sourceAddress_, payload_);
}
}
Loading

0 comments on commit b5abb94

Please sign in to comment.