diff --git a/contracts/_interfaces/bridge-gateways/IArbitrumGateway.sol b/contracts/_interfaces/bridge-gateways/IArbitrumGateway.sol new file mode 100644 index 0000000..ff7a806 --- /dev/null +++ b/contracts/_interfaces/bridge-gateways/IArbitrumGateway.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +import {IBCRebaseGatewayEvents, ISCRebaseGatewayEvents, ITransferGatewayEvents} from "./IGateway.sol"; +import {ITokenGateway} from "arb-bridge-peripherals/contracts/tokenbridge/libraries/gateway/ITokenGateway.sol"; + +// Arbitrum chains expect the cross chain transaction to "pre-pay" in eth +// for execution on the other chain +// https://developer.offchainlabs.com/docs/l1_l2_messages + +interface IArbitrumBCRebaseGateway is IBCRebaseGatewayEvents { + event RebaseReportInitiated(uint256 indexed _sequenceNumber); + + function reportRebaseInit( + uint256 _maxSubmissionCost, + uint256 _maxGas, + uint256 _gasPriceBid + ) external payable returns (bytes memory); +} + +interface IArbitrumSCRebaseGateway is ISCRebaseGatewayEvents { + event RebaseReportFinalized(uint256 indexed _exitNum); + + function reportRebaseCommit(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) external; +} + +interface IArbitrumTransferGateway is ITransferGatewayEvents, ITokenGateway { + function getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) external view returns (bytes memory); +} + +interface IArbitrumBCTransferGateway is IArbitrumTransferGateway { + event DepositInitiated( + address l1Token, + address indexed _from, + address indexed _to, + uint256 indexed _sequenceNumber, + uint256 _amount + ); + + event WithdrawalFinalized( + address l1Token, + address indexed _from, + address indexed _to, + uint256 indexed _exitNum, + uint256 _amount + ); +} + +interface IArbitrumSCTransferGateway is IArbitrumTransferGateway { + event DepositFinalized( + address indexed l1Token, + address indexed _from, + address indexed _to, + uint256 _amount + ); + + event WithdrawalInitiated( + address l1Token, + address indexed _from, + address indexed _to, + uint256 indexed _l2ToL1Id, + uint256 _exitNum, + uint256 _amount + ); +} diff --git a/contracts/_interfaces/bridge-gateways/IChainBridgeGateway.sol b/contracts/_interfaces/bridge-gateways/IChainBridgeGateway.sol new file mode 100644 index 0000000..31350cb --- /dev/null +++ b/contracts/_interfaces/bridge-gateways/IChainBridgeGateway.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +import {IBCRebaseGatewayEvents, ISCRebaseGatewayEvents, ITransferGatewayEvents} from "./IGateway.sol"; + +interface IChainBridgeBCRebaseGateway is IBCRebaseGatewayEvents { + function validateRebaseReport(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) external; +} + +interface IChainBridgeSCRebaseGateway is ISCRebaseGatewayEvents { + function reportRebase(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) external; +} + +interface IChainBridgeBCTransferGateway is ITransferGatewayEvents { + function validateAndLock( + address sender, + address recipientInTargetChain, + uint256 amount, + uint256 globalAMPLSupply + ) external; + + function unlock( + address senderInSourceChain, + address recipient, + uint256 amount, + uint256 globalAMPLSupply + ) external; +} + +interface IChainBridgeSCTransferGateway is ITransferGatewayEvents { + function mint( + address senderInSourceChain, + address recipient, + uint256 amount, + uint256 globalAMPLSupply + ) external; + + function validateAndBurn( + address sender, + address recipientInTargetChain, + uint256 amount, + uint256 globalAMPLSupply + ) external; +} diff --git a/contracts/_interfaces/bridge-gateways/ITransferGatewayEvents.sol b/contracts/_interfaces/bridge-gateways/IGateway.sol similarity index 54% rename from contracts/_interfaces/bridge-gateways/ITransferGatewayEvents.sol rename to contracts/_interfaces/bridge-gateways/IGateway.sol index 3669df7..7b676ec 100644 --- a/contracts/_interfaces/bridge-gateways/ITransferGatewayEvents.sol +++ b/contracts/_interfaces/bridge-gateways/IGateway.sol @@ -1,5 +1,29 @@ // SPDX-License-Identifier: GPL-3.0-or-later +interface IBCRebaseGatewayEvents { + // Logged on the base chain gateway (ethereum) when rebase report is propagated out + event XCRebaseReportOut( + // epoch from the Ampleforth Monetary Policy on the base chain + uint256 globalAmpleforthEpoch, + // totalSupply of AMPL ERC-20 contract on the base chain + uint256 globalAMPLSupply + ); +} + +interface ISCRebaseGatewayEvents { + // Logged on the satellite chain gateway when bridge reports most recent rebase + event XCRebaseReportIn( + // new value coming in from the base chain + uint256 globalAmpleforthEpoch, + // new value coming in from the base chain + uint256 globalAMPLSupply, + // existing value on the satellite chain + uint256 recordedGlobalAmpleforthEpoch, + // existing value on the satellite chain + uint256 recordedGlobalAMPLSupply + ); +} + interface ITransferGatewayEvents { // Logged on source chain when cross-chain transfer is initiated event XCTransferOut( diff --git a/contracts/_interfaces/bridge-gateways/IMaticGateway.sol b/contracts/_interfaces/bridge-gateways/IMaticGateway.sol new file mode 100644 index 0000000..ff00885 --- /dev/null +++ b/contracts/_interfaces/bridge-gateways/IMaticGateway.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +import {IBCRebaseGatewayEvents, ISCRebaseGatewayEvents, ITransferGatewayEvents} from "./IGateway.sol"; + +interface IMaticBCRebaseGateway is IBCRebaseGatewayEvents { + function reportRebase() external; +} + +interface IMaticSCRebaseGateway is ISCRebaseGatewayEvents {} + +interface IMaticTransferGateway is ITransferGatewayEvents { + function transfer(address recipientInTargetChain, uint256 amount) external; +} diff --git a/contracts/_interfaces/bridge-gateways/IRebaseGatewayEvents.sol b/contracts/_interfaces/bridge-gateways/IRebaseGatewayEvents.sol deleted file mode 100644 index 1bba692..0000000 --- a/contracts/_interfaces/bridge-gateways/IRebaseGatewayEvents.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -interface IRebaseGatewayEvents { - // Logged on the base chain gateway (ethereum) when rebase report is propagated out - event XCRebaseReportOut( - // epoch from the Ampleforth Monetary Policy on the base chain - uint256 globalAmpleforthEpoch, - // totalSupply of AMPL ERC-20 contract on the base chain - uint256 globalAMPLSupply - ); - - // Logged on the satellite chain gateway (tron, acala, near) when bridge reports most recent rebase - event XCRebaseReportIn( - // new value coming in from the base chain - uint256 globalAmpleforthEpoch, - // new value coming in from the base chain - uint256 globalAMPLSupply, - // existing value on the satellite chain - uint256 recordedGlobalAmpleforthEpoch, - // existing value on the satellite chain - uint256 recordedGlobalAMPLSupply - ); -} diff --git a/contracts/base-bridge-gateways/ChainBridgeRebaseGateway.sol b/contracts/base-bridge-gateways/ChainBridgeRebaseGateway.sol deleted file mode 100644 index 8ff6f89..0000000 --- a/contracts/base-bridge-gateways/ChainBridgeRebaseGateway.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -import {IRebaseGatewayEvents} from "../_interfaces/bridge-gateways/IRebaseGatewayEvents.sol"; - -contract ChainBridgeRebaseGateway is IRebaseGatewayEvents { - // overridden on the base chain gateway (ethereum) - function validateRebaseReport(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) - external - virtual - { - require(false, "Gateway function NOT_IMPLEMENTED"); - } - - // overridden on the satellite chain gateway (tron, acala, near) - function reportRebase(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) - external - virtual - { - require(false, "Gateway function NOT_IMPLEMENTED"); - } -} diff --git a/contracts/base-bridge-gateways/ChainBridgeTransferGateway.sol b/contracts/base-bridge-gateways/ChainBridgeTransferGateway.sol deleted file mode 100644 index ccd4c5e..0000000 --- a/contracts/base-bridge-gateways/ChainBridgeTransferGateway.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -import {ITransferGatewayEvents} from "../_interfaces/bridge-gateways/ITransferGatewayEvents.sol"; - -contract ChainBridgeTransferGateway is ITransferGatewayEvents { - // overridden on the base chain gateway (ethereum) - function validateAndLock( - address sender, - address recipientAddressInTargetChain, - uint256 amount, - uint256 globalAMPLSupply - ) external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } - - // overridden on the base chain gateway (ethereum) - function unlock( - address senderAddressInSourceChain, - address recipient, - uint256 amount, - uint256 globalAMPLSupply - ) external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } - - // overridden on the satellite chain gateway (tron, acala, near) - function mint( - address senderAddressInSourceChain, - address recipient, - uint256 amount, - uint256 globalAMPLSupply - ) external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } - - // overridden on the satellite chain gateway (tron, acala, near) - function validateAndBurn( - address sender, - address recipientAddressInTargetChain, - uint256 amount, - uint256 globalAMPLSupply - ) external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } -} diff --git a/contracts/base-bridge-gateways/Layer2RebaseGateway.sol b/contracts/base-bridge-gateways/Layer2RebaseGateway.sol deleted file mode 100644 index b76caf8..0000000 --- a/contracts/base-bridge-gateways/Layer2RebaseGateway.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -import {IRebaseGatewayEvents} from "../_interfaces/bridge-gateways/IRebaseGatewayEvents.sol"; - -contract Layer2RebaseGateway is IRebaseGatewayEvents { - // overridden on the base chain gateway (ethereum) - function reportRebase() external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } -} diff --git a/contracts/base-bridge-gateways/Layer2TransferGateway.sol b/contracts/base-bridge-gateways/Layer2TransferGateway.sol deleted file mode 100644 index 6ed324c..0000000 --- a/contracts/base-bridge-gateways/Layer2TransferGateway.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -import {ITransferGatewayEvents} from "../_interfaces/bridge-gateways/ITransferGatewayEvents.sol"; - -contract Layer2TransferGateway is ITransferGatewayEvents { - // overridden on the satellite chain gateway (tron, acala, near) - function transfer(address recipientInTargetChain, uint256 amount) external virtual { - require(false, "Gateway function NOT_IMPLEMENTED"); - } -} diff --git a/contracts/base-chain/bridge-gateways/AMPLArbitrumGateway.sol b/contracts/base-chain/bridge-gateways/AMPLArbitrumGateway.sol new file mode 100644 index 0000000..c634508 --- /dev/null +++ b/contracts/base-chain/bridge-gateways/AMPLArbitrumGateway.sol @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.6.11; +pragma experimental ABIEncoderV2; + +import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; +import {Initializable} from "@openzeppelin/contracts/proxy/Initializable.sol"; +import {GatewayMessageHandler} from "arb-bridge-peripherals/contracts/tokenbridge/libraries/gateway/GatewayMessageHandler.sol"; +import {L1ArbitrumMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/ethereum/L1ArbitrumMessenger.sol"; + +import {IArbitrumBCRebaseGateway, IArbitrumBCTransferGateway, IArbitrumSCRebaseGateway, IArbitrumSCTransferGateway} from "../../_interfaces/bridge-gateways/IArbitrumGateway.sol"; +import {IAmpleforth} from "uFragments/contracts/interfaces/IAmpleforth.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import {ITokenVault} from "../../_interfaces/ITokenVault.sol"; + +/// @dev Abstract l2 gateway contarct implementation to define function selectors +abstract contract ArbitrumXCAmpleGateway is IArbitrumSCRebaseGateway, IArbitrumSCTransferGateway { + function reportRebaseCommit(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) + external + override + { + require(false, "ArbitrumXCAmpleGateway: NOT_IMPLEMENTED"); + } + + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override { + require(false, "ArbitrumXCAmpleGateway: NOT_IMPLEMENTED"); + } +} + +/** + * @title AMPLArbitrumGateway: AMPL-Arbitrum Gateway Contract + * @dev This contract is deployed on the base chain (Ethereum). + * + * It's a pass-through contract between the Arbitrum's bridge and + * the Ampleforth policy. + * + */ +contract AMPLArbitrumGateway is + IArbitrumBCRebaseGateway, + IArbitrumBCTransferGateway, + L1ArbitrumMessenger, + Initializable +{ + using SafeMath for uint256; + using SafeERC20 for IERC20; + + //-------------------------------------------------------------------------- + // AMPL Base chain gateway attributes + + /// @dev Address of the AMPL ERC20 on the base chain. + address public immutable ampl; + + /// @dev Address of the Ampleforth monetary policy on the base chain. + address public immutable policy; + + /// @dev Address of the token vault which escrows funds on the base chain. + address public immutable vault; + + //-------------------------------------------------------------------------- + // Arbitrum gateway attributes + + /// @dev Address of the arbitrum bridge inbox. + address public inbox; + + /// @dev Address if the arbitrum bridge router. + address public router; + + /// @dev Address of the counterpart gateway contract on the arbitrum chain + /// which "finalizes" cross chain transactions. + address public counterpartGateway; + + /// @dev Address of XCAmple token on the satellite chain. + address public xcAmple; + + //-------------------------------------------------------------------------- + // Modifiers + + // @dev Validate incoming transactions before "finalization". + modifier onlyCounterpartGateway() { + address bridge = address(super.getBridge(inbox)); + require(msg.sender == bridge, "AMPLArbitrumGateway: NOT_FROM_BRIDGE"); + + address l2ToL1Sender = super.getL2ToL1Sender(inbox); + require( + l2ToL1Sender == counterpartGateway, + "AMPLArbitrumGateway: ONLY_COUNTERPART_GATEWAY" + ); + _; + } + + //-------------------------------------------------------------------------- + // Constructor + + /** + * @notice Instantiate the contract with references. + * @param ampl_ Address of the AMPL ERC-20 on the Base Chain. + * @param policy_ Address of the Ampleforth monetary policy on the Base Chain. + * @param vault_ Address of the vault contract. + */ + constructor( + address ampl_, + address policy_, + address vault_ + ) public { + ampl = ampl_; + policy = policy_; + vault = vault_; + } + + /** + * @notice Initialize contract with the addresses from the satellite chain (arbitrum). + * @param inbox_ Address of the arbitrum bridge inbox on the base chain. + * @param router_ Address of the arbitrum token transfer router on the base chain. + * @param xcAmple_ Address of the XCAmple ERC-20 on the satellite chain. + * @param counterpartGateway_ Address the counterpart gateway contract on the satellite chain. + */ + function initialize( + address inbox_, + address router_, + address xcAmple_, + address counterpartGateway_ + ) public initializer { + inbox = inbox_; + router = router_; + xcAmple = xcAmple_; + counterpartGateway = counterpartGateway_; + } + + //-------------------------------------------------------------------------- + // External methods + + /** + * @notice Builds the payload and transmits rebase report to Arbitrum. + * @param _maxSubmissionCost Amount of ETH allocated to pay for the base submission fee. + * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution. + * @param _gasPriceBid Gas price for L2 execution. + * @return res abi encoded inbox sequence number. + */ + function reportRebaseInit( + uint256 _maxSubmissionCost, + uint256 _maxGas, + uint256 _gasPriceBid + ) external payable override returns (bytes memory) { + require(xcAmple != address(0), "AMPLArbitrumGateway: NOT_INITIALIZED"); + + uint256 recordedGlobalAmpleforthEpoch = IAmpleforth(policy).epoch(); + uint256 recordedGlobalAMPLSupply = IERC20(ampl).totalSupply(); + + emit XCRebaseReportOut(recordedGlobalAmpleforthEpoch, recordedGlobalAMPLSupply); + + uint256 seqNumber = sendTxToL2( + inbox, + counterpartGateway, + tx.origin, + msg.value, + 0, + L2GasParams({ + _maxSubmissionCost: _maxSubmissionCost, + _maxGas: _maxGas, + _gasPriceBid: _gasPriceBid + }), + abi.encodeWithSelector( + ArbitrumXCAmpleGateway.reportRebaseCommit.selector, + recordedGlobalAmpleforthEpoch, + recordedGlobalAMPLSupply + ) + ); + + emit RebaseReportInitiated(seqNumber); + + return abi.encode(seqNumber); + } + + /** + * @notice Deposit AMPL from Ethereum into Arbitrum. + * @param _l1Token L1 address of the AMPL ERC20. + * @param _to account to be credited with the tokens in the L2 (can be the user's L2 account or a contract). + * @param _amount Token Amount. + * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution. + * @param _gasPriceBid Gas price for L2 execution. + * @param _data encoded data from router and user. + * @return res abi encoded inbox sequence number. + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable override returns (bytes memory) { + require(msg.sender == router, "AMPLArbitrumGateway: NOT_FROM_ROUTER"); + + require(_l1Token == ampl, "AMPLArbitrumGateway: ONLY_AMPL_ALLOWED"); + + require(xcAmple != address(0), "AMPLArbitrumGateway: NOT_INITIALIZED"); + + L2GasParams memory gasParams = L2GasParams({ + _maxSubmissionCost: 0, + _maxGas: _maxGas, + _gasPriceBid: _gasPriceBid + }); + + address from; + (from, gasParams._maxSubmissionCost) = _parseDataFromRouterOnTransfer(_data); + + // Lock funds and log outbound transfer + uint256 recordedGlobalAMPLSupply; + { + recordedGlobalAMPLSupply = IERC20(_l1Token).totalSupply(); + + // NOTE: The usual xc-transfer flow involves the depositer approving the vault + // and initiating the transfer. However the arbitrum implementation expects + // the user to approve the gateway. We thus add this extra step to confirm + // to both interfaces. + // 1) User approves the gateway + // 2) Tokens transfer from user => gateway => vault + IERC20(_l1Token).safeTransferFrom(from, address(this), _amount); + IERC20(_l1Token).approve(vault, _amount); + + ITokenVault(vault).lock(_l1Token, address(this), _amount); + + emit XCTransferOut(from, address(0), _amount, recordedGlobalAMPLSupply); + } + + // Execute cross-chain transfer + return + abi.encode( + createOutboundTransfer( + _l1Token, + from, + _to, + _amount, + gasParams, + recordedGlobalAMPLSupply + ) + ); + } + + /** + * @notice Finalizes a withdrawal via Outbox message; callable only by L2Gateway.outboundTransfer + * @param _l1Token L1 address of the AMPL ERC20. + * @param _from initiator of withdrawal. + * @param _to address the L2 withdrawal call set as the destination. + * @param _amount Token amount being withdrawn. + * @param _data encoded exitNum (Sequentially increasing exit counter determined by the L2Gateway) + * and recordedGlobalAMPLSupply from the source chain. + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override onlyCounterpartGateway { + require(_l1Token == ampl, "AMPLArbitrumGateway: ONLY_AMPL_ALLOWED"); + + // Decode data from the bridge + uint256 exitNum; + uint256 globalAMPLSupply; + (exitNum, globalAMPLSupply) = abi.decode(_data, (uint256, uint256)); + + // Log inbound transfer and release funds + // NOTE: this fails with the aribturm UI which approves this contract NOT the vault. + // TODO: safe transfer from user to self and then forward to the vault. + uint256 unlockAmount; + { + uint256 recordedGlobalAMPLSupply = IERC20(ampl).totalSupply(); + + emit XCTransferIn(address(0), _to, globalAMPLSupply, _amount, recordedGlobalAMPLSupply); + + unlockAmount = _amount.mul(recordedGlobalAMPLSupply).div(globalAMPLSupply); + + ITokenVault(vault).unlock(ampl, _to, unlockAmount); + } + + emit WithdrawalFinalized(_l1Token, _from, _to, exitNum, unlockAmount); + } + + //-------------------------------------------------------------------------- + // View methods + + /// @return The L2 AMPL token address. + function calculateL2TokenAddress(address token) public view override returns (address) { + if (token != ampl) { + return address(0); + } + return xcAmple; + } + + /// @return The encoded outbound call data with the current globalAMPLSupply. + function getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) external view override returns (bytes memory) { + return _getOutboundCalldata(_l1Token, _from, _to, _amount, IERC20(ampl).totalSupply()); + } + + //-------------------------------------------------------------------------- + // Internal methods + + /// @dev Parses data packed by the router + /// @return The depositor address and maxSubmissionCost + function _parseDataFromRouterOnTransfer(bytes calldata _data) + internal + returns (address, uint256) + { + address from; + bytes memory packedDataFromRouter; + (from, packedDataFromRouter) = GatewayMessageHandler.parseFromRouterToGateway(_data); + + uint256 maxSubmissionCost; + bytes memory extraData; + (maxSubmissionCost, extraData) = abi.decode(packedDataFromRouter, (uint256, bytes)); + + require(extraData.length == 0, "AMPLArbitrumGateway: EXTRA_DATA_DISABLED"); + + return (from, maxSubmissionCost); + } + + /// @dev Builds and executes the outbound transfer. + /// @return seqNumber The bridge sequence number. + function createOutboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + L2GasParams memory _gasParams, + uint256 recordedGlobalAMPLSupply + ) internal returns (uint256) { + // packed data sent over the bridge + bytes memory _outboundCallData = _getOutboundCalldata( + _l1Token, + _from, + _to, + _amount, + recordedGlobalAMPLSupply + ); + + // Send data through the arbitrum bridge + // Extra eth gets forwarded to the _from address on L2 + uint256 seqNumber = sendTxToL2( + inbox, + counterpartGateway, + _from, + msg.value, // we forward the L1 call value to the inbox + 0, // l2 call value 0 by default + _gasParams, + _outboundCallData + ); + + emit DepositInitiated(_l1Token, _from, _to, seqNumber, _amount); + + return seqNumber; + } + + /// @dev Packs data for the outbound token transfer (with the current AMPL supply). + /// @return The packed byte array. + function _getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + uint256 recordedGlobalAMPLSupply + ) internal view returns (bytes memory) { + bytes memory packedData = abi.encode(recordedGlobalAMPLSupply); + + bytes memory outboundCalldata = abi.encodeWithSelector( + ArbitrumXCAmpleGateway.finalizeInboundTransfer.selector, + _l1Token, + _from, + _to, + _amount, + packedData + ); + + return outboundCalldata; + } +} diff --git a/contracts/base-chain/bridge-gateways/AMPLChainBridgeGateway.sol b/contracts/base-chain/bridge-gateways/AMPLChainBridgeGateway.sol index 8e64f20..2210d50 100644 --- a/contracts/base-chain/bridge-gateways/AMPLChainBridgeGateway.sol +++ b/contracts/base-chain/bridge-gateways/AMPLChainBridgeGateway.sol @@ -4,9 +4,7 @@ pragma solidity 0.7.3; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; -import {ChainBridgeRebaseGateway} from "../../base-bridge-gateways/ChainBridgeRebaseGateway.sol"; -import {ChainBridgeTransferGateway} from "../../base-bridge-gateways/ChainBridgeTransferGateway.sol"; - +import {IChainBridgeBCRebaseGateway, IChainBridgeBCTransferGateway} from "../../_interfaces/bridge-gateways/IChainBridgeGateway.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IAmpleforth} from "uFragments/contracts/interfaces/IAmpleforth.sol"; import {ITokenVault} from "../../_interfaces/ITokenVault.sol"; @@ -18,7 +16,9 @@ import {ITokenVault} from "../../_interfaces/ITokenVault.sol"; * It's a pass-through contract between the ChainBridge handler contract and * the Ampleforth policy and the Token vault. * - * The contract is owned by the ChainBridge handler contract. + * The contract is owned by the ChainBridge handler contract and only it can + * execute functions.The user interacts with the bridge contract and the bridge + * callsback into this gateway. * * When rebase is transmitted across the bridge, It checks the consistency of rebase data * from the ChainBridge handler contract with the recorded on-chain value. @@ -37,7 +37,11 @@ import {ITokenVault} from "../../_interfaces/ITokenVault.sol"; * and the total ERC-20 AMPL supply on the current chain, at the time of unlock. * */ -contract AMPLChainBridgeGateway is ChainBridgeRebaseGateway, ChainBridgeTransferGateway, Ownable { +contract AMPLChainBridgeGateway is + IChainBridgeBCRebaseGateway, + IChainBridgeBCTransferGateway, + Ownable +{ using SafeMath for uint256; address public immutable ampl; @@ -74,13 +78,13 @@ contract AMPLChainBridgeGateway is ChainBridgeRebaseGateway, ChainBridgeTransfer * @dev Validates the data from the handler and transfers specified amount from * the sender's wallet and locks it in the vault contract. * @param sender Address of the sender wallet on the base chain. - * @param recipientAddressInTargetChain Address of the recipient wallet in the target chain. + * @param recipientInTargetChain Address of the recipient wallet in the target chain. * @param amount Amount of tokens to be locked on the current chain (source chain). * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer locking. */ function validateAndLock( address sender, - address recipientAddressInTargetChain, + address recipientInTargetChain, uint256 amount, uint256 globalAMPLSupply ) external override onlyOwner { @@ -99,13 +103,13 @@ contract AMPLChainBridgeGateway is ChainBridgeRebaseGateway, ChainBridgeTransfer /** * @dev Calculates the amount of amples to be unlocked based on the share of total supply and * transfers it to the recipient. - * @param senderAddressInSourceChain Address of the sender wallet in the transaction originating chain. + * @param senderInSourceChain Address of the sender wallet in the transaction originating chain. * @param recipient Address of the recipient wallet in the current chain (target chain). * @param amount Amount of tokens that were {locked/burnt} on the base chain. * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer. */ function unlock( - address senderAddressInSourceChain, + address senderInSourceChain, address recipient, uint256 amount, uint256 globalAMPLSupply diff --git a/contracts/base-chain/bridge-gateways/AMPLMaticRebaseGateway.sol b/contracts/base-chain/bridge-gateways/AMPLMaticRebaseGateway.sol index 45f15f5..859704c 100644 --- a/contracts/base-chain/bridge-gateways/AMPLMaticRebaseGateway.sol +++ b/contracts/base-chain/bridge-gateways/AMPLMaticRebaseGateway.sol @@ -2,8 +2,8 @@ pragma solidity 0.7.3; import {FxBaseRootTunnel} from "fx-portal/contracts/tunnel/FxBaseRootTunnel.sol"; -import {Layer2RebaseGateway} from "../../base-bridge-gateways/Layer2RebaseGateway.sol"; +import {IMaticBCRebaseGateway} from "../../_interfaces/bridge-gateways/IMaticGateway.sol"; import {IAmpleforth} from "uFragments/contracts/interfaces/IAmpleforth.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -15,17 +15,10 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; * the Ampleforth policy. * */ -contract AMPLMaticRebaseGateway is Layer2RebaseGateway, FxBaseRootTunnel { +contract AMPLMaticRebaseGateway is IMaticBCRebaseGateway, FxBaseRootTunnel { address public immutable ampl; address public immutable policy; - /** - * @dev No-op as rebase report is one-way. - */ - function _processMessageFromChild(bytes memory data) internal override { - return; - } - /** * @dev Builds the payload and transmits rebase report to matic. */ @@ -38,6 +31,13 @@ contract AMPLMaticRebaseGateway is Layer2RebaseGateway, FxBaseRootTunnel { _sendMessageToChild(abi.encode(recordedGlobalAmpleforthEpoch, recordedGlobalAMPLSupply)); } + /** + * @dev Bridge callback. No-op as rebase report is one-way. + */ + function _processMessageFromChild(bytes memory data) internal override { + return; + } + constructor( address _checkpointManager, address _fxRoot, diff --git a/contracts/base-chain/bridge-gateways/AMPLMaticTransferGateway.sol b/contracts/base-chain/bridge-gateways/AMPLMaticTransferGateway.sol index 3842257..53d88b1 100644 --- a/contracts/base-chain/bridge-gateways/AMPLMaticTransferGateway.sol +++ b/contracts/base-chain/bridge-gateways/AMPLMaticTransferGateway.sol @@ -4,8 +4,8 @@ pragma solidity 0.7.3; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {FxBaseRootTunnel} from "fx-portal/contracts/tunnel/FxBaseRootTunnel.sol"; -import {Layer2TransferGateway} from "../../base-bridge-gateways/Layer2TransferGateway.sol"; +import {IMaticTransferGateway} from "../../_interfaces/bridge-gateways/IMaticGateway.sol"; import {ITokenVault} from "../../_interfaces/ITokenVault.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -17,19 +17,32 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; * the and the Token vault. * */ -contract AMPLMaticTransferGateway is Layer2TransferGateway, FxBaseRootTunnel { +contract AMPLMaticTransferGateway is IMaticTransferGateway, FxBaseRootTunnel { using SafeMath for uint256; address public immutable ampl; address public immutable vault; /** - * @dev Calculates the amount of amples to be unlocked based on the share of total supply and - * transfers it to the recipient. - * "senderAddressInSourceChain": Address of the sender wallet in the matic chain. - * "recipient": Address of the recipient wallet in ethereum. - * "amount": Amount of tokens that were {burnt} on the matic. - * "globalAMPLSupply": AMPL ERC-20 total supply at the time of transfer. + * @dev Transfers specified amount from the {msg.sender}'s wallet and locks it in the vault contract, + * notifies the bridge about the transfer. + * @param recipientInTargetChain Address of the recipient wallet in the matic chain. + * @param amount Amount of tokens to be locked on ethereum. + */ + function transfer(address recipientInTargetChain, uint256 amount) external override { + uint256 recordedGlobalAMPLSupply = IERC20(ampl).totalSupply(); + + ITokenVault(vault).lock(ampl, msg.sender, amount); + + emit XCTransferOut(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply); + + _sendMessageToChild( + abi.encode(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply) + ); + } + + /** + * @dev Bridge callback. */ function _processMessageFromChild(bytes memory data) internal override { address senderInSourceChain; @@ -41,6 +54,23 @@ contract AMPLMaticTransferGateway is Layer2TransferGateway, FxBaseRootTunnel { (address, address, uint256, uint256) ); + _executeTransfer(senderInSourceChain, recipient, amount, globalAMPLSupply); + } + + /** + * @dev Calculates the amount of amples to be unlocked based on the share of total supply and + * transfers it to the recipient. + * @param senderInSourceChain Address of the sender wallet in the matic chain. + * @param recipient Address of the recipient wallet in ethereum. + * @param amount Amount of tokens that were {burnt} on the matic. + * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer. + */ + function _executeTransfer( + address senderInSourceChain, + address recipient, + uint256 amount, + uint256 globalAMPLSupply + ) internal { uint256 recordedGlobalAMPLSupply = IERC20(ampl).totalSupply(); emit XCTransferIn( @@ -55,24 +85,6 @@ contract AMPLMaticTransferGateway is Layer2TransferGateway, FxBaseRootTunnel { ITokenVault(vault).unlock(ampl, recipient, unlockAmount); } - /** - * @dev Transfers specified amount from the {msg.sender}'s wallet and locks it in the vault contract, - * notifies the bridge about the transfer. - * @param recipientInTargetChain Address of the recipient wallet in the matic chain. - * @param amount Amount of tokens to be locked on ethereum. - */ - function transfer(address recipientInTargetChain, uint256 amount) external override { - uint256 recordedGlobalAMPLSupply = IERC20(ampl).totalSupply(); - - ITokenVault(vault).lock(ampl, msg.sender, amount); - - emit XCTransferOut(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply); - - _sendMessageToChild( - abi.encode(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply) - ); - } - constructor( address _checkpointManager, address _fxRoot, diff --git a/contracts/satellite-chain/bridge-gateways/ChainBridgeXCAmpleGateway.sol b/contracts/satellite-chain/bridge-gateways/ChainBridgeXCAmpleGateway.sol index 1ffe833..f08d36a 100644 --- a/contracts/satellite-chain/bridge-gateways/ChainBridgeXCAmpleGateway.sol +++ b/contracts/satellite-chain/bridge-gateways/ChainBridgeXCAmpleGateway.sol @@ -4,16 +4,14 @@ pragma solidity 0.7.3; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; -import {ChainBridgeRebaseGateway} from "../../base-bridge-gateways/ChainBridgeRebaseGateway.sol"; -import {ChainBridgeTransferGateway} from "../../base-bridge-gateways/ChainBridgeTransferGateway.sol"; - +import {IChainBridgeSCRebaseGateway, IChainBridgeSCTransferGateway} from "../../_interfaces/bridge-gateways/IChainBridgeGateway.sol"; import {IXCAmpleController} from "../../_interfaces/IXCAmpleController.sol"; import {IXCAmpleControllerGateway} from "../../_interfaces/IXCAmpleControllerGateway.sol"; import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; /** * @title ChainBridgeXCAmpleGateway - * @dev This contract is deployed on the satellite EVM chains eg). tron, acala, near etc. + * @dev This contract is deployed on the satellite EVM chains (Bsc, avax etc). * * It's a pass-through contract between the ChainBridge handler contract and * the xc-ample controller contract. @@ -35,8 +33,8 @@ import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; * */ contract ChainBridgeXCAmpleGateway is - ChainBridgeRebaseGateway, - ChainBridgeTransferGateway, + IChainBridgeSCRebaseGateway, + IChainBridgeSCTransferGateway, Ownable { using SafeMath for uint256; @@ -76,13 +74,13 @@ contract ChainBridgeXCAmpleGateway is * @dev Calculates the amount of xc-amples to be mint based on the amount and the total supply * on the base chain when the transaction was initiated * and mints xc-amples to the recipient. - * @param senderAddressInSourceChain Address of the sender wallet in the transaction originating chain. + * @param senderInSourceChain Address of the sender wallet in the transaction originating chain. * @param recipient Address of the recipient wallet in the current chain (target chain). * @param amount Amount of tokens that were {locked/burnt} on the source chain. * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer. */ function mint( - address senderAddressInSourceChain, + address senderInSourceChain, address recipient, uint256 amount, uint256 globalAMPLSupply @@ -104,13 +102,13 @@ contract ChainBridgeXCAmpleGateway is /** * @dev Validates the data from the handler and burns specified amount from the sender's wallet. * @param sender Address of the sender wallet on the source chain. - * @param recipientAddressInTargetChain Address of the recipient wallet in the target chain. + * @param recipientInTargetChain Address of the recipient wallet in the target chain. * @param amount Amount of tokens to be burnt on the current chain (source chain). * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer burning. */ function validateAndBurn( address sender, - address recipientAddressInTargetChain, + address recipientInTargetChain, uint256 amount, uint256 globalAMPLSupply ) external override onlyOwner { diff --git a/contracts/satellite-chain/bridge-gateways/MaticXCAmpleRebaseGateway.sol b/contracts/satellite-chain/bridge-gateways/MaticXCAmpleRebaseGateway.sol index c61408f..848a363 100644 --- a/contracts/satellite-chain/bridge-gateways/MaticXCAmpleRebaseGateway.sol +++ b/contracts/satellite-chain/bridge-gateways/MaticXCAmpleRebaseGateway.sol @@ -2,8 +2,8 @@ pragma solidity 0.7.3; import {FxBaseChildTunnel} from "fx-portal/contracts/tunnel/FxBaseChildTunnel.sol"; -import {Layer2RebaseGateway} from "../../base-bridge-gateways/Layer2RebaseGateway.sol"; +import {IMaticSCRebaseGateway} from "../../_interfaces/bridge-gateways/IMaticGateway.sol"; import {IXCAmpleController} from "../../_interfaces/IXCAmpleController.sol"; import {IXCAmpleControllerGateway} from "../../_interfaces/IXCAmpleControllerGateway.sol"; import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; @@ -16,14 +16,12 @@ import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; * the xc-ample contracts. * */ -contract MaticXCAmpleRebaseGateway is Layer2RebaseGateway, FxBaseChildTunnel { +contract MaticXCAmpleRebaseGateway is IMaticSCRebaseGateway, FxBaseChildTunnel { address public immutable xcAmple; address public immutable xcController; /** - * @dev Forwards the most recent rebase information from the matic bridge to the xc-ample controller. - * "globalAmpleforthEpoch": Ampleforth monetary policy epoch from ethereum. - * "globalAMPLSupply": AMPL ERC-20 total supply from ethereum. + * @dev Bridge callback. */ function _processMessageFromRoot( uint256 stateId, @@ -34,6 +32,17 @@ contract MaticXCAmpleRebaseGateway is Layer2RebaseGateway, FxBaseChildTunnel { uint256 globalAMPLSupply; (globalAmpleforthEpoch, globalAMPLSupply) = abi.decode(data, (uint256, uint256)); + _executeReportRebase(globalAmpleforthEpoch, globalAMPLSupply); + } + + /* + * @dev Forwards the most recent rebase information from the matic bridge to the xc-ample controller. + * @param globalAmpleforthEpoch Ampleforth monetary policy epoch from ethereum. + * @param globalAMPLSupply AMPL ERC-20 total supply from ethereum. + */ + function _executeReportRebase(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) + internal + { uint256 recordedGlobalAmpleforthEpoch = IXCAmpleController(xcController) .globalAmpleforthEpoch(); diff --git a/contracts/satellite-chain/bridge-gateways/MaticXCAmpleTransferGateway.sol b/contracts/satellite-chain/bridge-gateways/MaticXCAmpleTransferGateway.sol index 7cf1609..0aef430 100644 --- a/contracts/satellite-chain/bridge-gateways/MaticXCAmpleTransferGateway.sol +++ b/contracts/satellite-chain/bridge-gateways/MaticXCAmpleTransferGateway.sol @@ -4,8 +4,8 @@ pragma solidity 0.7.3; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {FxBaseChildTunnel} from "fx-portal/contracts/tunnel/FxBaseChildTunnel.sol"; -import {Layer2TransferGateway} from "../../base-bridge-gateways/Layer2TransferGateway.sol"; +import {IMaticTransferGateway} from "../../_interfaces/bridge-gateways/IMaticGateway.sol"; import {IXCAmpleController} from "../../_interfaces/IXCAmpleController.sol"; import {IXCAmpleControllerGateway} from "../../_interfaces/IXCAmpleControllerGateway.sol"; import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; @@ -18,25 +18,37 @@ import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; * the xc-ample contracts. * */ -contract MaticXCAmpleTransferGateway is Layer2TransferGateway, FxBaseChildTunnel { +contract MaticXCAmpleTransferGateway is IMaticTransferGateway, FxBaseChildTunnel { using SafeMath for uint256; address public immutable xcAmple; address public immutable xcController; /** - * @dev Calculates the amount of xc-amples to be mint based on the amount and the total supply - * on ethereum when the transaction was initiated, and mints xc-amples to the recipient. - * "senderAddressInSourceChain": Address of the sender wallet in ethereum. - * "recipient": Address of the recipient wallet in matic. - * "amount": Amount of tokens that were locked on ethereum. - * "globalAMPLSupply": AMPL ERC-20 total supply on ethereum at the time of transfer. + * @dev Burns specified amount from the {msg.sender}'s and notifies the bridge about the transfer. + * @param recipientInTargetChain Address of the recipient wallet in the ethereum chain. + * @param amount Amount of tokens to be burnt on matic. + */ + function transfer(address recipientInTargetChain, uint256 amount) external override { + uint256 recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); + + IXCAmpleControllerGateway(xcController).burn(msg.sender, amount); + + emit XCTransferOut(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply); + + _sendMessageToRoot( + abi.encode(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply) + ); + } + + /** + * @dev Bridge callback. */ function _processMessageFromRoot( uint256 stateId, address sender, bytes memory data - ) internal override validateSender(sender) { + ) internal override { address senderInSourceChain; address recipient; uint256 amount; @@ -46,6 +58,23 @@ contract MaticXCAmpleTransferGateway is Layer2TransferGateway, FxBaseChildTunnel (address, address, uint256, uint256) ); + _executeTransfer(senderInSourceChain, recipient, amount, globalAMPLSupply); + } + + /** + * @dev Calculates the amount of amples to be unlocked based on the share of total supply and + * transfers it to the recipient. + * @param senderInSourceChain Address of the sender wallet in the matic chain. + * @param recipient Address of the recipient wallet in ethereum. + * @param amount Amount of tokens that were {burnt} on the matic. + * @param globalAMPLSupply AMPL ERC-20 total supply at the time of transfer. + */ + function _executeTransfer( + address senderInSourceChain, + address recipient, + uint256 amount, + uint256 globalAMPLSupply + ) internal { uint256 recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); uint256 mintAmount = amount.mul(recordedGlobalAMPLSupply).div(globalAMPLSupply); @@ -60,23 +89,6 @@ contract MaticXCAmpleTransferGateway is Layer2TransferGateway, FxBaseChildTunnel IXCAmpleControllerGateway(xcController).mint(recipient, mintAmount); } - /** - * @dev Burns specified amount from the {msg.sender}'s and notifies the bridge about the transfer. - * @param recipientInTargetChain Address of the recipient wallet in the ethereum chain. - * @param amount Amount of tokens to be burnt on matic. - */ - function transfer(address recipientInTargetChain, uint256 amount) external override { - uint256 recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); - - IXCAmpleControllerGateway(xcController).burn(msg.sender, amount); - - emit XCTransferOut(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply); - - _sendMessageToRoot( - abi.encode(msg.sender, recipientInTargetChain, amount, recordedGlobalAMPLSupply) - ); - } - constructor( address _fxChild, address xcAmple_,