From 67529a18de342258c89b2dfd50c77e6cb6b3755e Mon Sep 17 00:00:00 2001 From: aalavandhan1984 Date: Tue, 2 Nov 2021 15:05:13 -0400 Subject: [PATCH] arbitrum gateways --- contracts/_mocks/MockArbitrum.sol | 85 ++++ contracts/_test/UFragmentsTestnet.sol | 24 ++ .../ArbitrumXCAmpleGateway.sol | 325 +++++++++++++++ helpers/deploy/arbitrum.js | 72 ++++ .../dev3RinkebyArbitrumSatChain.json | 169 ++++++++ sdk/deployments/dev3RinkebyBaseChain.json | 262 ++++++++++++ tasks/deploy/arbitrum.js | 309 ++++++++++++++ .../bridge-gateways/ampl_arbitrum_gateway.js | 392 ++++++++++++++++++ .../arbitrum_xcampl_gateway.js | 320 ++++++++++++++ 9 files changed, 1958 insertions(+) create mode 100644 contracts/_mocks/MockArbitrum.sol create mode 100644 contracts/_test/UFragmentsTestnet.sol create mode 100644 contracts/satellite-chain/bridge-gateways/ArbitrumXCAmpleGateway.sol create mode 100644 helpers/deploy/arbitrum.js create mode 100644 sdk/deployments/dev3RinkebyArbitrumSatChain.json create mode 100644 sdk/deployments/dev3RinkebyBaseChain.json create mode 100644 tasks/deploy/arbitrum.js create mode 100644 test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js create mode 100644 test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js diff --git a/contracts/_mocks/MockArbitrum.sol b/contracts/_mocks/MockArbitrum.sol new file mode 100644 index 0000000..2dcdc08 --- /dev/null +++ b/contracts/_mocks/MockArbitrum.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.6.11; +pragma experimental ABIEncoderV2; + +import {InboxMock} from "arb-bridge-peripherals/contracts/tokenbridge/test/InboxMock.sol"; +import {L1ArbitrumMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/ethereum/L1ArbitrumMessenger.sol"; +import {L2ArbitrumMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/arbitrum/L2ArbitrumMessenger.sol"; +import {L1ArbitrumTestMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/test/GatewayTest.sol"; +import {L2ArbitrumTestMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/test/GatewayTest.sol"; +import {IBridge} from "arb-bridge-peripherals/contracts/tokenbridge/test/GatewayTest.sol"; +import {AMPLArbitrumGateway} from "../base-chain/bridge-gateways/AMPLArbitrumGateway.sol"; +import {ArbitrumXCAmpleGateway} from "../satellite-chain/bridge-gateways/ArbitrumXCAmpleGateway.sol"; + +contract MockArbitrumInbox is InboxMock {} + +// Mocking sendTxToL2 +// https://shorturl.at/dgABO +contract MockAMPLArbitrumGateway is L1ArbitrumTestMessenger, AMPLArbitrumGateway { + constructor( + address ampl_, + address policy_, + address vault_ + ) public AMPLArbitrumGateway(ampl_, policy_, vault_) {} + + function sendTxToL2( + address _inbox, + address _to, + address _user, + uint256 _l1CallValue, + uint256 _l2CallValue, + uint256 _maxSubmissionCost, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes memory _data + ) internal virtual override(L1ArbitrumMessenger, L1ArbitrumTestMessenger) returns (uint256) { + return + L1ArbitrumTestMessenger.sendTxToL2( + _inbox, + _to, + _user, + _l1CallValue, + _l2CallValue, + _maxSubmissionCost, + _maxGas, + _gasPriceBid, + _data + ); + } + + function getL2ToL1Sender(address _inbox) + internal + view + virtual + override(L1ArbitrumMessenger, L1ArbitrumTestMessenger) + returns (address) + { + return L1ArbitrumTestMessenger.getL2ToL1Sender(_inbox); + } + + function getBridge(address _inbox) + internal + view + virtual + override(L1ArbitrumMessenger, L1ArbitrumTestMessenger) + returns (IBridge) + { + return L1ArbitrumTestMessenger.getBridge(_inbox); + } +} + +contract MockArbitrumXCAmpleGateway is L2ArbitrumTestMessenger, ArbitrumXCAmpleGateway { + constructor(address xcAmple_, address xcController_) + public + ArbitrumXCAmpleGateway(xcAmple_, xcController_) + {} + + function sendTxToL1( + uint256 _l1CallValue, + address _from, + address _to, + bytes memory _data + ) internal virtual override(L2ArbitrumMessenger, L2ArbitrumTestMessenger) returns (uint256) { + return L2ArbitrumTestMessenger.sendTxToL1(_l1CallValue, _from, _to, _data); + } +} diff --git a/contracts/_test/UFragmentsTestnet.sol b/contracts/_test/UFragmentsTestnet.sol new file mode 100644 index 0000000..8bf1a18 --- /dev/null +++ b/contracts/_test/UFragmentsTestnet.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Importing uFragments contract dependencies to be compiled for integration tests +pragma solidity 0.7.6; + +import {UFragments} from "uFragments/contracts/UFragments.sol"; + +contract UFragmentsTestnet is UFragments { + event Result(bool result, bytes reason); + + function isArbitrumEnabled() external view returns (uint8) { + return uint8(0xa4b1); + } + + // NOTE: this allows the token contarct to register itself with the bridge on testnet + // The AMPL contract on mainnet is immutable and this can't be used! + function externalCall( + address destination, + bytes calldata data, + uint256 value + ) external payable { + (bool result, bytes memory reason) = destination.call{value: value}(data); + emit Result(result, reason); + } +} diff --git a/contracts/satellite-chain/bridge-gateways/ArbitrumXCAmpleGateway.sol b/contracts/satellite-chain/bridge-gateways/ArbitrumXCAmpleGateway.sol new file mode 100644 index 0000000..656c129 --- /dev/null +++ b/contracts/satellite-chain/bridge-gateways/ArbitrumXCAmpleGateway.sol @@ -0,0 +1,325 @@ +// 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 {L2ArbitrumMessenger} from "arb-bridge-peripherals/contracts/tokenbridge/arbitrum/L2ArbitrumMessenger.sol"; +import {AddressAliasHelper} from "arb-bridge-eth/contracts/libraries/AddressAliasHelper.sol"; + +import {IArbitrumBCRebaseGateway, IArbitrumBCTransferGateway, IArbitrumSCRebaseGateway, IArbitrumSCTransferGateway} from "../../_interfaces/bridge-gateways/IArbitrumGateway.sol"; +import {IXCAmpleController} from "../../_interfaces/IXCAmpleController.sol"; +import {IXCAmpleControllerGateway} from "../../_interfaces/IXCAmpleControllerGateway.sol"; +import {IXCAmple} from "../../_interfaces/IXCAmple.sol"; + +/// @dev Abstract l1 gateway contarct implementation to define function selector +abstract contract AMPLArbitrumGateway is IArbitrumBCRebaseGateway, IArbitrumBCTransferGateway { + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override { + require(false, "AMPLArbitrumGateway: NOT_IMPLEMENTED"); + } +} + +/** + * @title ArbitrumXCAmpleGateway: Arbitrum-XCAmple Gateway Contract + * @dev This contract is deployed on the l2 chain (Arbitrum). + * + * It's a pass-through contract between the Arbitrum's bridge and + * the the XCAmple contracts. + * + */ +contract ArbitrumXCAmpleGateway is + IArbitrumSCRebaseGateway, + IArbitrumSCTransferGateway, + L2ArbitrumMessenger, + Initializable +{ + using SafeMath for uint256; + + //-------------------------------------------------------------------------- + // XCAmple Satelltie chain gateway attributes + + /// @dev Address of XCAmple token on the satellite chain. + address public immutable xcAmple; + + /// @dev Address of XCAmple Controller on the satellite chain. + address public immutable xcController; + + //-------------------------------------------------------------------------- + // AMPL Base chain gateway attributes + + /// @dev Address of the AMPL ERC20 on the base chain. + address public ampl; + + //-------------------------------------------------------------------------- + // Arbitrum gateway attributes + + /// @dev Address if the arbitrum bridge router. + address public router; + + /// @dev Address of the counterpart gateway contract on the base chain + /// which "finalizes" cross chain transactions. + address public counterpartGateway; + + /// @dev Cross chain deposit nonce. + uint256 public exitNum; + + //-------------------------------------------------------------------------- + // Modifiers + + // @dev Validate incoming transactions before "finalization". + modifier onlyCounterpartGateway() { + require( + msg.sender == counterpartGateway || + AddressAliasHelper.undoL1ToL2Alias(msg.sender) == counterpartGateway, + "ArbitrumXCAmpleGateway: ONLY_COUNTERPART_GATEWAY" + ); + _; + } + + //-------------------------------------------------------------------------- + // Constructor + + /** + * @notice Instantiate the contract with references. + * @param xcAmple_ Address of the XCAmple ERC-20 on the satellite chain. + * @param xcController_ Address of the XCAmple Controller on the satellite chain. + */ + constructor(address xcAmple_, address xcController_) public { + xcAmple = xcAmple_; + xcController = xcController_; + } + + /** + * @notice Initialize contract with the addresses from the base chain. + * @param router_ Address of the arbitrum token transfer router on the satellite chain. + * @param ampl_ Address of the AMPL ERC-20 on the base chain. + * @param counterpartGateway_ Address the counterpart gateway contract on the base chain. + */ + function initialize( + address router_, + address ampl_, + address counterpartGateway_ + ) public initializer { + ampl = ampl_; + router = router_; + counterpartGateway = counterpartGateway_; + } + + //-------------------------------------------------------------------------- + // External methods + + /* + * @notice Forwards the most recent rebase information from the bridge to the XCAmple controller. + * @param globalAmpleforthEpoch Ampleforth monetary policy epoch from ethereum. + * @param globalAMPLSupply AMPL ERC-20 total supply from ethereum. + */ + function reportRebaseCommit(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply) + external + override + onlyCounterpartGateway + { + uint256 recordedGlobalAmpleforthEpoch = IXCAmpleController(xcController) + .globalAmpleforthEpoch(); + + uint256 recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); + + emit XCRebaseReportIn( + globalAmpleforthEpoch, + globalAMPLSupply, + recordedGlobalAmpleforthEpoch, + recordedGlobalAMPLSupply + ); + + IXCAmpleControllerGateway(xcController).reportRebase( + globalAmpleforthEpoch, + globalAMPLSupply + ); + } + + /** + * @notice Initiates a token withdrawal from Arbitrum to Ethereum. + * @param _l1Token l1 address of token. + * @param _to destination address. + * @param _amount amount of tokens withdrawn. + * @return res encoded unique identifier for withdrawal. + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256, /* _maxGas */ + uint256, /* _gasPriceBid */ + bytes calldata _data + ) public payable virtual override returns (bytes memory res) { + require(msg.sender == router, "ArbitrumXCAmpleGateway: NOT_FROM_ROUTER"); + + // the function is marked as payable to conform to the inheritance setup + // this particular code path shouldn't have a msg.value > 0 + require(msg.value == 0, "ArbitrumXCAmpleGateway: NO_VALUE"); + + require(_l1Token == ampl, "ArbitrumXCAmpleGateway: ONLY_AMPL_ALLOWED"); + + require(ampl != address(0), "AMPLArbitrumGateway: NOT_INITIALIZED"); + + address from = _parseDataFromRouterOnTransfer(_data); + + // Burn funds and log outbound transfer + uint256 recordedGlobalAMPLSupply; + { + recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); + + IXCAmpleControllerGateway(xcController).burn(from, _amount); + + emit XCTransferOut(from, address(0), _amount, recordedGlobalAMPLSupply); + } + + // Execute cross-chain transfer + return + abi.encode( + createOutboundTransfer(_l1Token, from, _to, _amount, recordedGlobalAMPLSupply) + ); + } + + /** + * @notice Mint on L2 upon L1 deposit. + * @dev Callable only by the L1ERC20Gateway.outboundTransfer method. + * @param _l1Token L1 address of ERC20. + * @param _from account that initiated the deposit in the L1. + * @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 to be minted to the user. + * @param _data encoded symbol/name/decimal data for deploy, in addition to any additional callhook data. + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override onlyCounterpartGateway { + require(_l1Token == ampl, "ArbitrumXCAmpleGateway: ONLY_AMPL_ALLOWED"); + + // Decode data from the bridge + uint256 globalAMPLSupply = abi.decode(_data, (uint256)); + + // Log inbound transfer and mint funds + uint256 mintAmount; + { + uint256 recordedGlobalAMPLSupply = IXCAmple(xcAmple).globalAMPLSupply(); + + mintAmount = _amount.mul(recordedGlobalAMPLSupply).div(globalAMPLSupply); + emit XCTransferIn( + address(0), + _to, + globalAMPLSupply, + mintAmount, + recordedGlobalAMPLSupply + ); + + IXCAmpleControllerGateway(xcController).mint(_to, mintAmount); + } + + emit DepositFinalized(_l1Token, _from, _to, mintAmount); + } + + //-------------------------------------------------------------------------- + // 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, + exitNum, + IXCAmple(xcAmple).globalAMPLSupply() + ); + } + + //-------------------------------------------------------------------------- + // Internal methods + + /// @dev Parses data packed by the router + /// @return The depositor address + function _parseDataFromRouterOnTransfer(bytes calldata _data) internal returns (address) { + address from; + bytes memory packedDataFromRouter; + (from, packedDataFromRouter) = GatewayMessageHandler.parseFromRouterToGateway(_data); + + require(packedDataFromRouter.length == 0, "ArbitrumXCAmpleGateway: EXTRA_DATA_DISABLED"); + + return from; + } + + /// @dev Builds and executes the outbound transfer. + /// @return seqNumber The bridge sequence number. + function createOutboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + uint256 recordedGlobalAMPLSupply + ) internal returns (uint256) { + // packed data sent over the bridge + bytes memory _outboundCallData = _getOutboundCalldata( + _l1Token, + _from, + _to, + _amount, + exitNum, + recordedGlobalAMPLSupply + ); + + uint256 id = sendTxToL1(0, _from, counterpartGateway, _outboundCallData); + + emit WithdrawalInitiated(_l1Token, _from, _to, id, exitNum, _amount); + + exitNum++; + + return id; + } + + function _getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + uint256 exitNum, + uint256 recordedGlobalAMPLSupply + ) internal view returns (bytes memory) { + bytes memory packedData = abi.encode(exitNum, recordedGlobalAMPLSupply); + + bytes memory outboundCalldata = abi.encodeWithSelector( + AMPLArbitrumGateway.finalizeInboundTransfer.selector, + _l1Token, + _from, + _to, + _amount, + packedData + ); + + return outboundCalldata; + } +} diff --git a/helpers/deploy/arbitrum.js b/helpers/deploy/arbitrum.js new file mode 100644 index 0000000..eda95b3 --- /dev/null +++ b/helpers/deploy/arbitrum.js @@ -0,0 +1,72 @@ +const { deployContract } = require('../contracts'); + +async function deployArbitrumBaseChainGatewayContracts( + { ampl, policy, tokenVault }, + ethers, + deployer, + txParams = {}, + waitBlocks = 0, +) { + const gateway = await deployContract( + ethers, + 'AMPLArbitrumGateway', + deployer, + [ampl.address, policy.address, tokenVault.address], + txParams, + waitBlocks, + ); + + const deployerAddress = await deployer.getAddress(); + if ((await tokenVault.owner()) == deployerAddress) { + await ( + await tokenVault.connect(deployer).addBridgeGateway(gateway.address) + ).wait(waitBlocks); + } else { + console.log( + 'Failed to add whitelist transfer gateway to vault as deployer not vault owner', + ); + console.log('Execute the following on-chain'); + console.log('addBridgeGateway', [gateway.address]); + } + + return { gateway }; +} + +async function deployArbitrumSatelliteChainGatewayContracts( + { xcAmple, xcAmpleController }, + ethers, + deployer, + txParams = {}, + waitBlocks = 0, +) { + const gateway = await deployContract( + ethers, + 'ArbitrumXCAmpleGateway', + deployer, + [xcAmple.address, xcAmpleController.address], + txParams, + waitBlocks, + ); + + const deployerAddress = await deployer.getAddress(); + if ((await xcAmpleController.owner()) == deployerAddress) { + await ( + await xcAmpleController + .connect(deployer) + .addBridgeGateway(gateway.address) + ).wait(waitBlocks); + } else { + console.log( + 'Failed to add whitelist transfer gateway to XCAmpleController as deployer owner', + ); + console.log('Execute the following on-chain'); + console.log('addBridgeGateway', [gateway.address]); + } + + return { gateway }; +} + +module.exports = { + deployArbitrumBaseChainGatewayContracts, + deployArbitrumSatelliteChainGatewayContracts, +}; diff --git a/sdk/deployments/dev3RinkebyArbitrumSatChain.json b/sdk/deployments/dev3RinkebyArbitrumSatChain.json new file mode 100644 index 0000000..c99eeed --- /dev/null +++ b/sdk/deployments/dev3RinkebyArbitrumSatChain.json @@ -0,0 +1,169 @@ +{ + "proxyAdmin": { + "address": "0xbAdBD05c2eC381B2DdF610E125A73baBda54fc7f", + "abi": [ + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function changeProxyAdmin(address proxy, address newAdmin)", + "function getProxyAdmin(address proxy) view returns (address)", + "function getProxyImplementation(address proxy) view returns (address)", + "function isOwner() view returns (bool)", + "function owner() view returns (address)", + "function renounceOwnership()", + "function transferOwnership(address newOwner)", + "function upgrade(address proxy, address implementation)", + "function upgradeAndCall(address proxy, address implementation, bytes data) payable" + ], + "hash": "0x800809255038a5d6659ff394b65b1334ac215c017d7ea8c43e16fa91c55ec753", + "blockNumber": 6019296 + }, + "xcAmple": { + "address": "0x4162aEE7665a2b8EafE5C257e600DEa7c4296f73", + "abi": [ + "event Approval(address indexed owner, address indexed spender, uint256 value)", + "event ControllerUpdated(address controller)", + "event LogRebase(uint256 indexed epoch, uint256 globalAMPLSupply)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "event Transfer(address indexed from, address indexed to, uint256 value)", + "function DOMAIN_SEPARATOR() view returns (bytes32)", + "function EIP712_DOMAIN() view returns (bytes32)", + "function EIP712_REVISION() view returns (string)", + "function PERMIT_TYPEHASH() view returns (bytes32)", + "function allowance(address owner_, address spender) view returns (uint256)", + "function approve(address spender, uint256 value) returns (bool)", + "function balanceOf(address who) view returns (uint256)", + "function burnFrom(address who, uint256 xcAmpleAmount)", + "function controller() view returns (address)", + "function decimals() view returns (uint8)", + "function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)", + "function globalAMPLSupply() view returns (uint256)", + "function increaseAllowance(address spender, uint256 addedValue) returns (bool)", + "function initialize(string name, string symbol, uint256 globalAMPLSupply_)", + "function mint(address who, uint256 xcAmpleAmount)", + "function name() view returns (string)", + "function nonces(address who) view returns (uint256)", + "function owner() view returns (address)", + "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)", + "function rebase(uint256 epoch, uint256 newGlobalAMPLSupply) returns (uint256)", + "function renounceOwnership()", + "function scaledBalanceOf(address who) view returns (uint256)", + "function scaledTotalSupply() pure returns (uint256)", + "function setController(address controller_)", + "function symbol() view returns (string)", + "function totalSupply() view returns (uint256)", + "function transfer(address to, uint256 value) returns (bool)", + "function transferAll(address to) returns (bool)", + "function transferAllFrom(address from, address to) returns (bool)", + "function transferFrom(address from, address to, uint256 value) returns (bool)", + "function transferOwnership(address newOwner)" + ], + "hash": "0xd14fd701a190ab766181adfeb5e94b94011cf6628672521b2e789f3066f5623d", + "blockNumber": 6019298 + }, + "xcAmpleController": { + "address": "0xDFbE71D6D42284e9a28aA84304c4696dE0e89f99", + "abi": [ + "event GatewayBurn(address indexed bridgeGateway, address indexed depositor, uint256 xcAmpleAmount)", + "event GatewayMint(address indexed bridgeGateway, address indexed recipient, uint256 xcAmpleAmount)", + "event GatewayRebaseReported(address indexed bridgeGateway, uint256 indexed epoch, uint256 globalAMPLSupply, uint256 timestampSec)", + "event GatewayWhitelistUpdated(address indexed bridgeGateway, bool active)", + "event LogRebase(uint256 indexed epoch, int256 requestedSupplyAdjustment, uint256 timestampSec)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function addBridgeGateway(address bridgeGateway)", + "function burn(address depositor, uint256 xcAmpleAmount)", + "function globalAmpleforthEpoch() view returns (uint256)", + "function globalAmpleforthEpochAndAMPLSupply() view returns (uint256, uint256)", + "function initialize(address xcAmple_, uint256 globalAmpleforthEpoch_)", + "function lastRebaseTimestampSec() view returns (uint256)", + "function mint(address recipient, uint256 xcAmpleAmount)", + "function nextGlobalAMPLSupply() view returns (uint256)", + "function nextGlobalAmpleforthEpoch() view returns (uint256)", + "function owner() view returns (address)", + "function rebase()", + "function rebaseRelayer() view returns (address)", + "function removeBridgeGateway(address bridgeGateway)", + "function renounceOwnership()", + "function reportRebase(uint256 nextGlobalAmpleforthEpoch_, uint256 nextGlobalAMPLSupply_)", + "function setRebaseRelayer(address rebaseRelayer_)", + "function transferOwnership(address newOwner)", + "function whitelistedBridgeGateways(address) view returns (bool)", + "function xcAmple() view returns (address)" + ], + "hash": "0x3081fdb2f1146254f743c8c4940b7fd6e6356927f94918227a07fc1ed4da4f9c", + "blockNumber": 6019305 + }, + "rebaseRelayer": { + "address": "0xBf1D78dEC7f1D82CCF0c45ab7Ee362e72BeE6f39", + "abi": [ + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "event TransactionFailed(address indexed destination, uint256 index, bytes data, bytes reason)", + "function addTransaction(address destination, uint256 value, bytes data)", + "function checkExecution(uint256 index) payable returns (bool)", + "function executeAll() payable returns (bool)", + "function owner() view returns (address)", + "function removeTransaction(uint256 index)", + "function renounceOwnership()", + "function setTransactionEnabled(uint256 index, bool enabled)", + "function totalValue() view returns (uint256)", + "function transactions(uint256) view returns (bool enabled, address destination, uint256 value, bytes data)", + "function transactionsSize() view returns (uint256)", + "function transferOwnership(address newOwner)" + ], + "hash": "0x48788898957d483844e03f54f7eb6c330f32d59503872c7864b8fc5469390c21", + "blockNumber": 6019311 + }, + "isBaseChain": false, + "arbitrum/transferGateway": { + "address": "0x75c00CF8B6C932AaD2E20fa238a57D389aE3842d", + "abi": [ + "constructor(address xcAmple_, address xcController_)", + "event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _to, uint256 _amount)", + "event RebaseReportFinalized(uint256 indexed _exitNum)", + "event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)", + "event WithdrawalInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _l2ToL1Id, uint256 _exitNum, uint256 _amount)", + "event XCRebaseReportIn(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply, uint256 recordedGlobalAmpleforthEpoch, uint256 recordedGlobalAMPLSupply)", + "event XCTransferIn(address indexed senderInSourceChain, address indexed recipient, uint256 globalAMPLSupply, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "event XCTransferOut(address indexed sender, address indexed recipientInTargetChain, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "function ampl() view returns (address)", + "function calculateL2TokenAddress(address token) view returns (address)", + "function counterpartGateway() view returns (address)", + "function exitNum() view returns (uint256)", + "function finalizeInboundTransfer(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) payable", + "function getOutboundCalldata(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes)", + "function initialize(address router_, address ampl_, address counterpartGateway_)", + "function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256, uint256, bytes _data) payable returns (bytes res)", + "function reportRebaseCommit(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply)", + "function router() view returns (address)", + "function xcAmple() view returns (address)", + "function xcController() view returns (address)" + ], + "hash": "0xb43520d64bc6ba1d1af688745608b7414e43457b484103f7f3f4a55c9bab2fe7", + "blockNumber": 6019546 + }, + "arbitrum/rebaseGateway": { + "address": "0x75c00CF8B6C932AaD2E20fa238a57D389aE3842d", + "abi": [ + "constructor(address xcAmple_, address xcController_)", + "event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _to, uint256 _amount)", + "event RebaseReportFinalized(uint256 indexed _exitNum)", + "event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)", + "event WithdrawalInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _l2ToL1Id, uint256 _exitNum, uint256 _amount)", + "event XCRebaseReportIn(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply, uint256 recordedGlobalAmpleforthEpoch, uint256 recordedGlobalAMPLSupply)", + "event XCTransferIn(address indexed senderInSourceChain, address indexed recipient, uint256 globalAMPLSupply, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "event XCTransferOut(address indexed sender, address indexed recipientInTargetChain, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "function ampl() view returns (address)", + "function calculateL2TokenAddress(address token) view returns (address)", + "function counterpartGateway() view returns (address)", + "function exitNum() view returns (uint256)", + "function finalizeInboundTransfer(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) payable", + "function getOutboundCalldata(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes)", + "function initialize(address router_, address ampl_, address counterpartGateway_)", + "function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256, uint256, bytes _data) payable returns (bytes res)", + "function reportRebaseCommit(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply)", + "function router() view returns (address)", + "function xcAmple() view returns (address)", + "function xcController() view returns (address)" + ], + "hash": "0xb43520d64bc6ba1d1af688745608b7414e43457b484103f7f3f4a55c9bab2fe7", + "blockNumber": 6019546 + } +} \ No newline at end of file diff --git a/sdk/deployments/dev3RinkebyBaseChain.json b/sdk/deployments/dev3RinkebyBaseChain.json new file mode 100644 index 0000000..dc7bf31 --- /dev/null +++ b/sdk/deployments/dev3RinkebyBaseChain.json @@ -0,0 +1,262 @@ +{ + "proxyAdmin": { + "address": "0x38fab7E342311cF384f76d1Ea31c4AFcc565c55c", + "abi": [ + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function changeProxyAdmin(address proxy, address newAdmin)", + "function getProxyAdmin(address proxy) view returns (address)", + "function getProxyImplementation(address proxy) view returns (address)", + "function isOwner() view returns (bool)", + "function owner() view returns (address)", + "function renounceOwnership()", + "function transferOwnership(address newOwner)", + "function upgrade(address proxy, address implementation)", + "function upgradeAndCall(address proxy, address implementation, bytes data) payable" + ], + "hash": "0x0c4d620b0220a581f617919b347f7a078f312bcc51c047a042b2a63fecb10835", + "blockNumber": 9535696 + }, + "ampl": { + "address": "0x244E12361f488adFa757E706466023673a96Fa3d", + "abi": [ + "event Approval(address indexed owner, address indexed spender, uint256 value)", + "event LogMonetaryPolicyUpdated(address monetaryPolicy)", + "event LogRebase(uint256 indexed epoch, uint256 totalSupply)", + "event OwnershipRenounced(address indexed previousOwner)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "event Result(bool result, bytes reason)", + "event Transfer(address indexed from, address indexed to, uint256 value)", + "function DOMAIN_SEPARATOR() view returns (bytes32)", + "function EIP712_DOMAIN() view returns (bytes32)", + "function EIP712_REVISION() view returns (string)", + "function PERMIT_TYPEHASH() view returns (bytes32)", + "function allowance(address owner_, address spender) view returns (uint256)", + "function approve(address spender, uint256 value) returns (bool)", + "function balanceOf(address who) view returns (uint256)", + "function decimals() view returns (uint8)", + "function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)", + "function externalCall(address destination, bytes data, uint256 value) payable", + "function increaseAllowance(address spender, uint256 addedValue) returns (bool)", + "function initialize(string name, string symbol, uint8 decimals)", + "function initialize(address owner_)", + "function isArbitrumEnabled() view returns (uint8)", + "function isOwner() view returns (bool)", + "function monetaryPolicy() view returns (address)", + "function name() view returns (string)", + "function nonces(address who) view returns (uint256)", + "function owner() view returns (address)", + "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)", + "function rebase(uint256 epoch, int256 supplyDelta) returns (uint256)", + "function renounceOwnership()", + "function scaledBalanceOf(address who) view returns (uint256)", + "function scaledTotalSupply() pure returns (uint256)", + "function setMonetaryPolicy(address monetaryPolicy_)", + "function symbol() view returns (string)", + "function totalSupply() view returns (uint256)", + "function transfer(address to, uint256 value) returns (bool)", + "function transferAll(address to) returns (bool)", + "function transferAllFrom(address from, address to) returns (bool)", + "function transferFrom(address from, address to, uint256 value) returns (bool)", + "function transferOwnership(address newOwner)" + ], + "hash": "0x4dc164878c11e4bd6ba0986435d68a013dfb1dcf762ac2624db4632802b58aa0", + "blockNumber": 9535696 + }, + "policy": { + "address": "0x4964610919942c8680A0717b342c2A6176FE16b2", + "abi": [ + "event LogRebase(uint256 indexed epoch, uint256 exchangeRate, uint256 cpi, int256 requestedSupplyAdjustment, uint256 timestampSec)", + "event OwnershipRenounced(address indexed previousOwner)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function cpiOracle() view returns (address)", + "function deviationThreshold() view returns (uint256)", + "function epoch() view returns (uint256)", + "function globalAmpleforthEpochAndAMPLSupply() view returns (uint256, uint256)", + "function inRebaseWindow() view returns (bool)", + "function initialize(address owner_, address uFrags_, uint256 baseCpi_)", + "function initialize(address sender)", + "function isOwner() view returns (bool)", + "function lastRebaseTimestampSec() view returns (uint256)", + "function marketOracle() view returns (address)", + "function minRebaseTimeIntervalSec() view returns (uint256)", + "function orchestrator() view returns (address)", + "function owner() view returns (address)", + "function rebase()", + "function rebaseLag() view returns (uint256)", + "function rebaseWindowLengthSec() view returns (uint256)", + "function rebaseWindowOffsetSec() view returns (uint256)", + "function renounceOwnership()", + "function setCpiOracle(address cpiOracle_)", + "function setDeviationThreshold(uint256 deviationThreshold_)", + "function setMarketOracle(address marketOracle_)", + "function setOrchestrator(address orchestrator_)", + "function setRebaseLag(uint256 rebaseLag_)", + "function setRebaseTimingParameters(uint256 minRebaseTimeIntervalSec_, uint256 rebaseWindowOffsetSec_, uint256 rebaseWindowLengthSec_)", + "function transferOwnership(address newOwner)", + "function uFrags() view returns (address)" + ], + "hash": "0xfbf40d4e2e276af206825e29bab48824a267fd9baec2822c60ab79321fdd4bc8", + "blockNumber": 9535712 + }, + "orchestrator": { + "address": "0x90dE686Ba9289A2978B23Bf199299A33eA3ef036", + "abi": [ + "constructor(address policy_)", + "event OwnershipRenounced(address indexed previousOwner)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function addTransaction(address destination, bytes data)", + "function initialize(address sender)", + "function isOwner() view returns (bool)", + "function owner() view returns (address)", + "function policy() view returns (address)", + "function rebase()", + "function removeTransaction(uint256 index)", + "function renounceOwnership()", + "function setTransactionEnabled(uint256 index, bool enabled)", + "function transactions(uint256) view returns (bool enabled, address destination, bytes data)", + "function transactionsSize() view returns (uint256)", + "function transferOwnership(address newOwner)" + ], + "hash": "0xe7bd741ac56bbbcaee274b1261f237864b537f2dfba5ac86ed696132fc72409d", + "blockNumber": 9535726 + }, + "rateOracle": { + "address": "0x7156F72425C0D6A729b0016F7DC1E00050f6DA6E", + "abi": [ + "function reportDelaySec() view returns (uint256)", + "function pushReport(uint256 payload)", + "function getData() returns (uint256, bool)", + "function addProvider(address provider)", + "function providers(uint256) view returns (address)", + "function renounceOwnership()", + "function removeProvider(address provider)", + "function owner() view returns (address)", + "function isOwner() view returns (bool)", + "function minimumProviders() view returns (uint256)", + "function purgeReports()", + "function setReportDelaySec(uint256 reportDelaySec_)", + "function providersSize() view returns (uint256)", + "function reportExpirationTimeSec() view returns (uint256)", + "function setMinimumProviders(uint256 minimumProviders_)", + "function providerReports(address, uint256) view returns (uint256 timestamp, uint256 payload)", + "function transferOwnership(address newOwner)", + "function setReportExpirationTimeSec(uint256 reportExpirationTimeSec_)", + "constructor(uint256 reportExpirationTimeSec_, uint256 reportDelaySec_, uint256 minimumProviders_)", + "event ProviderAdded(address provider)", + "event ProviderRemoved(address provider)", + "event ReportTimestampOutOfRange(address provider)", + "event ProviderReportPushed(address indexed provider, uint256 payload, uint256 timestamp)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)" + ], + "hash": "0x9f2e3ef3361cbce993f5dafc6bff2223164b6b75d61a842e721db1224577b3fc", + "blockNumber": 9535700 + }, + "cpiOracle": { + "address": "0x6e31D7Ea7176238392c07d58Fb9e3c6D78ae8910", + "abi": [ + "function reportDelaySec() view returns (uint256)", + "function pushReport(uint256 payload)", + "function getData() returns (uint256, bool)", + "function addProvider(address provider)", + "function providers(uint256) view returns (address)", + "function renounceOwnership()", + "function removeProvider(address provider)", + "function owner() view returns (address)", + "function isOwner() view returns (bool)", + "function minimumProviders() view returns (uint256)", + "function purgeReports()", + "function setReportDelaySec(uint256 reportDelaySec_)", + "function providersSize() view returns (uint256)", + "function reportExpirationTimeSec() view returns (uint256)", + "function setMinimumProviders(uint256 minimumProviders_)", + "function providerReports(address, uint256) view returns (uint256 timestamp, uint256 payload)", + "function transferOwnership(address newOwner)", + "function setReportExpirationTimeSec(uint256 reportExpirationTimeSec_)", + "constructor(uint256 reportExpirationTimeSec_, uint256 reportDelaySec_, uint256 minimumProviders_)", + "event ProviderAdded(address provider)", + "event ProviderRemoved(address provider)", + "event ReportTimestampOutOfRange(address provider)", + "event ProviderReportPushed(address indexed provider, uint256 payload, uint256 timestamp)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)" + ], + "hash": "0x62577b74d7b88b0a748651998d134af4de3cc692b95145430b24ff7fd7c9e721", + "blockNumber": 9535706 + }, + "isBaseChain": true, + "arbitrum/tokenVault": { + "address": "0x02C04C9DF6F994BFd54C66e770e5945f444A9526", + "abi": [ + "event GatewayLocked(address indexed bridgeGateway, address indexed token, address indexed depositor, uint256 amount)", + "event GatewayUnlocked(address indexed bridgeGateway, address indexed token, address indexed recipient, uint256 amount)", + "event GatewayWhitelistUpdated(address indexed bridgeGateway, bool active)", + "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", + "function addBridgeGateway(address bridgeGateway)", + "function lock(address token, address depositor, uint256 amount)", + "function owner() view returns (address)", + "function removeBridgeGateway(address bridgeGateway)", + "function renounceOwnership()", + "function totalLocked(address token) view returns (uint256)", + "function transferOwnership(address newOwner)", + "function unlock(address token, address recipient, uint256 amount)", + "function whitelistedBridgeGateways(address) view returns (bool)" + ], + "hash": "0x773e193e00f36636c7bb633e31e6acecc2b428b54f0543ee8c698be60110d7c1", + "blockNumber": 9535747 + }, + "arbitrum/transferGateway": { + "address": "0xbAb60bb39D6f086CA2e7e578fbF945E61a12cE3a", + "abi": [ + "constructor(address ampl_, address policy_, address vault_)", + "event DepositInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _sequenceNumber, uint256 _amount)", + "event RebaseReportInitiated(uint256 indexed _sequenceNumber)", + "event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data)", + "event WithdrawalFinalized(address l1Token, address indexed _from, address indexed _to, uint256 indexed _exitNum, uint256 _amount)", + "event XCRebaseReportOut(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply)", + "event XCTransferIn(address indexed senderInSourceChain, address indexed recipient, uint256 globalAMPLSupply, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "event XCTransferOut(address indexed sender, address indexed recipientInTargetChain, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "function ampl() view returns (address)", + "function calculateL2TokenAddress(address token) view returns (address)", + "function counterpartGateway() view returns (address)", + "function finalizeInboundTransfer(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) payable", + "function getOutboundCalldata(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes)", + "function inbox() view returns (address)", + "function initialize(address inbox_, address router_, address xcAmple_, address counterpartGateway_)", + "function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes _data) payable returns (bytes)", + "function policy() view returns (address)", + "function reportRebaseInit(uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid) payable returns (bytes)", + "function router() view returns (address)", + "function vault() view returns (address)", + "function xcAmple() view returns (address)" + ], + "hash": "0xbb5bda37bdd726212b9c9cc5c08045d8b4eeec3631d24ba4a3293b8f34362b54", + "blockNumber": 9535779 + }, + "arbitrum/rebaseGateway": { + "address": "0xbAb60bb39D6f086CA2e7e578fbF945E61a12cE3a", + "abi": [ + "constructor(address ampl_, address policy_, address vault_)", + "event DepositInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _sequenceNumber, uint256 _amount)", + "event RebaseReportInitiated(uint256 indexed _sequenceNumber)", + "event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data)", + "event WithdrawalFinalized(address l1Token, address indexed _from, address indexed _to, uint256 indexed _exitNum, uint256 _amount)", + "event XCRebaseReportOut(uint256 globalAmpleforthEpoch, uint256 globalAMPLSupply)", + "event XCTransferIn(address indexed senderInSourceChain, address indexed recipient, uint256 globalAMPLSupply, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "event XCTransferOut(address indexed sender, address indexed recipientInTargetChain, uint256 amount, uint256 recordedGlobalAMPLSupply)", + "function ampl() view returns (address)", + "function calculateL2TokenAddress(address token) view returns (address)", + "function counterpartGateway() view returns (address)", + "function finalizeInboundTransfer(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) payable", + "function getOutboundCalldata(address _l1Token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes)", + "function inbox() view returns (address)", + "function initialize(address inbox_, address router_, address xcAmple_, address counterpartGateway_)", + "function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes _data) payable returns (bytes)", + "function policy() view returns (address)", + "function reportRebaseInit(uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid) payable returns (bytes)", + "function router() view returns (address)", + "function vault() view returns (address)", + "function xcAmple() view returns (address)" + ], + "hash": "0xbb5bda37bdd726212b9c9cc5c08045d8b4eeec3631d24ba4a3293b8f34362b54", + "blockNumber": 9535779 + } +} \ No newline at end of file diff --git a/tasks/deploy/arbitrum.js b/tasks/deploy/arbitrum.js new file mode 100644 index 0000000..bccbcc5 --- /dev/null +++ b/tasks/deploy/arbitrum.js @@ -0,0 +1,309 @@ +const { Bridge } = require('arb-ts'); +const { hexDataLength } = require('@ethersproject/bytes'); +const { + txTask, + loadSignerSync, + etherscanVerify, +} = require('../../helpers/tasks'); +const { getEthersProvider } = require('../../helpers/utils'); +const { + getDeployedContractInstance, + readDeploymentData, + writeDeploymentData, + writeBulkDeploymentData, + getCompiledContractFactory, +} = require('../../helpers/contracts'); + +const { + deployArbitrumBaseChainGatewayContracts, + deployArbitrumSatelliteChainGatewayContracts, +} = require('../../helpers/deploy'); + +txTask( + 'deploy:arbitrum_base_chain', + 'Deploys the chain gateway on the base chain', +).setAction(async (args, hre) => { + const txParams = { gasPrice: args.gasPrice, gasLimit: args.gasLimit }; + if (txParams.gasPrice == 0) { + txParams.gasPrice = await hre.ethers.provider.getGasPrice(); + } + + const deployer = loadSignerSync(args, hre.ethers.provider); + const deployerAddress = await deployer.getAddress(); + const chainAddresses = await readDeploymentData(hre.network.name); + + console.log('------------------------------------------------------------'); + console.log('Deploying contracts on base-chain'); + console.log('Deployer:', deployerAddress); + console.log(txParams); + + const ampl = await getDeployedContractInstance( + hre.network.name, + 'ampl', + hre.ethers.provider, + ); + + const policy = await getDeployedContractInstance( + hre.network.name, + 'policy', + hre.ethers.provider, + ); + + const tokenVault = await getDeployedContractInstance( + hre.network.name, + 'arbitrum/tokenVault', + hre.ethers.provider, + ); + + const { gateway } = await deployArbitrumBaseChainGatewayContracts( + { + ampl, + policy, + tokenVault, + }, + hre.ethers, + deployer, + txParams, + 5, + ); + + console.log('------------------------------------------------------------'); + console.log('Writing data to file'); + await writeDeploymentData( + hre.network.name, + 'arbitrum/transferGateway', + gateway, + ); + await writeDeploymentData( + hre.network.name, + 'arbitrum/rebaseGateway', + gateway, + ); + + console.log('------------------------------------------------------------'); + console.log('Verify on etherscan'); + await etherscanVerify(hre, gateway.address, [ + ampl.address, + policy.address, + tokenVault.address, + ]); +}); + +txTask( + 'deploy:arbitrum_satellite_chain', + 'Deploys the chain gateway contract and connects it with arbitrum bridge and the cross-chain ample token', +).setAction(async (args, hre) => { + // NOTE: gas estimation is off on arbitrum + // const txParams = { gasPrice: args.gasPrice, gasLimit: args.gasLimit }; + // if (txParams.gasPrice == 0) { + // txParams.gasPrice = await hre.ethers.provider.getGasPrice(); + // } + const txParams = {}; + const deployer = loadSignerSync(args, hre.ethers.provider); + const deployerAddress = await deployer.getAddress(); + const chainAddresses = await readDeploymentData(hre.network.name); + + console.log('------------------------------------------------------------'); + console.log('Deploying contracts on satellite-chain'); + console.log('Deployer:', deployerAddress); + console.log(txParams); + + const xcAmple = await getDeployedContractInstance( + hre.network.name, + 'xcAmple', + hre.ethers.provider, + ); + + const xcAmpleController = await getDeployedContractInstance( + hre.network.name, + 'xcAmpleController', + hre.ethers.provider, + ); + + const { gateway } = await deployArbitrumSatelliteChainGatewayContracts( + { xcAmple, xcAmpleController }, + hre.ethers, + deployer, + txParams, + 5, + ); + + console.log('------------------------------------------------------------'); + console.log('Writing data to file'); + await writeDeploymentData( + hre.network.name, + 'arbitrum/transferGateway', + gateway, + ); + await writeDeploymentData( + hre.network.name, + 'arbitrum/rebaseGateway', + gateway, + ); + + console.log('------------------------------------------------------------'); + console.log('Verify on etherscan'); + await etherscanVerify(hre, gateway.address, [ + xcAmple.address, + xcAmpleController.address, + ]); +}); + +txTask('deploy:arbitrum_connection', 'Connects the two gateway contracts') + .addParam('baseChainNetwork', 'The network name of the base chain network') + .addParam( + 'satChainNetwork', + 'The network name of the satellite chain network', + ) + .addParam('baseInbox', 'The address of the arbitrum inbox on the base chain') + .addParam( + 'baseRouter', + 'The address of the arbitrum router contract on the base chain', + ) + .addParam( + 'satRouter', + 'The address of the arbitrum router contract on the satellite chain', + ) + .setAction(async (args, hre) => { + const txParams = { gasPrice: args.gasPrice, gasLimit: args.gasLimit }; + if (txParams.gasPrice == 0) { + txParams.gasPrice = await hre.ethers.provider.getGasPrice(); + } + + const baseChainProvider = getEthersProvider(args.baseChainNetwork); + const satChainProvider = getEthersProvider(args.satChainNetwork); + + const baseChainSigner = loadSignerSync(args, baseChainProvider); + const satChainSigner = loadSignerSync(args, satChainProvider); + + const baseGateway = await getDeployedContractInstance( + args.baseChainNetwork, + 'arbitrum/rebaseGateway', + baseChainProvider, + ); + const ampl = await getDeployedContractInstance( + args.baseChainNetwork, + 'ampl', + baseChainProvider, + ); + + const satGateway = await getDeployedContractInstance( + args.satChainNetwork, + 'arbitrum/rebaseGateway', + satChainProvider, + ); + const xcAmple = await getDeployedContractInstance( + args.satChainNetwork, + 'xcAmple', + satChainProvider, + ); + + await ( + await baseGateway + .connect(baseChainSigner) + .initialize( + args.baseInbox, + args.baseRouter, + xcAmple.address, + satGateway.address, + txParams, + ) + ).wait(2); + + // NOTE: gas estimation is off on arbitrum + await ( + await satGateway + .connect(satChainSigner) + .initialize(args.satRouter, ampl.address, baseGateway.address, {}) + ).wait(2); + }); + +txTask('deploy:arbitrum_register_testnet', 'Registers the token to the router') + .addParam('baseChainNetwork', 'The network name of the base chain network') + .addParam( + 'satChainNetwork', + 'The network name of the satellite chain network', + ) + .addParam( + 'baseRouter', + 'The address of the arbitrum router contract on the base chain', + ) + .setAction(async (args, hre) => { + const txParams = { gasPrice: args.gasPrice, gasLimit: args.gasLimit }; + if (txParams.gasPrice == 0) { + txParams.gasPrice = await hre.ethers.provider.getGasPrice(); + } + + const baseChainProvider = getEthersProvider(args.baseChainNetwork); + const satChainProvider = getEthersProvider(args.satChainNetwork); + + const baseChainSigner = loadSignerSync(args, baseChainProvider); + const satChainSigner = loadSignerSync(args, satChainProvider); + + const routerABI = [ + { + inputs: [ + { internalType: 'address', name: '_gateway', type: 'address' }, + { internalType: 'uint256', name: '_maxGas', type: 'uint256' }, + { internalType: 'uint256', name: '_gasPriceBid', type: 'uint256' }, + { + internalType: 'uint256', + name: '_maxSubmissionCost', + type: 'uint256', + }, + ], + name: 'setGateway', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'payable', + type: 'function', + }, + ]; + const router = new ethers.Contract( + args.baseRouter, + routerABI, + hre.ethers.provider, + ); + + const ampl = await getDeployedContractInstance( + args.baseChainNetwork, + 'ampl', + baseChainProvider, + ); + + const baseGateway = await getDeployedContractInstance( + args.baseChainNetwork, + 'arbitrum/transferGateway', + baseChainProvider, + ); + + const arb = await Bridge.init(baseChainSigner, satChainSigner); + const fnDataBytes = ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'uint256', 'uint256'], + [baseGateway.address, '0', '0', '0'], + ); + const fnBytesLength = hexDataLength(fnDataBytes) + 4; + const [_submissionPriceWei, nextUpdateTimestamp] = + await arb.l2Bridge.getTxnSubmissionPrice(fnBytesLength); + const submissionPriceWei = _submissionPriceWei.mul(5); // buffer can be reduced + const maxGas = 500000; + const gasPriceBid = await satChainProvider.getGasPrice(); + const callValue = submissionPriceWei.add(gasPriceBid.mul(maxGas)); + + const ptx = await router.populateTransaction.setGateway( + baseGateway.address, + maxGas, + gasPriceBid, + submissionPriceWei, + ); + + const tx = await ampl + .connect(baseChainSigner) + .externalCall(ptx.to, ptx.data, callValue, { + ...txParams, + value: callValue, + }); + + console.log(tx.hash); + + await tx.wait(2); + }); diff --git a/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js b/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js new file mode 100644 index 0000000..c219995 --- /dev/null +++ b/test/unit/base-chain/bridge-gateways/ampl_arbitrum_gateway.js @@ -0,0 +1,392 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); + +let accounts, + deployer, + depositor, + depositorAddress, + recipient, + recipientAddress, + router, + routerAddress, + counterpartGateway, + counterpartAddress, + inbox, + ampl, + policy, + vault, + xcAmple, + gateway; + +// l2 gas paramters +const maxSubmissionCost = 1; +const maxGas = 500000; +const gasPriceBid = 0; + +async function setupContracts() { + accounts = await ethers.getSigners(); + deployer = accounts[0]; + depositor = deployer; + depositorAddress = await deployer.getAddress(); + recipient = accounts[1]; + recipientAddress = await recipient.getAddress(); + router = accounts[2]; + routerAddress = await router.getAddress(); + counterpartGateway = accounts[3]; + counterpartAddress = await counterpartGateway.getAddress(); + + inbox = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockArbitrumInbox', + ) + ) + .connect(deployer) + .deploy(); + + const uFragmentsFactory = await ethers.getContractFactory( + 'uFragments/contracts/UFragments.sol:UFragments', + ); + ampl = await upgrades.deployProxy( + uFragmentsFactory.connect(deployer), + [depositorAddress], + { initializer: 'initialize(address)' }, + ); + await ampl.setMonetaryPolicy(depositorAddress); + + policy = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockAmpleforth.sol:MockAmpleforth', + ) + ) + .connect(deployer) + .deploy(); + vault = await ( + await ethers.getContractFactory('contracts/_mocks/MockVault.sol:MockVault') + ) + .connect(deployer) + .deploy(); + + xcAmple = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmple.sol:MockXCAmple', + ) + ) + .connect(deployer) + .deploy(); + + gateway = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockAMPLArbitrumGateway', + ) + ) + .connect(deployer) + .deploy(ampl.address, policy.address, vault.address); + + await gateway.initialize( + inbox.address, + routerAddress, + xcAmple.address, + counterpartAddress, + ); + await inbox.setL2ToL1Sender(counterpartAddress); + + await policy.updateEpoch(1); + await ampl.rebase(1, '-49999999999950000'); +} + +describe('AMPLArbitrumGateway:Initialization', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should initialize the references', async function () { + expect(await gateway.ampl()).to.eq(ampl.address); + expect(await gateway.policy()).to.eq(policy.address); + expect(await gateway.vault()).to.eq(vault.address); + expect(await gateway.inbox()).to.eq(inbox.address); + expect(await gateway.router()).to.eq(routerAddress); + expect(await gateway.counterpartGateway()).to.eq(counterpartAddress); + expect(await gateway.xcAmple()).to.eq(xcAmple.address); + expect(await gateway.calculateL2TokenAddress(ampl.address)).to.eq( + xcAmple.address, + ); + }); +}); + +describe('AMPLArbitrumGateway:reportRebaseInit', () => { + let r, seqNumber; + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + seqNumber = await gateway + .connect(depositor) + .callStatic.reportRebaseInit(maxSubmissionCost, maxGas, gasPriceBid); + r = gateway + .connect(depositor) + .reportRebaseInit(maxSubmissionCost, maxGas, gasPriceBid); + }); + + it('should emit XCRebaseReportOut', async function () { + await expect(r).to.emit(gateway, 'XCRebaseReportOut').withArgs(1, 50000); + }); + + it('should emit RebaseReportInitiated', async function () { + await expect(r) + .to.emit(gateway, 'RebaseReportInitiated') + .withArgs(seqNumber); + }); +}); + +describe('AMPLArbitrumGateway:outboundTransfer:accessControl', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should NOT be callable by non-router', async function () { + await expect( + gateway + .connect(depositor) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + [], + ), + ).to.be.revertedWith('AMPLArbitrumGateway: NOT_FROM_ROUTER'); + }); + + it('should NOT be callable for other tokens', async function () { + await expect( + gateway + .connect(router) + .outboundTransfer( + xcAmple.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + [], + ), + ).to.be.revertedWith('AMPLArbitrumGateway: ONLY_AMPL_ALLOWED'); + }); + + it('should NOT allow extra data', async function () { + let data = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [ + maxSubmissionCost, + ethers.utils.defaultAbiCoder.encode(['uint256'], ['123']), + ], + ); + + data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, data], + ); + + await expect( + gateway + .connect(router) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ), + ).to.be.revertedWith('AMPLArbitrumGateway: EXTRA_DATA_DISABLED'); + }); +}); + +describe('AMPLArbitrumGateway:outboundTransfer', () => { + let r, seqNumber; + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + // router usually does this encoding part + let data = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, '0x'], + ); + + data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, data], + ); + + await ampl.connect(depositor).approve(gateway.address, 1001); + + seqNumber = await gateway + .connect(router) + .callStatic.outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ); + r = gateway + .connect(router) + .outboundTransfer( + ampl.address, + recipientAddress, + 1001, + maxGas, + gasPriceBid, + data, + ); + }); + + it('should emit XCTransferOut', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferOut') + .withArgs(depositorAddress, ethers.constants.AddressZero, 1001, 50000); + }); + + it('should emit DepositInitiated', async function () { + await expect(r) + .to.emit(gateway, 'DepositInitiated') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + seqNumber, + 1001, + ); + }); + + it('should lock into vault', async function () { + await expect(r) + .to.emit(vault, 'Lock') + .withArgs(ampl.address, gateway.address, 1001); + }); +}); + +describe('AMPLArbitrumGateway:finalizeInboundTransfer:accessControl', () => { + before('setup AMPLArbitrumGateway contract', setupContracts); + + it('should revert when called by non counterpart', async function () { + const r = gateway + .connect(deployer) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + [], + ); + await expect(r).to.be.revertedWith( + 'AMPLArbitrumGateway: ONLY_COUNTERPART_GATEWAY', + ); + }); +}); + +describe('AMPLArbitrumGateway:finalizeInboundTransfer', () => { + let r, seqNumber; + + describe('when supply is out of sync', function () { + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + const exitNum = 123213213; + const withdrawData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [exitNum, 50000], + ); + + await policy.updateEpoch(2); + await ampl.rebase(2, 50000); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + withdrawData, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 100000, + ); + }); + + it('should emit WithdrawalFinalized', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalFinalized') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + 123213213, + 2002, + ); + }); + + it('should unlock from vault', async function () { + await expect(r) + .to.emit(vault, 'Unlock') + .withArgs(ampl.address, recipientAddress, 2002); + }); + }); + + describe('when supply is in sync', function () { + before('setup AMPLArbitrumGateway contract', async function () { + await setupContracts(); + + const exitNum = 89324; + const withdrawData = ethers.utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [exitNum, 50000], + ); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + withdrawData, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 50000, + ); + }); + + it('should emit WithdrawalFinalized', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalFinalized') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + 89324, + 1001, + ); + }); + + it('should unlock from vault', async function () { + await expect(r) + .to.emit(vault, 'Unlock') + .withArgs(ampl.address, recipientAddress, 1001); + }); + }); +}); diff --git a/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js b/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js new file mode 100644 index 0000000..ad3dc81 --- /dev/null +++ b/test/unit/satellite-chain/bridge-gateways/arbitrum_xcampl_gateway.js @@ -0,0 +1,320 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); + +let accounts, + deployer, + depositor, + depositorAddress, + recipient, + recipientAddress, + router, + routerAddress, + counterpartGateway, + counterpartAddress, + ampl, + xcAmple, + xcController, + gateway; + +async function setupContracts() { + accounts = await ethers.getSigners(); + deployer = accounts[0]; + depositor = deployer; + depositorAddress = await deployer.getAddress(); + recipient = accounts[1]; + recipientAddress = await recipient.getAddress(); + router = accounts[2]; + routerAddress = await router.getAddress(); + counterpartGateway = accounts[3]; + counterpartAddress = await counterpartGateway.getAddress(); + + xcAmple = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmple.sol:MockXCAmple', + ) + ) + .connect(deployer) + .deploy(); + xcController = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockXCAmpleController.sol:MockXCAmpleController', + ) + ) + .connect(deployer) + .deploy(); + + ampl = await ( + await ethers.getContractFactory('contracts/_mocks/MockAmpl.sol:MockAmpl') + ) + .connect(deployer) + .deploy(); + + gateway = await ( + await ethers.getContractFactory( + 'contracts/_mocks/MockArbitrum.sol:MockArbitrumXCAmpleGateway', + ) + ) + .connect(deployer) + .deploy(xcAmple.address, xcController.address); + + await gateway.initialize(routerAddress, ampl.address, counterpartAddress); + + await ampl.updateTotalSupply(50000); + await xcController.updateAMPLEpoch(1); + await xcAmple.updateGlobalAMPLSupply(50000); +} + +describe('ArbitrumXCAmpleGateway:Initialization', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should initialize the references', async function () { + expect(await gateway.xcAmple()).to.eq(xcAmple.address); + expect(await gateway.xcController()).to.eq(xcController.address); + expect(await gateway.ampl()).to.eq(ampl.address); + expect(await gateway.router()).to.eq(routerAddress); + expect(await gateway.counterpartGateway()).to.eq(counterpartAddress); + }); +}); + +describe('ArbitrumXCAmpleGateway:reportRebaseCommit:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should NOT be callable by non gateway', async function () { + await expect( + gateway.connect(deployer).reportRebaseCommit(1, 50000), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: ONLY_COUNTERPART_GATEWAY'); + }); +}); + +describe('ArbitrumXCAmpleGateway:reportRebaseCommit', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + describe('when on-chain supply is different', async function () { + it('should emit XCRebaseReportIn', async function () { + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(2, 100000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(2, 100000, 1, 50000); + + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(3, 40000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(3, 40000, 1, 50000); + }); + }); + + describe('when on-chain supply is the same', async function () { + it('should emit XCRebaseReportIn', async function () { + await expect( + gateway.connect(counterpartGateway).reportRebaseCommit(2, 50000), + ) + .to.emit(gateway, 'XCRebaseReportIn') + .withArgs(2, 50000, 1, 50000); + }); + }); +}); + +describe('ArbitrumXCAmpleGateway:outboundTransfer:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should NOT be callable by non-gateway', async function () { + await expect( + gateway + .connect(depositor) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, []), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: NOT_FROM_ROUTER'); + }); + + it('should NOT be callable for other tokens', async function () { + await expect( + gateway + .connect(router) + .outboundTransfer(xcAmple.address, recipientAddress, 1001, 0, 0, []), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: ONLY_AMPL_ALLOWED'); + }); + + it('should NOT allow extra data', async function () { + const data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, '0x01'], + ); + + await expect( + gateway + .connect(router) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, data), + ).to.be.revertedWith('ArbitrumXCAmpleGateway: EXTRA_DATA_DISABLED'); + }); +}); + +describe('ArbitrumXCAmpleGateway:outboundTransfer', () => { + let r, seqNumber, exitNum; + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + // router usually does this encoding part + const data = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [depositorAddress, '0x'], + ); + + exitNum = await gateway.exitNum(); + + seqNumber = await gateway + .connect(router) + .callStatic.outboundTransfer( + ampl.address, + recipientAddress, + 1001, + 0, + 0, + data, + ); + + r = gateway + .connect(router) + .outboundTransfer(ampl.address, recipientAddress, 1001, 0, 0, data); + }); + + it('should emit XCTransferOut', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferOut') + .withArgs(depositorAddress, ethers.constants.AddressZero, 1001, 50000); + }); + + it('should emit WithdrawalInitiated', async function () { + await expect(r) + .to.emit(gateway, 'WithdrawalInitiated') + .withArgs( + ampl.address, + depositorAddress, + recipientAddress, + seqNumber, + exitNum, + 1001, + ); + }); + + it('should increment exitNum', async function () { + expect(await gateway.exitNum()).to.eq(exitNum.add(1)); + }); + + it('should burn xcAmples', async function () { + await expect(r) + .to.emit(xcController, 'Burn') + .withArgs(depositorAddress, 1001); + }); +}); + +describe('ArbitrumXCAmpleGateway:finalizeInboundTransfer:accessControl', () => { + before('setup ArbitrumXCAmpleGateway contract', setupContracts); + + it('should revert when called by non counterpart', async function () { + const r = gateway + .connect(deployer) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + [], + ); + await expect(r).to.be.revertedWith( + 'ArbitrumXCAmpleGateway: ONLY_COUNTERPART_GATEWAY', + ); + }); +}); + +describe('ArbitrumXCAmpleGateway:finalizeInboundTransfer', () => { + let r, seqNumber; + + describe('when supply is out of sync', function () { + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + const exitNum = 0; + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [50000]); + + await xcAmple.updateGlobalAMPLSupply(100000); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + data, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 2002, + 100000, + ); + }); + + it('should emit DepositFinalized', async function () { + await expect(r) + .to.emit(gateway, 'DepositFinalized') + .withArgs(ampl.address, depositorAddress, recipientAddress, 2002); + }); + + it('should mint', async function () { + await expect(r) + .to.emit(xcController, 'Mint') + .withArgs(recipientAddress, 2002); + }); + }); + + describe('when supply is in sync', function () { + before('setup ArbitrumXCAmpleGateway contract', async function () { + await setupContracts(); + + const exitNum = 0; + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [50000]); + + r = gateway + .connect(counterpartGateway) + .finalizeInboundTransfer( + ampl.address, + depositorAddress, + recipientAddress, + 1001, + data, + ); + }); + + it('should emit XCTransferIn', async function () { + await expect(r) + .to.emit(gateway, 'XCTransferIn') + .withArgs( + ethers.constants.AddressZero, + recipientAddress, + 50000, + 1001, + 50000, + ); + }); + + it('should emit DepositFinalized', async function () { + await expect(r) + .to.emit(gateway, 'DepositFinalized') + .withArgs(ampl.address, depositorAddress, recipientAddress, 1001); + }); + + it('should mint', async function () { + await expect(r) + .to.emit(xcController, 'Mint') + .withArgs(recipientAddress, 1001); + }); + }); +});