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.