Commit da7ee222 authored by Luca Donno's avatar Luca Donno Committed by Maurelian

contracts-bedrock: fix solhint ordering warnings

parent 483e27c7
...@@ -38,15 +38,6 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver { ...@@ -38,15 +38,6 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver {
__CrossDomainMessenger_init(Predeploys.L2_CROSS_DOMAIN_MESSENGER, blockedSystemAddresses); __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. * @notice Sends a message via the OptimismPortal contract.
* *
...@@ -63,4 +54,13 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver { ...@@ -63,4 +54,13 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, Semver {
) internal override { ) internal override {
portal.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data); 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;
}
} }
...@@ -94,23 +94,27 @@ contract L1StandardBridge is StandardBridge, Semver { ...@@ -94,23 +94,27 @@ contract L1StandardBridge is StandardBridge, Semver {
initialize(_messenger); 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 * @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) { function finalizeERC20Withdrawal(
return address(otherBridge); 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 { ...@@ -231,25 +235,21 @@ contract L1StandardBridge is StandardBridge, Semver {
/** /**
* @custom:legacy * @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. * @return Address of the corresponding L2 bridge contract.
* @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 finalizeERC20Withdrawal( function l2TokenBridge() external view returns (address) {
address _l1Token, return address(otherBridge);
address _l2Token, }
address _from,
address _to, /**
uint256 _amount, * @notice Initializer.
bytes calldata _extraData *
) external onlyOtherBridge { * @param _messenger Address of the L1CrossDomainMessenger.
emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _extraData); */
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData); function initialize(address payable _messenger) public initializer {
__StandardBridge_init(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE));
} }
/** /**
......
...@@ -16,40 +16,6 @@ import { Types } from "../libraries/Types.sol"; ...@@ -16,40 +16,6 @@ import { Types } from "../libraries/Types.sol";
*/ */
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
contract L2OutputOracle is OwnableUpgradeable, Semver { 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. * @notice The interval in L2 blocks at which checkpoints must be submitted.
*/ */
...@@ -97,6 +63,40 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { ...@@ -97,6 +63,40 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
*/ */
mapping(uint256 => Types.OutputProposal) internal l2Outputs; 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. * @notice Reverts if called by any account other than the proposer.
*/ */
...@@ -142,24 +142,31 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { ...@@ -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 _proposal Represents the output proposal to delete
* @param _startingBlockNumber The timestamp to start L2 block at.
* @param _proposer The address of the proposer.
* @param _owner The address of the owner.
*/ */
function initialize( function deleteL2Output(Types.OutputProposal memory _proposal) external onlyOwner {
bytes32 _genesisL2Output, Types.OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber];
uint256 _startingBlockNumber,
address _proposer, require(
address _owner _proposal.outputRoot == outputToDelete.outputRoot,
) public initializer { "L2OutputOracle: output root to delete does not match the latest output proposal"
l2Outputs[_startingBlockNumber] = Types.OutputProposal(_genesisL2Output, block.timestamp); );
latestBlockNumber = _startingBlockNumber;
__Ownable_init(); require(
changeProposer(_proposer); _proposal.timestamp == outputToDelete.timestamp,
_transferOwnership(_owner); "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 { ...@@ -214,41 +221,6 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
emit OutputProposed(_outputRoot, block.timestamp, _l2BlockNumber); 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 * @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 * L2 block number provided is between checkpoints, this function will rerutn the next
...@@ -286,18 +258,24 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { ...@@ -286,18 +258,24 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
} }
/** /**
* @notice Returns the L2 timestamp corresponding to a given L2 block number. * @notice Initializer.
* Returns a null output proposal if none is found.
* *
* @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) { function initialize(
require( bytes32 _genesisL2Output,
_l2BlockNumber >= STARTING_BLOCK_NUMBER, uint256 _startingBlockNumber,
"L2OutputOracle: block number must be greater than or equal to starting block number" address _proposer,
); address _owner
) public initializer {
return STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * L2_BLOCK_TIME); l2Outputs[_startingBlockNumber] = Types.OutputProposal(_genesisL2Output, block.timestamp);
latestBlockNumber = _startingBlockNumber;
__Ownable_init();
changeProposer(_proposer);
_transferOwnership(_owner);
} }
/** /**
...@@ -318,4 +296,26 @@ contract L2OutputOracle is OwnableUpgradeable, Semver { ...@@ -318,4 +296,26 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
emit ProposerChanged(proposer, _newProposer); emit ProposerChanged(proposer, _newProposer);
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);
}
} }
...@@ -24,30 +24,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -24,30 +24,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/ */
uint256 internal constant DEPOSIT_VERSION = 0; 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. * @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 { ...@@ -92,6 +68,30 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/ */
uint256[48] private __gap; 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 * @custom:semver 0.0.1
* *
...@@ -104,14 +104,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -104,14 +104,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
initialize(); 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 * @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 * funds be deposited to their address on L2. This is intended as a convenience
...@@ -122,77 +114,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -122,77 +114,6 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
depositTransaction(msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, false, bytes("")); 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. * @notice Finalizes a withdrawal transaction.
* *
...@@ -291,6 +212,85 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -291,6 +212,85 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
emit WithdrawalFinalized(withdrawalHash, success); 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 * @notice Verifies a Merkle Trie inclusion proof that a given withdrawal hash is present in
* the storage of the L2ToL1MessagePasser contract. * the storage of the L2ToL1MessagePasser contract.
......
...@@ -63,19 +63,6 @@ abstract contract ResourceMetering is Initializable { ...@@ -63,19 +63,6 @@ abstract contract ResourceMetering is Initializable {
*/ */
uint256[49] private __gap; 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. * @notice Meters access to a function based an amount of a requested resource.
* *
...@@ -164,4 +151,17 @@ abstract contract ResourceMetering is Initializable { ...@@ -164,4 +151,17 @@ abstract contract ResourceMetering is Initializable {
Burn.gas(gasCost - usedGas); 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)
});
}
} }
...@@ -44,15 +44,6 @@ contract GasPriceOracle is Ownable, Semver { ...@@ -44,15 +44,6 @@ contract GasPriceOracle is Ownable, Semver {
*/ */
uint256 public decimals; 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. * @notice Emitted when the overhead value is updated.
*/ */
...@@ -69,30 +60,12 @@ contract GasPriceOracle is Ownable, Semver { ...@@ -69,30 +60,12 @@ contract GasPriceOracle is Ownable, Semver {
event DecimalsUpdated(uint256 decimals); event DecimalsUpdated(uint256 decimals);
/** /**
* @notice Retrieves the current gas price (base fee). * @custom:semver 0.0.1
*
* @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. * @param _owner Address that will initially own this contract.
*/ */
function l1BaseFee() public view returns (uint256) { constructor(address _owner) Ownable() Semver(0, 0, 1) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); transferOwnership(_owner);
} }
/** /**
...@@ -142,6 +115,33 @@ contract GasPriceOracle is Ownable, Semver { ...@@ -142,6 +115,33 @@ contract GasPriceOracle is Ownable, Semver {
return scaled; 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 * @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 * represents the per-transaction gas overhead of posting the transaction and state
......
...@@ -47,15 +47,6 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { ...@@ -47,15 +47,6 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
return otherMessenger; 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. * @notice Sends a message from L2 to L1.
* *
...@@ -74,4 +65,13 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { ...@@ -74,4 +65,13 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
value: _value value: _value
}(_to, _gasLimit, _data); }(_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;
}
} }
...@@ -87,15 +87,6 @@ contract L2StandardBridge is StandardBridge, Semver { ...@@ -87,15 +87,6 @@ contract L2StandardBridge is StandardBridge, Semver {
initialize(_otherBridge); 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 * @custom:legacy
* @notice Initiates a withdrawal from L2 to L1. * @notice Initiates a withdrawal from L2 to L1.
...@@ -165,6 +156,15 @@ contract L2StandardBridge is StandardBridge, Semver { ...@@ -165,6 +156,15 @@ contract L2StandardBridge is StandardBridge, Semver {
emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _extraData); 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 * @custom:legacy
* @notice Internal function to a withdrawal from L2 to L1 to a target account on L1. * @notice Internal function to a withdrawal from L2 to L1 to a target account on L1.
......
...@@ -15,6 +15,21 @@ import { Semver } from "../universal/Semver.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. * of the L2 output to reduce the cost of proving the existence of sent messages.
*/ */
contract L2ToL1MessagePasser is Semver { 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. * @notice Emitted any time a withdrawal is initiated.
* *
...@@ -41,21 +56,6 @@ contract L2ToL1MessagePasser is Semver { ...@@ -41,21 +56,6 @@ contract L2ToL1MessagePasser is Semver {
*/ */
event WithdrawerBalanceBurnt(uint256 indexed amount); 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 * @custom:semver 0.0.1
*/ */
...@@ -68,6 +68,18 @@ contract L2ToL1MessagePasser is Semver { ...@@ -68,6 +68,18 @@ contract L2ToL1MessagePasser is Semver {
initiateWithdrawal(msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")); 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. * @notice Sends a message from L2 to L1.
* *
...@@ -98,16 +110,4 @@ contract L2ToL1MessagePasser is Semver { ...@@ -98,16 +110,4 @@ contract L2ToL1MessagePasser is Semver {
++nonce; ++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);
}
} }
...@@ -12,6 +12,11 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; ...@@ -12,6 +12,11 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
* with several older contracts. * with several older contracts.
*/ */
contract AddressManager is Ownable { 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. * @notice Emitted when an address is modified in the registry.
* *
...@@ -21,11 +26,6 @@ contract AddressManager is Ownable { ...@@ -21,11 +26,6 @@ contract AddressManager is Ownable {
*/ */
event AddressSet(string indexed name, address newAddress, address oldAddress); 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. * @notice Changes the address associated with a particular name.
* *
......
...@@ -15,6 +15,17 @@ import { Semver } from "../universal/Semver.sol"; ...@@ -15,6 +15,17 @@ import { Semver } from "../universal/Semver.sol";
* system and could, in theory, be removed entirely. * system and could, in theory, be removed entirely.
*/ */
contract DeployerWhitelist is Semver { 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. * @notice Emitted when the owner of this contract changes.
* *
...@@ -38,22 +49,6 @@ contract DeployerWhitelist is Semver { ...@@ -38,22 +49,6 @@ contract DeployerWhitelist is Semver {
*/ */
event WhitelistDisabled(address oldOwner); 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. * @notice Blocks functions to anyone except the contract owner.
*/ */
...@@ -65,6 +60,11 @@ contract DeployerWhitelist is Semver { ...@@ -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. * @notice Adds or removes an address from the deployment whitelist.
* *
......
...@@ -40,13 +40,6 @@ contract L1ChugSplashProxy { ...@@ -40,13 +40,6 @@ contract L1ChugSplashProxy {
bytes32 internal constant OWNER_KEY = bytes32 internal constant OWNER_KEY =
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 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 * @notice Blocks a function from being called when the parent signals that the system should
* be paused via an isUpgrading function. * be paused via an isUpgrading function.
...@@ -100,14 +93,21 @@ contract L1ChugSplashProxy { ...@@ -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 // slither-disable-next-line locked-ether
fallback() external payable { receive() external payable {
// Proxy call by default. // Proxy call by default.
_doProxyCall(); _doProxyCall();
} }
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
receive() external payable { fallback() external payable {
// Proxy call by default. // Proxy call by default.
_doProxyCall(); _doProxyCall();
} }
...@@ -206,27 +206,57 @@ contract L1ChugSplashProxy { ...@@ -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) { function _setOwner(address _owner) internal {
address implementation;
assembly { 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 { assembly {
sstore(OWNER_KEY, _owner) implementation := sload(IMPLEMENTATION_KEY)
} }
return implementation;
} }
/** /**
...@@ -256,34 +286,4 @@ contract L1ChugSplashProxy { ...@@ -256,34 +286,4 @@ contract L1ChugSplashProxy {
} }
return codeHash; 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())
}
}
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; 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 * @title Burn
* @notice Utilities for burning stuff. * @notice Utilities for burning stuff.
...@@ -40,3 +28,15 @@ library Burn { ...@@ -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)));
}
}
...@@ -9,11 +9,6 @@ pragma solidity ^0.8.0; ...@@ -9,11 +9,6 @@ pragma solidity ^0.8.0;
* various tweaks to improve readability. * various tweaks to improve readability.
*/ */
library RLPReader { library RLPReader {
/**
* @notice Max list length that this library will accept.
*/
uint256 internal constant MAX_LIST_LENGTH = 32;
/** /**
* @notice RLP item types. * @notice RLP item types.
* *
...@@ -33,6 +28,11 @@ library RLPReader { ...@@ -33,6 +28,11 @@ library RLPReader {
uint256 ptr; 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. * @notice Converts bytes to a reference to memory position and length.
* *
......
...@@ -27,37 +27,6 @@ abstract contract CrossDomainMessenger is ...@@ -27,37 +27,6 @@ abstract contract CrossDomainMessenger is
PausableUpgradeable, PausableUpgradeable,
ReentrancyGuardUpgradeable 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. * @notice Current message version identifier.
*/ */
...@@ -146,68 +115,50 @@ abstract contract CrossDomainMessenger is ...@@ -146,68 +115,50 @@ abstract contract CrossDomainMessenger is
mapping(address => bool) public blockedSystemAddresses; mapping(address => bool) public blockedSystemAddresses;
/** /**
* @notice Allows the owner of this contract to temporarily pause message relaying. Backup * @notice Emitted whenever a message is sent to the other chain.
* 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. * @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 { event SentMessage(
_pause(); 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 { event RelayedMessage(bytes32 indexed msgHash);
_unpause();
}
/** /**
* @notice Retrieves the address of the contract or wallet that initiated the currently * @notice Emitted whenever a message fails to be relayed on this chain.
* 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. * @param msgHash Hash of the message that failed to be relayed.
*/ */
function xDomainMessageSender() external view returns (address) { event FailedRelayedMessage(bytes32 indexed msgHash);
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 * @notice Allows the owner of this contract to temporarily pause message relaying. Backup
* bytes of the message nonce. Message version allows us to treat messages as having * security mechanism just in case. Owner should be the same as the upgrade wallet to
* different structures. * maintain the security model of the system as a whole.
*
* @return Nonce of the next message to be sent, with added message version.
*/ */
function messageNonce() public view returns (uint256) { function pause() external onlyOwner {
return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION); _pause();
} }
/** /**
* @notice Computes the amount of gas required to guarantee that a given message will be * @notice Allows the owner of this contract to resume message relaying once paused.
* 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) { function unpause() external onlyOwner {
return _unpause();
// 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;
} }
/** /**
...@@ -326,6 +277,55 @@ abstract contract CrossDomainMessenger is ...@@ -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. * @notice Intializer.
* *
...@@ -354,13 +354,6 @@ abstract contract CrossDomainMessenger is ...@@ -354,13 +354,6 @@ abstract contract CrossDomainMessenger is
__ReentrancyGuard_init_unchained(); __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 * @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 * contracts because the logic for this depends on the network where the messenger is
...@@ -372,4 +365,11 @@ abstract contract CrossDomainMessenger is ...@@ -372,4 +365,11 @@ abstract contract CrossDomainMessenger is
uint256 _value, uint256 _value,
bytes memory _data bytes memory _data
) internal virtual; ) 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);
} }
...@@ -13,6 +13,16 @@ import "./SupportedInterfaces.sol"; ...@@ -13,6 +13,16 @@ import "./SupportedInterfaces.sol";
* meant for use on L2. * meant for use on L2.
*/ */
contract OptimismMintableERC20 is ERC20 { 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. * @notice Emitted whenever tokens are minted for an account.
* *
...@@ -30,14 +40,12 @@ contract OptimismMintableERC20 is ERC20 { ...@@ -30,14 +40,12 @@ contract OptimismMintableERC20 is ERC20 {
event Burn(address indexed account, uint256 amount); event Burn(address indexed account, uint256 amount);
/** /**
* @notice Address of the corresponding version of this token on the remote chain. * @notice A modifier that only allows the bridge to call
*/
address public remoteToken;
/**
* @notice Address of the StandardBridge on this network.
*/ */
address public bridge; modifier onlyBridge() {
require(msg.sender == bridge, "OptimismMintableERC20: only bridge can mint and burn");
_;
}
/** /**
* @param _bridge Address of the L2 standard bridge. * @param _bridge Address of the L2 standard bridge.
...@@ -56,27 +64,25 @@ contract OptimismMintableERC20 is ERC20 { ...@@ -56,27 +64,25 @@ contract OptimismMintableERC20 is ERC20 {
} }
/** /**
* @custom:legacy * @notice Allows the StandardBridge on this network to mint tokens.
* @notice Legacy getter for the remote token. Use remoteToken going forward. *
*/ * @param _to Address to mint tokens to.
function l1Token() public view returns (address) { * @param _amount Amount of tokens to mint.
return remoteToken;
}
/**
* @custom:legacy
* @notice Legacy getter for the bridge. Use bridge going forward.
*/ */
function l2Bridge() public view returns (address) { function mint(address _to, uint256 _amount) external virtual onlyBridge {
return bridge; _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() { function burn(address _from, uint256 _amount) external virtual onlyBridge {
require(msg.sender == bridge, "OptimismMintableERC20: only bridge can mint and burn"); _burn(_from, _amount);
_; emit Burn(_from, _amount);
} }
/** /**
...@@ -94,24 +100,18 @@ contract OptimismMintableERC20 is ERC20 { ...@@ -94,24 +100,18 @@ contract OptimismMintableERC20 is ERC20 {
} }
/** /**
* @notice Allows the StandardBridge on this network to mint tokens. * @custom:legacy
* * @notice Legacy getter for the remote token. Use remoteToken going forward.
* @param _to Address to mint tokens to.
* @param _amount Amount of tokens to mint.
*/ */
function mint(address _to, uint256 _amount) external virtual onlyBridge { function l1Token() public view returns (address) {
_mint(_to, _amount); return remoteToken;
emit Mint(_to, _amount);
} }
/** /**
* @notice Allows the StandardBridge on this network to burn tokens. * @custom:legacy
* * @notice Legacy getter for the bridge. Use bridge going forward.
* @param _from Address to burn tokens from.
* @param _amount Amount of tokens to burn.
*/ */
function burn(address _from, uint256 _amount) external virtual onlyBridge { function l2Bridge() public view returns (address) {
_burn(_from, _amount); return bridge;
emit Burn(_from, _amount);
} }
} }
...@@ -14,6 +14,11 @@ import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; ...@@ -14,6 +14,11 @@ import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
* compatible with the older StandardL2ERC20Factory contract. * compatible with the older StandardL2ERC20Factory contract.
*/ */
contract OptimismMintableERC20Factory { contract OptimismMintableERC20Factory {
/**
* @notice Address of the StandardBridge on this chain.
*/
address public immutable bridge;
/** /**
* @custom:legacy * @custom:legacy
* @notice Emitted whenever a new OptimismMintableERC20 is created. Legacy version of the newer * @notice Emitted whenever a new OptimismMintableERC20 is created. Legacy version of the newer
...@@ -37,11 +42,6 @@ contract OptimismMintableERC20Factory { ...@@ -37,11 +42,6 @@ contract OptimismMintableERC20Factory {
address deployer address deployer
); );
/**
* @notice Address of the StandardBridge on this chain.
*/
address public immutable bridge;
/** /**
* @param _bridge Address of the StandardBridge on this chain. * @param _bridge Address of the StandardBridge on this chain.
*/ */
......
...@@ -8,6 +8,20 @@ pragma solidity 0.8.15; ...@@ -8,6 +8,20 @@ pragma solidity 0.8.15;
* simulation. * simulation.
*/ */
contract Proxy { 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 * @notice An event that is emitted each time the implementation is changed. This event is part
* of the EIP-1967 specification. * of the EIP-1967 specification.
...@@ -26,18 +40,19 @@ contract Proxy { ...@@ -26,18 +40,19 @@ contract Proxy {
event AdminChanged(address previousAdmin, address newAdmin); event AdminChanged(address previousAdmin, address newAdmin);
/** /**
* @notice The storage slot that holds the address of the implementation. * @notice A modifier that reverts if not called by the owner or by address(0) to allow
* bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) * 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
bytes32 internal constant IMPLEMENTATION_KEY = * normal EVM execution.
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @notice The storage slot that holds the address of the owner.
* bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
*/ */
bytes32 internal constant OWNER_KEY = modifier proxyCallIfNotAdmin() {
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 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 * @notice Sets the initial admin during contract deployment. Admin address is stored at the
...@@ -52,32 +67,17 @@ contract Proxy { ...@@ -52,32 +67,17 @@ contract Proxy {
} }
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
fallback() external payable { receive() external payable {
// Proxy call by default. // Proxy call by default.
_doProxyCall(); _doProxyCall();
} }
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
receive() external payable { fallback() external payable {
// Proxy call by default. // Proxy call by default.
_doProxyCall(); _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 * @notice Set the implementation contract address. The code at the given address will execute
* when this contract is called. * when this contract is called.
...@@ -146,19 +146,6 @@ contract Proxy { ...@@ -146,19 +146,6 @@ contract Proxy {
emit Upgraded(_implementation); 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. * @notice Changes the owner of the proxy contract.
* *
...@@ -172,19 +159,6 @@ contract Proxy { ...@@ -172,19 +159,6 @@ contract Proxy {
emit AdminChanged(previous, _admin); 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. * @notice Performs the proxy call via a delegatecall.
*/ */
...@@ -213,4 +187,30 @@ contract Proxy { ...@@ -213,4 +187,30 @@ contract Proxy {
return(0x0, returndatasize()) 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;
}
} }
...@@ -125,24 +125,67 @@ contract ProxyAdmin is Owned { ...@@ -125,24 +125,67 @@ contract ProxyAdmin is Owned {
/** /**
* @custom:legacy * @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 * @param _upgrading Whether or not the system is upgrading.
* 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 isUpgrading() external view returns (bool) { function setUpgrading(bool _upgrading) external onlyOwner {
return upgrading; 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 * @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 { function isUpgrading() external view returns (bool) {
upgrading = _upgrading; return upgrading;
} }
/** /**
...@@ -185,25 +228,6 @@ contract ProxyAdmin is Owned { ...@@ -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. * @notice Changes a proxy's implementation contract.
* *
...@@ -226,28 +250,4 @@ contract ProxyAdmin is Owned { ...@@ -226,28 +250,4 @@ contract ProxyAdmin is Owned {
revert("ProxyAdmin: unknown proxy type"); 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");
}
}
} }
...@@ -17,6 +17,26 @@ import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol"; ...@@ -17,6 +17,26 @@ import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol";
abstract contract StandardBridge is Initializable { abstract contract StandardBridge is Initializable {
using SafeERC20 for IERC20; 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. * @notice Emitted when an ETH bridge is initiated to the other chain.
* *
...@@ -104,26 +124,6 @@ abstract contract StandardBridge is Initializable { ...@@ -104,26 +124,6 @@ abstract contract StandardBridge is Initializable {
bytes extraData 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 * @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 * calling code within their constructors, but also doesn't really matter since we're
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment