diff --git a/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol b/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol index 8451215af6e46..8bf28caf15fcb 100644 --- a/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol @@ -38,15 +38,6 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver { __CrossDomainMessenger_init(Predeploys.L2_CROSS_DOMAIN_MESSENGER, blockedSystemAddresses); } - /** - * @notice Checks whether the message being sent from the other messenger. - * - * @return True if the message was sent from the messenger, false otherwise. - */ - function _isOtherMessenger() internal view override returns (bool) { - return msg.sender == address(portal) && portal.l2Sender() == otherMessenger; - } - /** * @notice Sends a message via the OptimismPortal contract. * @@ -63,4 +54,13 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver { ) internal override { portal.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data); } + + /** + * @notice Checks whether the message being sent from the other messenger. + * + * @return True if the message was sent from the messenger, false otherwise. + */ + function _isOtherMessenger() internal view override returns (bool) { + return msg.sender == address(portal) && portal.l2Sender() == otherMessenger; + } } diff --git a/packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol b/packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol index 34423d3772be7..52023a87a9311 100644 --- a/packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/contracts/L1/L1StandardBridge.sol @@ -94,23 +94,27 @@ contract L1StandardBridge is StandardBridge, Semver { initialize(_messenger); } - /** - * @notice Initializer. - * - * @param _messenger Address of the L1CrossDomainMessenger. - */ - function initialize(address payable _messenger) public initializer { - __StandardBridge_init(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE)); - } - /** * @custom:legacy - * @notice Retrieves the access of the corresponding L2 bridge contract. + * @notice Finalizes a withdrawal of ERC20 tokens from L2. * - * @return Address of the corresponding L2 bridge contract. + * @param _l1Token Address of the token on L1. + * @param _l2Token Address of the corresponding token on L2. + * @param _from Address of the withdrawer on L2. + * @param _to Address of the recipient on L1. + * @param _amount Amount of ETH to withdraw. + * @param _extraData Optional data forwarded from L2. */ - function l2TokenBridge() external view returns (address) { - return address(otherBridge); + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _extraData + ) external onlyOtherBridge { + emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _extraData); + finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData); } /** @@ -231,25 +235,21 @@ contract L1StandardBridge is StandardBridge, Semver { /** * @custom:legacy - * @notice Finalizes a withdrawal of ERC20 tokens from L2. + * @notice Retrieves the access of the corresponding L2 bridge contract. * - * @param _l1Token Address of the token on L1. - * @param _l2Token Address of the corresponding token on L2. - * @param _from Address of the withdrawer on L2. - * @param _to Address of the recipient on L1. - * @param _amount Amount of ETH to withdraw. - * @param _extraData Optional data forwarded from L2. + * @return Address of the corresponding L2 bridge contract. */ - function finalizeERC20Withdrawal( - address _l1Token, - address _l2Token, - address _from, - address _to, - uint256 _amount, - bytes calldata _extraData - ) external onlyOtherBridge { - emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _extraData); - finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData); + function l2TokenBridge() external view returns (address) { + return address(otherBridge); + } + + /** + * @notice Initializer. + * + * @param _messenger Address of the L1CrossDomainMessenger. + */ + function initialize(address payable _messenger) public initializer { + __StandardBridge_init(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE)); } /** diff --git a/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol b/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol index a5d9a7f8d1b0b..a308f156f03b8 100644 --- a/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol +++ b/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol @@ -16,40 +16,6 @@ import { Types } from "../libraries/Types.sol"; */ // slither-disable-next-line locked-ether contract L2OutputOracle is OwnableUpgradeable, Semver { - /** - * @notice Emitted when an output is proposed. - * - * @param outputRoot The output root. - * @param l1Timestamp The L1 timestamp when proposed. - * @param l2BlockNumber The L2 block number of the output root. - */ - event OutputProposed( - bytes32 indexed outputRoot, - uint256 indexed l1Timestamp, - uint256 indexed l2BlockNumber - ); - - /** - * @notice Emitted when an output is deleted. - * - * @param outputRoot The output root. - * @param l1Timestamp The L1 timestamp when proposed. - * @param l2BlockNumber The L2 block number of the output root. - */ - event OutputDeleted( - bytes32 indexed outputRoot, - uint256 indexed l1Timestamp, - uint256 indexed l2BlockNumber - ); - - /** - * @notice Emitted when the proposer address is changed. - * - * @param previousProposer The previous proposer address. - * @param newProposer The new proposer address. - */ - event ProposerChanged(address indexed previousProposer, address indexed newProposer); - /** * @notice The interval in L2 blocks at which checkpoints must be submitted. */ @@ -97,6 +63,40 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { */ mapping(uint256 => Types.OutputProposal) internal l2Outputs; + /** + * @notice Emitted when an output is proposed. + * + * @param outputRoot The output root. + * @param l1Timestamp The L1 timestamp when proposed. + * @param l2BlockNumber The L2 block number of the output root. + */ + event OutputProposed( + bytes32 indexed outputRoot, + uint256 indexed l1Timestamp, + uint256 indexed l2BlockNumber + ); + + /** + * @notice Emitted when an output is deleted. + * + * @param outputRoot The output root. + * @param l1Timestamp The L1 timestamp when proposed. + * @param l2BlockNumber The L2 block number of the output root. + */ + event OutputDeleted( + bytes32 indexed outputRoot, + uint256 indexed l1Timestamp, + uint256 indexed l2BlockNumber + ); + + /** + * @notice Emitted when the proposer address is changed. + * + * @param previousProposer The previous proposer address. + * @param newProposer The new proposer address. + */ + event ProposerChanged(address indexed previousProposer, address indexed newProposer); + /** * @notice Reverts if called by any account other than the proposer. */ @@ -142,24 +142,31 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { } /** - * @notice Initializer. + * @notice Deletes the most recent output. This is used to remove the most recent output in the + * event that an erreneous output is submitted. It can only be called by the contract's + * owner, not the proposer. Longer term, this should be replaced with a more robust + * mechanism which will allow deletion of proposals shown to be invalid by a fault + * proof. * - * @param _genesisL2Output The initial L2 output of the L2 chain. - * @param _startingBlockNumber The timestamp to start L2 block at. - * @param _proposer The address of the proposer. - * @param _owner The address of the owner. + * @param _proposal Represents the output proposal to delete */ - function initialize( - bytes32 _genesisL2Output, - uint256 _startingBlockNumber, - address _proposer, - address _owner - ) public initializer { - l2Outputs[_startingBlockNumber] = Types.OutputProposal(_genesisL2Output, block.timestamp); - latestBlockNumber = _startingBlockNumber; - __Ownable_init(); - changeProposer(_proposer); - _transferOwnership(_owner); + function deleteL2Output(Types.OutputProposal memory _proposal) external onlyOwner { + Types.OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber]; + + require( + _proposal.outputRoot == outputToDelete.outputRoot, + "L2OutputOracle: output root to delete does not match the latest output proposal" + ); + + require( + _proposal.timestamp == outputToDelete.timestamp, + "L2OutputOracle: timestamp to delete does not match the latest output proposal" + ); + + emit OutputDeleted(outputToDelete.outputRoot, outputToDelete.timestamp, latestBlockNumber); + + delete l2Outputs[latestBlockNumber]; + latestBlockNumber = latestBlockNumber - SUBMISSION_INTERVAL; } /** @@ -214,41 +221,6 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { emit OutputProposed(_outputRoot, block.timestamp, _l2BlockNumber); } - /** - * @notice Deletes the most recent output. This is used to remove the most recent output in the - * event that an erreneous output is submitted. It can only be called by the contract's - * owner, not the proposer. Longer term, this should be replaced with a more robust - * mechanism which will allow deletion of proposals shown to be invalid by a fault - * proof. - * - * @param _proposal Represents the output proposal to delete - */ - function deleteL2Output(Types.OutputProposal memory _proposal) external onlyOwner { - Types.OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber]; - - require( - _proposal.outputRoot == outputToDelete.outputRoot, - "L2OutputOracle: output root to delete does not match the latest output proposal" - ); - - require( - _proposal.timestamp == outputToDelete.timestamp, - "L2OutputOracle: timestamp to delete does not match the latest output proposal" - ); - - emit OutputDeleted(outputToDelete.outputRoot, outputToDelete.timestamp, latestBlockNumber); - - delete l2Outputs[latestBlockNumber]; - latestBlockNumber = latestBlockNumber - SUBMISSION_INTERVAL; - } - - /** - * @notice Computes the block number of the next L2 block that needs to be checkpointed. - */ - function nextBlockNumber() public view returns (uint256) { - return latestBlockNumber + SUBMISSION_INTERVAL; - } - /** * @notice Returns the L2 output proposal associated with a target L2 block number. If the * L2 block number provided is between checkpoints, this function will rerutn the next @@ -286,18 +258,24 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { } /** - * @notice Returns the L2 timestamp corresponding to a given L2 block number. - * Returns a null output proposal if none is found. + * @notice Initializer. * - * @param _l2BlockNumber The L2 block number of the target block. + * @param _genesisL2Output The initial L2 output of the L2 chain. + * @param _startingBlockNumber The timestamp to start L2 block at. + * @param _proposer The address of the proposer. + * @param _owner The address of the owner. */ - function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) { - require( - _l2BlockNumber >= STARTING_BLOCK_NUMBER, - "L2OutputOracle: block number must be greater than or equal to starting block number" - ); - - return STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * L2_BLOCK_TIME); + function initialize( + bytes32 _genesisL2Output, + uint256 _startingBlockNumber, + address _proposer, + address _owner + ) public initializer { + l2Outputs[_startingBlockNumber] = Types.OutputProposal(_genesisL2Output, block.timestamp); + latestBlockNumber = _startingBlockNumber; + __Ownable_init(); + changeProposer(_proposer); + _transferOwnership(_owner); } /** @@ -318,4 +296,26 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { emit ProposerChanged(proposer, _newProposer); proposer = _newProposer; } + + /** + * @notice Computes the block number of the next L2 block that needs to be checkpointed. + */ + function nextBlockNumber() public view returns (uint256) { + return latestBlockNumber + SUBMISSION_INTERVAL; + } + + /** + * @notice Returns the L2 timestamp corresponding to a given L2 block number. + * Returns a null output proposal if none is found. + * + * @param _l2BlockNumber The L2 block number of the target block. + */ + function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) { + require( + _l2BlockNumber >= STARTING_BLOCK_NUMBER, + "L2OutputOracle: block number must be greater than or equal to starting block number" + ); + + return STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * L2_BLOCK_TIME); + } } diff --git a/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol b/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol index 80ae0b21c0f14..af34c0b782174 100644 --- a/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol +++ b/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol @@ -24,30 +24,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { */ uint256 internal constant DEPOSIT_VERSION = 0; - /** - * @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event - * are read by the rollup node and used to derive deposit transactions on L2. - * - * @param from Address that triggered the deposit transaction. - * @param to Address that the deposit transaction is directed to. - * @param version Version of this deposit transaction event. - * @param opaqueData ABI encoded deposit data to be parsed off-chain. - */ - event TransactionDeposited( - address indexed from, - address indexed to, - uint256 indexed version, - bytes opaqueData - ); - - /** - * @notice Emitted when a withdrawal transaction is finalized. - * - * @param withdrawalHash Hash of the withdrawal transaction. - * @param success Whether the withdrawal transaction was successful. - */ - event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - /** * @notice Value used to reset the l2Sender, this is more efficient than setting it to zero. */ @@ -92,6 +68,30 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { */ uint256[48] private __gap; + /** + * @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event + * are read by the rollup node and used to derive deposit transactions on L2. + * + * @param from Address that triggered the deposit transaction. + * @param to Address that the deposit transaction is directed to. + * @param version Version of this deposit transaction event. + * @param opaqueData ABI encoded deposit data to be parsed off-chain. + */ + event TransactionDeposited( + address indexed from, + address indexed to, + uint256 indexed version, + bytes opaqueData + ); + + /** + * @notice Emitted when a withdrawal transaction is finalized. + * + * @param withdrawalHash Hash of the withdrawal transaction. + * @param success Whether the withdrawal transaction was successful. + */ + event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); + /** * @custom:semver 0.0.1 * @@ -104,14 +104,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { initialize(); } - /** - * @notice Initializer; - */ - function initialize() public initializer { - l2Sender = DEFAULT_L2_SENDER; - __ResourceMetering_init(); - } - /** * @notice Accepts value so that users can send ETH directly to this contract and have the * funds be deposited to their address on L2. This is intended as a convenience @@ -122,77 +114,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { depositTransaction(msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, false, bytes("")); } - /** - * @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in - * deriving deposit transactions. Note that if a deposit is made by a contract, its - * address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider - * using the CrossDomainMessenger contracts for a simpler developer experience. - * - * @param _to Target address on L2. - * @param _value ETH value to send to the recipient. - * @param _gasLimit Minimum L2 gas limit (can be greater than or equal to this value). - * @param _isCreation Whether or not the transaction is a contract creation. - * @param _data Data to trigger the recipient with. - */ - function depositTransaction( - address _to, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) public payable metered(_gasLimit) { - // Just to be safe, make sure that people specify address(0) as the target when doing - // contract creations. - if (_isCreation) { - require( - _to == address(0), - "OptimismPortal: must send to address(0) when creating a contract" - ); - } - - // Transform the from-address to its alias if the caller is a contract. - address from = msg.sender; - if (msg.sender != tx.origin) { - from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); - } - - bytes memory opaqueData = abi.encodePacked( - msg.value, - _value, - _gasLimit, - _isCreation, - _data - ); - - // Emit a TransactionDeposited event so that the rollup node can derive a deposit - // transaction for this deposit. - emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); - } - - /** - * @notice Determine if a given block number is finalized. Reverts if the call to - * L2_ORACLE.getL2Output reverts. Returns a boolean otherwise. - * - * @param _l2BlockNumber The number of the L2 block. - */ - function isBlockFinalized(uint256 _l2BlockNumber) external view returns (bool) { - Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber); - return _isOutputFinalized(proposal); - } - - /** - * @notice Determine if an L2 Output is finalized. - * - * @param _proposal The output proposal to check. - */ - function _isOutputFinalized(Types.OutputProposal memory _proposal) - internal - view - returns (bool) - { - return block.timestamp > _proposal.timestamp + FINALIZATION_PERIOD_SECONDS; - } - /** * @notice Finalizes a withdrawal transaction. * @@ -291,6 +212,85 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { emit WithdrawalFinalized(withdrawalHash, success); } + /** + * @notice Determine if a given block number is finalized. Reverts if the call to + * L2_ORACLE.getL2Output reverts. Returns a boolean otherwise. + * + * @param _l2BlockNumber The number of the L2 block. + */ + function isBlockFinalized(uint256 _l2BlockNumber) external view returns (bool) { + Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber); + return _isOutputFinalized(proposal); + } + + /** + * @notice Initializer; + */ + function initialize() public initializer { + l2Sender = DEFAULT_L2_SENDER; + __ResourceMetering_init(); + } + + /** + * @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in + * deriving deposit transactions. Note that if a deposit is made by a contract, its + * address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider + * using the CrossDomainMessenger contracts for a simpler developer experience. + * + * @param _to Target address on L2. + * @param _value ETH value to send to the recipient. + * @param _gasLimit Minimum L2 gas limit (can be greater than or equal to this value). + * @param _isCreation Whether or not the transaction is a contract creation. + * @param _data Data to trigger the recipient with. + */ + function depositTransaction( + address _to, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) public payable metered(_gasLimit) { + // Just to be safe, make sure that people specify address(0) as the target when doing + // contract creations. + if (_isCreation) { + require( + _to == address(0), + "OptimismPortal: must send to address(0) when creating a contract" + ); + } + + // Transform the from-address to its alias if the caller is a contract. + address from = msg.sender; + if (msg.sender != tx.origin) { + from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); + } + + bytes memory opaqueData = abi.encodePacked( + msg.value, + _value, + _gasLimit, + _isCreation, + _data + ); + + // Emit a TransactionDeposited event so that the rollup node can derive a deposit + // transaction for this deposit. + emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); + } + + /** + * @notice Determine if an L2 Output is finalized. + * + * @param _proposal The output proposal to check. + */ + function _isOutputFinalized(Types.OutputProposal memory _proposal) + internal + view + returns (bool) + { + return block.timestamp > _proposal.timestamp + FINALIZATION_PERIOD_SECONDS; + } + /** * @notice Verifies a Merkle Trie inclusion proof that a given withdrawal hash is present in * the storage of the L2ToL1MessagePasser contract. diff --git a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol index c4c841e65a93c..50ac1724277ac 100644 --- a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol +++ b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol @@ -63,19 +63,6 @@ abstract contract ResourceMetering is Initializable { */ uint256[49] private __gap; - /** - * @notice Sets initial resource parameter values. This function must either be called by the - * initializer function of an upgradeable child contract. - */ - // solhint-disable-next-line func-name-mixedcase - function __ResourceMetering_init() internal onlyInitializing { - params = ResourceParams({ - prevBaseFee: INITIAL_BASE_FEE, - prevBoughtGas: 0, - prevBlockNum: uint64(block.number) - }); - } - /** * @notice Meters access to a function based an amount of a requested resource. * @@ -164,4 +151,17 @@ abstract contract ResourceMetering is Initializable { Burn.gas(gasCost - usedGas); } } + + /** + * @notice Sets initial resource parameter values. This function must either be called by the + * initializer function of an upgradeable child contract. + */ + // solhint-disable-next-line func-name-mixedcase + function __ResourceMetering_init() internal onlyInitializing { + params = ResourceParams({ + prevBaseFee: INITIAL_BASE_FEE, + prevBoughtGas: 0, + prevBlockNum: uint64(block.number) + }); + } } diff --git a/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol b/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol index 5d87aa3a05a80..6c782dbb8cdab 100644 --- a/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol +++ b/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol @@ -44,15 +44,6 @@ contract GasPriceOracle is Ownable, Semver { */ uint256 public decimals; - /** - * @custom:semver 0.0.1 - * - * @param _owner Address that will initially own this contract. - */ - constructor(address _owner) Ownable() Semver(0, 0, 1) { - transferOwnership(_owner); - } - /** * @notice Emitted when the overhead value is updated. */ @@ -69,30 +60,12 @@ contract GasPriceOracle is Ownable, Semver { event DecimalsUpdated(uint256 decimals); /** - * @notice Retrieves the current gas price (base fee). - * - * @return Current L2 gas price (base fee). - */ - function gasPrice() public view returns (uint256) { - return block.basefee; - } - - /** - * @notice Retrieves the current base fee. - * - * @return Current L2 base fee. - */ - function baseFee() public view returns (uint256) { - return block.basefee; - } - - /** - * @notice Retrieves the latest known L1 base fee. + * @custom:semver 0.0.1 * - * @return Latest known L1 base fee. + * @param _owner Address that will initially own this contract. */ - function l1BaseFee() public view returns (uint256) { - return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); + constructor(address _owner) Ownable() Semver(0, 0, 1) { + transferOwnership(_owner); } /** @@ -142,6 +115,33 @@ contract GasPriceOracle is Ownable, Semver { return scaled; } + /** + * @notice Retrieves the current gas price (base fee). + * + * @return Current L2 gas price (base fee). + */ + function gasPrice() public view returns (uint256) { + return block.basefee; + } + + /** + * @notice Retrieves the current base fee. + * + * @return Current L2 base fee. + */ + function baseFee() public view returns (uint256) { + return block.basefee; + } + + /** + * @notice Retrieves the latest known L1 base fee. + * + * @return Latest known L1 base fee. + */ + function l1BaseFee() public view returns (uint256) { + return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); + } + /** * @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which * represents the per-transaction gas overhead of posting the transaction and state diff --git a/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol b/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol index 63dc25dc99aea..ef60d3f458614 100644 --- a/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol @@ -47,15 +47,6 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { return otherMessenger; } - /** - * @notice Checks that the message sender is the L1CrossDomainMessenger on L1. - * - * @return True if the message sender is the L1CrossDomainMessenger on L1. - */ - function _isOtherMessenger() internal view override returns (bool) { - return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == otherMessenger; - } - /** * @notice Sends a message from L2 to L1. * @@ -74,4 +65,13 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { value: _value }(_to, _gasLimit, _data); } + + /** + * @notice Checks that the message sender is the L1CrossDomainMessenger on L1. + * + * @return True if the message sender is the L1CrossDomainMessenger on L1. + */ + function _isOtherMessenger() internal view override returns (bool) { + return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == otherMessenger; + } } diff --git a/packages/contracts-bedrock/contracts/L2/L2StandardBridge.sol b/packages/contracts-bedrock/contracts/L2/L2StandardBridge.sol index 85e61cb3118ef..0a79d8c92ca62 100644 --- a/packages/contracts-bedrock/contracts/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/contracts/L2/L2StandardBridge.sol @@ -87,15 +87,6 @@ contract L2StandardBridge is StandardBridge, Semver { initialize(_otherBridge); } - /** - * @notice Initializer. - * - * @param _otherBridge Address of the L1StandardBridge. - */ - function initialize(address payable _otherBridge) public initializer { - __StandardBridge_init(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER), _otherBridge); - } - /** * @custom:legacy * @notice Initiates a withdrawal from L2 to L1. @@ -165,6 +156,15 @@ contract L2StandardBridge is StandardBridge, Semver { emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _extraData); } + /** + * @notice Initializer. + * + * @param _otherBridge Address of the L1StandardBridge. + */ + function initialize(address payable _otherBridge) public initializer { + __StandardBridge_init(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER), _otherBridge); + } + /** * @custom:legacy * @notice Internal function to a withdrawal from L2 to L1 to a target account on L1. diff --git a/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol b/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol index 3c413cfe84b15..543acbc432edc 100644 --- a/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol +++ b/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol @@ -15,6 +15,21 @@ import { Semver } from "../universal/Semver.sol"; * of the L2 output to reduce the cost of proving the existence of sent messages. */ contract L2ToL1MessagePasser is Semver { + /** + * @notice The L1 gas limit set when eth is withdrawn using the receive() function. + */ + uint256 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; + + /** + * @notice Includes the message hashes for all withdrawals + */ + mapping(bytes32 => bool) public sentMessages; + + /** + * @notice A unique value hashed with each withdrawal. + */ + uint256 public nonce; + /** * @notice Emitted any time a withdrawal is initiated. * @@ -41,21 +56,6 @@ contract L2ToL1MessagePasser is Semver { */ event WithdrawerBalanceBurnt(uint256 indexed amount); - /** - * @notice The L1 gas limit set when eth is withdrawn using the receive() function. - */ - uint256 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; - - /** - * @notice Includes the message hashes for all withdrawals - */ - mapping(bytes32 => bool) public sentMessages; - - /** - * @notice A unique value hashed with each withdrawal. - */ - uint256 public nonce; - /** * @custom:semver 0.0.1 */ @@ -68,6 +68,18 @@ contract L2ToL1MessagePasser is Semver { initiateWithdrawal(msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")); } + /** + * @notice Removes all ETH held by this contract from the state. Used to prevent the amount of + * ETH on L2 inflating when ETH is withdrawn. Currently only way to do this is to + * create a contract and self-destruct it to itself. Anyone can call this function. Not + * incentivized since this function is very cheap. + */ + function burn() external { + uint256 balance = address(this).balance; + Burn.eth(balance); + emit WithdrawerBalanceBurnt(balance); + } + /** * @notice Sends a message from L2 to L1. * @@ -98,16 +110,4 @@ contract L2ToL1MessagePasser is Semver { ++nonce; } } - - /** - * @notice Removes all ETH held by this contract from the state. Used to prevent the amount of - * ETH on L2 inflating when ETH is withdrawn. Currently only way to do this is to - * create a contract and self-destruct it to itself. Anyone can call this function. Not - * incentivized since this function is very cheap. - */ - function burn() external { - uint256 balance = address(this).balance; - Burn.eth(balance); - emit WithdrawerBalanceBurnt(balance); - } } diff --git a/packages/contracts-bedrock/contracts/legacy/AddressManager.sol b/packages/contracts-bedrock/contracts/legacy/AddressManager.sol index 5fa23ab10c58d..246c81770c56f 100644 --- a/packages/contracts-bedrock/contracts/legacy/AddressManager.sol +++ b/packages/contracts-bedrock/contracts/legacy/AddressManager.sol @@ -12,6 +12,11 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; * with several older contracts. */ contract AddressManager is Ownable { + /** + * @notice Mapping of the hashes of string names to addresses. + */ + mapping(bytes32 => address) private addresses; + /** * @notice Emitted when an address is modified in the registry. * @@ -21,11 +26,6 @@ contract AddressManager is Ownable { */ event AddressSet(string indexed name, address newAddress, address oldAddress); - /** - * @notice Mapping of the hashes of string names to addresses. - */ - mapping(bytes32 => address) private addresses; - /** * @notice Changes the address associated with a particular name. * diff --git a/packages/contracts-bedrock/contracts/legacy/DeployerWhitelist.sol b/packages/contracts-bedrock/contracts/legacy/DeployerWhitelist.sol index a2969794edd3a..4d8269abd912c 100644 --- a/packages/contracts-bedrock/contracts/legacy/DeployerWhitelist.sol +++ b/packages/contracts-bedrock/contracts/legacy/DeployerWhitelist.sol @@ -15,6 +15,17 @@ import { Semver } from "../universal/Semver.sol"; * system and could, in theory, be removed entirely. */ contract DeployerWhitelist is Semver { + /** + * @notice Address of the owner of this contract. Note that when this address is set to + * address(0), the whitelist is disabled. + */ + address public owner; + + /** + * @notice Mapping of deployer addresses to boolean whitelist status. + */ + mapping(address => bool) public whitelist; + /** * @notice Emitted when the owner of this contract changes. * @@ -38,22 +49,6 @@ contract DeployerWhitelist is Semver { */ event WhitelistDisabled(address oldOwner); - /** - * @notice Address of the owner of this contract. Note that when this address is set to - * address(0), the whitelist is disabled. - */ - address public owner; - - /** - * @notice Mapping of deployer addresses to boolean whitelist status. - */ - mapping(address => bool) public whitelist; - - /** - * @custom:semver 0.0.1 - */ - constructor() Semver(0, 0, 1) {} - /** * @notice Blocks functions to anyone except the contract owner. */ @@ -65,6 +60,11 @@ contract DeployerWhitelist is Semver { _; } + /** + * @custom:semver 0.0.1 + */ + constructor() Semver(0, 0, 1) {} + /** * @notice Adds or removes an address from the deployment whitelist. * diff --git a/packages/contracts-bedrock/contracts/legacy/L1ChugSplashProxy.sol b/packages/contracts-bedrock/contracts/legacy/L1ChugSplashProxy.sol index 4a4549da9e5e2..220e3805a215f 100644 --- a/packages/contracts-bedrock/contracts/legacy/L1ChugSplashProxy.sol +++ b/packages/contracts-bedrock/contracts/legacy/L1ChugSplashProxy.sol @@ -40,13 +40,6 @@ contract L1ChugSplashProxy { bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - /** - * @param _owner Address of the initial contract owner. - */ - constructor(address _owner) { - _setOwner(_owner); - } - /** * @notice Blocks a function from being called when the parent signals that the system should * be paused via an isUpgrading function. @@ -100,14 +93,21 @@ contract L1ChugSplashProxy { } } + /** + * @param _owner Address of the initial contract owner. + */ + constructor(address _owner) { + _setOwner(_owner); + } + // slither-disable-next-line locked-ether - fallback() external payable { + receive() external payable { // Proxy call by default. _doProxyCall(); } // slither-disable-next-line locked-ether - receive() external payable { + fallback() external payable { // Proxy call by default. _doProxyCall(); } @@ -206,27 +206,57 @@ contract L1ChugSplashProxy { } /** - * @notice Queries the implementation address. + * @notice Changes the owner of the proxy contract. * - * @return Implementation address. + * @param _owner New owner of the proxy contract. */ - function _getImplementation() internal view returns (address) { - address implementation; + function _setOwner(address _owner) internal { assembly { - implementation := sload(IMPLEMENTATION_KEY) + sstore(OWNER_KEY, _owner) } - return implementation; } /** - * @notice Changes the owner of the proxy contract. + * @notice Performs the proxy call via a delegatecall. + */ + function _doProxyCall() internal onlyWhenNotPaused { + address implementation = _getImplementation(); + + require(implementation != address(0), "L1ChugSplashProxy: implementation is not set yet"); + + assembly { + // Copy calldata into memory at 0x0....calldatasize. + calldatacopy(0x0, 0x0, calldatasize()) + + // Perform the delegatecall, make sure to pass all available gas. + let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0) + + // Copy returndata into memory at 0x0....returndatasize. Note that this *will* + // overwrite the calldata that we just copied into memory but that doesn't really + // matter because we'll be returning in a second anyway. + returndatacopy(0x0, 0x0, returndatasize()) + + // Success == 0 means a revert. We'll revert too and pass the data up. + if iszero(success) { + revert(0x0, returndatasize()) + } + + // Otherwise we'll just return and pass the data up. + return(0x0, returndatasize()) + } + } + + /** + * @notice Queries the implementation address. * - * @param _owner New owner of the proxy contract. + * @return Implementation address. */ - function _setOwner(address _owner) internal { + function _getImplementation() internal view returns (address) { + address implementation; assembly { - sstore(OWNER_KEY, _owner) + implementation := sload(IMPLEMENTATION_KEY) } + return implementation; } /** @@ -256,34 +286,4 @@ contract L1ChugSplashProxy { } return codeHash; } - - /** - * @notice Performs the proxy call via a delegatecall. - */ - function _doProxyCall() internal onlyWhenNotPaused { - address implementation = _getImplementation(); - - require(implementation != address(0), "L1ChugSplashProxy: implementation is not set yet"); - - assembly { - // Copy calldata into memory at 0x0....calldatasize. - calldatacopy(0x0, 0x0, calldatasize()) - - // Perform the delegatecall, make sure to pass all available gas. - let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0) - - // Copy returndata into memory at 0x0....returndatasize. Note that this *will* - // overwrite the calldata that we just copied into memory but that doesn't really - // matter because we'll be returning in a second anyway. - returndatacopy(0x0, 0x0, returndatasize()) - - // Success == 0 means a revert. We'll revert too and pass the data up. - if iszero(success) { - revert(0x0, returndatasize()) - } - - // Otherwise we'll just return and pass the data up. - return(0x0, returndatasize()) - } - } } diff --git a/packages/contracts-bedrock/contracts/libraries/Burn.sol b/packages/contracts-bedrock/contracts/libraries/Burn.sol index 9e3eebbe62166..558960bef5959 100644 --- a/packages/contracts-bedrock/contracts/libraries/Burn.sol +++ b/packages/contracts-bedrock/contracts/libraries/Burn.sol @@ -1,18 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -/** - * @title Burner - * @notice Burner self-destructs on creation and sends all ETH to itself, removing all ETH given to - * the contract from the circulating supply. Self-destructing is the only way to remove ETH - * from the circulating supply. - */ -contract Burner { - constructor() payable { - selfdestruct(payable(address(this))); - } -} - /** * @title Burn * @notice Utilities for burning stuff. @@ -40,3 +28,15 @@ library Burn { } } } + +/** + * @title Burner + * @notice Burner self-destructs on creation and sends all ETH to itself, removing all ETH given to + * the contract from the circulating supply. Self-destructing is the only way to remove ETH + * from the circulating supply. + */ +contract Burner { + constructor() payable { + selfdestruct(payable(address(this))); + } +} diff --git a/packages/contracts-bedrock/contracts/libraries/rlp/RLPReader.sol b/packages/contracts-bedrock/contracts/libraries/rlp/RLPReader.sol index a7d476d23c6c8..18e41e78a378b 100644 --- a/packages/contracts-bedrock/contracts/libraries/rlp/RLPReader.sol +++ b/packages/contracts-bedrock/contracts/libraries/rlp/RLPReader.sol @@ -9,11 +9,6 @@ pragma solidity ^0.8.0; * various tweaks to improve readability. */ library RLPReader { - /** - * @notice Max list length that this library will accept. - */ - uint256 internal constant MAX_LIST_LENGTH = 32; - /** * @notice RLP item types. * @@ -33,6 +28,11 @@ library RLPReader { uint256 ptr; } + /** + * @notice Max list length that this library will accept. + */ + uint256 internal constant MAX_LIST_LENGTH = 32; + /** * @notice Converts bytes to a reference to memory position and length. * diff --git a/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol b/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol index dcc55ab936f9c..3f56a32dd1b6b 100644 --- a/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol @@ -27,37 +27,6 @@ abstract contract CrossDomainMessenger is PausableUpgradeable, ReentrancyGuardUpgradeable { - /** - * @notice Emitted whenever a message is sent to the other chain. - * - * @param target Address of the recipient of the message. - * @param sender Address of the sender of the message. - * @param message Message to trigger the recipient address with. - * @param messageNonce Unique nonce attached to the message. - * @param gasLimit Minimum gas limit that the message can be executed with. - */ - event SentMessage( - address indexed target, - address sender, - bytes message, - uint256 messageNonce, - uint256 gasLimit - ); - - /** - * @notice Emitted whenever a message is successfully relayed on this chain. - * - * @param msgHash Hash of the message that was relayed. - */ - event RelayedMessage(bytes32 indexed msgHash); - - /** - * @notice Emitted whenever a message fails to be relayed on this chain. - * - * @param msgHash Hash of the message that failed to be relayed. - */ - event FailedRelayedMessage(bytes32 indexed msgHash); - /** * @notice Current message version identifier. */ @@ -146,68 +115,50 @@ abstract contract CrossDomainMessenger is mapping(address => bool) public blockedSystemAddresses; /** - * @notice Allows the owner of this contract to temporarily pause message relaying. Backup - * security mechanism just in case. Owner should be the same as the upgrade wallet to - * maintain the security model of the system as a whole. + * @notice Emitted whenever a message is sent to the other chain. + * + * @param target Address of the recipient of the message. + * @param sender Address of the sender of the message. + * @param message Message to trigger the recipient address with. + * @param messageNonce Unique nonce attached to the message. + * @param gasLimit Minimum gas limit that the message can be executed with. */ - function pause() external onlyOwner { - _pause(); - } + event SentMessage( + address indexed target, + address sender, + bytes message, + uint256 messageNonce, + uint256 gasLimit + ); /** - * @notice Allows the owner of this contract to resume message relaying once paused. + * @notice Emitted whenever a message is successfully relayed on this chain. + * + * @param msgHash Hash of the message that was relayed. */ - function unpause() external onlyOwner { - _unpause(); - } + event RelayedMessage(bytes32 indexed msgHash); /** - * @notice Retrieves the address of the contract or wallet that initiated the currently - * executing message on the other chain. Will throw an error if there is no message - * currently being executed. Allows the recipient of a call to see who triggered it. + * @notice Emitted whenever a message fails to be relayed on this chain. * - * @return Address of the sender of the currently executing message on the other chain. + * @param msgHash Hash of the message that failed to be relayed. */ - function xDomainMessageSender() external view returns (address) { - require( - xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, - "CrossDomainMessenger: xDomainMessageSender is not set" - ); - - return xDomainMsgSender; - } + event FailedRelayedMessage(bytes32 indexed msgHash); /** - * @notice Retrieves the next message nonce. Message version will be added to the upper two - * bytes of the message nonce. Message version allows us to treat messages as having - * different structures. - * - * @return Nonce of the next message to be sent, with added message version. + * @notice Allows the owner of this contract to temporarily pause message relaying. Backup + * security mechanism just in case. Owner should be the same as the upgrade wallet to + * maintain the security model of the system as a whole. */ - function messageNonce() public view returns (uint256) { - return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION); + function pause() external onlyOwner { + _pause(); } /** - * @notice Computes the amount of gas required to guarantee that a given message will be - * received on the other chain without running out of gas. Guaranteeing that a message - * will not run out of gas is important because this ensures that a message can always - * be replayed on the other chain if it fails to execute completely. - * - * @param _message Message to compute the amount of required gas for. - * @param _minGasLimit Minimum desired gas limit when message goes to target. - * - * @return Amount of gas required to guarantee message receipt. + * @notice Allows the owner of this contract to resume message relaying once paused. */ - function baseGas(bytes calldata _message, uint32 _minGasLimit) public pure returns (uint32) { - return - // Dynamic overhead - ((_minGasLimit * MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR) / - MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR) + - // Calldata overhead - (uint32(_message.length) * MIN_GAS_CALLDATA_OVERHEAD) + - // Constant overhead - MIN_GAS_CONSTANT_OVERHEAD; + function unpause() external onlyOwner { + _unpause(); } /** @@ -326,6 +277,55 @@ abstract contract CrossDomainMessenger is } } + /** + * @notice Retrieves the address of the contract or wallet that initiated the currently + * executing message on the other chain. Will throw an error if there is no message + * currently being executed. Allows the recipient of a call to see who triggered it. + * + * @return Address of the sender of the currently executing message on the other chain. + */ + function xDomainMessageSender() external view returns (address) { + require( + xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, + "CrossDomainMessenger: xDomainMessageSender is not set" + ); + + return xDomainMsgSender; + } + + /** + * @notice Retrieves the next message nonce. Message version will be added to the upper two + * bytes of the message nonce. Message version allows us to treat messages as having + * different structures. + * + * @return Nonce of the next message to be sent, with added message version. + */ + function messageNonce() public view returns (uint256) { + return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION); + } + + /** + * @notice Computes the amount of gas required to guarantee that a given message will be + * received on the other chain without running out of gas. Guaranteeing that a message + * will not run out of gas is important because this ensures that a message can always + * be replayed on the other chain if it fails to execute completely. + * + * @param _message Message to compute the amount of required gas for. + * @param _minGasLimit Minimum desired gas limit when message goes to target. + * + * @return Amount of gas required to guarantee message receipt. + */ + function baseGas(bytes calldata _message, uint32 _minGasLimit) public pure returns (uint32) { + return + // Dynamic overhead + ((_minGasLimit * MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR) / + MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR) + + // Calldata overhead + (uint32(_message.length) * MIN_GAS_CALLDATA_OVERHEAD) + + // Constant overhead + MIN_GAS_CONSTANT_OVERHEAD; + } + /** * @notice Intializer. * @@ -354,13 +354,6 @@ abstract contract CrossDomainMessenger is __ReentrancyGuard_init_unchained(); } - /** - * @notice Checks whether the message is coming from the other messenger. Implemented by child - * contracts because the logic for this depends on the network where the messenger is - * being deployed. - */ - function _isOtherMessenger() internal view virtual returns (bool); - /** * @notice Sends a low-level message to the other messenger. Needs to be implemented by child * contracts because the logic for this depends on the network where the messenger is @@ -372,4 +365,11 @@ abstract contract CrossDomainMessenger is uint256 _value, bytes memory _data ) internal virtual; + + /** + * @notice Checks whether the message is coming from the other messenger. Implemented by child + * contracts because the logic for this depends on the network where the messenger is + * being deployed. + */ + function _isOtherMessenger() internal view virtual returns (bool); } diff --git a/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20.sol b/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20.sol index e9c90e2836fc5..9ed0edeb20570 100644 --- a/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20.sol +++ b/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20.sol @@ -13,6 +13,16 @@ import "./SupportedInterfaces.sol"; * meant for use on L2. */ contract OptimismMintableERC20 is ERC20 { + /** + * @notice Address of the corresponding version of this token on the remote chain. + */ + address public remoteToken; + + /** + * @notice Address of the StandardBridge on this network. + */ + address public bridge; + /** * @notice Emitted whenever tokens are minted for an account. * @@ -30,14 +40,12 @@ contract OptimismMintableERC20 is ERC20 { event Burn(address indexed account, uint256 amount); /** - * @notice Address of the corresponding version of this token on the remote chain. - */ - address public remoteToken; - - /** - * @notice Address of the StandardBridge on this network. + * @notice A modifier that only allows the bridge to call */ - address public bridge; + modifier onlyBridge() { + require(msg.sender == bridge, "OptimismMintableERC20: only bridge can mint and burn"); + _; + } /** * @param _bridge Address of the L2 standard bridge. @@ -56,27 +64,25 @@ contract OptimismMintableERC20 is ERC20 { } /** - * @custom:legacy - * @notice Legacy getter for the remote token. Use remoteToken going forward. - */ - function l1Token() public view returns (address) { - return remoteToken; - } - - /** - * @custom:legacy - * @notice Legacy getter for the bridge. Use bridge going forward. + * @notice Allows the StandardBridge on this network to mint tokens. + * + * @param _to Address to mint tokens to. + * @param _amount Amount of tokens to mint. */ - function l2Bridge() public view returns (address) { - return bridge; + function mint(address _to, uint256 _amount) external virtual onlyBridge { + _mint(_to, _amount); + emit Mint(_to, _amount); } /** - * @notice A modifier that only allows the bridge to call + * @notice Allows the StandardBridge on this network to burn tokens. + * + * @param _from Address to burn tokens from. + * @param _amount Amount of tokens to burn. */ - modifier onlyBridge() { - require(msg.sender == bridge, "OptimismMintableERC20: only bridge can mint and burn"); - _; + function burn(address _from, uint256 _amount) external virtual onlyBridge { + _burn(_from, _amount); + emit Burn(_from, _amount); } /** @@ -94,24 +100,18 @@ contract OptimismMintableERC20 is ERC20 { } /** - * @notice Allows the StandardBridge on this network to mint tokens. - * - * @param _to Address to mint tokens to. - * @param _amount Amount of tokens to mint. + * @custom:legacy + * @notice Legacy getter for the remote token. Use remoteToken going forward. */ - function mint(address _to, uint256 _amount) external virtual onlyBridge { - _mint(_to, _amount); - emit Mint(_to, _amount); + function l1Token() public view returns (address) { + return remoteToken; } /** - * @notice Allows the StandardBridge on this network to burn tokens. - * - * @param _from Address to burn tokens from. - * @param _amount Amount of tokens to burn. + * @custom:legacy + * @notice Legacy getter for the bridge. Use bridge going forward. */ - function burn(address _from, uint256 _amount) external virtual onlyBridge { - _burn(_from, _amount); - emit Burn(_from, _amount); + function l2Bridge() public view returns (address) { + return bridge; } } diff --git a/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20Factory.sol index 8f37934f9cdeb..f9e8b7dc92141 100644 --- a/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20Factory.sol @@ -14,6 +14,11 @@ import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; * compatible with the older StandardL2ERC20Factory contract. */ contract OptimismMintableERC20Factory { + /** + * @notice Address of the StandardBridge on this chain. + */ + address public immutable bridge; + /** * @custom:legacy * @notice Emitted whenever a new OptimismMintableERC20 is created. Legacy version of the newer @@ -37,11 +42,6 @@ contract OptimismMintableERC20Factory { address deployer ); - /** - * @notice Address of the StandardBridge on this chain. - */ - address public immutable bridge; - /** * @param _bridge Address of the StandardBridge on this chain. */ diff --git a/packages/contracts-bedrock/contracts/universal/Proxy.sol b/packages/contracts-bedrock/contracts/universal/Proxy.sol index e8849fd505803..f2fead040b611 100644 --- a/packages/contracts-bedrock/contracts/universal/Proxy.sol +++ b/packages/contracts-bedrock/contracts/universal/Proxy.sol @@ -8,6 +8,20 @@ pragma solidity 0.8.15; * simulation. */ contract Proxy { + /** + * @notice The storage slot that holds the address of the implementation. + * bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + */ + bytes32 internal constant IMPLEMENTATION_KEY = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @notice The storage slot that holds the address of the owner. + * bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) + */ + bytes32 internal constant OWNER_KEY = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + /** * @notice An event that is emitted each time the implementation is changed. This event is part * of the EIP-1967 specification. @@ -26,18 +40,19 @@ contract Proxy { event AdminChanged(address previousAdmin, address newAdmin); /** - * @notice The storage slot that holds the address of the implementation. - * bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) - */ - bytes32 internal constant IMPLEMENTATION_KEY = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @notice The storage slot that holds the address of the owner. - * bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) + * @notice A modifier that reverts if not called by the owner or by address(0) to allow + * eth_call to interact with this proxy without needing to use low-level storage + * inspection. We assume that nobody is able to trigger calls from address(0) during + * normal EVM execution. */ - bytes32 internal constant OWNER_KEY = - 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + modifier proxyCallIfNotAdmin() { + if (msg.sender == _getAdmin() || msg.sender == address(0)) { + _; + } else { + // This WILL halt the call frame on completion. + _doProxyCall(); + } + } /** * @notice Sets the initial admin during contract deployment. Admin address is stored at the @@ -52,32 +67,17 @@ contract Proxy { } // slither-disable-next-line locked-ether - fallback() external payable { + receive() external payable { // Proxy call by default. _doProxyCall(); } // slither-disable-next-line locked-ether - receive() external payable { + fallback() external payable { // Proxy call by default. _doProxyCall(); } - /** - * @notice A modifier that reverts if not called by the owner or by address(0) to allow - * eth_call to interact with this proxy without needing to use low-level storage - * inspection. We assume that nobody is able to trigger calls from address(0) during - * normal EVM execution. - */ - modifier proxyCallIfNotAdmin() { - if (msg.sender == _getAdmin() || msg.sender == address(0)) { - _; - } else { - // This WILL halt the call frame on completion. - _doProxyCall(); - } - } - /** * @notice Set the implementation contract address. The code at the given address will execute * when this contract is called. @@ -146,19 +146,6 @@ contract Proxy { emit Upgraded(_implementation); } - /** - * @notice Queries the implementation address. - * - * @return Implementation address. - */ - function _getImplementation() internal view returns (address) { - address impl; - assembly { - impl := sload(IMPLEMENTATION_KEY) - } - return impl; - } - /** * @notice Changes the owner of the proxy contract. * @@ -172,19 +159,6 @@ contract Proxy { emit AdminChanged(previous, _admin); } - /** - * @notice Queries the owner of the proxy contract. - * - * @return Owner address. - */ - function _getAdmin() internal view returns (address) { - address owner; - assembly { - owner := sload(OWNER_KEY) - } - return owner; - } - /** * @notice Performs the proxy call via a delegatecall. */ @@ -213,4 +187,30 @@ contract Proxy { return(0x0, returndatasize()) } } + + /** + * @notice Queries the implementation address. + * + * @return Implementation address. + */ + function _getImplementation() internal view returns (address) { + address impl; + assembly { + impl := sload(IMPLEMENTATION_KEY) + } + return impl; + } + + /** + * @notice Queries the owner of the proxy contract. + * + * @return Owner address. + */ + function _getAdmin() internal view returns (address) { + address owner; + assembly { + owner := sload(OWNER_KEY) + } + return owner; + } } diff --git a/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol b/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol index 588b40b2907cd..48b0b9c20bd23 100644 --- a/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol +++ b/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol @@ -125,24 +125,67 @@ contract ProxyAdmin is Owned { /** * @custom:legacy - * @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening. + * @notice Set the upgrading status for the Chugsplash proxy type. * - * @return Whether or not there is an upgrade going on. May not actually tell you whether an - * upgrade is going on, since we don't currently plan to use this variable for anything - * other than a legacy indicator to fix a UX bug in the ChugSplash proxy. + * @param _upgrading Whether or not the system is upgrading. */ - function isUpgrading() external view returns (bool) { - return upgrading; + function setUpgrading(bool _upgrading) external onlyOwner { + upgrading = _upgrading; + } + + /** + * @notice Updates the admin of the given proxy address. + * + * @param _proxy Address of the proxy to update. + * @param _newAdmin Address of the new proxy admin. + */ + function changeProxyAdmin(address payable _proxy, address _newAdmin) external onlyOwner { + ProxyType ptype = proxyType[_proxy]; + if (ptype == ProxyType.ERC1967) { + Proxy(_proxy).changeAdmin(_newAdmin); + } else if (ptype == ProxyType.CHUGSPLASH) { + L1ChugSplashProxy(_proxy).setOwner(_newAdmin); + } else if (ptype == ProxyType.RESOLVED) { + addressManager.transferOwnership(_newAdmin); + } else { + revert("ProxyAdmin: unknown proxy type"); + } + } + + /** + * @notice Changes a proxy's implementation contract and delegatecalls the new implementation + * with some given data. Useful for atomic upgrade-and-initialize calls. + * + * @param _proxy Address of the proxy to upgrade. + * @param _implementation Address of the new implementation address. + * @param _data Data to trigger the new implementation with. + */ + function upgradeAndCall( + address payable _proxy, + address _implementation, + bytes memory _data + ) external payable onlyOwner { + ProxyType ptype = proxyType[_proxy]; + if (ptype == ProxyType.ERC1967) { + Proxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data); + } else { + // reverts if proxy type is unknown + upgrade(_proxy, _implementation); + (bool success, ) = _proxy.call{ value: msg.value }(_data); + require(success, "ProxyAdmin: call to proxy after upgrade failed"); + } } /** * @custom:legacy - * @notice Set the upgrading status for the Chugsplash proxy type. + * @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening. * - * @param _upgrading Whether or not the system is upgrading. + * @return Whether or not there is an upgrade going on. May not actually tell you whether an + * upgrade is going on, since we don't currently plan to use this variable for anything + * other than a legacy indicator to fix a UX bug in the ChugSplash proxy. */ - function setUpgrading(bool _upgrading) external onlyOwner { - upgrading = _upgrading; + function isUpgrading() external view returns (bool) { + return upgrading; } /** @@ -185,25 +228,6 @@ contract ProxyAdmin is Owned { } } - /** - * @notice Updates the admin of the given proxy address. - * - * @param _proxy Address of the proxy to update. - * @param _newAdmin Address of the new proxy admin. - */ - function changeProxyAdmin(address payable _proxy, address _newAdmin) external onlyOwner { - ProxyType ptype = proxyType[_proxy]; - if (ptype == ProxyType.ERC1967) { - Proxy(_proxy).changeAdmin(_newAdmin); - } else if (ptype == ProxyType.CHUGSPLASH) { - L1ChugSplashProxy(_proxy).setOwner(_newAdmin); - } else if (ptype == ProxyType.RESOLVED) { - addressManager.transferOwnership(_newAdmin); - } else { - revert("ProxyAdmin: unknown proxy type"); - } - } - /** * @notice Changes a proxy's implementation contract. * @@ -226,28 +250,4 @@ contract ProxyAdmin is Owned { revert("ProxyAdmin: unknown proxy type"); } } - - /** - * @notice Changes a proxy's implementation contract and delegatecalls the new implementation - * with some given data. Useful for atomic upgrade-and-initialize calls. - * - * @param _proxy Address of the proxy to upgrade. - * @param _implementation Address of the new implementation address. - * @param _data Data to trigger the new implementation with. - */ - function upgradeAndCall( - address payable _proxy, - address _implementation, - bytes memory _data - ) external payable onlyOwner { - ProxyType ptype = proxyType[_proxy]; - if (ptype == ProxyType.ERC1967) { - Proxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data); - } else { - // reverts if proxy type is unknown - upgrade(_proxy, _implementation); - (bool success, ) = _proxy.call{ value: msg.value }(_data); - require(success, "ProxyAdmin: call to proxy after upgrade failed"); - } - } } diff --git a/packages/contracts-bedrock/contracts/universal/StandardBridge.sol b/packages/contracts-bedrock/contracts/universal/StandardBridge.sol index 3e46407e879b4..24e3cf8e07f05 100644 --- a/packages/contracts-bedrock/contracts/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/contracts/universal/StandardBridge.sol @@ -17,6 +17,26 @@ import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol"; abstract contract StandardBridge is Initializable { using SafeERC20 for IERC20; + /** + * @notice The L2 gas limit set when eth is depoisited using the receive() function. + */ + uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000; + + /** + * @notice Messenger contract on this domain. + */ + CrossDomainMessenger public messenger; + + /** + * @notice Corresponding bridge on the other domain. + */ + StandardBridge public otherBridge; + + /** + * @notice Mapping that stores deposits for a given pair of local and remote tokens. + */ + mapping(address => mapping(address => uint256)) public deposits; + /** * @notice Emitted when an ETH bridge is initiated to the other chain. * @@ -104,26 +124,6 @@ abstract contract StandardBridge is Initializable { bytes extraData ); - /** - * @notice The L2 gas limit set when eth is depoisited using the receive() function. - */ - uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000; - - /** - * @notice Messenger contract on this domain. - */ - CrossDomainMessenger public messenger; - - /** - * @notice Corresponding bridge on the other domain. - */ - StandardBridge public otherBridge; - - /** - * @notice Mapping that stores deposits for a given pair of local and remote tokens. - */ - mapping(address => mapping(address => uint256)) public deposits; - /** * @notice Only allow EOAs to call the functions. Note that this is not safe against contracts * calling code within their constructors, but also doesn't really matter since we're