Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interop: token standard without data #268

Merged
Merged
Changes from 2 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
75 changes: 51 additions & 24 deletions specs/interop/token-bridging.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

**Table of Contents**

- [Overview](#overview)
Expand All @@ -17,6 +18,7 @@
- [Invariants](#invariants)
- [Future Considerations](#future-considerations)
- [Cross Chain `transferFrom`](#cross-chain-transferfrom)
- [Concatenated Action](#concatenated-action)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Expand All @@ -38,15 +40,13 @@ The standard will build on top of ERC20 and include the following functions:

#### `sendERC20`

Transfer `_amount` amount of tokens to address `_to` in chain `_chainId`,
alongside with optional data `_data`.
Transfer `_amount` amount of tokens to address `_to` in chain `_chainId`.

It SHOULD burn `_amount` tokens and initialize a message to the `L2ToL2CrossChainMessenger` to mint the `_amount`
in the target address `_to` at `_chainId`. The optional `_data` will be included in as part of the
`L2ToL2CrossChainMessenger` message.
in the target address `_to` at `_chainId`.

```solidity
sendERC20(address _to, uint256 _amount, uint256 _chainId, bytes _data)
sendERC20(address _to, uint256 _amount, uint256 _chainId)
```

#### `relayERC20`
Expand All @@ -55,11 +55,9 @@ Process incoming messages IF AND ONLY IF initiated
by the same contract (token) address on a different chain
and come from the `L2ToL2CrossChainMessenger` in the local chain.
It will mint `_amount` to address `_to`, as defined in `sendERC20`.
If the message containes `_data`,
it will also include an external call to address `_to` with `_data`.

```solidity
relayERC20(address _to, uint256 _amount, bytes _data)
relayERC20(address _to, uint256 _amount)
```

### Events
Expand All @@ -69,15 +67,15 @@ relayERC20(address _to, uint256 _amount, bytes _data)
MUST trigger when a cross-chain transfer is initiated using `sendERC20`.

```solidity
event SendERC20(address indexed _from, address indexed _to, uint256 _amount, uint256 _chainId, bytes memory _data)
event SendERC20(address indexed _from, address indexed _to, uint256 _amount, uint256 _chainId)
```

#### `RelayERC20`

MUST trigger when a cross-chain transfer is finalized using `relayERC20`.

```solidity
event RelayERC20(address indexed to, uint256 amount, bytes data);
event RelayERC20(address indexed to, uint256 amount);
```

## Diagram
Expand All @@ -94,16 +92,14 @@ sequenceDiagram
participant SuperERC20_B as SuperchainERC20 (Chain B)
participant Recipient as to

from->>SuperERC20_A: sendERC20To(to, amount, chainID, data)
from->>SuperERC20_A: sendERC20To(to, amount, chainID)
SuperERC20_A->>SuperERC20_A: burn(from, amount)
SuperERC20_A->>Messenger_A: sendMessage(chainId, message)
note right of Messenger_A: relay or user calls
Messenger_A->>Inbox: executeMessage()
Inbox->>Messenger_B: relayMessage()
Messenger_B->>SuperERC20_B: relayERC20(to, amount, data)
Messenger_B->>SuperERC20_B: relayERC20(to, amount)
SuperERC20_B->>SuperERC20_B: mint(to, amount)
note right of SuperERC20_B: Optional
SuperERC20_B->>Recipient: call(data)
```

## Implementation
Expand All @@ -116,24 +112,19 @@ for both replay protection and domain binding.
[l2-to-l2]: ./predeploys.md#l2tol2crossdomainmessenger

```solidity
function sendERC20(address _to, uint256 _amount, uint256 _chainId, bytes memory _data) public {
function sendERC20(address _to, uint256 _amount, uint256 _chainId) public {
_burn(msg.sender, _amount);
bytes memory _message = abi.encodeCall(this.relayERC20, (_to, _amount, _data));
bytes memory _message = abi.encodeCall(this.relayERC20, (_to, _amount));
L2ToL2CrossDomainMessenger.sendMessage(_chainId, address(this), _message);
emit SendERC20(msg.sender, _to, _amount, _chainId, _data);
emit SendERC20(msg.sender, _to, _amount, _chainId);
}

function relayERC20(address _to, uint256 _amount, bytes memory _data) external {
function relayERC20(address _to, uint256 _amount) external {
require(msg.sender == address(L2ToL2CrossChainMessenger));
require(L2ToL2CrossChainMessenger.crossDomainMessageSender() == address(this));
_mint(_to, _amount);

if (data.length > 0) {
(bool success, ) = _to.call(_data);
require(success, "External call failed");
}

emit RelayERC20(_to, _amount, _data)
emit RelayERC20(_to, _amount)
}
```

Expand Down Expand Up @@ -186,3 +177,39 @@ For the moment, the standard will not include any specific functionality
to facilitate such an action and rely on the usage of `permit2`.
If, at some point in the future, these actions were to be included in the standard,
a possible design could introduce a `remoteTransferFrom()` function.

### Concatenated Action

It is possible to have an additional input `bytes _data` in both `sendERC20()` and `relayERC20()` that would make an
additional call to the `_to` address.
This feature could be used for cross-chain concatenated actions,
i.e. bridge funds and then do X.

This vertical has much potential but can also be achieved outside the standard in the following way:

```mermaid
sequenceDiagram
participant from
participant Intermediate_A as intermediate A
participant SuperERC20_A as SuperERC20 (Chain A)
participant Messenger_A as L2ToL2CrossDomainMessenger (Chain A)
participant Inbox_B as CrossL2Inbox (Chain B)
participant Messenger_B as L2ToL2CrossDomainMessenger (Chain B)
participant SuperERC20_B as SuperERC20 (Chain B)

from->>Intermediate_A: sendWithData(data)
Intermediate_A->>SuperERC20_A: sendERC20(amount, chainId)
SuperERC20_A->>SuperERC20_A: burn(from, amount)
SuperERC20_A->>Messenger_A: sendMessage(chainId, message)
Intermediate_A->>Messenger_A: sendMessage(chainId, to, data)
Inbox_B->>Messenger_B: relayMessage(): transfer
Messenger_B->>SuperERC20_B: finalizeSendERC20(to, amount)
SuperERC20_B->>SuperERC20_B: mint(to, amount)
Inbox_B->>Messenger_B: relayMessage(): call
Messenger_B->>to: call(data)
```

Adding the call to the standard would remove the dependence on the sequencer regarding the proper tx ordering
at the sequencer level, but would also introduce more risk for cross-chain fund transferring,
as an incorrectly formatted call would burn funds in the initiating chain but would revert
in destination and could never be successfully replayed.