From 13e66f1fefa8f2951bbf66522a04836ef11900b9 Mon Sep 17 00:00:00 2001 From: Benjamin Patch Date: Wed, 1 Nov 2023 11:03:23 +1100 Subject: [PATCH] WIP --- src/child/ChildAxelarBridgeAdaptor.sol | 21 ++++++--- src/child/ChildERC20Bridge.sol | 53 ++++++++++++++++++++++ src/interfaces/child/IChildERC20Bridge.sol | 6 +++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/child/ChildAxelarBridgeAdaptor.sol b/src/child/ChildAxelarBridgeAdaptor.sol index fd0f983ac..4408bb77d 100644 --- a/src/child/ChildAxelarBridgeAdaptor.sol +++ b/src/child/ChildAxelarBridgeAdaptor.sol @@ -2,20 +2,29 @@ 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 {IChildERC20Bridge} from "../interfaces/child/IChildERC20Bridge.sol"; import {IChildAxelarBridgeAdaptorErrors} from "../interfaces/child/IChildAxelarBridgeAdaptor.sol"; -contract ChildAxelarBridgeAdaptor is AxelarExecutable, IChildAxelarBridgeAdaptorErrors { +contract ChildAxelarBridgeAdaptor is AxelarExecutable, Initializable, IChildAxelarBridgeAdaptorErrors { /// @notice Address of bridge to relay messages to. - IChildERC20Bridge public immutable CHILD_BRIDGE; + IChildERC20Bridge public childBridge; string public rootBridgeAdaptor; - constructor(address _gateway, address _childBridge) AxelarExecutable(_gateway) { + constructor(address _gateway) AxelarExecutable(_gateway) {} + + /** + * @notice Initializes the contract. + * @param _childBridge Address of the child bridge contract. + * @dev Always sets the rootBridgeAdaptor to whatever the rootERC20BridgeAdaptor of the bridge contract is. + */ + function initialize(address _childBridge) external initializer { if (_childBridge == address(0)) { revert ZeroAddress(); } - CHILD_BRIDGE = IChildERC20Bridge(_childBridge); + childBridge = IChildERC20Bridge(_childBridge); + rootBridgeAdaptor = childBridge.rootERC20BridgeAdaptor(); } // TODO tests for this @@ -25,7 +34,7 @@ contract ChildAxelarBridgeAdaptor is AxelarExecutable, IChildAxelarBridgeAdaptor * @dev Always sets it to whatever the rootERC20BridgeAdaptor of the bridge contract is. */ function setRootBridgeAdaptor() external { - rootBridgeAdaptor = CHILD_BRIDGE.rootERC20BridgeAdaptor(); + rootBridgeAdaptor = childBridge.rootERC20BridgeAdaptor(); } /** @@ -36,6 +45,6 @@ contract ChildAxelarBridgeAdaptor is AxelarExecutable, IChildAxelarBridgeAdaptor internal override { - CHILD_BRIDGE.onMessageReceive(sourceChain_, sourceAddress_, payload_); + childBridge.onMessageReceive(sourceChain_, sourceAddress_, payload_); } } diff --git a/src/child/ChildERC20Bridge.sol b/src/child/ChildERC20Bridge.sol index dc40ed384..bc845ffdb 100644 --- a/src/child/ChildERC20Bridge.sol +++ b/src/child/ChildERC20Bridge.sol @@ -38,6 +38,7 @@ contract ChildERC20Bridge is bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN"); bytes32 public constant DEPOSIT_SIG = keccak256("DEPOSIT"); + bytes32 public constant WITHDRAW_SIG = keccak256("WITHDRAW"); address public constant NATIVE_ETH = address(0xeee); IChildERC20BridgeAdaptor public bridgeAdaptor; @@ -125,6 +126,58 @@ contract ChildERC20Bridge is } } + + function withdraw(IChildERC20 childToken, uint256 amount) external { + _withdraw(childToken, msg.sender, amount); + } + + function withdrawTo(IChildERC20 childToken, address receiver, uint256 amount) external { + _beforeTokenWithdraw(); + _withdraw(childToken, receiver, amount); + _afterTokenWithdraw(); + } + + function _withdraw(IChildERC20 childToken, address receiver, uint256 amount) private { + if (address(childToken).code.length == 0) { + revert EmptyTokenContract(); + } + + address rootToken = childToken.rootToken(); + + if (rootTokenToChildToken[rootToken] != address(childToken)) { + revert NotMapped(); + } + + // A mapped token should never a root token unset + if (rootToken == address(0)) { + revert ZeroAddressRootToken(); + } + + // A mapped token should never have the bridge unset + if (childToken.bridge() != address(this)) { + revert BrigeNotSet(); + } + + if (!childToken.burn(msg.sender, amount)) { + revert BurnFailed(); + } + + // Encode the message payload + bytes memory payload = abi.encode(WITHDRAW_SIG, rootToken, msg.sender, receiver, amount); + + // Send the message to the bridge adaptor and up to root chain + bridgeAdaptor.sendMessage(rootChain, rootERC20BridgeAdaptor, payload); + + if (address(childToken) == childETHToken) { + childToken.burn(msg.sender, amount); + Address.sendValue(payable(receiver), amount); + } else { + childToken.burn(msg.sender, amount); + IERC20Metadata rootToken = IERC20Metadata(childToken.rootToken()); + rootToken.safeTransfer(receiver, amount); + } + } + function _mapToken(bytes calldata data) private { (, address rootToken, string memory name, string memory symbol, uint8 decimals) = abi.decode(data, (bytes32, address, string, string, uint8)); diff --git a/src/interfaces/child/IChildERC20Bridge.sol b/src/interfaces/child/IChildERC20Bridge.sol index 90bffc196..0608d1aa7 100644 --- a/src/interfaces/child/IChildERC20Bridge.sol +++ b/src/interfaces/child/IChildERC20Bridge.sol @@ -70,4 +70,10 @@ interface IChildERC20BridgeErrors { error InvalidSourceChain(); /// @notice Error when the source chain's message sender is not a recognised address. error InvalidSourceAddress(); + /// @notice Error when a given child token's root token is the zero address. + error ZeroAddressRootToken(); + /// @notice Error when a given child token's bridge address is not set. + error BrigeNotSet(); + /// @notice Error when a call to the given child token's `burn` function fails. + error BurnFailed(); }