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.
Custom SuperchainERC20 tokens
Overview
This guide explains how to modify the behavior of SuperchainERC20
(opens in a new tab) contracts to create custom tokens that can then be bridged quickly and safely using the SuperchainTokenBridge
(opens in a new tab) contract (once interop is operational).
For more information on how it works, see the explainer.
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 (opens in a new tab).
Here we will use the SuperchainERC20 Starter Kit.
What you'll do
- Use the SuperchainERC20 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
(opens in a new tab) 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.
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
Prepare for deployment
-
Follow the setup steps in the SuperchainERC20 Starter Kit. Don't start the development environment (step 5).
-
Follow the deployment preparations steps 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
.
// 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);
}
}
Explanation
function faucet() external {
_mint(msg.sender, 10**_decimals);
}
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.
Deploy the new token
-
Edit
packages/contracts/scripts/SuperchainERC20Deployer.s.sol
:-
Change line 6 to import the new token.
import {CustomSuperchainToken} from "../src/CustomSuperchainToken.sol";
-
Update lines 52-54 to get the
CustomSuperchainToken
initialization code.bytes memory initCode = abi.encodePacked( type(CustomSuperchainToken).creationCode, abi.encode(ownerAddr_, name, symbol, uint8(decimals)) );
-
Modify line 62 to deploy a
CustomSuperchainToken
contract.addr_ = address(new CustomSuperchainToken{salt: _implSalt()}(ownerAddr_, name, symbol, uint8(decimals)));
-
-
Deploy the token contract.
pnpm contracts:deploy:token
Sanity check
-
Set
TOKEN_ADDRESS
to the address where the token is deployed. You can also play with the token I created, which is at address0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8
(opens in a new tab).TOKEN_ADDRESS=0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8
-
Source the
.env
file to get the private key and the address to which it corresponds.. packages/contracts/.env MY_ADDRESS=`cast wallet address $DEPLOYER_PRIVATE_KEY`
-
Set variables for the RPC URLs.
RPC_DEV0=https://interop-alpha-0.optimism.io RPC_DEV1=https://interop-alpha-1.optimism.io
-
Get your current balance (it should be zero).
cast call --rpc-url $RPC_DEV0 $TOKEN_ADDRESS "balanceOf(address)" $MY_ADDRESS | cast --from-wei
-
Call the faucet to get a token and check the balance again.
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
How does this work?
To allow for superchain interoperability, an ERC-20 token has to implement ERC-7802 (opens in a new tab).
You can either use the SuperchainERC20
implementation (opens in a new tab), or write your own.
For more details see the explainer.
Next steps
- Use the SuperchainERC20 Starter Kit to deploy your token across the Superchain.
- Explore the SuperchainERC20 specifications (opens in a new tab) for in-depth implementation details.
- Review the Superchain Interop Explainer for answers to common questions about interoperability.