Smart contracts for Optimism Layer 2 scaling solution including L1/L2 messaging, bridging, rollup management, and predeploy contracts
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Predeploy contracts deployed at fixed addresses on Optimism L2 that provide essential system functionality including gas pricing, message passing, fee collection, whitelist management, and L1 block information.
Contract Path: L2/predeploys/OVM_GasPriceOracle.sol
Predeploy Address: 0x420000000000000000000000000000000000000F
Provides L1 gas pricing data for calculating the L1 portion of transaction fees on L2.
contract OVM_GasPriceOracle {
function gasPrice() external view returns (uint256);
function l1BaseFee() external view returns (uint256);
function overhead() external view returns (uint256);
function scalar() external view returns (uint256);
function decimals() external view returns (uint256);
function getL1Fee(bytes memory _data) external view returns (uint256);
function getL1GasUsed(bytes memory _data) external view returns (uint256);
// Owner functions
function setGasPrice(uint256 _gasPrice) external;
function setL1BaseFee(uint256 _baseFee) external;
function setOverhead(uint256 _overhead) external;
function setScalar(uint256 _scalar) external;
function setDecimals(uint256 _decimals) external;
}Returns the current L2 gas price.
Returns: uint256 - Current L2 gas price in wei
Returns the L1 base fee used for L1 cost calculations.
Returns: uint256 - L1 base fee in wei
Returns the overhead value used in L1 cost calculation.
Returns: uint256 - Overhead value
Returns the scalar value used to scale L1 costs.
Returns: uint256 - Scalar value
Returns the number of decimals used in scalar calculations.
Returns: uint256 - Number of decimals
Calculates the L1 data fee for a given transaction.
Parameters:
_data (bytes): Transaction data to calculate fee forReturns: uint256 - L1 fee in wei
Usage:
// Calculate L1 fee for transaction data
bytes memory txData = abi.encodeWithSignature("transfer(address,uint256)", to, amount);
uint256 l1Fee = oracle.getL1Fee(txData);Calculates the L1 gas usage for transaction data.
Parameters:
_data (bytes): Transaction data to calculate gas forReturns: uint256 - L1 gas units used
Sets the L2 gas price (owner only).
Parameters:
_gasPrice (uint256): New L2 gas price in weiSets the L1 base fee (owner only).
Parameters:
_baseFee (uint256): New L1 base fee in weiSets the overhead value (owner only).
Parameters:
_overhead (uint256): New overhead valueSets the scalar value (owner only).
Parameters:
_scalar (uint256): New scalar valueSets the decimals value (owner only).
Parameters:
_decimals (uint256): New decimals valueevent GasPriceUpdated(uint256 _gasPrice);
event L1BaseFeeUpdated(uint256 _l1BaseFee);
event OverheadUpdated(uint256 _overhead);
event ScalarUpdated(uint256 _scalar);
event DecimalsUpdated(uint256 _decimals);Contract Path: L2/predeploys/OVM_L2ToL1MessagePasser.sol
Predeploy Address: 0x4200000000000000000000000000000000000000
Passes messages from L2 to L1 by storing them in the L2 state for inclusion in withdrawal proofs.
contract OVM_L2ToL1MessagePasser {
function passMessageToL1(bytes memory _message) external;
function sentMessages(bytes32 _msgHash) external view returns (bool);
}Passes a message from L2 to L1 by storing it for proof generation.
Parameters:
_message (bytes): Message data to pass to L1Events Emitted:
L2ToL1Message(uint256 _nonce, address _sender, bytes _data)Usage:
// Pass message to L1
bytes memory message = abi.encode(recipient, amount, data);
messagePasser.passMessageToL1(message);Checks if a message hash has been sent.
Parameters:
_msgHash (bytes32): Hash of the message to checkReturns: bool - True if message was sent
event L2ToL1Message(
uint256 indexed _nonce,
address indexed _sender,
bytes _data
);Contract Path: L2/predeploys/OVM_SequencerFeeVault.sol
Predeploy Address: 0x4200000000000000000000000000000000000011
Collects sequencer fees and facilitates withdrawal to L1 when a threshold is reached.
constructor(address _recipient);Parameters:
_recipient (address): L1 address to receive withdrawn feescontract OVM_SequencerFeeVault {
function withdraw() external;
function l1FeeWallet() external view returns (address);
}Withdraws collected fees to L1 when the threshold balance is reached.
Usage:
// Anyone can trigger withdrawal if threshold is met
vault.withdraw();Returns the L1 address that receives withdrawn fees.
Returns: address - L1 recipient address
Contract Path: L2/predeploys/OVM_DeployerWhitelist.sol
Predeploy Address: 0x4200000000000000000000000000000000000002
Manages the deployment whitelist when contract deployment restrictions are enabled.
contract OVM_DeployerWhitelist {
function enableArbitraryContractDeployment() external;
function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external;
function setOwner(address _newOwner) external;
function isDeployerAllowed(address _deployer) external view returns (bool);
function owner() external view returns (address);
function arbitraryContractDeploymentAllowed() external view returns (bool);
}Permanently disables the whitelist, allowing anyone to deploy contracts (owner only).
Sets the whitelist status for a specific deployer (owner only).
Parameters:
_deployer (address): Address to set whitelist status for_isWhitelisted (bool): Whether the deployer should be whitelistedTransfers ownership of the whitelist (owner only).
Parameters:
_newOwner (address): New owner addressChecks if a deployer is allowed to deploy contracts.
Parameters:
_deployer (address): Address to checkReturns: bool - True if deployment is allowed
Usage:
// Check if deployment is allowed
require(
whitelist.isDeployerAllowed(msg.sender),
"Deployment not allowed"
);event OwnerChanged(address oldOwner, address newOwner);
event WhitelistStatusChanged(address deployer, bool whitelisted);
event WhitelistDisabled(address oldOwner);Contract Path: L2/predeploys/OVM_L1BlockNumber.sol
Predeploy Address: 0x4200000000000000000000000000000000000013
Provides the current L1 block number to L2 contracts.
contract OVM_L1BlockNumber {
function getL1BlockNumber() external view returns (uint256);
}Returns the latest L1 block number known to L2.
Returns: uint256 - Current L1 block number
Usage:
// Get current L1 block number
uint256 l1BlockNumber = l1BlockNumber.getL1BlockNumber();Contract Path: L2/predeploys/WETH9.sol
Predeploy Address: 0x4200000000000000000000000000000000000006
Standard Wrapped Ether contract deployed on L2.
contract WETH9 {
function deposit() external payable;
function withdraw(uint256 wad) external;
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
// Standard ERC20 functions
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}Deposits ETH and mints WETH tokens.
Usage:
// Deposit 1 ETH and receive 1 WETH
weth.deposit{value: 1 ether}();Burns WETH tokens and returns ETH.
Parameters:
wad (uint256): Amount of WETH to burn and withdraw as ETHUsage:
// Withdraw 1 ETH by burning 1 WETH
weth.withdraw(1 ether);event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);Contract Path: L2/predeploys/OVM_ETH.sol
Predeploy Address: 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000 (disabled)
Native ETH representation on L2 (currently disabled in favor of WETH9).
contract OVM_ETH {
// Extends L2StandardERC20
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
// Standard ERC20 functions inherited from L2StandardERC20
}Note: This contract is currently disabled and replaced by WETH9 at address 0x4200000000000000000000000000000000000006.
contract FeeCalculator {
IOVMGasPriceOracle constant oracle = IOVMGasPriceOracle(0x420000000000000000000000000000000000000F);
function calculateTotalFee(bytes memory txData, uint256 gasUsed)
external
view
returns (uint256)
{
uint256 l2Fee = gasUsed * oracle.gasPrice();
uint256 l1Fee = oracle.getL1Fee(txData);
return l2Fee + l1Fee;
}
function estimateL1Cost(bytes memory callData) external view returns (uint256) {
return oracle.getL1Fee(callData);
}
}contract L2Notifier {
address constant MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
function sendNotificationToL1(address recipient, uint256 value) external {
bytes memory message = abi.encode(recipient, value, block.timestamp);
// This will be included in the L2 state for withdrawal proof
IOVMMessagePasser(MESSAGE_PASSER).passMessageToL1(message);
}
}contract DeploymentManager {
address constant WHITELIST = 0x4200000000000000000000000000000000000002;
modifier onlyAllowedDeployer() {
require(
IOVMDeployerWhitelist(WHITELIST).isDeployerAllowed(msg.sender),
"Deployment not permitted"
);
_;
}
function deployContract(bytes memory bytecode)
external
onlyAllowedDeployer
returns (address)
{
address deployed;
assembly {
deployed := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
return deployed;
}
}contract WETHManager {
IWETH9 constant weth = IWETH9(0x4200000000000000000000000000000000000006);
function wrapETH() external payable {
weth.deposit{value: msg.value}();
weth.transfer(msg.sender, msg.value);
}
function unwrapWETH(uint256 amount) external {
weth.transferFrom(msg.sender, address(this), amount);
weth.withdraw(amount);
payable(msg.sender).transfer(amount);
}
}contract TimeBasedContract {
address constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013;
function getL1BlockInfo() external view returns (uint256) {
return IOVMBlockNumber(L1_BLOCK_NUMBER).getL1BlockNumber();
}
function isL1BlockRecent(uint256 maxAge) external view returns (bool) {
uint256 currentL1Block = IOVMBlockNumber(L1_BLOCK_NUMBER).getL1BlockNumber();
return (currentL1Block >= maxAge);
}
}