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
Standard contracts and interfaces that define common patterns and APIs used throughout the Optimism ecosystem, including ERC20 tokens, cross-domain messaging interfaces, and address aliasing utilities.
Contract Path: standards/L2StandardERC20.sol
Standard ERC20 implementation for L2 tokens with mint/burn capabilities for bridge integration.
constructor(
address _l2Bridge,
address _l1Token,
string memory _name,
string memory _symbol
);Parameters:
_l2Bridge (address): L2 bridge contract address that can mint/burn_l1Token (address): Corresponding L1 token address_name (string): Token name_symbol (string): Token symbolcontract L2StandardERC20 {
// Standard ERC20 functions
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
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);
// Bridge integration functions
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
// Metadata functions
function l1Token() external view returns (address);
function l2Bridge() external view returns (address);
// ERC165 support
function supportsInterface(bytes4 _interfaceId) external view returns (bool);
}Mints tokens to an address (bridge only).
Parameters:
_to (address): Address to mint tokens to_amount (uint256): Amount of tokens to mintRestrictions: Only callable by the L2 bridge contract
Burns tokens from an address (bridge only).
Parameters:
_from (address): Address to burn tokens from_amount (uint256): Amount of tokens to burnRestrictions: Only callable by the L2 bridge contract
Returns the address of the corresponding L1 token.
Returns: address - L1 token contract address
Returns the address of the L2 bridge contract.
Returns: address - L2 bridge contract address
// Standard ERC20 events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// Bridge-specific events
event Mint(address indexed _account, uint256 _amount);
event Burn(address indexed _account, uint256 _amount);// Deploy L2 token for bridging
L2StandardERC20 l2Token = new L2StandardERC20(
l2BridgeAddress,
l1TokenAddress,
"Optimism USD Coin",
"USDC"
);
// Bridge will mint tokens when deposits arrive from L1
// Bridge will burn tokens when withdrawing to L1Contract Path: standards/AddressAliasHelper.sol
Utilities for L1↔L2 address aliasing to prevent cross-domain replay attacks and ensure secure cross-layer communication.
library AddressAliasHelper {
function applyL1ToL2Alias(address l1Address)
internal
pure
returns (address l2Address);
function undoL1ToL2Alias(address l2Address)
internal
pure
returns (address l1Address);
}Applies address aliasing for L1→L2 messages to prevent replay attacks.
Parameters:
l1Address (address): Original L1 addressReturns: address - Aliased L2 address
Usage:
// When an L1 contract sends a message, its address is aliased on L2
address l1Sender = 0x1234567890123456789012345678901234567890;
address aliasedSender = AddressAliasHelper.applyL1ToL2Alias(l1Sender);
// L2 contracts should check for the aliased address
require(msg.sender == aliasedSender, "Invalid L1 sender");Removes address aliasing to recover the original L1 address.
Parameters:
l2Address (address): Aliased L2 addressReturns: address - Original L1 address
Usage:
// Recover original L1 address from aliased L2 address
address originalL1 = AddressAliasHelper.undoL1ToL2Alias(msg.sender);The address aliasing uses a constant offset to transform addresses:
l2Address = l1Address + 0x1111000000000000000000000000000000001111l1Address = l2Address - 0x1111000000000000000000000000000000001111Contract Path: libraries/bridge/ICrossDomainMessenger.sol
Base interface for cross-domain messenger contracts (both L1 and L2).
interface ICrossDomainMessenger {
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) external;
function xDomainMessageSender() external view returns (address);
}Sends a message to the other domain.
Parameters:
_target (address): Target contract address on the other domain_message (bytes): Message data to send_gasLimit (uint32): Gas limit for execution on the other domainReturns the sender address from the other domain when processing a cross-domain message.
Returns: address - Cross-domain sender address
Contract Path: L1/messaging/IL1CrossDomainMessenger.sol
Interface for L1 cross-domain messenger functionality.
interface IL1CrossDomainMessenger is ICrossDomainMessenger {
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
) external;
function replayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _queueIndex,
uint32 _oldGasLimit,
uint32 _newGasLimit
) external;
}Contract Path: L2/messaging/IL2CrossDomainMessenger.sol
Interface for L2 cross-domain messenger functionality.
interface IL2CrossDomainMessenger is ICrossDomainMessenger {
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
) external;
}Contract Path: L1/messaging/IL1StandardBridge.sol
Interface for L1 standard bridge functionality.
interface IL1StandardBridge {
function depositETH(uint32 _l2Gas, bytes calldata _data) external payable;
function depositETHTo(
address _to,
uint32 _l2Gas,
bytes calldata _data
) external payable;
function depositERC20(
address _l1Token,
address _l2Token,
uint256 _amount,
uint32 _l2Gas,
bytes calldata _data
) external;
function finalizeETHWithdrawal(
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external;
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external;
}Contract Path: L2/messaging/IL2ERC20Bridge.sol
Interface for L2 bridge functionality.
interface IL2ERC20Bridge {
function withdraw(
address _l2Token,
uint256 _amount,
uint32 _l1Gas,
bytes calldata _data
) external;
function withdrawTo(
address _l2Token,
address _to,
uint256 _amount,
uint32 _l1Gas,
bytes calldata _data
) external;
function finalizeDeposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external;
}Contract Path: standards/IL2StandardERC20.sol
Interface for L2 standard ERC20 tokens with mint/burn capabilities.
interface IL2StandardERC20 {
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
function l1Token() external returns (address);
// Standard ERC20 functions
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
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);
}contract SecureCrossDomainReceiver {
ICrossDomainMessenger public messenger;
address public trustedRemoteSender;
modifier onlyFromTrustedRemote() {
require(
msg.sender == address(messenger) &&
messenger.xDomainMessageSender() == trustedRemoteSender,
"Unauthorized cross-domain call"
);
_;
}
function handleRemoteCall(bytes calldata data)
external
onlyFromTrustedRemote
{
// Process trusted cross-domain message
}
}contract L2ContractReceiver {
using AddressAliasHelper for address;
address public trustedL1Contract;
function handleL1Message(bytes calldata data) external {
// Check if sender is the aliased L1 contract
address expectedAlias = trustedL1Contract.applyL1ToL2Alias();
require(msg.sender == expectedAlias, "Invalid L1 sender");
// Process message from trusted L1 contract
}
}contract MyL2Token is L2StandardERC20 {
constructor(
address _l2Bridge,
address _l1Token,
string memory _name,
string memory _symbol
) L2StandardERC20(_l2Bridge, _l1Token, _name, _symbol) {
// Additional initialization if needed
}
// Tokens are automatically mintable/burnable by the bridge
// Standard ERC20 functionality is inherited
}contract BridgeIntegration {
IL1StandardBridge public l1Bridge;
IL2ERC20Bridge public l2Bridge;
function bridgeTokensToL2(
address l1Token,
address l2Token,
uint256 amount
) external {
// Approve bridge to spend tokens
IERC20(l1Token).approve(address(l1Bridge), amount);
// Deposit tokens to L2
l1Bridge.depositERC20(
l1Token,
l2Token,
amount,
200000, // L2 gas limit
""
);
}
function bridgeTokensToL1(
address l2Token,
uint256 amount
) external {
// Withdraw tokens to L1
l2Bridge.withdraw(
l2Token,
amount,
200000, // L1 gas limit
""
);
}
}// L1 Contract
contract L1Controller {
IL1CrossDomainMessenger public messenger;
address public l2Controller;
function sendCommandToL2(uint256 command, bytes calldata data) external {
bytes memory message = abi.encodeWithSignature(
"executeCommand(uint256,bytes)",
command,
data
);
messenger.sendMessage(l2Controller, message, 200000);
}
function handleL2Response(uint256 result) external {
require(
msg.sender == address(messenger) &&
messenger.xDomainMessageSender() == l2Controller,
"Invalid L2 response"
);
// Process result from L2
}
}
// L2 Contract
contract L2Controller {
using AddressAliasHelper for address;
IL2CrossDomainMessenger public messenger;
address public l1Controller;
function executeCommand(uint256 command, bytes calldata data) external {
// Verify sender is aliased L1 controller
require(
msg.sender == l1Controller.applyL1ToL2Alias(),
"Invalid L1 sender"
);
// Execute command
uint256 result = processCommand(command, data);
// Send response back to L1
bytes memory response = abi.encodeWithSignature(
"handleL2Response(uint256)",
result
);
messenger.sendMessage(l1Controller, response, 200000);
}
function processCommand(uint256 command, bytes calldata data)
internal
returns (uint256)
{
// Command processing logic
return 42;
}
}This standards and interfaces documentation provides the foundation for building secure, interoperable contracts within the Optimism ecosystem, with proper cross-domain authentication and standard token implementations.