Skip to content

Tutorial that teaches how to write custom SuperchainERC20 token contracts #1390

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

Merged
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions pages/stack/interop/tutorials/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"message-passing": "Interop message passing",
"deploy-superchain-erc20": "Issuing new assets with SuperchainERC20",
"transfer-superchainERC20": "Transferring a SuperchainERC20",
"deploy-superchain-erc20": "Issuing new assets with SuperchainERC20",
"custom-superchain-erc20": "Custom SuperchainERC20 tokens",
"bridge-crosschain-eth": "Bridging native cross-chain ETH transfers",
"relay-messages-cast": "Relaying interop messages using `cast`",
"relay-messages-viem": "Relaying interop messages using `viem`",
Expand Down
168 changes: 168 additions & 0 deletions pages/stack/interop/tutorials/custom-superchain-erc20.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
title: Creating custom SuperchainERC20 tokens
lang: en-US
description: Create SuperchainERC20 tokens with custom behaviors
---

import { Callout } from 'nextra/components'
import { Steps } from 'nextra/components'

<Callout>
The SuperchainERC20 standard is ready for production deployments.
Please note that the OP Stack interoperability upgrade, required for crosschain messaging, is currently still in active development.
</Callout>

# Custom SuperchainERC20 tokens

## Overview

This guide explains how to modify the behavior of [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) contracts to create custom tokens that can then be bridged quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract (once interop is operational).
For more information on how it works, [see the explainer](/stack/interop/superchain-erc20).

To ensure fungibility across chains, `SuperchainERC20` assets *must* have the same contract address on all chains.
This requirement abstracts away the complexity of cross-chain validation.
Achieving this requires deterministic deployment methods. There are [many ways to do this](https://github.com/Arachnid/deterministic-deployment-proxy).
Here we will use the [SuperchainERC20 Starter Kit](/app-developers/starter-kit).

### What you'll do

* Use the [SuperchainERC20 Starter Kit](/app-developers/starter-kit) to deploy tokens with your custom code.

### What you'll learn

* How to deploy custom ERC-20 tokens on different chains at the same address so that they can be bridged with the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract.

## Prerequisites

Before starting this tutorial, ensure your development environment meets the following requirements:

### Technical knowledge

* Understanding of smart contract development
* Familiarity with blockchain concepts
* Familiarity with [standard SuperchainERC20 deployments](./deploy-superchain-erc20).

### Development environment

* Unix-like operating system (Linux, macOS, or WSL for Windows)
* Git for version control

### Required tools

The tutorial uses these primary tools:

* Foundry: For sending transactions to blockchains.

## Step by step explanation

<Steps>
### Prepare for deployment

1. Follow the setup steps in the [SuperchainERC20 Starter Kit](/app-developers/starter-kit#setup).
Don't start the development environment (step 5).

2. Follow [the deployment preparations steps](./deploy-superchain-erc20#prepare-for-deployment) in the issuing new assets page.
Don't deploy the contracts yet.

**Note:** Make sure to specify a previously unused value for the salt, for example your address and a timestamp.
This is necessary because if the same constructor code is used with the same salt when using the deployment script, it gets the same address, which is a problem if you want a fresh deployment.

### Create the custom contract

The easiest way to do this is to copy and modify the `L2NativeSuperchainERC20.sol` contract.
Use this code, for example, as `packages/contracts/src/CustomSuperchainToken.sol`.

```solidity file=<rootDir>/public/tutorials/CustomSuperchainToken.sol hash=4ad95b9203ce523351eba0501f8b972d
```

<details>
<summary>Explanation</summary>

```solidity file=<rootDir>/public/tutorials/CustomSuperchainToken.sol#L36-L38 hash=4e402ea88c9cd796500425172a6de16d
```

This function lets users get tokens for themselves.
This token is for testing purposes, so it is useful for users to get their own tokens to run tests.
</details>

### Deploy the new token

1. Edit `packages/contracts/scripts/SuperchainERC20Deployer.s.sol`:

* Change line 6 to import the new token.

```solidity
import {CustomSuperchainToken} from "../src/CustomSuperchainToken.sol";
```

* Update lines 52-54 to get the `CustomSuperchainToken` initialization code.

```solidity
bytes memory initCode = abi.encodePacked(
type(CustomSuperchainToken).creationCode, abi.encode(ownerAddr_, name, symbol, uint8(decimals))
);
```

* Modify line 62 to deploy a `CustomSuperchainToken` contract.

```solidity
addr_ = address(new CustomSuperchainToken{salt: _implSalt()}(ownerAddr_, name, symbol, uint8(decimals)));
```

2. Deploy the token contract.

```sh
pnpm contracts:deploy:token
```

<details>
<summary>Sanity check</summary>

1. Set `TOKEN_ADDRESS` to the address where the token is deployed.
You can also play with the token I created, which is at address [`0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8`](https://sid.testnet.routescan.io/address/0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8).

```sh
TOKEN_ADDRESS=0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8
```

2. Source the `.env` file to get the private key and the address to which it corresponds.

```sh
. packages/contracts/.env
MY_ADDRESS=`cast wallet address $DEPLOYER_PRIVATE_KEY`
```

3. Set variables for the RPC URLs.

```sh
RPC_DEV0=https://interop-alpha-0.optimism.io
RPC_DEV1=https://interop-alpha-1.optimism.io
```

4. Get your current balance (it should be zero).

```sh
cast call --rpc-url $RPC_DEV0 $TOKEN_ADDRESS "balanceOf(address)" $MY_ADDRESS | cast --from-wei
```

5. Call the faucet to get a token and check the balance again.

```sh
cast send --private-key $DEPLOYER_PRIVATE_KEY --rpc-url $RPC_DEV0 $TOKEN_ADDRESS "faucet()"
cast call --rpc-url $RPC_DEV0 $TOKEN_ADDRESS "balanceOf(address)" $MY_ADDRESS | cast --from-wei
```
</details>
</Steps>

## How does this work?

To allow for superchain interoperability, an ERC-20 token has to implement [ERC-7802](https://specs.optimism.io/interop/token-bridging.html#ierc7802).
You can either use [the `SuperchainERC20` implementation](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol#L26-L46), or write your own.

For more details [see the explainer](../superchain-erc20).

## Next steps

* Use the [SuperchainERC20 Starter Kit](/app-developers/starter-kit) to deploy your token across the Superchain.
* Explore the [SuperchainERC20 specifications](https://specs.optimism.io/interop/token-bridging.html) for in-depth implementation details.
* Review the [Superchain Interop Explainer](../explainer) for answers to common questions about interoperability.
39 changes: 39 additions & 0 deletions public/tutorials/CustomSuperchainToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {SuperchainERC20} from "./SuperchainERC20.sol";
import {Ownable} from "@solady/auth/Ownable.sol";

contract CustomSuperchainToken is SuperchainERC20, Ownable {
string private _name;
string private _symbol;
uint8 private immutable _decimals;

constructor(address owner_, string memory name_, string memory symbol_, uint8 decimals_) {
_name = name_;
_symbol = symbol_;
_decimals = decimals_;

_initializeOwner(owner_);
}

function name() public view virtual override returns (string memory) {
return _name;
}

function symbol() public view virtual override returns (string memory) {
return _symbol;
}

function decimals() public view override returns (uint8) {
return _decimals;
}

function mintTo(address to_, uint256 amount_) external onlyOwner {
_mint(to_, amount_);
}

function faucet() external {
_mint(msg.sender, 10**_decimals);
}
}