Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
40214739
Unverified
Commit
40214739
authored
Nov 07, 2023
by
Maurelian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(ctb): Checked out pre-mcp impl contracts
parent
b01ff541
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
564 additions
and
662 deletions
+564
-662
L1CrossDomainMessenger.sol
packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol
+18
-21
L1ERC721Bridge.sol
packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol
+18
-24
L1StandardBridge.sol
packages/contracts-bedrock/src/L1/L1StandardBridge.sol
+57
-54
L2OutputOracle.sol
packages/contracts-bedrock/src/L1/L2OutputOracle.sol
+67
-80
OptimismPortal.sol
packages/contracts-bedrock/src/L1/OptimismPortal.sol
+95
-88
ResourceMetering.sol
packages/contracts-bedrock/src/L1/ResourceMetering.sol
+9
-5
SystemConfig.sol
packages/contracts-bedrock/src/L1/SystemConfig.sol
+70
-198
L2ERC721Bridge.sol
packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol
+21
-23
L2StandardBridge.sol
packages/contracts-bedrock/src/L2/L2StandardBridge.sol
+36
-50
ERC721Bridge.sol
packages/contracts-bedrock/src/universal/ERC721Bridge.sol
+37
-29
Semver.sol
packages/contracts-bedrock/src/universal/Semver.sol
+45
-0
StandardBridge.sol
packages/contracts-bedrock/src/universal/StandardBridge.sol
+91
-90
No files found.
packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol
View file @
40214739
...
...
@@ -12,36 +12,33 @@ import { Constants } from "src/libraries/Constants.sol";
/// @notice The L1CrossDomainMessenger is a message passing interface between L1 and L2 responsible
/// for sending and receiving data on the L1 side. Users are encouraged to use this
/// interface instead of interacting with lower-level contracts directly.
contract L1CrossDomainMessenger is CrossDomainMessenger, ISemver {
/// @notice Address of the OptimismPortal. The public getter for this
/// is legacy and will be removed in the future. Use `portal()` instead.
/// @custom:network-specific
/// @custom:legacy
OptimismPortal public PORTAL;
/// @notice Semantic version.
/// @custom:semver 1.7.1
string public constant version = "1.7.1";
contract L1CrossDomainMessenger is CrossDomainMessenger, Semver {
/// @notice Address of the OptimismPortal.
OptimismPortal public immutable PORTAL;
/// @custom:semver 1.4.1
/// @notice Constructs the L1CrossDomainMessenger contract.
constructor() CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER) {
initialize({ _portal: OptimismPortal(payable(0)) });
}
/// @notice Initializes the contract.
/// @param _portal Address of the OptimismPortal contract on this network.
function initialize(OptimismPortal _portal) public reinitializer(Constants.INITIALIZER) {
constructor(OptimismPortal _portal)
Semver(1, 4, 1)
CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER)
{
PORTAL = _portal;
__CrossDomainMessenger_init
();
initialize
();
}
/// @notice
Getter for the OptimismPortal address
.
function
portal() external view returns (address)
{
return address(PORTAL
);
/// @notice
Initializes the contract
.
function
initialize() public initializer
{
__CrossDomainMessenger_init(
);
}
/// @inheritdoc CrossDomainMessenger
function _sendMessage(address _to, uint64 _gasLimit, uint256 _value, bytes memory _data) internal override {
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal override {
PORTAL.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data);
}
...
...
packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol
View file @
40214739
...
...
@@ -13,25 +13,19 @@ import { Constants } from "src/libraries/Constants.sol";
/// @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to
/// make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
/// acts as an escrow for ERC721 tokens deposited into L2.
contract L1ERC721Bridge is ERC721Bridge,
I
Semver {
contract L1ERC721Bridge is ERC721Bridge, Semver {
/// @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token
/// by ID was deposited for a given L2 token.
mapping(address => mapping(address => mapping(uint256 => bool))) public deposits;
/// @notice Semantic version.
/// @custom:semver 1.4.1
string public constant version = "1.4.1";
/// @notice Constructs the contract.
constructor() ERC721Bridge(Predeploys.L2_ERC721_BRIDGE) {
initialize({ _messenger: CrossDomainMessenger(address(0)) });
}
/// @notice Initializes the contract.
/// @custom:semver 1.1.2
/// @notice Constructs the L1ERC721Bridge contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
function initialize(CrossDomainMessenger _messenger) public reinitializer(Constants.INITIALIZER) {
__ERC721Bridge_init({ _messenger: _messenger });
}
/// @param _otherBridge Address of the ERC721 bridge on the other network.
constructor(address _messenger, address _otherBridge)
Semver(1, 1, 2)
ERC721Bridge(_messenger, _otherBridge)
{}
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// recipient on this domain.
...
...
@@ -50,10 +44,7 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver {
address _to,
uint256 _tokenId,
bytes calldata _extraData
)
external
onlyOtherBridge
{
) external onlyOtherBridge {
require(_localToken != address(this), "L1ERC721Bridge: local token cannot be self");
// Checks that the L1/L2 NFT pair has a token ID that is escrowed in the L1 Bridge.
...
...
@@ -83,15 +74,18 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
)
internal
override
{
) internal override {
require(_remoteToken != address(0), "L1ERC721Bridge: remote token cannot be address(0)");
// Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)
bytes memory message = abi.encodeWithSelector(
L2ERC721Bridge.finalizeBridgeERC721.selector, _remoteToken, _localToken, _from, _to, _tokenId, _extraData
L2ERC721Bridge.finalizeBridgeERC721.selector,
_remoteToken,
_localToken,
_from,
_to,
_tokenId,
_extraData
);
// Lock token into bridge
...
...
@@ -99,7 +93,7 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver {
IERC721(_localToken).transferFrom(_from, address(this), _tokenId);
// Send calldata into L2
messenger
.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
MESSENGER
.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
}
packages/contracts-bedrock/src/L1/L1StandardBridge.sol
View file @
40214739
...
...
@@ -17,14 +17,19 @@ import { Constants } from "src/libraries/Constants.sol";
/// NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples
/// of some token types that may not be properly supported by this contract include, but are
/// not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists.
contract L1StandardBridge is StandardBridge,
I
Semver {
contract L1StandardBridge is StandardBridge, Semver {
/// @custom:legacy
/// @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated.
/// @param from Address of the depositor.
/// @param to Address of the recipient on L2.
/// @param amount Amount of ETH deposited.
/// @param extraData Extra data attached to the deposit.
event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
event ETHDepositInitiated(
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/// @custom:legacy
/// @notice Emitted whenever a withdrawal of ETH from L2 to L1 is finalized.
...
...
@@ -32,7 +37,12 @@ contract L1StandardBridge is StandardBridge, ISemver {
/// @param to Address of the recipient on L1.
/// @param amount Amount of ETH withdrawn.
/// @param extraData Extra data attached to the withdrawal.
event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
event ETHWithdrawalFinalized(
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/// @custom:legacy
/// @notice Emitted whenever an ERC20 deposit is initiated.
...
...
@@ -68,19 +78,13 @@ contract L1StandardBridge is StandardBridge, ISemver {
bytes extraData
);
/// @notice Semantic version.
/// @custom:semver 1.4.1
string public constant version = "1.4.1";
/// @custom:semver 1.1.1
/// @notice Constructs the L1StandardBridge contract.
constructor() StandardBridge(StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE))) {
initialize({ _messenger: CrossDomainMessenger(address(0)) });
}
/// @notice Initializer
function initialize(CrossDomainMessenger _messenger) public reinitializer(Constants.INITIALIZER) {
__StandardBridge_init({ _messenger: _messenger });
}
/// @param _messenger Address of the L1CrossDomainMessenger.
constructor(address payable _messenger)
Semver(1, 1, 1)
StandardBridge(_messenger, payable(Predeploys.L2_STANDARD_BRIDGE))
{}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
receive() external payable override onlyEOA {
...
...
@@ -108,7 +112,11 @@ contract L1StandardBridge is StandardBridge, ISemver {
/// @param _extraData Optional data to forward to L2.
/// Data supplied here will not be used to execute any code on L2 and is
/// only emitted as extra data for the convenience of off-chain tooling.
function depositETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) external payable {
function depositETHTo(
address _to,
uint32 _minGasLimit,
bytes calldata _extraData
) external payable {
_initiateETHDeposit(msg.sender, _to, _minGasLimit, _extraData);
}
...
...
@@ -127,12 +135,16 @@ contract L1StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
virtual
onlyEOA
{
_initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
) external virtual onlyEOA {
_initiateERC20Deposit(
_l1Token,
_l2Token,
msg.sender,
msg.sender,
_amount,
_minGasLimit,
_extraData
);
}
/// @custom:legacy
...
...
@@ -152,11 +164,16 @@ contract L1StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
virtual
{
_initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData);
) external virtual {
_initiateERC20Deposit(
_l1Token,
_l2Token,
msg.sender,
_to,
_amount,
_minGasLimit,
_extraData
);
}
/// @custom:legacy
...
...
@@ -170,10 +187,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes calldata _extraData
)
external
payable
{
) external payable {
finalizeBridgeETH(_from, _to, _amount, _extraData);
}
...
...
@@ -192,9 +206,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes calldata _extraData
)
external
{
) external {
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData);
}
...
...
@@ -210,7 +222,12 @@ contract L1StandardBridge is StandardBridge, ISemver {
/// @param _to Address of the recipient on L2.
/// @param _minGasLimit Minimum gas limit for the deposit message on L2.
/// @param _extraData Optional data to forward to L2.
function _initiateETHDeposit(address _from, address _to, uint32 _minGasLimit, bytes memory _extraData) internal {
function _initiateETHDeposit(
address _from,
address _to,
uint32 _minGasLimit,
bytes memory _extraData
) internal {
_initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _extraData);
}
...
...
@@ -230,9 +247,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
internal
{
) internal {
_initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _extraData);
}
...
...
@@ -244,10 +259,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit ETHDepositInitiated(_from, _to, _amount, _extraData);
super._emitETHBridgeInitiated(_from, _to, _amount, _extraData);
}
...
...
@@ -260,10 +272,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit ETHWithdrawalFinalized(_from, _to, _amount, _extraData);
super._emitETHBridgeFinalized(_from, _to, _amount, _extraData);
}
...
...
@@ -278,10 +287,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit ERC20DepositInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
super._emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
...
...
@@ -296,10 +302,7 @@ contract L1StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit ERC20WithdrawalFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
super._emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
...
...
packages/contracts-bedrock/src/L1/L2OutputOracle.sol
View file @
40214739
...
...
@@ -11,25 +11,22 @@ import { Constants } from "src/libraries/Constants.sol";
/// @notice The L2OutputOracle contains an array of L2 state outputs, where each output is a
/// commitment to the state of the L2 chain. Other contracts like the OptimismPortal use
/// these outputs to verify information about the state of L2.
contract L2OutputOracle is Initializable,
I
Semver {
contract L2OutputOracle is Initializable, Semver {
/// @notice The interval in L2 blocks at which checkpoints must be submitted.
/// Although this is immutable, it can safely be modified by upgrading the
/// implementation contract.
/// Public getter is legacy and will be removed in the future. Use `submissionInterval`
/// instead.
/// @custom:legacy
uint256 public immutable SUBMISSION_INTERVAL;
/// @notice The time between L2 blocks in seconds. Once set, this value MUST NOT be modified.
/// Public getter is legacy and will be removed in the future. Use `l2BlockTime`
/// instead.
/// @custom:legacy
uint256 public immutable L2_BLOCK_TIME;
/// @notice The address of the challenger. Can be updated via upgrade.
address public immutable CHALLENGER;
/// @notice The address of the proposer. Can be updated via upgrade.
address public immutable PROPOSER;
/// @notice The minimum time (in seconds) that must elapse before a withdrawal can be finalized.
/// Public getter is legacy and will be removed in the future. Use
// `finalizationPeriodSeconds` instead.
/// @custom:legacy
uint256 public immutable FINALIZATION_PERIOD_SECONDS;
/// @notice The number of the first L2 block recorded in this contract.
...
...
@@ -41,21 +38,16 @@ contract L2OutputOracle is Initializable, ISemver {
/// @notice An array of L2 output proposals.
Types.OutputProposal[] internal l2Outputs;
/// @notice The address of the challenger. Can be updated via reinitialize.
/// @custom:network-specific
address public challenger;
/// @notice The address of the proposer. Can be updated via reinitialize.
/// @custom:network-specific
address public proposer;
/// @notice Emitted when an output is proposed.
/// @param outputRoot The output root.
/// @param l2OutputIndex The index of the output in the l2Outputs array.
/// @param l2BlockNumber The L2 block number of the output root.
/// @param l1Timestamp The L1 timestamp when proposed.
event OutputProposed(
bytes32 indexed outputRoot, uint256 indexed l2OutputIndex, uint256 indexed l2BlockNumber, uint256 l1Timestamp
bytes32 indexed outputRoot,
uint256 indexed l2OutputIndex,
uint256 indexed l2BlockNumber,
uint256 l1Timestamp
);
/// @notice Emitted when outputs are deleted.
...
...
@@ -63,39 +55,44 @@ contract L2OutputOracle is Initializable, ISemver {
/// @param newNextOutputIndex Next L2 output index after the deletion.
event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);
/// @notice Semantic version.
/// @custom:semver 1.6.0
string public constant version = "1.6.0";
/// @custom:semver 1.3.1
/// @notice Constructs the L2OutputOracle contract.
/// @param _submissionInterval Interval in blocks at which checkpoints must be submitted.
/// @param _l2BlockTime The time per L2 block, in seconds.
/// @param _finalizationPeriodSeconds The amount of time that must pass for an output proposal
// to be considered canonical.
constructor(uint256 _submissionInterval, uint256 _l2BlockTime, uint256 _finalizationPeriodSeconds) {
/// @param _startingBlockNumber The number of the first L2 block.
/// @param _startingTimestamp The timestamp of the first L2 block.
/// @param _proposer The address of the proposer.
/// @param _challenger The address of the challenger.
constructor(
uint256 _submissionInterval,
uint256 _l2BlockTime,
uint256 _startingBlockNumber,
uint256 _startingTimestamp,
address _proposer,
address _challenger,
uint256 _finalizationPeriodSeconds
) Semver(1, 3, 1) {
require(_l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0");
require(_submissionInterval > 0, "L2OutputOracle: submission interval must be greater than 0");
require(
_submissionInterval > 0,
"L2OutputOracle: submission interval must be greater than 0"
);
SUBMISSION_INTERVAL = _submissionInterval;
L2_BLOCK_TIME = _l2BlockTime;
PROPOSER = _proposer;
CHALLENGER = _challenger;
FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds;
initialize(
{ _startingBlockNumber: 0, _startingTimestamp: 0, _proposer: address(0), _challenger: address(0) }
);
initialize(
_startingBlockNumber, _startingTimestamp
);
}
/// @notice Initializer.
/// @param _startingBlockNumber Block number for the first recoded L2 block.
/// @param _startingTimestamp Timestamp for the first recoded L2 block.
/// @param _proposer The address of the proposer.
/// @param _challenger The address of the challenger.
function initialize(
uint256 _startingBlockNumber,
uint256 _startingTimestamp,
address _proposer,
address _challenger
)
function initialize(uint256 _startingBlockNumber, uint256 _startingTimestamp)
public
reinitializer(Constants.INITIALIZER)
initializer
{
require(
_startingTimestamp <= block.timestamp,
...
...
@@ -104,37 +101,6 @@ contract L2OutputOracle is Initializable, ISemver {
startingTimestamp = _startingTimestamp;
startingBlockNumber = _startingBlockNumber;
proposer = _proposer;
challenger = _challenger;
}
/// @notice Getter for the output proposal submission interval.
function submissionInterval() external view returns (uint256) {
return SUBMISSION_INTERVAL;
}
/// @notice Getter for the L2 block time.
function l2BlockTime() external view returns (uint256) {
return L2_BLOCK_TIME;
}
/// @notice Getter for the finalization period.
function finalizationPeriodSeconds() external view returns (uint256) {
return FINALIZATION_PERIOD_SECONDS;
}
/// @notice Getter for the challenger address. This will be removed
/// in the future, use `challenger` instead.
/// @custom:legacy
function CHALLENGER() external view returns (address) {
return challenger;
}
/// @notice Getter for the proposer address. This will be removed in the
/// future, use `proposer` instead.
/// @custom:legacy
function PROPOSER() external view returns (address) {
return proposer;
}
/// @notice Deletes all output proposals after and including the proposal that corresponds to
...
...
@@ -143,11 +109,15 @@ contract L2OutputOracle is Initializable, ISemver {
/// All outputs after this output will also be deleted.
// solhint-disable-next-line ordering
function deleteL2Outputs(uint256 _l2OutputIndex) external {
require(msg.sender == challenger, "L2OutputOracle: only the challenger address can delete outputs");
require(
msg.sender == CHALLENGER,
"L2OutputOracle: only the challenger address can delete outputs"
);
// Make sure we're not *increasing* the length of the array.
require(
_l2OutputIndex < l2Outputs.length, "L2OutputOracle: cannot delete outputs after the latest output index"
_l2OutputIndex < l2Outputs.length,
"L2OutputOracle: cannot delete outputs after the latest output index"
);
// Do not allow deleting any outputs that have already been finalized.
...
...
@@ -178,11 +148,11 @@ contract L2OutputOracle is Initializable, ISemver {
uint256 _l2BlockNumber,
bytes32 _l1BlockHash,
uint256 _l1BlockNumber
)
external
payable
{
require(msg.sender == proposer, "L2OutputOracle: only the proposer address can propose new outputs"
);
)
external payable {
require(
msg.sender == PROPOSER,
"L2OutputOracle: only the proposer address can propose new outputs"
);
require(
_l2BlockNumber == nextBlockNumber(),
...
...
@@ -194,7 +164,10 @@ contract L2OutputOracle is Initializable, ISemver {
"L2OutputOracle: cannot propose L2 output in the future"
);
require(_outputRoot != bytes32(0), "L2OutputOracle: L2 output proposal cannot be the zero hash");
require(
_outputRoot != bytes32(0),
"L2OutputOracle: L2 output proposal cannot be the zero hash"
);
if (_l1BlockHash != bytes32(0)) {
// This check allows the proposer to propose an output based on a given L1 block,
...
...
@@ -225,7 +198,11 @@ contract L2OutputOracle is Initializable, ISemver {
/// @notice Returns an output by index. Needed to return a struct instead of a tuple.
/// @param _l2OutputIndex Index of the output to return.
/// @return The output at the given index.
function getL2Output(uint256 _l2OutputIndex) external view returns (Types.OutputProposal memory) {
function getL2Output(uint256 _l2OutputIndex)
external
view
returns (Types.OutputProposal memory)
{
return l2Outputs[_l2OutputIndex];
}
...
...
@@ -242,7 +219,10 @@ contract L2OutputOracle is Initializable, ISemver {
);
// Make sure there's at least one output proposed.
require(l2Outputs.length > 0, "L2OutputOracle: cannot get output as no outputs have been proposed yet");
require(
l2Outputs.length > 0,
"L2OutputOracle: cannot get output as no outputs have been proposed yet"
);
// Find the output via binary search, guaranteed to exist.
uint256 lo = 0;
...
...
@@ -264,7 +244,11 @@ contract L2OutputOracle is Initializable, ISemver {
/// block.
/// @param _l2BlockNumber L2 block number to find a checkpoint for.
/// @return First checkpoint that commits to the given L2 block number.
function getL2OutputAfter(uint256 _l2BlockNumber) external view returns (Types.OutputProposal memory) {
function getL2OutputAfter(uint256 _l2BlockNumber)
external
view
returns (Types.OutputProposal memory)
{
return l2Outputs[getL2OutputIndexAfter(_l2BlockNumber)];
}
...
...
@@ -286,7 +270,10 @@ contract L2OutputOracle is Initializable, ISemver {
/// block number.
/// @return Latest submitted L2 block number.
function latestBlockNumber() public view returns (uint256) {
return l2Outputs.length == 0 ? startingBlockNumber : l2Outputs[l2Outputs.length - 1].l2BlockNumber;
return
l2Outputs.length == 0
? startingBlockNumber
: l2Outputs[l2Outputs.length - 1].l2BlockNumber;
}
/// @notice Computes the block number of the next L2 block that needs to be checkpointed.
...
...
packages/contracts-bedrock/src/L1/OptimismPortal.sol
View file @
40214739
...
...
@@ -19,7 +19,7 @@ import { Constants } from "src/libraries/Constants.sol";
/// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1
/// and L2. Messages sent directly to the OptimismPortal have no form of replayability.
/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface.
contract OptimismPortal is Initializable, ResourceMetering,
I
Semver {
contract OptimismPortal is Initializable, ResourceMetering, Semver {
/// @notice Represents a proven withdrawal.
/// @custom:field outputRoot Root of the L2 output this was proven against.
/// @custom:field timestamp Timestamp at whcih the withdrawal was proven.
...
...
@@ -36,6 +36,15 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @notice The L2 gas limit set when eth is deposited using the receive() function.
uint64 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000;
/// @notice Address of the L2OutputOracle contract.
L2OutputOracle public immutable L2_ORACLE;
/// @notice Address of the SystemConfig contract.
SystemConfig public immutable SYSTEM_CONFIG;
/// @notice Address that has the ability to pause and unpause withdrawals.
address public immutable GUARDIAN;
/// @notice Address of the L2 account which initiated a withdrawal in this transaction.
/// If the of this variable is the default L2 sender address, then we are NOT inside of
/// a call to finalizeWithdrawalTransaction.
...
...
@@ -52,18 +61,6 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// This may be removed in the future.
bool public paused;
/// @notice Address of the L2OutputOracle contract.
/// @custom:network-specific
L2OutputOracle public l2Oracle;
/// @notice Address of the SystemConfig contract.
/// @custom:network-specific
SystemConfig public systemConfig;
/// @notice Address that has the ability to pause and unpause withdrawals.
/// @custom:network-specific
address public guardian;
/// @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.
...
...
@@ -71,13 +68,22 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @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);
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 indexed version,
bytes opaqueData
);
/// @notice Emitted when a withdrawal transaction is proven.
/// @param withdrawalHash Hash of the withdrawal transaction.
/// @param from Address that triggered the withdrawal transaction.
/// @param to Address that the withdrawal transaction is directed to.
event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to);
event WithdrawalProven(
bytes32 indexed withdrawalHash,
address indexed from,
address indexed to
);
/// @notice Emitted when a withdrawal transaction is finalized.
/// @param withdrawalHash Hash of the withdrawal transaction.
...
...
@@ -98,70 +104,41 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
_;
}
/// @notice Semantic version.
/// @custom:semver 1.10.0
string public constant version = "1.10.0";
/// @custom:semver 1.7.2
/// @notice Constructs the OptimismPortal contract.
constructor() {
initialize({
_l2Oracle: L2OutputOracle(address(0)),
_guardian: address(0),
_systemConfig: SystemConfig(address(0)),
_paused: true
});
}
/// @notice Initializer.
/// @param _l2Oracle Address of the L2OutputOracle contract.
/// @param _guardian Address that can pause withdrawals.
/// @param _paused Sets the contract's pausability state.
/// @param _
systemC
onfig Address of the SystemConfig contract.
function initialize
(
/// @param _
c
onfig Address of the SystemConfig contract.
constructor
(
L2OutputOracle _l2Oracle,
address _guardian,
SystemConfig _systemConfig,
bool _paused
)
public
reinitializer(Constants.INITIALIZER)
{
bool _paused,
SystemConfig _config
) Semver(1, 7, 2) {
L2_ORACLE = _l2Oracle;
GUARDIAN = _guardian;
SYSTEM_CONFIG = _config;
initialize(_paused);
}
/// @notice Initializer.
function initialize(bool _paused) public initializer {
l2Sender = Constants.DEFAULT_L2_SENDER;
l2Oracle = _l2Oracle;
systemConfig = _systemConfig;
guardian = _guardian;
paused = _paused;
__ResourceMetering_init();
}
/// @notice Getter for the L2OutputOracle
/// @custom:legacy
function L2_ORACLE() external view returns (L2OutputOracle) {
return l2Oracle;
}
/// @notice Getter for the SystemConfig
/// @custom:legacy
function SYSTEM_CONFIG() external view returns (SystemConfig) {
return systemConfig;
}
/// @notice Getter for the Guardian
/// @custom:legacy
function GUARDIAN() external view returns (address) {
return guardian;
}
/// @notice Pauses withdrawals.
function pause() external {
require(msg.sender ==
guardian
, "OptimismPortal: only guardian can pause");
require(msg.sender ==
GUARDIAN
, "OptimismPortal: only guardian can pause");
paused = true;
emit Paused(msg.sender);
}
/// @notice Unpauses withdrawals.
function unpause() external {
require(msg.sender ==
guardian
, "OptimismPortal: only guardian can unpause");
require(msg.sender ==
GUARDIAN
, "OptimismPortal: only guardian can unpause");
paused = false;
emit Unpaused(msg.sender);
}
...
...
@@ -197,8 +174,13 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// Used internally by the ResourceMetering contract.
/// The SystemConfig is the source of truth for the resource config.
/// @return ResourceMetering ResourceConfig
function _resourceConfig() internal view override returns (ResourceMetering.ResourceConfig memory) {
return systemConfig.resourceConfig();
function _resourceConfig()
internal
view
override
returns (ResourceMetering.ResourceConfig memory)
{
return SYSTEM_CONFIG.resourceConfig();
}
/// @notice Proves a withdrawal transaction.
...
...
@@ -211,22 +193,23 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
uint256 _l2OutputIndex,
Types.OutputRootProof calldata _outputRootProof,
bytes[] calldata _withdrawalProof
)
external
whenNotPaused
{
) external whenNotPaused {
// Prevent users from creating a deposit transaction where this address is the message
// sender on L2. Because this is checked here, we do not need to check again in
// `finalizeWithdrawalTransaction`.
require(_tx.target != address(this), "OptimismPortal: you cannot send messages to the portal contract");
require(
_tx.target != address(this),
"OptimismPortal: you cannot send messages to the portal contract"
);
// Get the output root and load onto the stack to prevent multiple mloads. This will
// revert if there is no output root for the given block number.
bytes32 outputRoot =
l2Oracle
.getL2Output(_l2OutputIndex).outputRoot;
bytes32 outputRoot =
L2_ORACLE
.getL2Output(_l2OutputIndex).outputRoot;
// Verify that the output root can be generated with the elements in the proof.
require(
outputRoot == Hashing.hashOutputRootProof(_outputRootProof), "OptimismPortal: invalid output root proof"
outputRoot == Hashing.hashOutputRootProof(_outputRootProof),
"OptimismPortal: invalid output root proof"
);
// Load the ProvenWithdrawal into memory, using the withdrawal hash as a unique identifier.
...
...
@@ -240,8 +223,9 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// to re-prove their withdrawal only in the case that the output root for their specified
// output index has been updated.
require(
provenWithdrawal.timestamp == 0
|| l2Oracle.getL2Output(provenWithdrawal.l2OutputIndex).outputRoot != provenWithdrawal.outputRoot,
provenWithdrawal.timestamp == 0 ||
L2_ORACLE.getL2Output(provenWithdrawal.l2OutputIndex).outputRoot !=
provenWithdrawal.outputRoot,
"OptimismPortal: withdrawal hash has already been proven"
);
...
...
@@ -261,7 +245,10 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// be relayed on L1.
require(
SecureMerkleTrie.verifyInclusionProof(
abi.encode(storageKey), hex"01", _withdrawalProof, _outputRootProof.messagePasserStorageRoot
abi.encode(storageKey),
hex"01",
_withdrawalProof,
_outputRootProof.messagePasserStorageRoot
),
"OptimismPortal: invalid withdrawal inclusion proof"
);
...
...
@@ -281,12 +268,16 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @notice Finalizes a withdrawal transaction.
/// @param _tx Withdrawal transaction to finalize.
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external whenNotPaused {
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx)
external
whenNotPaused
{
// Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other
// than the default value when a withdrawal transaction is being finalized. This check is
// a defacto reentrancy guard.
require(
l2Sender == Constants.DEFAULT_L2_SENDER, "OptimismPortal: can only trigger one withdrawal per transaction"
l2Sender == Constants.DEFAULT_L2_SENDER,
"OptimismPortal: can only trigger one withdrawal per transaction"
);
// Grab the proven withdrawal from the `provenWithdrawals` map.
...
...
@@ -296,13 +287,16 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// A withdrawal can only be finalized if it has been proven. We know that a withdrawal has
// been proven at least once when its timestamp is non-zero. Unproven withdrawals will have
// a timestamp of zero.
require(provenWithdrawal.timestamp != 0, "OptimismPortal: withdrawal has not been proven yet");
require(
provenWithdrawal.timestamp != 0,
"OptimismPortal: withdrawal has not been proven yet"
);
// As a sanity check, we make sure that the proven withdrawal's timestamp is greater than
// starting timestamp inside the L2OutputOracle. Not strictly necessary but extra layer of
// safety against weird bugs in the proving step.
require(
provenWithdrawal.timestamp >=
l2Oracle
.startingTimestamp(),
provenWithdrawal.timestamp >=
L2_ORACLE
.startingTimestamp(),
"OptimismPortal: withdrawal timestamp less than L2 Oracle starting timestamp"
);
...
...
@@ -317,7 +311,9 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// Grab the OutputProposal from the L2OutputOracle, will revert if the output that
// corresponds to the given index has not been proposed yet.
Types.OutputProposal memory proposal = l2Oracle.getL2Output(provenWithdrawal.l2OutputIndex);
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(
provenWithdrawal.l2OutputIndex
);
// Check that the output root that was used to prove the withdrawal is the same as the
// current output root for the given output index. An output root may change if it is
...
...
@@ -334,7 +330,10 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
);
// Check that this withdrawal has not already been finalized, this is replay protection.
require(finalizedWithdrawals[withdrawalHash] == false, "OptimismPortal: withdrawal has already been finalized");
require(
finalizedWithdrawals[withdrawalHash] == false,
"OptimismPortal: withdrawal has already been finalized"
);
// Mark the withdrawal as finalized so it can't be replayed.
finalizedWithdrawals[withdrawalHash] = true;
...
...
@@ -381,20 +380,22 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
)
public
payable
metered(_gasLimit)
{
) 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");
require(
_to == address(0),
"OptimismPortal: must send to address(0) when creating a contract"
);
}
// Prevent depositing transactions that have too small of a gas limit. Users should pay
// more for more resource usage.
require(_gasLimit >= minimumGasLimit(uint64(_data.length)), "OptimismPortal: gas limit too small");
require(
_gasLimit >= minimumGasLimit(uint64(_data.length)),
"OptimismPortal: gas limit too small"
);
// Prevent the creation of deposit transactions that have too much calldata. This gives an
// upper limit on the size of unsafe blocks over the p2p network. 120kb is chosen to ensure
...
...
@@ -411,7 +412,13 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
// Compute the opaque data that will be emitted as part of the TransactionDeposited event.
// We use opaque data so that we can update the TransactionDeposited event in the future
// without breaking the current interface.
bytes memory opaqueData = abi.encodePacked(msg.value, _value, _gasLimit, _isCreation, _data);
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.
...
...
@@ -424,7 +431,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @param _l2OutputIndex Index of the L2 output to check.
/// @return Whether or not the output is finalized.
function isOutputFinalized(uint256 _l2OutputIndex) external view returns (bool) {
return _isFinalizationPeriodElapsed(
l2Oracle
.getL2Output(_l2OutputIndex).timestamp);
return _isFinalizationPeriodElapsed(
L2_ORACLE
.getL2Output(_l2OutputIndex).timestamp);
}
/// @notice Determines whether the finalization period has elapsed with respect to
...
...
@@ -432,6 +439,6 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @param _timestamp Timestamp to check.
/// @return Whether or not the finalization period has elapsed.
function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) {
return block.timestamp > _timestamp +
l2Oracle
.FINALIZATION_PERIOD_SECONDS();
return block.timestamp > _timestamp +
L2_ORACLE
.FINALIZATION_PERIOD_SECONDS();
}
}
packages/contracts-bedrock/src/L1/ResourceMetering.sol
View file @
40214739
...
...
@@ -76,16 +76,16 @@ abstract contract ResourceMetering is Initializable {
uint256 blockDiff = block.number - params.prevBlockNum;
ResourceConfig memory config = _resourceConfig();
int256 targetResourceLimit =
int256(uint256(config.
maxResourceLimit)) / int256(uint256(config.
elasticityMultiplier));
int256 targetResourceLimit =
int256(uint256(config.maxResourceLimit)) /
int256(uint256(config.elasticityMultiplier));
if (blockDiff > 0) {
// Handle updating EIP-1559 style gas parameters. We use EIP-1559 to restrict the rate
// at which deposits can be created and therefore limit the potential for deposits to
// spam the L2 system. Fee scheme is very similar to EIP-1559 with minor changes.
int256 gasUsedDelta = int256(uint256(params.prevBoughtGas)) - targetResourceLimit;
int256 baseFeeDelta = (int256(uint256(params.prevBaseFee)) * gasUsedDelta)
/
(targetResourceLimit * int256(uint256(config.baseFeeMaxChangeDenominator)));
int256 baseFeeDelta = (int256(uint256(params.prevBaseFee)) * gasUsedDelta)
/
(targetResourceLimit * int256(uint256(config.baseFeeMaxChangeDenominator)));
// Update base fee by adding the base fee delta and clamp the resulting value between
// min and max.
...
...
@@ -155,6 +155,10 @@ abstract contract ResourceMetering is Initializable {
/// child contract.
// solhint-disable-next-line func-name-mixedcase
function __ResourceMetering_init() internal onlyInitializing {
params = ResourceParams({ prevBaseFee: 1 gwei, prevBoughtGas: 0, prevBlockNum: uint64(block.number) });
params = ResourceParams({
prevBaseFee: 1 gwei,
prevBoughtGas: 0,
prevBlockNum: uint64(block.number)
});
}
}
packages/contracts-bedrock/src/L1/SystemConfig.sol
View file @
40214739
...
...
@@ -11,7 +11,7 @@ import { Constants } from "src/libraries/Constants.sol";
/// @notice The SystemConfig contract is used to manage configuration of an Optimism network.
/// All configuration is stored on L1 and picked up by L2 as part of the derviation of
/// the L2 chain.
contract SystemConfig is OwnableUpgradeable,
I
Semver {
contract SystemConfig is OwnableUpgradeable, Semver {
/// @notice Enum representing different types of updates.
/// @custom:value BATCHER Represents an update to the batcher hash.
/// @custom:value GAS_CONFIG Represents an update to txn fee config on L2.
...
...
@@ -25,17 +25,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
UNSAFE_BLOCK_SIGNER
}
/// @notice Struct representing the addresses of L1 system contracts. These should be the
/// proxies and will differ for each OP Stack chain.
struct Addresses {
address l1CrossDomainMessenger;
address l1ERC721Bridge;
address l1StandardBridge;
address l2OutputOracle;
address optimismPortal;
address optimismMintableERC20Factory;
}
/// @notice Version identifier, used for upgrades.
uint256 public constant VERSION = 0;
...
...
@@ -43,35 +32,8 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
/// Storing it at this deterministic storage slot allows for decoupling the storage
/// layout from the way that `solc` lays out storage. The `op-node` uses a storage
/// proof to fetch this value.
/// @dev NOTE: this value will be migrated to another storage slot in a future version.
/// User input should not be placed in storage in this contract until this migration
/// happens. It is unlikely that keccak second preimage resistance will be broken,
/// but it is better to be safe than sorry.
bytes32 public constant UNSAFE_BLOCK_SIGNER_SLOT = keccak256("systemconfig.unsafeblocksigner");
/// @notice Storage slot that the L1CrossDomainMessenger address is stored at.
bytes32 public constant L1_CROSS_DOMAIN_MESSENGER_SLOT =
bytes32(uint256(keccak256("systemconfig.l1crossdomainmessenger")) - 1);
/// @notice Storage slot that the L1ERC721Bridge address is stored at.
bytes32 public constant L1_ERC_721_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1erc721bridge")) - 1);
/// @notice Storage slot that the L1StandardBridge address is stored at.
bytes32 public constant L1_STANDARD_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1standardbridge")) - 1);
/// @notice Storage slot that the L2OutputOracle address is stored at.
bytes32 public constant L2_OUTPUT_ORACLE_SLOT = bytes32(uint256(keccak256("systemconfig.l2outputoracle")) - 1);
/// @notice Storage slot that the OptimismPortal address is stored at.
bytes32 public constant OPTIMISM_PORTAL_SLOT = bytes32(uint256(keccak256("systemconfig.optimismportal")) - 1);
/// @notice Storage slot that the OptimismMintableERC20Factory address is stored at.
bytes32 public constant OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT =
bytes32(uint256(keccak256("systemconfig.optimismmintableerc20factory")) - 1);
/// @notice Storage slot that the batch inbox address is stored at.
bytes32 public constant BATCH_INBOX_SLOT = bytes32(uint256(keccak256("systemconfig.batchinbox")) - 1);
/// @notice Fixed L2 gas overhead. Used as part of the L2 fee calculation.
uint256 public overhead;
...
...
@@ -97,42 +59,32 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
/// @param data Encoded update data.
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice The block at which the op-node can start searching for logs from.
uint256 public startBlock;
/// @notice Semantic version.
/// @custom:semver 1.10.0
string public constant version = "1.10.0";
/// @notice Constructs the SystemConfig contract. Cannot set
/// the owner to `address(0)` due to the Ownable contract's
/// implementation, so set it to `address(0xdEaD)`
constructor() {
/// @custom:semver 1.3.1
/// @notice Constructs the SystemConfig contract.
/// @param _owner Initial owner of the contract.
/// @param _overhead Initial overhead value.
/// @param _scalar Initial scalar value.
/// @param _batcherHash Initial batcher hash.
/// @param _gasLimit Initial gas limit.
/// @param _unsafeBlockSigner Initial unsafe block signer address.
/// @param _config Initial resource config.
constructor(
address _owner,
uint256 _overhead,
uint256 _scalar,
bytes32 _batcherHash,
uint64 _gasLimit,
address _unsafeBlockSigner,
ResourceMetering.ResourceConfig memory _config
) Semver(1, 3, 1) {
initialize({
_owner: address(0xdEaD),
_overhead: 0,
_scalar: 0,
_batcherHash: bytes32(0),
_gasLimit: 1,
_unsafeBlockSigner: address(0),
_config: ResourceMetering.ResourceConfig({
maxResourceLimit: 1,
elasticityMultiplier: 1,
baseFeeMaxChangeDenominator: 2,
minimumBaseFee: 0,
systemTxMaxGas: 0,
maximumBaseFee: 0
}),
_startBlock: type(uint256).max,
_batchInbox: address(0),
_addresses: SystemConfig.Addresses({
l1CrossDomainMessenger: address(0),
l1ERC721Bridge: address(0),
l1StandardBridge: address(0),
l2OutputOracle: address(0),
optimismPortal: address(0),
optimismMintableERC20Factory: address(0)
})
_owner: _owner,
_overhead: _overhead,
_scalar: _scalar,
_batcherHash: _batcherHash,
_gasLimit: _gasLimit,
_unsafeBlockSigner: _unsafeBlockSigner,
_config: _config
});
}
...
...
@@ -145,13 +97,6 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
/// @param _gasLimit Initial gas limit.
/// @param _unsafeBlockSigner Initial unsafe block signer address.
/// @param _config Initial ResourceConfig.
/// @param _startBlock Starting block for the op-node to search for logs from.
/// Contracts that were deployed before this field existed
/// need to have this field set manually via an override.
/// Newly deployed contracts should set this value to uint256(0).
/// @param _batchInbox Batch inbox address. An identifier for the op-node to find
/// canonical data.
/// @param _addresses Set of L1 contract addresses. These should be the proxies.
function initialize(
address _owner,
uint256 _overhead,
...
...
@@ -159,33 +104,15 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
bytes32 _batcherHash,
uint64 _gasLimit,
address _unsafeBlockSigner,
ResourceMetering.ResourceConfig memory _config,
uint256 _startBlock,
address _batchInbox,
SystemConfig.Addresses memory _addresses
)
public
reinitializer(Constants.INITIALIZER)
{
ResourceMetering.ResourceConfig memory _config
) public initializer {
__Ownable_init();
transferOwnership(_owner);
// These are set in ascending order of their UpdateTypes.
_setBatcherHash(_batcherHash);
_setGasConfig({ _overhead: _overhead, _scalar: _scalar });
_setGasLimit(_gasLimit);
overhead = _overhead;
scalar = _scalar;
batcherHash = _batcherHash;
gasLimit = _gasLimit;
_setUnsafeBlockSigner(_unsafeBlockSigner);
Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox);
Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT, _addresses.l1CrossDomainMessenger);
Storage.setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge);
Storage.setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge);
Storage.setAddress(L2_OUTPUT_ORACLE_SLOT, _addresses.l2OutputOracle);
Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal);
Storage.setAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT, _addresses.optimismMintableERC20Factory);
_setStartBlock(_startBlock);
_setResourceConfig(_config);
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
}
...
...
@@ -203,109 +130,39 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
/// @notice High level getter for the unsafe block signer address.
/// Unsafe blocks can be propagated across the p2p network if they are signed by the
/// key corresponding to this address.
/// @return
addr_
Address of the unsafe block signer.
/// @return Address of the unsafe block signer.
// solhint-disable-next-line ordering
function unsafeBlockSigner() public view returns (address addr_) {
addr_ = Storage.getAddress(UNSAFE_BLOCK_SIGNER_SLOT);
}
/// @notice Getter for the L1CrossDomainMessenger address.
function l1CrossDomainMessenger() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT);
}
/// @notice Getter for the L1ERC721Bridge address.
function l1ERC721Bridge() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_ERC_721_BRIDGE_SLOT);
}
/// @notice Getter for the L1StandardBridge address.
function l1StandardBridge() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_STANDARD_BRIDGE_SLOT);
}
/// @notice Getter for the L2OutputOracle address.
function l2OutputOracle() external view returns (address addr_) {
addr_ = Storage.getAddress(L2_OUTPUT_ORACLE_SLOT);
}
/// @notice Getter for the OptimismPortal address.
function optimismPortal() external view returns (address addr_) {
addr_ = Storage.getAddress(OPTIMISM_PORTAL_SLOT);
}
/// @notice Getter for the OptimismMintableERC20Factory address.
function optimismMintableERC20Factory() external view returns (address addr_) {
addr_ = Storage.getAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT);
}
/// @notice Getter for the BatchInbox address.
function batchInbox() external view returns (address addr_) {
addr_ = Storage.getAddress(BATCH_INBOX_SLOT);
}
/// @notice Sets the start block in a backwards compatible way. Proxies
/// that were initialized before the startBlock existed in storage
/// can have their start block set by a user provided override.
/// A start block of 0 indicates that there is no override and the
/// start block will be set by `block.number`.
/// @dev This logic is used to patch legacy deployments with new storage values.
/// Use the override if it is provided as a non zero value and the value
/// has not already been set in storage. Use `block.number` if the value
/// has already been set in storage
/// @param _startBlock The start block override to set in storage.
function _setStartBlock(uint256 _startBlock) internal {
if (_startBlock != 0 && startBlock == 0) {
// There is an override and it is not already set, this is for legacy chains.
startBlock = _startBlock;
} else if (startBlock == 0) {
// There is no override and it is not set in storage. Set it to the block number.
// This is for newly deployed chains.
startBlock = block.number;
function unsafeBlockSigner() external view returns (address) {
address addr;
bytes32 slot = UNSAFE_BLOCK_SIGNER_SLOT;
assembly {
addr := sload(slot)
}
return addr;
}
/// @notice Updates the unsafe block signer address.
Can only be called by the owner.
/// @notice Updates the unsafe block signer address.
/// @param _unsafeBlockSigner New unsafe block signer address.
function setUnsafeBlockSigner(address _unsafeBlockSigner) external onlyOwner {
_setUnsafeBlockSigner(_unsafeBlockSigner);
}
/// @notice Updates the unsafe block signer address.
/// @param _unsafeBlockSigner New unsafe block signer address.
function _setUnsafeBlockSigner(address _unsafeBlockSigner) internal {
Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner);
bytes memory data = abi.encode(_unsafeBlockSigner);
emit ConfigUpdate(VERSION, UpdateType.UNSAFE_BLOCK_SIGNER, data);
}
/// @notice Updates the batcher hash.
Can only be called by the owner.
/// @notice Updates the batcher hash.
/// @param _batcherHash New batcher hash.
function setBatcherHash(bytes32 _batcherHash) external onlyOwner {
_setBatcherHash(_batcherHash);
}
/// @notice Internal function for updating the batcher hash.
/// @param _batcherHash New batcher hash.
function _setBatcherHash(bytes32 _batcherHash) internal {
batcherHash = _batcherHash;
bytes memory data = abi.encode(_batcherHash);
emit ConfigUpdate(VERSION, UpdateType.BATCHER, data);
}
/// @notice Updates gas config.
Can only be called by the owner.
/// @notice Updates gas config.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function setGasConfig(uint256 _overhead, uint256 _scalar) external onlyOwner {
_setGasConfig(_overhead, _scalar);
}
/// @notice Internal function for updating the gas config.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function _setGasConfig(uint256 _overhead, uint256 _scalar) internal {
overhead = _overhead;
scalar = _scalar;
...
...
@@ -313,15 +170,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data);
}
/// @notice Updates the L2 gas limit.
Can only be called by the owner.
/// @notice Updates the L2 gas limit.
/// @param _gasLimit New gas limit.
function setGasLimit(uint64 _gasLimit) external onlyOwner {
_setGasLimit(_gasLimit);
}
/// @notice Internal function for updating the L2 gas limit.
/// @param _gasLimit New gas limit.
function _setGasLimit(uint64 _gasLimit) internal {
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
gasLimit = _gasLimit;
...
...
@@ -329,6 +180,17 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
emit ConfigUpdate(VERSION, UpdateType.GAS_LIMIT, data);
}
/// @notice Low level setter for the unsafe block signer address.
/// This function exists to deduplicate code around storing the unsafeBlockSigner
/// address in storage.
/// @param _unsafeBlockSigner New unsafeBlockSigner value.
function _setUnsafeBlockSigner(address _unsafeBlockSigner) internal {
bytes32 slot = UNSAFE_BLOCK_SIGNER_SLOT;
assembly {
sstore(slot, _unsafeBlockSigner)
}
}
/// @notice A getter for the resource config.
/// Ensures that the struct is returned instead of a tuple.
/// @return ResourceConfig
...
...
@@ -350,19 +212,29 @@ contract SystemConfig is OwnableUpgradeable, ISemver {
function _setResourceConfig(ResourceMetering.ResourceConfig memory _config) internal {
// Min base fee must be less than or equal to max base fee.
require(
_config.minimumBaseFee <= _config.maximumBaseFee, "SystemConfig: min base fee must be less than max base"
_config.minimumBaseFee <= _config.maximumBaseFee,
"SystemConfig: min base fee must be less than max base"
);
// Base fee change denominator must be greater than 1.
require(_config.baseFeeMaxChangeDenominator > 1, "SystemConfig: denominator must be larger than 1");
require(
_config.baseFeeMaxChangeDenominator > 1,
"SystemConfig: denominator must be larger than 1"
);
// Max resource limit plus system tx gas must be less than or equal to the L2 gas limit.
// The gas limit must be increased before these values can be increased.
require(_config.maxResourceLimit + _config.systemTxMaxGas <= gasLimit, "SystemConfig: gas limit too low");
require(
_config.maxResourceLimit + _config.systemTxMaxGas <= gasLimit,
"SystemConfig: gas limit too low"
);
// Elasticity multiplier must be greater than 0.
require(_config.elasticityMultiplier > 0, "SystemConfig: elasticity multiplier cannot be 0");
require(
_config.elasticityMultiplier > 0,
"SystemConfig: elasticity multiplier cannot be 0"
);
// No precision loss when computing target resource limit.
require(
((_config.maxResourceLimit / _config.elasticityMultiplier) *
_config.elasticityMultiplier)
== _config.maxResourceLimit,
((_config.maxResourceLimit / _config.elasticityMultiplier) *
_config.elasticityMultiplier)
== _config.maxResourceLimit,
"SystemConfig: precision loss with target resource limit"
);
...
...
packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol
View file @
40214739
...
...
@@ -19,20 +19,15 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
/// bridge ONLY supports ERC721s originally deployed on Ethereum. Users will need to
/// wait for the one-week challenge period to elapse before their Optimism-native NFT
/// can be refunded on L2.
contract L2ERC721Bridge is ERC721Bridge, ISemver {
/// @custom:semver 1.4.0
string public constant version = "1.4.0";
contract L2ERC721Bridge is ERC721Bridge, Semver {
/// @custom:semver 1.1.1
/// @notice Constructs the L2ERC721Bridge contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
/// @param _otherBridge Address of the ERC721 bridge on the other network.
constructor(address _otherBridge) ERC721Bridge(_otherBridge) {
initialize();
}
/// @notice Initializes the contract.
function initialize() public reinitializer(Constants.INITIALIZER) {
__ERC721Bridge_init({ _messenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER) });
}
constructor(address _messenger, address _otherBridge)
Semver(1, 1, 1)
ERC721Bridge(_messenger, _otherBridge)
{}
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// recipient on this domain.
...
...
@@ -51,10 +46,7 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver {
address _to,
uint256 _tokenId,
bytes calldata _extraData
)
external
onlyOtherBridge
{
) external onlyOtherBridge {
require(_localToken != address(this), "L2ERC721Bridge: local token cannot be self");
// Note that supportsInterface makes a callback to the _localToken address which is user
...
...
@@ -86,10 +78,7 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
)
internal
override
{
) internal override {
require(_remoteToken != address(0), "L2ERC721Bridge: remote token cannot be address(0)");
// Check that the withdrawal is being initiated by the NFT owner
...
...
@@ -101,7 +90,10 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver {
// Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId)
// slither-disable-next-line reentrancy-events
address remoteToken = IOptimismMintableERC721(_localToken).remoteToken();
require(remoteToken == _remoteToken, "L2ERC721Bridge: remote token does not match given value");
require(
remoteToken == _remoteToken,
"L2ERC721Bridge: remote token does not match given value"
);
// When a withdrawal is initiated, we burn the withdrawer's NFT to prevent subsequent L2
// usage
...
...
@@ -109,12 +101,18 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver {
IOptimismMintableERC721(_localToken).burn(_from, _tokenId);
bytes memory message = abi.encodeWithSelector(
L1ERC721Bridge.finalizeBridgeERC721.selector, remoteToken, _localToken, _from, _to, _tokenId, _extraData
L1ERC721Bridge.finalizeBridgeERC721.selector,
remoteToken,
_localToken,
_from,
_to,
_tokenId,
_extraData
);
// Send message to L1 bridge
// slither-disable-next-line reentrancy-events
messenger
.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
MESSENGER
.sendMessage(OTHER_BRIDGE, message, _minGasLimit);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData);
...
...
packages/contracts-bedrock/src/L2/L2StandardBridge.sol
View file @
40214739
...
...
@@ -17,7 +17,7 @@ import { Constants } from "src/libraries/Constants.sol";
/// NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples
/// of some token types that may not be properly supported by this contract include, but are
/// not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists.
contract L2StandardBridge is StandardBridge,
I
Semver {
contract L2StandardBridge is StandardBridge, Semver {
/// @custom:legacy
/// @notice Emitted whenever a withdrawal from L2 to L1 is initiated.
/// @param l1Token Address of the token on L1.
...
...
@@ -52,24 +52,23 @@ contract L2StandardBridge is StandardBridge, ISemver {
bytes extraData
);
/// @custom:semver 1.4.0
string public constant version = "1.4.0";
/// @custom:semver 1.1.1
/// @notice Constructs the L2StandardBridge contract.
/// @param _otherBridge Address of the L1StandardBridge.
constructor(StandardBridge _otherBridge) StandardBridge(_otherBridge) {
initialize();
}
/// @notice Initializer
function initialize() public reinitializer(Constants.INITIALIZER) {
__StandardBridge_init({ _messenger: CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER) });
}
constructor(address payable _otherBridge)
Semver(1, 1, 1)
StandardBridge(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER), _otherBridge)
{}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
receive() external payable override onlyEOA {
_initiateWithdrawal(
Predeploys.LEGACY_ERC20_ETH, msg.sender, msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")
Predeploys.LEGACY_ERC20_ETH,
msg.sender,
msg.sender,
msg.value,
RECEIVE_DEFAULT_GAS_LIMIT,
bytes("")
);
}
...
...
@@ -86,12 +85,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
payable
virtual
onlyEOA
{
) external payable virtual onlyEOA {
_initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
}
...
...
@@ -114,11 +108,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
payable
virtual
{
) external payable virtual {
_initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData);
}
...
...
@@ -138,11 +128,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes calldata _extraData
)
external
payable
virtual
{
) external payable virtual {
if (_l1Token == address(0) && _l2Token == Predeploys.LEGACY_ERC20_ETH) {
finalizeBridgeETH(_from, _to, _amount, _extraData);
} else {
...
...
@@ -172,9 +158,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
internal
{
) internal {
if (_l2Token == Predeploys.LEGACY_ERC20_ETH) {
_initiateBridgeETH(_from, _to, _amount, _minGasLimit, _extraData);
} else {
...
...
@@ -191,11 +175,15 @@ contract L2StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
emit WithdrawalInitiated(address(0), Predeploys.LEGACY_ERC20_ETH, _from, _to, _amount, _extraData);
) internal override {
emit WithdrawalInitiated(
address(0),
Predeploys.LEGACY_ERC20_ETH,
_from,
_to,
_amount,
_extraData
);
super._emitETHBridgeInitiated(_from, _to, _amount, _extraData);
}
...
...
@@ -207,11 +195,15 @@ contract L2StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
emit DepositFinalized(address(0), Predeploys.LEGACY_ERC20_ETH, _from, _to, _amount, _extraData);
) internal override {
emit DepositFinalized(
address(0),
Predeploys.LEGACY_ERC20_ETH,
_from,
_to,
_amount,
_extraData
);
super._emitETHBridgeFinalized(_from, _to, _amount, _extraData);
}
...
...
@@ -225,10 +217,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit WithdrawalInitiated(_remoteToken, _localToken, _from, _to, _amount, _extraData);
super._emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
...
...
@@ -243,10 +232,7 @@ contract L2StandardBridge is StandardBridge, ISemver {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
override
{
) internal override {
emit DepositFinalized(_remoteToken, _localToken, _from, _to, _amount, _extraData);
super._emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
...
...
packages/contracts-bedrock/src/universal/ERC721Bridge.sol
View file @
40214739
...
...
@@ -7,17 +7,15 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
/// @title ERC721Bridge
/// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges.
abstract contract ERC721Bridge
is Initializable
{
abstract contract ERC721Bridge {
/// @notice Messenger contract on this domain.
/// @custom:network-specific
CrossDomainMessenger public messenger;
CrossDomainMessenger public immutable MESSENGER;
/// @notice Address of the bridge on the other network.
/// @custom:legacy
address public immutable OTHER_BRIDGE;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
uint256[4
8
] private __gap;
uint256[4
9
] private __gap;
/// @notice Emitted when an ERC721 bridge to the other network is initiated.
/// @param localToken Address of the token on this domain.
...
...
@@ -54,31 +52,31 @@ abstract contract ERC721Bridge is Initializable {
/// @notice Ensures that the caller is a cross-chain message from the other bridge.
modifier onlyOtherBridge() {
require(
msg.sender == address(
messenger) && messenger
.xDomainMessageSender() == OTHER_BRIDGE,
msg.sender == address(
MESSENGER) && MESSENGER
.xDomainMessageSender() == OTHER_BRIDGE,
"ERC721Bridge: function can only be called from the other bridge"
);
_;
}
/// @
notice Constructs the contract
.
/// @
param _messenger Address of the CrossDomainMessenger on this network
.
/// @param _otherBridge Address of the ERC721 bridge on the other network.
constructor(address _otherBridge) {
constructor(address _messenger, address _otherBridge) {
require(_messenger != address(0), "ERC721Bridge: messenger cannot be address(0)");
require(_otherBridge != address(0), "ERC721Bridge: other bridge cannot be address(0)");
OTHER_BRIDGE = _otherBridge;
}
// @notice Initializes the contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
function __ERC721Bridge_init(CrossDomainMessenger _messenger) internal onlyInitializing {
messenger = _messenger;
MESSENGER = CrossDomainMessenger(_messenger);
OTHER_BRIDGE = _otherBridge;
}
/// @notice Getter for messenger contract.
function MESSENGER() external view returns (CrossDomainMessenger) {
return messenger;
/// @custom:legacy
/// @notice Legacy getter for messenger contract.
/// @return Messenger contract on this domain.
function messenger() external view returns (CrossDomainMessenger) {
return MESSENGER;
}
/// @notice Getter for other bridge address.
/// @custom:legacy
/// @notice Legacy getter for other bridge address.
/// @return Address of the bridge on the other network.
function otherBridge() external view returns (address) {
return OTHER_BRIDGE;
...
...
@@ -106,9 +104,7 @@ abstract contract ERC721Bridge is Initializable {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
{
) external {
// Modifier requiring sender to be EOA. This prevents against a user error that would occur
// if the sender is a smart contract wallet that has a different address on the remote chain
// (or doesn't have an address on the remote chain at all). The user would fail to receive
...
...
@@ -117,7 +113,15 @@ abstract contract ERC721Bridge is Initializable {
// care of the user error we want to avoid.
require(!Address.isContract(msg.sender), "ERC721Bridge: account is not externally owned");
_initiateBridgeERC721(_localToken, _remoteToken, msg.sender, msg.sender, _tokenId, _minGasLimit, _extraData);
_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
msg.sender,
_tokenId,
_minGasLimit,
_extraData
);
}
/// @notice Initiates a bridge of an NFT to some recipient's account on the other chain. Note
...
...
@@ -142,12 +146,18 @@ abstract contract ERC721Bridge is Initializable {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
)
external
{
) external {
require(_to != address(0), "ERC721Bridge: nft recipient cannot be address(0)");
_initiateBridgeERC721(_localToken, _remoteToken, msg.sender, _to, _tokenId, _minGasLimit, _extraData);
_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
_to,
_tokenId,
_minGasLimit,
_extraData
);
}
/// @notice Internal function for initiating a token bridge to the other domain.
...
...
@@ -168,7 +178,5 @@ abstract contract ERC721Bridge is Initializable {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
)
internal
virtual;
) internal virtual;
}
packages/contracts-bedrock/src/universal/Semver.sol
0 → 100644
View file @
40214739
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
/// @title Semver
/// @notice Semver is a simple contract for managing contract versions.
contract Semver {
/// @notice Contract version number (major).
uint256 private immutable MAJOR_VERSION;
/// @notice Contract version number (minor).
uint256 private immutable MINOR_VERSION;
/// @notice Contract version number (patch).
uint256 private immutable PATCH_VERSION;
/// @param _major Version number (major).
/// @param _minor Version number (minor).
/// @param _patch Version number (patch).
constructor(
uint256 _major,
uint256 _minor,
uint256 _patch
) {
MAJOR_VERSION = _major;
MINOR_VERSION = _minor;
PATCH_VERSION = _patch;
}
/// @notice Returns the full semver contract version.
/// @return Semver contract version as a string.
function version() public view returns (string memory) {
return
string(
abi.encodePacked(
Strings.toString(MAJOR_VERSION),
".",
Strings.toString(MINOR_VERSION),
".",
Strings.toString(PATCH_VERSION)
)
);
}
}
packages/contracts-bedrock/src/universal/StandardBridge.sol
View file @
40214739
...
...
@@ -16,26 +16,22 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
/// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles
/// the core bridging logic, including escrowing tokens that are native to the local chain
/// and minting/burning tokens that are native to the remote chain.
abstract contract StandardBridge
is Initializable
{
abstract contract StandardBridge {
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 Corresponding bridge on the other domain. This public getter is deprecated
/// and will be removed in the future. Please use `otherBridge` instead.
/// This can safely be an immutable because for the L1StandardBridge, it will
/// be set to the L2StandardBridge address, which is the same for all OP Stack
/// chains. For the L2StandardBridge, there are not multiple proxies using the
/// same implementation.
/// @custom:legacy
/// @custom:network-specific
/// @notice Messenger contract on this domain.
CrossDomainMessenger public immutable MESSENGER;
/// @notice Corresponding bridge on the other domain.
StandardBridge public immutable OTHER_BRIDGE;
/// @custom:legacy
/// @custom:spacer messenger
/// @notice Spacer for backwards compatibility.
address private spacer_0_
2
_20;
address private spacer_0_
0
_20;
/// @custom:legacy
/// @custom:spacer l2TokenBridge
...
...
@@ -45,29 +41,34 @@ abstract contract StandardBridge is Initializable {
/// @notice Mapping that stores deposits for a given pair of local and remote tokens.
mapping(address => mapping(address => uint256)) public deposits;
/// @notice Messenger contract on this domain. This public getter is deprecated
/// and will be removed in the future. Please use `messenger` instead.
/// @custom:network-specific
CrossDomainMessenger public messenger;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
/// A gap size of 4
6
was chosen here, so that the first slot used in a child contract
/// A gap size of 4
7
was chosen here, so that the first slot used in a child contract
/// would be a multiple of 50.
uint256[4
6
] private __gap;
uint256[4
7
] private __gap;
/// @notice Emitted when an ETH bridge is initiated to the other chain.
/// @param from Address of the sender.
/// @param to Address of the receiver.
/// @param amount Amount of ETH sent.
/// @param extraData Extra data sent with the transaction.
event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
event ETHBridgeInitiated(
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/// @notice Emitted when an ETH bridge is finalized on this chain.
/// @param from Address of the sender.
/// @param to Address of the receiver.
/// @param amount Amount of ETH sent.
/// @param extraData Extra data sent with the transaction.
event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
event ETHBridgeFinalized(
address indexed from,
address indexed to,
uint256 amount,
bytes extraData
);
/// @notice Emitted when an ERC20 bridge is initiated to the other chain.
/// @param localToken Address of the ERC20 on this chain.
...
...
@@ -105,45 +106,39 @@ abstract contract StandardBridge is Initializable {
/// calling code within their constructors, but also doesn't really matter since we're
/// just trying to prevent users accidentally depositing with smart contract wallets.
modifier onlyEOA() {
require(!Address.isContract(msg.sender), "StandardBridge: function can only be called from an EOA");
require(
!Address.isContract(msg.sender),
"StandardBridge: function can only be called from an EOA"
);
_;
}
/// @notice Ensures that the caller is a cross-chain message from the other bridge.
modifier onlyOtherBridge() {
require(
msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(OTHER_BRIDGE),
msg.sender == address(MESSENGER) &&
MESSENGER.xDomainMessageSender() == address(OTHER_BRIDGE),
"StandardBridge: function can only be called from the other bridge"
);
_;
}
/// @param _otherBridge Address of the other StandardBridge contract.
constructor(StandardBridge _otherBridge) {
OTHER_BRIDGE = _otherBridge;
}
/// @notice Initializer.
/// @param _messenger Address of CrossDomainMessenger on this network.
// solhint-disable-next-line func-name-mixedcase
function __StandardBridge_init(CrossDomainMessenger _messenger) internal onlyInitializing {
messenger = _messenger;
/// @param _otherBridge Address of the other StandardBridge contract.
constructor(address payable _messenger, address payable _otherBridge) {
MESSENGER = CrossDomainMessenger(_messenger);
OTHER_BRIDGE = StandardBridge(_otherBridge);
}
/// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
/// Must be implemented by contracts that inherit.
receive() external payable virtual;
/// @notice Getter for messenger contract.
/// @custom:legacy
/// @notice Legacy getter for messenger contract.
/// @return Messenger contract on this domain.
function MESSENGER() external view returns (CrossDomainMessenger) {
return messenger;
}
/// @notice Getter for the remote domain bridge contract.
function otherBridge() external view returns (StandardBridge) {
return OTHER_BRIDGE;
function messenger() external view returns (CrossDomainMessenger) {
return MESSENGER;
}
/// @notice Sends ETH to the sender's address on the other chain.
...
...
@@ -167,7 +162,11 @@ abstract contract StandardBridge is Initializable {
/// @param _extraData Extra data to be sent with the transaction. Note that the recipient will
/// not be triggered with this data, but it will be emitted and can be used
/// to identify the transaction.
function bridgeETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) public payable {
function bridgeETHTo(
address _to,
uint32 _minGasLimit,
bytes calldata _extraData
) public payable {
_initiateBridgeETH(msg.sender, _to, msg.value, _minGasLimit, _extraData);
}
...
...
@@ -188,12 +187,16 @@ abstract contract StandardBridge is Initializable {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
public
virtual
onlyEOA
{
_initiateBridgeERC20(_localToken, _remoteToken, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
) public virtual onlyEOA {
_initiateBridgeERC20(
_localToken,
_remoteToken,
msg.sender,
msg.sender,
_amount,
_minGasLimit,
_extraData
);
}
/// @notice Sends ERC20 tokens to a receiver's address on the other chain. Note that if the
...
...
@@ -215,11 +218,16 @@ abstract contract StandardBridge is Initializable {
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _extraData
)
public
virtual
{
_initiateBridgeERC20(_localToken, _remoteToken, msg.sender, _to, _amount, _minGasLimit, _extraData);
) public virtual {
_initiateBridgeERC20(
_localToken,
_remoteToken,
msg.sender,
_to,
_amount,
_minGasLimit,
_extraData
);
}
/// @notice Finalizes an ETH bridge on this chain. Can only be triggered by the other
...
...
@@ -235,14 +243,10 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes calldata _extraData
)
public
payable
onlyOtherBridge
{
) public payable onlyOtherBridge {
require(msg.value == _amount, "StandardBridge: amount sent does not match amount required");
require(_to != address(this), "StandardBridge: cannot send to self");
require(_to != address(
messenger
), "StandardBridge: cannot send to messenger");
require(_to != address(
MESSENGER
), "StandardBridge: cannot send to messenger");
// Emit the correct events. By default this will be _amount, but child
// contracts may override this function in order to emit legacy events as well.
...
...
@@ -269,10 +273,7 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes calldata _extraData
)
public
onlyOtherBridge
{
) public onlyOtherBridge {
if (_isOptimismMintableERC20(_localToken)) {
require(
_isCorrectTokenPair(_localToken, _remoteToken),
...
...
@@ -304,18 +305,25 @@ abstract contract StandardBridge is Initializable {
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
internal
{
require(msg.value == _amount, "StandardBridge: bridging ETH must include sufficient ETH value");
) internal {
require(
msg.value == _amount,
"StandardBridge: bridging ETH must include sufficient ETH value"
);
// Emit the correct events. By default this will be _amount, but child
// contracts may override this function in order to emit legacy events as well.
_emitETHBridgeInitiated(_from, _to, _amount, _extraData);
messenger
.sendMessage{ value: _amount }(
MESSENGER
.sendMessage{ value: _amount }(
address(OTHER_BRIDGE),
abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData),
abi.encodeWithSelector(
this.finalizeBridgeETH.selector,
_from,
_to,
_amount,
_extraData
),
_minGasLimit
);
}
...
...
@@ -337,9 +345,7 @@ abstract contract StandardBridge is Initializable {
uint256 _amount,
uint32 _minGasLimit,
bytes memory _extraData
)
internal
{
) internal {
if (_isOptimismMintableERC20(_localToken)) {
require(
_isCorrectTokenPair(_localToken, _remoteToken),
...
...
@@ -356,7 +362,7 @@ abstract contract StandardBridge is Initializable {
// contracts may override this function in order to emit legacy events as well.
_emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
messenger
.sendMessage(
MESSENGER
.sendMessage(
address(OTHER_BRIDGE),
abi.encodeWithSelector(
this.finalizeBridgeERC20.selector,
...
...
@@ -379,8 +385,9 @@ abstract contract StandardBridge is Initializable {
/// @param _token Address of the token to check.
/// @return True if the token is an OptimismMintableERC20.
function _isOptimismMintableERC20(address _token) internal view returns (bool) {
return ERC165Checker.supportsInterface(_token, type(ILegacyMintableERC20).interfaceId)
|| ERC165Checker.supportsInterface(_token, type(IOptimismMintableERC20).interfaceId);
return
ERC165Checker.supportsInterface(_token, type(ILegacyMintableERC20).interfaceId) ||
ERC165Checker.supportsInterface(_token, type(IOptimismMintableERC20).interfaceId);
}
/// @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20.
...
...
@@ -389,8 +396,14 @@ abstract contract StandardBridge is Initializable {
/// @param _mintableToken OptimismMintableERC20 to check against.
/// @param _otherToken Pair token to check.
/// @return True if the other token is the correct pair token for the OptimismMintableERC20.
function _isCorrectTokenPair(address _mintableToken, address _otherToken) internal view returns (bool) {
if (ERC165Checker.supportsInterface(_mintableToken, type(ILegacyMintableERC20).interfaceId)) {
function _isCorrectTokenPair(address _mintableToken, address _otherToken)
internal
view
returns (bool)
{
if (
ERC165Checker.supportsInterface(_mintableToken, type(ILegacyMintableERC20).interfaceId)
) {
return _otherToken == ILegacyMintableERC20(_mintableToken).l1Token();
} else {
return _otherToken == IOptimismMintableERC20(_mintableToken).remoteToken();
...
...
@@ -408,10 +421,7 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
virtual
{
) internal virtual {
emit ETHBridgeInitiated(_from, _to, _amount, _extraData);
}
...
...
@@ -426,10 +436,7 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
virtual
{
) internal virtual {
emit ETHBridgeFinalized(_from, _to, _amount, _extraData);
}
...
...
@@ -448,10 +455,7 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
virtual
{
) internal virtual {
emit ERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
...
...
@@ -470,10 +474,7 @@ abstract contract StandardBridge is Initializable {
address _to,
uint256 _amount,
bytes memory _extraData
)
internal
virtual
{
) internal virtual {
emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment