Interoperability
Custom SuperchainERC20 tokens
💡

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 upgrade an ERC20 to a SuperchainERC20 (opens in a new tab) that can then teleport across the Superchain interop cluster quickly and safely using the SuperchainTokenBridge (opens in a new tab) contract. 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.

About this tutorial

What you'll learn

Prerequisite technical knowledge

Development environment

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

What you'll do

Step by step

General setup

Follow the setup steps in the SuperchainERC20 starter kit, steps 1-4.

  1. Install Foundry (opens in a new tab).

  2. Run these commands:

    git clone https://github.com/ethereum-optimism/superchainerc20-starter.git
    cd superchainerc20-starter
    pnpm install
    pnpm init:env
  3. Edit packages/contracts/configs/deploy-config.toml's [token] section.

    ParameterMeaningExample
    owner_addressOwner of the tokenYour address1
    nameToken nameQuick Transfer Token
    symbolToken symbolQTT
    decimalsNumber of decimal places18

    (1) This should be an address you control (for which you know the private key), which has some ETH on the blockchains in question. In the case of Supersim, the default has 10k ETH and you can use it. For the devnets, see here to send ETH.

  4. If you change owner_address, you need to also set the private key. Edit packages/contracts/.env to set DEPLOYER_PRIVATE_KEY to the private key of an account that has ETH on both devnet blockchains.

    DEPLOYER_PRIVATE_KEY= <<<private key goes here>>>

Blockchain-specific setup

The SuperchainERC20 Starter Kit is already set up for Supersim. All you need to do is start it.

./supersim --interop.autorelay

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

  1. 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)));
  2. Deploy the token contract.

pnpm contracts:deploy:token

Verify the installation

  1. Set TOKEN_ADDRESS to the address where the token is deployed. You can also play with a previously created token, which is at address 0xF3Ce0794cB4Ef75A902e07e5D2b75E4D71495ee8 (opens in a new tab) on the devnets.

    TOKEN_ADDRESS=<<< Your token address >>>
  2. Source the .env file to get the private key and the address to which it corresponds.

    . packages/contracts/.env
    USER_ADDRESS=`cast wallet address $DEPLOYER_PRIVATE_KEY`
  3. Set variables for the RPC URLs.

    Set these parameters for Supersim.

    URL_CHAIN_A=http://127.0.0.1:9545
    URL_CHAIN_B=http://127.0.0.1:9546
  4. Get your current balance (it should be zero).

    cast call --rpc-url $URL_CHAIN_A $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS | cast --from-wei
  5. Call the faucet to get a token and check the balance again.

    cast send  --private-key $DEPLOYER_PRIVATE_KEY --rpc-url $URL_CHAIN_A $TOKEN_ADDRESS "faucet()"
    cast call --rpc-url $URL_CHAIN_A $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS | cast --from-wei
  6. Repeat the same steps on Chain B.

    cast call --rpc-url $URL_CHAIN_B $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS | cast --from-wei
    cast send  --private-key $DEPLOYER_PRIVATE_KEY --rpc-url $URL_CHAIN_B $TOKEN_ADDRESS "faucet()"
    cast call --rpc-url $URL_CHAIN_B $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS | cast --from-wei

For more details see the explainer.

Next steps