Skip to content

Commit

Permalink
Merge pull request #18 from immutable/smr-1935-erc20-withdraw-L1
Browse files Browse the repository at this point in the history
Smr 1935 erc20 withdraw l1
  • Loading branch information
Benjimmutable authored Nov 6, 2023
2 parents 73b9139 + 7da7c83 commit e678f72
Show file tree
Hide file tree
Showing 24 changed files with 650 additions and 66 deletions.
10 changes: 5 additions & 5 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 @@ -36,18 +37,17 @@ contract DeployRootContracts is Script {

RootERC20Bridge rootERC20BridgeImplementation = new RootERC20Bridge();
rootERC20BridgeImplementation.initialize(
address(1), address(1), "filler", address(1), address(1), address(1), 1
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();
rootBridgeAdaptorImplementation.initialize(
address(rootERC20BridgeImplementation), "Filler", address(1), address(1)
);
RootAxelarBridgeAdaptor rootBridgeAdaptorImplementation = new RootAxelarBridgeAdaptor(rootGateway);
rootBridgeAdaptorImplementation.initialize(address(rootERC20BridgeImplementation), "Filler", address(1));

TransparentUpgradeableProxy rootBridgeAdaptorProxy = new TransparentUpgradeableProxy(
address(rootBridgeAdaptorImplementation),
address(proxyAdmin),
Expand Down
4 changes: 1 addition & 3 deletions script/InitializeRootContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ struct InitializeRootContractsParams {
address rootIMXToken;
address rootWETHToken;
string childChainName;
address rootGateway;
address rootGasService;
uint256 initialIMXCumulativeDepositLimit;
}
Expand All @@ -42,7 +41,6 @@ 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"),
initialIMXCumulativeDepositLimit: vm.envUint("INITIAL_IMX_CUMULATIVE_DEPOSIT_LIMIT")
});
Expand All @@ -63,13 +61,13 @@ contract InitializeRootContracts is Script {
params.rootChainChildTokenTemplate,
params.rootIMXToken,
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 @@ -109,20 +109,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 @@ -73,7 +73,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_);
}
26 changes: 26 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 @@ -64,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 @@ -73,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 @@ -87,6 +105,14 @@ 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
Expand Down
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 e678f72

Please sign in to comment.