Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 96 additions & 88 deletions specs/interop/predeploys.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,35 +70,43 @@

## Overview

Four new system level predeploys are introduced for managing cross chain messaging and tokens, along with
an update to the `L1Block`, `OptimismMintableERC20Factory` and `L2StandardBridge` contracts with additional functionalities.
Interop introduces new predeploys that facilitate cross chain messaging and token bridging.
### Predeploy Addresses

## CrossL2Inbox
These predeploys are added or modified as part of the interop network upgrade.

| Constant | Value |
| -------- | -------------------------------------------- |
| Address | `0x4200000000000000000000000000000000000022` |
| Name | Value | Type | Link |
|-------------------|---------|----------------|
| `CrossL2Inbox` | `0x4200000000000000000000000000000000000022` | `address` | [CrossL2Inbox](#crossl2inbox) |
| `L2ToL2CrossDomainMessenger` | `0x4200000000000000000000000000000000000023` | `address` | [L2ToL2CrossDomainMessenger](#l2tol2crossdomainmessenger) |
| `L1Block` | `0x4200000000000000000000000000000000000028` | `address` | [L1Block](#l1block) |
| `OptimismMintableERC20Factory` | `0x4200000000000000000000000000000000000029` | `address` | [OptimismMintableERC20Factory](#optimismmintableerc20factory) |
| `SuperchainTokenBridge` | `0x420000000000000000000000000000000000002A` | `address` | [SuperchainTokenBridge](#superchaintokenbridge) |

The `CrossL2Inbox` is the system predeploy for cross chain messaging. Anyone can trigger the execution or validation
of cross chain messages, on behalf of any user.
## CrossL2Inbox

The `CrossL2Inbox` is the central predeploy for cross chain messaging. Any cross chain message MUST pass through
the destination chain's `CrossL2Inbox` for it to be considered safe to consume. It is permissionless to validate
any initiating message, on behalf of any user. There is no replay protection but it can be built at a higher layer.

To ensure safety of the protocol, the [Message Invariants](./messaging.md#messaging-invariants) must be enforced.

[`Identifier`]: ./messaging.md#message-identifier
[`ExecutingMessage`]: #executingmessage-event

### Functions
### Interface

#### validateMessage

A helper to enable contracts to provide their own public entrypoints for cross chain interactions.
Emits the `ExecutingMessage` event to signal the transaction has a cross chain message to validate.
Emits the [`ExecutingMessage`] event to signal the transaction has a cross chain message that must be validated.
This function should be called by applications that utilize cross chain messages.

The following fields are required for validating a cross chain message:

| Name | Type | Description |
| -------- | ---------- | -------------------------------------------------------------------------- |
| `_id` | Identifier | A [`Identifier`] pointing to the initiating message. |
| `_msgHash` | `bytes32` | The keccak256 hash of the message payload matching the initiating message. |
| `_id` | Identifier | An [`Identifier`] pointing to the initiating message. |
| `_msgHash` | `bytes32` | The keccak256 hash of the message payload representing the initiating message. |

```solidity
function validateMessage(Identifier calldata _id, bytes32 _msgHash)
Expand All @@ -125,19 +133,7 @@ Emitting the hash of the message is more efficient than emitting the
message in its entirety. Equality with the initiating message can be handled off-chain through
hash comparison.

### Reference implementation

A simple implementation of the `validateMessage` function is included below.

```solidity
function validateMessage(Identifier calldata _id, bytes32 _msgHash) external {
// We need to know if this is being called on a depositTx
if (IL1BlockInterop(Predeploys.L1_BLOCK_ATTRIBUTES).isDeposit()) revert NoExecutingDeposits();

emit ExecutingMessage(_msgHash, _id);
}
}
```
### Example Integration

An example of a custom entrypoint utilizing `validateMessage` to consume a known
event. Note that in this example, the contract is consuming its own event
Expand Down Expand Up @@ -169,82 +165,56 @@ contract MyCrossChainApp {

### Deposit Handling

Any call to the `CrossL2Inbox` that would emit an `ExecutingMessage` event will reverts
Any call to the `CrossL2Inbox` that would emit an `ExecutingMessage` event MUST revert
if the call is made in a [deposit context](./derivation.md#deposit-context).
The deposit context status can be determined by calling `isDeposit` on the `L1Block` contract.

In the future, deposit handling will be modified to be more permissive.
It will revert only in specific cases where interop dependency resolution is not feasible.

### `Identifier` Getters

The `Identifier` MUST be exposed via `public` getters so that contracts can call back to authenticate
properties about the `_msg`.
See [this issue](https://github.com/ethereum-optimism/specs/issues/520) for more information.

## L2ToL2CrossDomainMessenger

| Constant | Value |
| ----------------- | -------------------------------------------- |
| Address | `0x4200000000000000000000000000000000000023` |
| `MESSAGE_VERSION` | `uint256(0)` |


The `L2ToL2CrossDomainMessenger` is a higher level abstraction on top of the `CrossL2Inbox` that
provides general message passing, utilized for secure transfers ERC20 tokens between L2 chains.
provides general message passing, utilized for secure transfers or ether or tokens between L2 chains.
Messages sent through the `L2ToL2CrossDomainMessenger` on the source chain receive both replay protection
as well as domain binding, i.e. the executing transaction can only be valid on a single chain.

### `relayMessage` Invariants

- The `Identifier.origin` MUST be `address(L2ToL2CrossDomainMessenger)`
- The `_destination` chain id MUST be equal to the local chain id
- Messages MUST NOT be relayed more than once

### `sendMessage` Invariants

- Sent Messages MUST be uniquely identifiable
- It must emit the `SentMessage` event

### Message Versioning

Versioning is handled in the most significant bits of the nonce, similarly to how it is handled by
the `CrossDomainMessenger`.

```solidity
function messageNonce() public view returns (uint256) {
return Encoding.encodeVersionedNonce(nonce, MESSAGE_VERSION);
}
```

### No Native Support for Cross Chain Ether Sends

To enable interoperability between chains that use a custom gas token, there is no native support for
sending `ether` between chains. `ether` must first be wrapped into WETH before sending between chains.
See [SuperchainWETH](./superchain-weth.md) for more information.

### Interfaces
### Interface

The `L2ToL2CrossDomainMessenger` uses a similar interface to the `L2CrossDomainMessenger`, but
the `_minGasLimit` is removed to prevent complexity around EVM gas introspection and the `_destination`
chain is included instead.

#### Sending Messages
#### `sendMessage`

The following function is used for sending messages between domains:
The `sendMessage` function is used to initiate a cross chain call on a remote chain.

```solidity
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32);
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32 msgHash_);
```

It returns the hash of the message being sent,
which is used to track whether the message has successfully been relayed.
It also emits a `SentMessage` event with the necessary metadata to execute when relayed on the destination chain.
The `sendMessage` function is used to initiate a cross chain call on a remote chain. It is permissionless to call.
The `_destination` is the chain id of the remote chain to call. The `_target` is the address of the contract to call on the remote chain.
The `_message` is the calldata to call the contract with.

It returns the hash of the message being sent, which is used to track whether the message has successfully been relayed.
It MUST emit `SentMessage` event with the necessary metadata to authorize the execution of the message when relayed on the destination chain.
```solidity
event SentMessage(uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message);
```

An explicit `_destination` chain and `nonce` are used to ensure that the message can only be played on a single remote
chain a single time. The `_destination` is enforced to not be the local chain to avoid edge cases.
chain one time. The `_destination` is enforced to not be the local chain to avoid edge cases.

The `sendMessage` function is not `payable` as it is not intended to be used for sending ether between chains.

##### Address Aliasing

There is no need for address aliasing as the aliased address would need to commit to the source chain's chain id
to create a unique alias that commits to a particular sender on a particular domain and it is far more simple
Expand All @@ -253,10 +223,63 @@ In both cases, the source chain's chain id is required for security. Executing m
assume the identity of an account because `msg.sender` will never be the identity that initiated the message,
it will be the `L2ToL2CrossDomainMessenger` and users will need to callback to get the initiator of the message.

The `_destination` MUST NOT be the chainid of the local chain and a locally defined `nonce` MUST increment on
every call to `sendMessage`.
Address aliasing would need to be implemented if the top level `msg.sender` value was set based on the caller
of the `sendMessage` function. This is not the case as it would require a diff to the EVM spec.

Note that `sendMessage` is not `payable`.
##### `sendMessage` Invariants

- Sent Messages MUST be uniquely identifiable
- It must emit the `SentMessage` event
- The `_destination` MUST NOT be the chainid of the local chain
- A locally defined `nonce` MUST increment on every call to `sendMessage`

#### `relayMessage`

The `relayMessage` function makes up the second half of a cross chain message.
The call made to `sendMessage` executes when `relayMessage` is called on the destination chain.

The `_id` is the [identifier](./messaging.md#message-identifier) of the `SentMessage` event that was emitted by the `sendMessage` call.
The `_sentMessage` is the [message payload](./messaging.md#message-payload) of the `SentMessage` event.

```solidity
function relayMessage(Identifier calldata _id, bytes calldata _sentMessage) external payable returns (bytes memory returnData_);
```

This needs to be made into its own section!
The `relayMessage` function is `payable` to enable relayers to earn for relaying messages.

To enable cross chain authorization patterns, both the `_sender` and the `_source` MUST be exposed via `public`
getters.

##### `relayMessage` Invariants

- The `Identifier.origin` MUST be `address(L2ToL2CrossDomainMessenger)`
- The `_destination` chain id MUST be equal to the local chain id
- Messages MUST NOT be relayed more than once

##### Dependency Set Management

The `L2ToL2CrossDomainMessenger` has no legibility of the dependency set. It is up to the offchain software to ensure that only
messages from the dependency set can be relayed. Messages from outside the dependency set will be reorg'd out of the chain.

### Message Versioning

Versioning is handled in the most significant bits of the nonce, similarly to how it is handled by
the `CrossDomainMessenger`.

```solidity
function messageNonce() public view returns (uint256) {
return Encoding.encodeVersionedNonce(nonce, MESSAGE_VERSION);
}
```

### No Native Support for Cross Chain Ether Sends

To enable interoperability between chains that use a custom gas token, there is no native support for
sending `ether` between chains. `ether` must first be wrapped into WETH before sending between chains.
See [SuperchainWETH](./superchain-weth.md) for more information.

### Interfaces

#### Relaying Messages

Expand Down Expand Up @@ -288,16 +311,6 @@ flowchart LR
end
```

When relaying a message through the `L2ToL2CrossDomainMessenger`, it is important to require that
the `_destination` be equal to `block.chainid` to ensure that the message is only valid on a single
chain. The hash of the message is used for replay protection.

It is important to ensure that the source chain is in the dependency set of the destination chain, otherwise
it is possible to send a message that is not playable.

A message is relayed by providing the [identifier](./messaging.md#message-identifier) of a `SentMessage`
event along with its corresponding [message payload](./messaging.md#message-payload).

```solidity
function relayMessage(ICrossL2Inbox.Identifier calldata _id, bytes calldata _sentMessage) external payable returns (bytes memory returnData_) {
require(_id.origin == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
Expand All @@ -321,11 +334,6 @@ function relayMessage(ICrossL2Inbox.Identifier calldata _id, bytes calldata _sen
}
```

Note that the `relayMessage` function is `payable` to enable relayers to earn in the gas paying asset.

To enable cross chain authorization patterns, both the `_sender` and the `_source` MUST be exposed via `public`
getters.

## OptimismSuperchainERC20Factory

| Constant | Value |
Expand Down