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

Initial draft of Alligator Interop spec #285

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from 3 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
136 changes: 136 additions & 0 deletions specs/governance/alligator-interop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<!-- 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)
- [Interface](#interface)
- [Core Functions](#core-functions)
- [Diagram](#diagram)
- [Implementation](#implementation)
- [Backwards Compatibility](#backwards-compatibility)
- [Security Considerations](#security-considerations)

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

# Overview

The goal of this specification is to allow interoperability between the base and superchains for that use the [`Alligator`](alligator.md) predeploy. This allows the support of the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One common misconception is that each chain is called a superchain, but really the superchain is made up of individual chains. What do you mean the base here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicated the use of other OP chains that are not mainnet and their interaction with the Alligator contract

subdelegation flow with for the [`GovernanceToken`](gov-token.md) by making a few changes to the existing interface for cross-chain message passing as it pertains to the checkpoint state.

## Interface

### Core Functions

```solidity
function afterTokenTransfer(
address src,
address dst,
uint256 amount
) external
```

In addition to performing it's standard functionality this method will now use the `L2ToL2CrossDomainMessenger` to send a message to a new method on the `Alligator` contract, `afterTokenTransferInterop`. It will ensure prior to doing so that the `chainId` is not OP Mainnet i.e. 10(0xa). Moreover, it will still handle a check that both address have been migrated using
the new storage field and update the voting power of the delegate.

```solidity
function afterTokenTransferInterop(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just add this to afterTokenTransfer instead of having 2 functions? ie update the post hook?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, updates now only happens if the contract is deployed on OP mainnet

address src,
address dst,
uint256 amount
) external
```

The new function that will be added to the `Alligator` contract will process the interop messages being sent by the `afterTokenTransfer` method. The only address that can call
this method is the `L2ToL2CrossChainMessenger`. Furthermore, it will check that the sender of this message is in fact another OP token with the same address, which is assumed since the
goverance token is a predeploy. Given that messages are received from other tokens that exist on the superchain, an additional check needs to be performed that ensures we are on OP
mainnet by checking the `chainId` before updating the checkpoints for the delegate voting power.

## Diagram

```mermaid
sequenceDiagram
participant delegate
participant AlligatorSuperChain as AlligatorSuperChain (Chain A)
participant AlligatorOPMainnet as AlligatorOPMainnet (Chain B)
participant Inbox as CrossL2Inbox
participant Messenger_A as L2ToL2CrossDomainMessenger (Chain A)
participant Messenger_B as L2ToL2CrossDomainMessenger (Chain B)

delegate->>AlligatorSuperChain: afterTokenTransfer(src, dst, amt)
AlligatorSuperChain->>Messenger_A: sendMessage(nativeChainId, message)
Messenger_A->>Inbox: executeMessage()
Inbox->>Messenger_B: relayMessage()
Messenger_B->>AlligatorOPMainnet: afterTokenTransferInterop(src, dst, amount, chainId)
```

## Implementation

```solidity
function afterTokenTransfer(
address src,
address dst,
uint256 amount
) external {
if (src != dst && amount > 0) {
if (src != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
emit DelegateVotesChanged(src, oldWeight, newWeight);
}

if (dst != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
emit DelegateVotesChanged(dst, oldWeight, newWeight);
}

uint256 nativeChainId;
assembly {
nativeChainId := chainid()
}

if (nativeChainId != uint256(10)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not going to be scalable as every token transfer on a remote chain will require a relayer to send a transaction on op mainnet. This means gov will need to front the costs here because nobody else has the incentive to do so. Wondering if there is a way that we can:

  • send batched diffs
  • not need to be aware of the chainid

Going to brainstorm a bit on this

L2ToL2CrossDomainMessenger.sendMessage({
_destination: nativeChainId,
_target: address(this),
_message: abi.encodeCall(this.afterTokenTransferInterop, (src, dst, amount))
});
}
}
}

function afterTokenTransferInterop(
address src,
address dst,
uint256 amount
) external {
require(msg.sender == address(L2ToL2CrossChainMessenger));
require(L2ToL2CrossChainMessenger.crossDomainMessageSender() == address(this));

uint256 nativeChainId;
assembly {
nativeChainId := chainid()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do block.chainid in modern solidity

}

require(nativeChainId == uint256(10));

if (src != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
emit DelegateVotesChanged(src, oldWeight, newWeight);
}

if (dst != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
emit DelegateVotesChanged(dst, oldWeight, newWeight);
}
}
```

The contract may support a function to get the chainId or another modifier to prevent the Interop function to be called outside of OP mainnet.

## Backwards Compatibility

Previous instances of `Alligator` will not be able to interface with the OP mainnet contract given that they do not support the cross-chain message passing. However, the existing state
of voting power on the superchain shall remained unchanged.

## Security Considerations

We must ensure that that both the `GovernanceToken` and `Alligator` use the same address across all chains.