-
Notifications
You must be signed in to change notification settings - Fork 267
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
bradleycamacho
merged 8 commits into
ethereum-optimism:main
from
qbzzt:250218-custom-superchain-erc20
Feb 21, 2025
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b70d6da
WIP
qbzzt 91febcf
First draft of tutorial for creating custom SuperchainERC20 contracts
qbzzt 0fc2e4e
Lint
qbzzt e851568
From `cast--to-dec`
qbzzt 8c618a7
Coderabbit and lint
qbzzt 98256cc
Update custom-superchain-erc20.mdx
qbzzt a95eddb
Code rabbit
qbzzt 7ac523f
Merge branch 'main' into 250218-custom-superchain-erc20
bradleycamacho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
pages/stack/interop/tutorials/custom-superchain-erc20.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
|
||
# Creating 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`. | ||
|
||
```typescript file=<rootDir>/public/tutorials/CustomSuperchainToken.sol hash=4ad95b9203ce523351eba0501f8b972d | ||
``` | ||
|
||
<details> | ||
<summary>Explanation</summary> | ||
|
||
```typescript 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
qbzzt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.