From 7fbd3481f4876e2800ec17a8367b0dee24b68490 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 21 Mar 2025 22:12:58 -0500 Subject: [PATCH 01/34] WIP --- pages/stack/interop/tutorials/_meta.json | 3 +- .../tutorials/upgrade-to-superchain-erc20.mdx | 152 ++++++++++++++++++ public/tutorials/upgrade-erc20-setup.mjs | 114 +++++++++++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx create mode 100644 public/tutorials/upgrade-erc20-setup.mjs diff --git a/pages/stack/interop/tutorials/_meta.json b/pages/stack/interop/tutorials/_meta.json index 67816f137..6ff75fe05 100644 --- a/pages/stack/interop/tutorials/_meta.json +++ b/pages/stack/interop/tutorials/_meta.json @@ -2,7 +2,8 @@ "message-passing": "Interop message passing", "deploy-superchain-erc20": "Deploying a SuperchainERC20", "transfer-superchainERC20": "Transferring a SuperchainERC20", - "custom-superchain-erc20": "Custom SuperchainERC20 tokens", + "custom-superchain-erc20": "Custom SuperchainERC20 tokens", + "upgrade-to-superchain-erc20": "Upgrading ERC-20 tokens to SuperchainERC20", "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`", diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx new file mode 100644 index 000000000..5cb7950d0 --- /dev/null +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -0,0 +1,152 @@ +--- +title: Upgrading ERC-20 tokens to SuperchainERC20 +lang: en-US +description: What to do if you already have an ERC-20 token, and now you need to support Interop. +--- + +import { Callout } from 'nextra/components' +import { Steps } from 'nextra/components' + + + 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. + + +# Upgrading ERC-20 tokens to SuperchainERC20 + +## Overview + +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can then teleport across the Superchain interop cluster quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). + +se the [SuperchainERC20 Starter Kit](/app-developers/starter-kit). + +
+ +About this tutorial + +**What you'll learn** + +* How do upgrade an ERC-20 token to enable Superchain interoperability under various conditions. + +**Prerequisite knowlege** + +* You should already know how to [deploy SuperchainERC20 tokens with custom code](./custom-superchain-erc20). + +
+ +### What you'll do + +* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements. +* Create a "lockbox" SuperchainERC20 contract to permissionlessly enable interop. + + +## Setup + + + +### Install and run supersim + +[Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + +### Setup the ERC-20 tokens + +Download and run [this Node.js script](/tutorials/upgrade-erc20-setup.mjs). + +```sh +curl https://docs.optimism.io/tutorials/upgrade-erc20-setup.mjs > upgrade-erc20-setup.mjs +node upgrade-erc20-setup.mjs +``` + + + +## Upgrade a proxied contract + +There are two requirements for an ERC-20 to use Superchain interop: + +- The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + The easiest way to achieve this is to inherit from [`SuperchainERC20`](./custom-superchain-erc20). + We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. +- The equivalent ERC-20 contracts need to be on the same address on the other chains. + +The exact method to get the same address depends on how the proxy contract was initially deployed. +In this case we can see it in the upgrade script. + +```js file=/public/tutorials/upgrade-erc20-setup.mjs#L98-L105 hash=916df10cad1571f3a4528c5015cfe732 +``` + +We need to follow the same steps, from the same address, with the same nonce. +Luckily, we should be able to do it in this case. + + + +### Create a Foundry project + +We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. + +```sh +mkdir proxy-upgrade +cd proxy-upgrade +forge init +forge install OpenZeppelin/openzeppelin-contracts +forge install OpenZeppelin/openzeppelin-contracts-upgradeable +``` + +### Create a deployer contract + +Create an `src/DeployProxy.sol` file with this content: + +```solidity file=/public/tutorials/upgrade-erc20-setup.mjs#L68-L80 hash=aab37e500300404962fccc14894b9c89 filename="src/DeployProxy.sol" +``` + +This is the same deployer contract used for the original deployment. + +### Deploy the deployer contract + +Run this command to deploy the deployer contract on chain B. +Do it repeatedly until you get the same contract address as the one you have on chain A (it has to be the same deployer addresss, which we are using, *and* the same nonce). + +```sh +forge create DeployProxy --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast +``` + + +We can do this because the address we used to deploy, `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`, has a lower nonce on chain B than the one it had on chain A when it deployed the proxy contract originally. +Had this not been the case, it would have been impossible to create a contract in the same address on chain B as the address of the proxy on chain A. + + +### Deploy SuperchainERC20 contracts on both chains + +These contracts are necessary because we need the ERC-20 tokens to support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). +Note that we are using [the starter kit](./deploy-superchain-erc20) because it is the easiest way. +Technically speaking we don't need it, because we don't need the actual `SuperchainERC20` contracts to be on the same address. +The address that we need to be the same is the *proxy* address. + +1. Install the contracts in the starter kit, and copy `SuperchainERC20`. + + ```sh + forge install ethereum-optimism/superchainerc20-starter + cp lib/superchainerc20-starter/packages/contracts/src/SuperchainERC20.sol src + ``` + +1. To map the `interop-lib` library correctly, create a `remappings.txt` file. + + ```text filename="remappings.txt" + @interop-lib/=lib/superchainerc20-starter/packages/contracts/lib/interop-lib/src/ + ``` + +1. Deploy `SuperchainERC20` to both chains, and keep the addresses in environment variables. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + URL_CHAIN_A=http://localhost:9545 + URL_CHAIN_B=http://localhost:9546 + SUPERCHAIN_ERC20_A=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` + SUPERCHAIN_ERC20_B=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` + ``` + + +### + +cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 "deploy(address,address,bytes)" 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x + + diff --git a/public/tutorials/upgrade-erc20-setup.mjs b/public/tutorials/upgrade-erc20-setup.mjs new file mode 100644 index 000000000..752fc9163 --- /dev/null +++ b/public/tutorials/upgrade-erc20-setup.mjs @@ -0,0 +1,114 @@ +// First, create a new Foundry project with the OpenZeppelin libraries + +const {execSync} = await import("child_process") +const process = await import('node:process') +const {writeFile} = await import("node:fs/promises") + +// The supersim environment +const urls = { + "A": "http://127.0.0.1:9545", + "B": "http://127.0.0.1:9546", + "L1":"http://127.0.0.1:8545" +} + +const privateKeys = [ + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", + "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", + "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", + "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6" +] + +const addresses = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", + "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" +] + +// Create the directory +execSync("mkdir upgrade-erc20") +process.chdir("upgrade-erc20") + +// Create a Foundry project with the OpenZeppelin contracts +execSync("forge init") +execSync("forge install OpenZeppelin/openzeppelin-contracts") +execSync("forge install OpenZeppelin/openzeppelin-contracts-upgradeable") + +// Contract files +await writeFile("src/MyToken.sol", ` +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable { + function initialize(string memory name, string memory symbol, uint256 initialSupply) public initializer { + __ERC20_init(name, symbol); + __Ownable_init(msg.sender); + _mint(msg.sender, initialSupply); + } +} +`) + +await writeFile("src/DeployProxy.sol", ` +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DeployProxy { + + function deploy(address logic, address admin, bytes memory data) public returns (address) { + address newProxyAddress = address(new TransparentUpgradeableProxy(logic, admin, data)); + return newProxyAddress; + } +} +`) + +const forgeParams = (chain, account) => + `--rpc-url ${urls[chain]} --private-key ${privateKeys[account]}` + +const deployContract = name => + execSync(`forge create ${name} ${forgeParams("A", 0)} --broadcast \ + | awk '/Deployed to:/ {print $3}' + `) + .toString().slice(0,-1) + + +// Deploy the ERC-20 +// Don't initialize it, because the storage in that address is irrelevant. +// The relevant storage is what will be in the proxy address. +const erc20Address = deployContract("MyToken") + +// Deploy and initialize the proxy +const proxyDeployerAddress = deployContract("DeployProxy") +const res = JSON.parse( + execSync(`cast send ${proxyDeployerAddress} ${forgeParams("A", 0)} "deploy(address,address,bytes)" ${erc20Address} ${addresses[0]} 0x \ + | awk '/logs / {print $2}' +`).toString()) +// The first log entry comes from the proxy +const proxyAddress = res[0].address + +// Initialize the ERC-20 contract in the proxy storage +execSync(`cast send ${proxyAddress} ${forgeParams("A", 0)} "initialize(string,string,uint256)" Test Tst ${"1".padEnd(19, "0")}`) + +console.log(`Proxy deployer on chain A: ${proxyDeployerAddress}`) +console.log(`ERC-20 on chain A: ${proxyAddress}`) +console.log(`One token owned by ${addresses[0]}`) +console.log(`Run this command to verify: + cast call ${proxyAddress} --rpc-url ${urls.A} "balanceOf(address)" ${addresses[0]} | cast from-wei`) \ No newline at end of file From 3697d7dd1ecbc787d161bc3e64ef49e56eec8643 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 21 Mar 2025 22:15:01 -0500 Subject: [PATCH 02/34] WIP --- .../tutorials/upgrade-to-superchain-erc20.mdx | 145 +++++++++--------- 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 5cb7950d0..dbdcbb2d8 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -21,54 +21,50 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git se the [SuperchainERC20 Starter Kit](/app-developers/starter-kit).
+ About this tutorial -About this tutorial + **What you'll learn** -**What you'll learn** + * How do upgrade an ERC-20 token to enable Superchain interoperability under various conditions. -* How do upgrade an ERC-20 token to enable Superchain interoperability under various conditions. - -**Prerequisite knowlege** - -* You should already know how to [deploy SuperchainERC20 tokens with custom code](./custom-superchain-erc20). + **Prerequisite knowlege** + * You should already know how to [deploy SuperchainERC20 tokens with custom code](./custom-superchain-erc20).
### What you'll do * Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements. -* Create a "lockbox" SuperchainERC20 contract to permissionlessly enable interop. - +* Create a "lockbox" SuperchainERC20 contract to permissionlessly enable interop. ## Setup + ### Install and run supersim -### Install and run supersim - -[Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. -### Setup the ERC-20 tokens - -Download and run [this Node.js script](/tutorials/upgrade-erc20-setup.mjs). - -```sh -curl https://docs.optimism.io/tutorials/upgrade-erc20-setup.mjs > upgrade-erc20-setup.mjs -node upgrade-erc20-setup.mjs -``` + ### Setup the ERC-20 tokens + {/* + Download and run [this Node.js script](/tutorials/upgrade-erc20-setup.mjs). + */} + ```sh + curl https://docs.optimism.io/tutorials/upgrade-erc20-setup.mjs > upgrade-erc20-setup.mjs + node upgrade-erc20-setup.mjs + ``` ## Upgrade a proxied contract There are two requirements for an ERC-20 to use Superchain interop: -- The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - The easiest way to achieve this is to inherit from [`SuperchainERC20`](./custom-superchain-erc20). - We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. -- The equivalent ERC-20 contracts need to be on the same address on the other chains. +* The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + The easiest way to achieve this is to inherit from [`SuperchainERC20`](./custom-superchain-erc20). + We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. +* The equivalent ERC-20 contracts need to be on the same address on the other chains. -The exact method to get the same address depends on how the proxy contract was initially deployed. +The exact method to get the same address depends on how the proxy contract was initially deployed. In this case we can see it in the upgrade script. ```js file=/public/tutorials/upgrade-erc20-setup.mjs#L98-L105 hash=916df10cad1571f3a4528c5015cfe732 @@ -78,75 +74,72 @@ We need to follow the same steps, from the same address, with the same nonce. Luckily, we should be able to do it in this case. + ### Create a Foundry project -### Create a Foundry project + We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. -We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. + ```sh + mkdir proxy-upgrade + cd proxy-upgrade + forge init + forge install OpenZeppelin/openzeppelin-contracts + forge install OpenZeppelin/openzeppelin-contracts-upgradeable + ``` -```sh -mkdir proxy-upgrade -cd proxy-upgrade -forge init -forge install OpenZeppelin/openzeppelin-contracts -forge install OpenZeppelin/openzeppelin-contracts-upgradeable -``` + ### Create a deployer contract -### Create a deployer contract + Create an `src/DeployProxy.sol` file with this content: -Create an `src/DeployProxy.sol` file with this content: + ```solidity file=/public/tutorials/upgrade-erc20-setup.mjs#L68-L80 hash=aab37e500300404962fccc14894b9c89 filename="src/DeployProxy.sol" + ``` -```solidity file=/public/tutorials/upgrade-erc20-setup.mjs#L68-L80 hash=aab37e500300404962fccc14894b9c89 filename="src/DeployProxy.sol" -``` - -This is the same deployer contract used for the original deployment. + This is the same deployer contract used for the original deployment. -### Deploy the deployer contract + ### Deploy the deployer contract -Run this command to deploy the deployer contract on chain B. -Do it repeatedly until you get the same contract address as the one you have on chain A (it has to be the same deployer addresss, which we are using, *and* the same nonce). - -```sh -forge create DeployProxy --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast -``` - - -We can do this because the address we used to deploy, `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`, has a lower nonce on chain B than the one it had on chain A when it deployed the proxy contract originally. -Had this not been the case, it would have been impossible to create a contract in the same address on chain B as the address of the proxy on chain A. - + Run this command to deploy the deployer contract on chain B. + Do it repeatedly until you get the same contract address as the one you have on chain A (it has to be the same deployer addresss, which we are using, *and* the same nonce). -### Deploy SuperchainERC20 contracts on both chains + ```sh + forge create DeployProxy --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast + ``` -These contracts are necessary because we need the ERC-20 tokens to support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). -Note that we are using [the starter kit](./deploy-superchain-erc20) because it is the easiest way. -Technically speaking we don't need it, because we don't need the actual `SuperchainERC20` contracts to be on the same address. -The address that we need to be the same is the *proxy* address. + + We can do this because the address we used to deploy, `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`, has a lower nonce on chain B than the one it had on chain A when it deployed the proxy contract originally. + Had this not been the case, it would have been impossible to create a contract in the same address on chain B as the address of the proxy on chain A. + -1. Install the contracts in the starter kit, and copy `SuperchainERC20`. + ### Deploy SuperchainERC20 contracts on both chains - ```sh - forge install ethereum-optimism/superchainerc20-starter - cp lib/superchainerc20-starter/packages/contracts/src/SuperchainERC20.sol src - ``` + These contracts are necessary because we need the ERC-20 tokens to support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + Note that we are using [the starter kit](./deploy-superchain-erc20) because it is the easiest way. + Technically speaking we don't need it, because we don't need the actual `SuperchainERC20` contracts to be on the same address. + The address that we need to be the same is the *proxy* address. -1. To map the `interop-lib` library correctly, create a `remappings.txt` file. + 1. Install the contracts in the starter kit, and copy `SuperchainERC20`. - ```text filename="remappings.txt" - @interop-lib/=lib/superchainerc20-starter/packages/contracts/lib/interop-lib/src/ - ``` + ```sh + forge install ethereum-optimism/superchainerc20-starter + cp lib/superchainerc20-starter/packages/contracts/src/SuperchainERC20.sol src + ``` -1. Deploy `SuperchainERC20` to both chains, and keep the addresses in environment variables. + 2. To map the `interop-lib` library correctly, create a `remappings.txt` file. - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - URL_CHAIN_A=http://localhost:9545 - URL_CHAIN_B=http://localhost:9546 - SUPERCHAIN_ERC20_A=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` - SUPERCHAIN_ERC20_B=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` - ``` + ```text filename="remappings.txt" + @interop-lib/=lib/superchainerc20-starter/packages/contracts/lib/interop-lib/src/ + ``` + 3. Deploy `SuperchainERC20` to both chains, and keep the addresses in environment variables. -### + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + URL_CHAIN_A=http://localhost:9545 + URL_CHAIN_B=http://localhost:9546 + SUPERCHAIN_ERC20_A=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` + SUPERCHAIN_ERC20_B=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` + ``` -cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 "deploy(address,address,bytes)" 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x + ### + cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 --rpc-url [http://localhost:9546](http://localhost:9546) --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 "deploy(address,address,bytes)" 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x From c1ed880a28534d808ffa4265d91f21af5b7bb885 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 21 Mar 2025 22:17:53 -0500 Subject: [PATCH 03/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../stack/interop/tutorials/upgrade-to-superchain-erc20.mdx | 5 ++--- words.txt | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index dbdcbb2d8..bd2a5c00e 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -46,9 +46,8 @@ se the [SuperchainERC20 Starter Kit](/app-developers/starter-kit). ### Setup the ERC-20 tokens - {/* - Download and run [this Node.js script](/tutorials/upgrade-erc20-setup.mjs). - */} + Download and run the setup script. + ```sh curl https://docs.optimism.io/tutorials/upgrade-erc20-setup.mjs > upgrade-erc20-setup.mjs node upgrade-erc20-setup.mjs diff --git a/words.txt b/words.txt index 766de50f1..cfbb67766 100644 --- a/words.txt +++ b/words.txt @@ -186,6 +186,7 @@ JSPATH jspath jwtsecret Keccak +knowlege leveldb lightkdf Lisk @@ -318,6 +319,7 @@ productionize productionized Protip Proxied +proxied Proxyd proxyd Pyth From a4f98e2f1e11cf690f61ee5fe6275deca422e8e0 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 24 Mar 2025 08:45:15 -0500 Subject: [PATCH 04/34] Proxy method works --- .../tutorials/upgrade-to-superchain-erc20.mdx | 190 +++++++++++++----- public/tutorials/InteropToken.sol | 44 ++++ public/tutorials/setup-for-erc20-upgrade.sh | 66 ++++++ public/tutorials/upgrade-erc20-setup.mjs | 114 ----------- 4 files changed, 254 insertions(+), 160 deletions(-) create mode 100644 public/tutorials/InteropToken.sol create mode 100755 public/tutorials/setup-for-erc20-upgrade.sh delete mode 100644 public/tutorials/upgrade-erc20-setup.mjs diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index bd2a5c00e..99c3acc60 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -18,8 +18,6 @@ import { Steps } from 'nextra/components' This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can then teleport across the Superchain interop cluster quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). -se the [SuperchainERC20 Starter Kit](/app-developers/starter-kit). -
About this tutorial @@ -34,45 +32,63 @@ se the [SuperchainERC20 Starter Kit](/app-developers/starter-kit). ### What you'll do -* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements. +* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). * Create a "lockbox" SuperchainERC20 contract to permissionlessly enable interop. ## Setup - ### Install and run supersim + ### Install and run Supersim [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. - ### Setup the ERC-20 tokens + + + Make sure to run Supersim with autorelay on. + + ```sh + ./supersim --interop.authorelay true + ``` + + + ### Setup the ERC-20 token on chain A Download and run the setup script. - + ```sh - curl https://docs.optimism.io/tutorials/upgrade-erc20-setup.mjs > upgrade-erc20-setup.mjs - node upgrade-erc20-setup.mjs + curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh + chmod +x setup-for-erc20-upgrade.sh + ./setup-for-erc20-upgrade.sh + ``` + + ### Store the addresses + + Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. + + ```sh + BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` ## Upgrade a proxied contract -There are two requirements for an ERC-20 to use Superchain interop: +There are several requirements for an ERC-20 to use Superchain interop: * The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). The easiest way to achieve this is to inherit from [`SuperchainERC20`](./custom-superchain-erc20). We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. * The equivalent ERC-20 contracts need to be on the same address on the other chains. +* The storage layout of the ERC-20 contract needs to be the same as the one of `SuperchainERC20`. The exact method to get the same address depends on how the proxy contract was initially deployed. In this case we can see it in the upgrade script. -```js file=/public/tutorials/upgrade-erc20-setup.mjs#L98-L105 hash=916df10cad1571f3a4528c5015cfe732 -``` - We need to follow the same steps, from the same address, with the same nonce. Luckily, we should be able to do it in this case. + ### Create a Foundry project We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. @@ -83,62 +99,144 @@ Luckily, we should be able to do it in this case. forge init forge install OpenZeppelin/openzeppelin-contracts forge install OpenZeppelin/openzeppelin-contracts-upgradeable + forge install ethereum-optimism/interop-lib ``` - ### Create a deployer contract + ### Create and run the deployment script - Create an `src/DeployProxy.sol` file with this content: + 1. Create an `script/LabSetup.s.sol` file with this content: - ```solidity file=/public/tutorials/upgrade-erc20-setup.mjs#L68-L80 hash=aab37e500300404962fccc14894b9c89 filename="src/DeployProxy.sol" - ``` + ```solidity file=/public/tutorials/setup-for-erc20-upgrade.sh#L17-L56 hash=8c54bec9989ae4e1734825d3828ae220 filename="script/LabSetup.s.sol" + ``` + + This is the same deployment script used for the original deployment on chain A. - This is the same deployer contract used for the original deployment. + 1. Run this command to deploy the same contracts on chain B. - ### Deploy the deployer contract + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - Run this command to deploy the deployer contract on chain B. - Do it repeatedly until you get the same contract address as the one you have on chain A (it has to be the same deployer addresss, which we are using, *and* the same nonce). + Scroll up and see the Logs section of the output: - ```sh - forge create DeployProxy --rpc-url http://localhost:9546 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast - ``` + ``` + == Logs == + Token address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 + msg.sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + UpgradeableBeacon: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + Proxy: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + ``` + + Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. + +
+ + What to do when the values are not the same + + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + + You can see the nonce values using these commands: + + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` + + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` + + If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. + +
- We can do this because the address we used to deploy, `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`, has a lower nonce on chain B than the one it had on chain A when it deployed the proxy contract originally. - Had this not been the case, it would have been impossible to create a contract in the same address on chain B as the address of the proxy on chain A. + Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. + + There are two alternative solutions: + - [Lockboxes](#lockboxes-for-permissionless-interop), which require users to exchange their tokens for tokens that can go through interop. + - Writing your own bridge, which allows for ERC-20 tokens on different addresses to be exchanged through interop. - ### Deploy SuperchainERC20 contracts on both chains + ### Deploy ERC-7802 contracts - These contracts are necessary because we need the ERC-20 tokens to support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - Note that we are using [the starter kit](./deploy-superchain-erc20) because it is the easiest way. - Technically speaking we don't need it, because we don't need the actual `SuperchainERC20` contracts to be on the same address. - The address that we need to be the same is the *proxy* address. + We need to replace the ERC-20 contracts with contracts that: - 1. Install the contracts in the starter kit, and copy `SuperchainERC20`. + - Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + - Have the same storage layout as the ERC-20 contracts they replace. - ```sh - forge install ethereum-optimism/superchainerc20-starter - cp lib/superchainerc20-starter/packages/contracts/src/SuperchainERC20.sol src - ``` + + + These contracts do *not* need to be deployed to the same address. + The address that needs to be the same is not the address of the ERC-20 contract itself, but of the proxy. + + - 2. To map the `interop-lib` library correctly, create a `remappings.txt` file. + 1. Create a file, `src/InteropToken.sol`: - ```text filename="remappings.txt" - @interop-lib/=lib/superchainerc20-starter/packages/contracts/lib/interop-lib/src/ + ```solidity file=/public/tutorials/InteropToken.sol hash=5e728534c265028c94d60dcb6550699d filename="src/InteropToken.sol" ``` - 3. Deploy `SuperchainERC20` to both chains, and keep the addresses in environment variables. + 1. Deploy this contract on both chains, and store the addresses (which may or may not be the same). ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - URL_CHAIN_A=http://localhost:9545 - URL_CHAIN_B=http://localhost:9546 - SUPERCHAIN_ERC20_A=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` - SUPERCHAIN_ERC20_B=`forge create SuperchainERC20 --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --broadcast | awk '/Deployed to:/ {print $3}'` + ERC7802_A=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A --broadcast | awk '/Deployed to:/ {print $3}'` + ERC7802_B=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B --broadcast | awk '/Deployed to:/ {print $3}'` ``` - ### - cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 --rpc-url [http://localhost:9546](http://localhost:9546) --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 "deploy(address,address,bytes)" 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x5FbDB2315678afecb367f032d93F642f64180aa3 0x + + ### Update proxies + + Notify the beacon contracts of the new implementation contracts. + + ```sh + cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_A --rpc-url $URL_CHAIN_A + cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_B --rpc-url $URL_CHAIN_B + ``` +
+ +
+ + Sanity check + + 1. See your balance on chain A. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 1. See your balance on chain B. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 1. Transfer 0.1 token. + + ```sh + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + AMOUNT=`echo 0.1 | cast to-wei` + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 + ``` + + 1. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + +
+ + + +## Lockboxes for permissionless interop \ No newline at end of file diff --git a/public/tutorials/InteropToken.sol b/public/tutorials/InteropToken.sol new file mode 100644 index 000000000..e48ed7311 --- /dev/null +++ b/public/tutorials/InteropToken.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.8.28; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IERC7802, IERC165} from "lib/interop-lib/src/interfaces/IERC7802.sol"; +import {PredeployAddresses} from "lib/interop-lib/src/libraries/PredeployAddresses.sol"; + +contract InteropToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, IERC7802 { + function initialize(string memory name, string memory symbol, uint256 initialSupply) public initializer { + __ERC20_init(name, symbol); + __Ownable_init(msg.sender); + _mint(msg.sender, initialSupply); + } + + /// @notice Allows the SuperchainTokenBridge to mint tokens. + /// @param _to Address to mint tokens to. + /// @param _amount Amount of tokens to mint. + function crosschainMint(address _to, uint256 _amount) external { + require(msg.sender == PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE, "Unauthorized"); + + _mint(_to, _amount); + + emit CrosschainMint(_to, _amount, msg.sender); + } + + /// @notice Allows the SuperchainTokenBridge to burn tokens. + /// @param _from Address to burn tokens from. + /// @param _amount Amount of tokens to burn. + function crosschainBurn(address _from, uint256 _amount) external { + require(msg.sender == PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE, "Unauthorized"); + + _burn(_from, _amount); + + emit CrosschainBurn(_from, _amount, msg.sender); + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) { + return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId + || _interfaceId == type(IERC165).interfaceId; + } +} + diff --git a/public/tutorials/setup-for-erc20-upgrade.sh b/public/tutorials/setup-for-erc20-upgrade.sh new file mode 100755 index 000000000..b7f3801c0 --- /dev/null +++ b/public/tutorials/setup-for-erc20-upgrade.sh @@ -0,0 +1,66 @@ +#! /bin/sh + +rm -rf upgrade-erc20 +mkdir upgrade-erc20 +cd upgrade-erc20 + +PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +URL_CHAIN_A=http://127.0.0.1:9545 +URL_CHAIN_B=http://127.0.0.1:9546 + + +forge init +forge install OpenZeppelin/openzeppelin-contracts-upgradeable + +cat > script/LabSetup.s.sol < - `--rpc-url ${urls[chain]} --private-key ${privateKeys[account]}` - -const deployContract = name => - execSync(`forge create ${name} ${forgeParams("A", 0)} --broadcast \ - | awk '/Deployed to:/ {print $3}' - `) - .toString().slice(0,-1) - - -// Deploy the ERC-20 -// Don't initialize it, because the storage in that address is irrelevant. -// The relevant storage is what will be in the proxy address. -const erc20Address = deployContract("MyToken") - -// Deploy and initialize the proxy -const proxyDeployerAddress = deployContract("DeployProxy") -const res = JSON.parse( - execSync(`cast send ${proxyDeployerAddress} ${forgeParams("A", 0)} "deploy(address,address,bytes)" ${erc20Address} ${addresses[0]} 0x \ - | awk '/logs / {print $2}' -`).toString()) -// The first log entry comes from the proxy -const proxyAddress = res[0].address - -// Initialize the ERC-20 contract in the proxy storage -execSync(`cast send ${proxyAddress} ${forgeParams("A", 0)} "initialize(string,string,uint256)" Test Tst ${"1".padEnd(19, "0")}`) - -console.log(`Proxy deployer on chain A: ${proxyDeployerAddress}`) -console.log(`ERC-20 on chain A: ${proxyAddress}`) -console.log(`One token owned by ${addresses[0]}`) -console.log(`Run this command to verify: - cast call ${proxyAddress} --rpc-url ${urls.A} "balanceOf(address)" ${addresses[0]} | cast from-wei`) \ No newline at end of file From ac5de2c6ff2938f9a75bd70b4fce61f8f2537e6b Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 24 Mar 2025 08:46:14 -0500 Subject: [PATCH 05/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../tutorials/upgrade-to-superchain-erc20.mdx | 118 ++++++++---------- 1 file changed, 53 insertions(+), 65 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 99c3acc60..13918f42d 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -43,12 +43,11 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + Make sure to run Supersim with autorelay on. - Make sure to run Supersim with autorelay on. - - ```sh - ./supersim --interop.authorelay true - ``` + ```sh + ./supersim --interop.authorelay true + ``` ### Setup the ERC-20 token on chain A @@ -88,7 +87,6 @@ We need to follow the same steps, from the same address, with the same nonce. Luckily, we should be able to do it in this case. - ### Create a Foundry project We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. @@ -104,14 +102,14 @@ Luckily, we should be able to do it in this case. ### Create and run the deployment script - 1. Create an `script/LabSetup.s.sol` file with this content: + 1. Create an `script/LabSetup.s.sol` file with this content: ```solidity file=/public/tutorials/setup-for-erc20-upgrade.sh#L17-L56 hash=8c54bec9989ae4e1734825d3828ae220 filename="script/LabSetup.s.sol" ``` This is the same deployment script used for the original deployment on chain A. - 1. Run this command to deploy the same contracts on chain B. + 2. Run this command to deploy the same contracts on chain B. ```sh PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 @@ -131,68 +129,63 @@ Luckily, we should be able to do it in this case. Proxy: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` - Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. - -
+ Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. - What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. - -
+ If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
- Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. + Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. There are two alternative solutions: - - [Lockboxes](#lockboxes-for-permissionless-interop), which require users to exchange their tokens for tokens that can go through interop. - - Writing your own bridge, which allows for ERC-20 tokens on different addresses to be exchanged through interop. + + * [Lockboxes](#lockboxes-for-permissionless-interop), which require users to exchange their tokens for tokens that can go through interop. + * Writing your own bridge, which allows for ERC-20 tokens on different addresses to be exchanged through interop. ### Deploy ERC-7802 contracts We need to replace the ERC-20 contracts with contracts that: - - Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - - Have the same storage layout as the ERC-20 contracts they replace. + * Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + * Have the same storage layout as the ERC-20 contracts they replace. - These contracts do *not* need to be deployed to the same address. The address that needs to be the same is not the address of the ERC-20 contract itself, but of the proxy. - - 1. Create a file, `src/InteropToken.sol`: + 1. Create a file, `src/InteropToken.sol`: ```solidity file=/public/tutorials/InteropToken.sol hash=5e728534c265028c94d60dcb6550699d filename="src/InteropToken.sol" ``` - 1. Deploy this contract on both chains, and store the addresses (which may or may not be the same). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). ```sh ERC7802_A=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A --broadcast | awk '/Deployed to:/ {print $3}'` ERC7802_B=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B --broadcast | awk '/Deployed to:/ {print $3}'` ``` - - ### Update proxies Notify the beacon contracts of the new implementation contracts. @@ -201,42 +194,37 @@ Luckily, we should be able to do it in this case. cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_A --rpc-url $URL_CHAIN_A cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_B --rpc-url $URL_CHAIN_B ``` -
-
- - Sanity check - - 1. See your balance on chain A. - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - ``` - - 1. See your balance on chain B. +
+ Sanity check - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - ``` + 1. See your balance on chain A. - 1. Transfer 0.1 token. + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` - ```sh - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - AMOUNT=`echo 0.1 | cast to-wei` - cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 - ``` + 2. See your balance on chain B. - 1. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - ``` + 3. Transfer 0.1 token. -
+ ```sh + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + AMOUNT=`echo 0.1 | cast to-wei` + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 + ``` + 4. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` +
-## Lockboxes for permissionless interop \ No newline at end of file +## Lockboxes for permissionless interop From 6c6fd85bbe405cad24844120f81ad9d686eac7a7 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 24 Mar 2025 10:42:28 -0500 Subject: [PATCH 06/34] Added explanation of InteropToken.sol --- .../tutorials/upgrade-to-superchain-erc20.mdx | 46 ++++++++++++++++++- public/tutorials/InteropToken.sol | 1 - 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 13918f42d..9a309ab52 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -176,9 +176,53 @@ Luckily, we should be able to do it in this case. 1. Create a file, `src/InteropToken.sol`: - ```solidity file=/public/tutorials/InteropToken.sol hash=5e728534c265028c94d60dcb6550699d filename="src/InteropToken.sol" + ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` +
+ + Detailed explanation + + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` + + Most of the code is identical to the original `MyToken`. + + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` + + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` + + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` + + This function is identical to the one in `MyToken`. + + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` + + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` + + Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. + +
+ + + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + + + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). ```sh diff --git a/public/tutorials/InteropToken.sol b/public/tutorials/InteropToken.sol index e48ed7311..5d34ae636 100644 --- a/public/tutorials/InteropToken.sol +++ b/public/tutorials/InteropToken.sol @@ -41,4 +41,3 @@ contract InteropToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, IE || _interfaceId == type(IERC165).interfaceId; } } - From eb9ba157344894bf0aea30ac6ffcf7ef9fd0e862 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 24 Mar 2025 10:43:19 -0500 Subject: [PATCH 07/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../tutorials/upgrade-to-superchain-erc20.mdx | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 9a309ab52..d23d1952b 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -179,49 +179,45 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- - Detailed explanation - - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` - - Most of the code is identical to the original `MyToken`. - - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` +
+ Detailed explanation - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + Most of the code is identical to the original `MyToken`. - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - This function is identical to the one in `MyToken`. + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + This function is identical to the one in `MyToken`. - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` -
+ Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). From 7327294f124c39c0aa17b161ab9d21e677359c11 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 24 Mar 2025 11:03:01 -0500 Subject: [PATCH 08/34] fixes --- pages/stack/interop/tutorials.mdx | 2 ++ pages/stack/interop/tutorials/_meta.json | 2 +- .../tutorials/upgrade-to-superchain-erc20.mdx | 17 +++++++++-------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pages/stack/interop/tutorials.mdx b/pages/stack/interop/tutorials.mdx index 526df9ff5..3679e0257 100644 --- a/pages/stack/interop/tutorials.mdx +++ b/pages/stack/interop/tutorials.mdx @@ -31,4 +31,6 @@ Documentation covering Interop related tutorials. } /> + } /> + diff --git a/pages/stack/interop/tutorials/_meta.json b/pages/stack/interop/tutorials/_meta.json index 6ff75fe05..ce86c95c7 100644 --- a/pages/stack/interop/tutorials/_meta.json +++ b/pages/stack/interop/tutorials/_meta.json @@ -3,7 +3,7 @@ "deploy-superchain-erc20": "Deploying a SuperchainERC20", "transfer-superchainERC20": "Transferring a SuperchainERC20", "custom-superchain-erc20": "Custom SuperchainERC20 tokens", - "upgrade-to-superchain-erc20": "Upgrading ERC-20 tokens to SuperchainERC20", + "upgrade-to-superchain-erc20": "Upgrading ERC-20 tokens for interop support", "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`", diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index d23d1952b..925105381 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -1,5 +1,5 @@ --- -title: Upgrading ERC-20 tokens to SuperchainERC20 +title: Upgrading ERC-20 tokens for interop support lang: en-US description: What to do if you already have an ERC-20 token, and now you need to support Interop. --- @@ -12,7 +12,7 @@ import { Steps } from 'nextra/components' Please note that the OP Stack interoperability upgrade, required for crosschain messaging, is currently still in active development. -# Upgrading ERC-20 tokens to SuperchainERC20 +# Upgrading ERC-20 tokens for interop support ## Overview @@ -131,7 +131,8 @@ Luckily, we should be able to do it in this case. Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
+
+ What to do when the values are not the same This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. @@ -151,7 +152,7 @@ Luckily, we should be able to do it in this case. ``` If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+
Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. @@ -179,7 +180,7 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
+
Detailed explanation ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b @@ -212,12 +213,12 @@ Luckily, we should be able to do it in this case. ``` Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+
- + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). From 0f4388b49ae254e697bda2a5d36f53f6c4c468db Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 29 Mar 2025 16:35:15 -0500 Subject: [PATCH 09/34] WIP --- .../tutorials/upgrade-to-superchain-erc20.mdx | 138 +++++++++++++++++- public/tutorials/LockboxDeployer.s.sol | 85 +++++++++++ public/tutorials/LockboxSuperchainERC20.sol | 69 +++++++++ 3 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 public/tutorials/LockboxDeployer.s.sol create mode 100644 public/tutorials/LockboxSuperchainERC20.sol diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 925105381..7bb6bb2e9 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -30,6 +30,21 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git * You should already know how to [deploy SuperchainERC20 tokens with custom code](./custom-superchain-erc20).
+ + The code on the documentation site is sample code, *not* production code. + This means that we ran it, and it works as advertised. + However, it did not pass through the rigorous audit process that most Optimism code undergoes. + You're welcome to use it, but if you need it for production purposes you should get it audited first. + + +{/* + +I put this warning here, when we don't have it on most pages, because this tutorial +has, IMHO, code that is a lot more likely to be used in production. It doesn't just +show what is possible, it does the exact job needed. + +*/} + ### What you'll do * Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). @@ -66,8 +81,21 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git ```sh BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` + + ### Specify environment variables + + Specify these variables, which will be useful later. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` + ## Upgrade a proxied contract @@ -75,7 +103,6 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git There are several requirements for an ERC-20 to use Superchain interop: * The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - The easiest way to achieve this is to inherit from [`SuperchainERC20`](./custom-superchain-erc20). We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. * The equivalent ERC-20 contracts need to be on the same address on the other chains. * The storage layout of the ERC-20 contract needs to be the same as the one of `SuperchainERC20`. @@ -112,10 +139,6 @@ Luckily, we should be able to do it in this case. 2. Run this command to deploy the same contracts on chain B. ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup ``` @@ -255,7 +278,6 @@ Luckily, we should be able to do it in this case. 3. Transfer 0.1 token. ```sh - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 AMOUNT=`echo 0.1 | cast to-wei` cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 ``` @@ -269,3 +291,105 @@ Luckily, we should be able to do it in this case. ## Lockboxes for permissionless interop + +There are many cases in which the previous technique is not usable: + +- The original ERC-20 contract was not deployed behind a proxy, so there is no way to make it implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802). +- The original ERC-20 contract was a proxy, but it was deployed either directly or using the [`CREATE`](https://www.evm.codes/?fork=cancun#f0) opcode and that nonce value has already been used by that address on one of the chains you need to interoperate with. +- The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. + +For those cases there is an alternative solution, the lockbox. +We create a separate ERC-20 contract, which we control. +Users can transfer the original ERC-20 to the new ERC-20 contract, and in return get an equal number of new tokens. +Those tokens, because we control them, are interoperable. +Eventually, the owner of new ERC-20 tokens (either the original depositor or somebody else) can redeem them back for the original tokens. + + + + ### Setup environment variables + + We need a few more environment variables to make token information available for the new deployment. + + ```sh + export ERC20_ADDRESS + export ERC20_CHAINID=`cast chain-id --rpc-url $URL_CHAIN_A` + ORIGINAL_TOKEN_NAME=`cast call $ERC20_ADDRESS "name()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_NAME="$ORIGINAL_TOKEN_NAME Lockbox" + ORIGINAL_TOKEN_SYMBOL=`cast call $ERC20_ADDRESS "symbol()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_SYMBOL="$ORIGINAL_TOKEN_SYMBOL-L" + export TOKEN_DECIMALS=`cast call $ERC20_ADDRESS "decimals()" --rpc-url $URL_CHAIN_A | cast to-dec` + ``` + + ### Update the deployment utilities + + The next `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. + To be able to deploy it, we need to modify some of the deployment utilities. + + 1. Replace `packages/contracts/package.json` with this code: + + ```json filename="packages/contracts/package.json" + { + "name": "@superchainerc20-starter/contracts", + "main": "index.js", + "scripts": { + "deploy:dev": "env-cmd -f .env cross-env-shell 'wait-port http://:8420/ready && forge script scripts/Superc> "deploy:token": "env-cmd -f .env cross-env-shell 'forge script scripts/LockboxDeployer.s.sol --broadcast --> "update:rpcs": "cd ../.. && ./scripts/fetch-superchain-rpc-urls.sh", + "install": "forge install", + "build": "forge build", + "test": "forge test", + "init:env": "cp .env.example .env" + }, + "dependencies": { + "viem": "^2.21.37" + } + } + ``` + + 1. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: + + ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 + ``` + +
+ + Explanation of the modified functions + + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the [SuperchainERC20 starter kit](./deploy-superchain-erc20). + Some functions are modified, as explained below. + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` + + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. + + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` + + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us. + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` + + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. + + + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + + +
+ + 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: + + ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb + ``` + +
\ No newline at end of file diff --git a/public/tutorials/LockboxDeployer.s.sol b/public/tutorials/LockboxDeployer.s.sol new file mode 100644 index 000000000..1e16f6770 --- /dev/null +++ b/public/tutorials/LockboxDeployer.s.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import {Script, console} from "forge-std/Script.sol"; +import {Vm} from "forge-std/Vm.sol"; +import {LockboxSuperchainERC20} from "../src/LockboxSuperchainERC20.sol"; + +contract LockboxDeployer is Script { + string deployConfig; + uint256 timestamp; + + constructor() { + string memory deployConfigPath = vm.envOr("DEPLOY_CONFIG_PATH", string("/configs/deploy-config.toml")); + string memory filePath = string.concat(vm.projectRoot(), deployConfigPath); + deployConfig = vm.readFile(filePath); + timestamp = vm.unixTime(); + } + + /// @notice Modifier that wraps a function in broadcasting. + modifier broadcast() { + vm.startBroadcast(msg.sender); + _; + vm.stopBroadcast(); + } + + function setUp() public {} + + function run() public { + string[] memory chainsToDeployTo = vm.parseTomlStringArray(deployConfig, ".deploy_config.chains"); + + address deployedAddress; + + for (uint256 i = 0; i < chainsToDeployTo.length; i++) { + string memory chainToDeployTo = chainsToDeployTo[i]; + + console.log("Deploying to chain: ", chainToDeployTo); + + vm.createSelectFork(chainToDeployTo); + address _deployedAddress = deployLockboxSuperchainERC20(); + deployedAddress = _deployedAddress; + } + + outputDeploymentResult(deployedAddress); + } + + function deployLockboxSuperchainERC20() public broadcast returns (address addr_) { + string memory name = vm.envString("NEW_TOKEN_NAME"); + string memory symbol = vm.envString("NEW_TOKEN_SYMBOL"); + uint256 decimals = vm.envUint("TOKEN_DECIMALS"); + require(decimals <= type(uint8).max, "decimals exceeds uint8 range"); + address originalTokenAddress = vm.envAddress("ERC20_ADDRESS"); + uint256 originalChainId = vm.envUint("ERC20_CHAINID"); + + bytes memory initCode = abi.encodePacked( + type(LockboxSuperchainERC20).creationCode, + abi.encode(name, symbol, uint8(decimals), originalTokenAddress, originalChainId) + ); + address preComputedAddress = vm.computeCreate2Address(_implSalt(), keccak256(initCode)); + if (preComputedAddress.code.length > 0) { + console.log( + "There is already a contract at %s", preComputedAddress, "on chain id: ", block.chainid + ); + addr_ = preComputedAddress; + } else { + addr_ = address(new LockboxSuperchainERC20{salt: _implSalt()}( + name, symbol, uint8(decimals), originalTokenAddress, originalChainId)); + console.log("Deployed LockboxSuperchainERC20 at address: ", addr_, "on chain id: ", block.chainid); + } + } + + function outputDeploymentResult(address deployedAddress) public { + console.log("Outputting deployment result"); + + string memory obj = "result"; + string memory jsonOutput = vm.serializeAddress(obj, "deployedAddress", deployedAddress); + + vm.writeJson(jsonOutput, "deployment.json"); + } + + /// @notice The CREATE2 salt to be used when deploying the token. + function _implSalt() internal view returns (bytes32) { + string memory salt = vm.parseTomlString(deployConfig, ".deploy_config.salt"); + return keccak256(abi.encodePacked(salt, timestamp)); + } +} diff --git a/public/tutorials/LockboxSuperchainERC20.sol b/public/tutorials/LockboxSuperchainERC20.sol new file mode 100644 index 000000000..94eb379f3 --- /dev/null +++ b/public/tutorials/LockboxSuperchainERC20.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import {SuperchainERC20} from "./SuperchainERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; + +contract LockboxSuperchainERC20 is SuperchainERC20 { + string private _name; + string private _symbol; + uint8 private immutable _decimals; + address immutable _originalTokenAddress; + uint256 immutable _originalChainId; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + address originalTokenAddress_, + uint256 originalChainId_) { + _name = name_; + _symbol = symbol_; + _decimals = decimals_; + _originalTokenAddress = originalTokenAddress_; + _originalChainId = originalChainId_; + } + + 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 originalTokenAddress() public view returns (address) { + return _originalTokenAddress; + } + + function originalChainId() public view returns (uint256) { + return _originalChainId; + } + + function lockAndMint(uint256 amount_) external { + IERC20 originalToken = IERC20(_originalTokenAddress); + + require(block.chainid == _originalChainId, "Wrong chain"); + bool success = originalToken.transferFrom(msg.sender, address(this), amount_); + + // Not necessariy if the ERC-20 contract reverts rather than reverting. + // However, the standard allows the ERC-20 contract to return false instead. + require(success, "No tokens to lock, no mint either"); + _mint(msg.sender, amount_); + } + + function redeemAndBurn(uint256 amount_) external { + IERC20 originalToken = IERC20(_originalTokenAddress); + + require(block.chainid == _originalChainId, "Wrong chain"); + _burn(msg.sender, amount_); + + bool success = originalToken.transfer(msg.sender, amount_); + require(success, "Transfer failed, this should not happen"); + } +} + From c75befd84b7ce1c04e07335093a89d00539509a1 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 29 Mar 2025 16:35:45 -0500 Subject: [PATCH 10/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../tutorials/upgrade-to-superchain-erc20.mdx | 86 +++++++++---------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 7bb6bb2e9..ca05913b3 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -95,7 +95,6 @@ show what is possible, it does the exact job needed. URL_CHAIN_B=http://127.0.0.1:9546 INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 ``` - ## Upgrade a proxied contract @@ -154,8 +153,7 @@ Luckily, we should be able to do it in this case. Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- +
What to do when the values are not the same This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. @@ -175,7 +173,7 @@ Luckily, we should be able to do it in this case. ``` If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+
Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. @@ -203,7 +201,7 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
+
Detailed explanation ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b @@ -236,12 +234,12 @@ Luckily, we should be able to do it in this case. ``` Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+
- + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). @@ -294,9 +292,9 @@ Luckily, we should be able to do it in this case. There are many cases in which the previous technique is not usable: -- The original ERC-20 contract was not deployed behind a proxy, so there is no way to make it implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802). -- The original ERC-20 contract was a proxy, but it was deployed either directly or using the [`CREATE`](https://www.evm.codes/?fork=cancun#f0) opcode and that nonce value has already been used by that address on one of the chains you need to interoperate with. -- The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. +* The original ERC-20 contract was not deployed behind a proxy, so there is no way to make it implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802). +* The original ERC-20 contract was a proxy, but it was deployed either directly or using the [`CREATE`](https://www.evm.codes/?fork=cancun#f0) opcode and that nonce value has already been used by that address on one of the chains you need to interoperate with. +* The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. For those cases there is an alternative solution, the lockbox. We create a separate ERC-20 contract, which we control. @@ -305,7 +303,6 @@ Those tokens, because we control them, are interoperable. Eventually, the owner of new ERC-20 tokens (either the original depositor or somebody else) can redeem them back for the original tokens. - ### Setup environment variables We need a few more environment variables to make token information available for the new deployment. @@ -325,7 +322,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som The next `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. To be able to deploy it, we need to modify some of the deployment utilities. - 1. Replace `packages/contracts/package.json` with this code: + 1. Replace `packages/contracts/package.json` with this code: ```json filename="packages/contracts/package.json" { @@ -344,52 +341,49 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som } ``` - 1. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: + 2. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
+
+ Explanation of the modified functions - Explanation of the modified functions + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the [SuperchainERC20 starter kit](./deploy-superchain-erc20). + Some functions are modified, as explained below. - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the [SuperchainERC20 starter kit](./deploy-superchain-erc20). - Some functions are modified, as explained below. - - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` - - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us. - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
- 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: + 3. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb - ``` - -
\ No newline at end of file + ``` + From 76442146b9b5cd7acb65a2d85349ec5b86714e3a Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 29 Mar 2025 19:03:53 -0500 Subject: [PATCH 11/34] Need to polish --- .../tutorials/upgrade-to-superchain-erc20.mdx | 150 +++++++++++++++++- 1 file changed, 145 insertions(+), 5 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index ca05913b3..77fdf30c2 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -61,7 +61,7 @@ show what is possible, it does the exact job needed. Make sure to run Supersim with autorelay on. ```sh - ./supersim --interop.authorelay true + ./supersim --interop.autorelay true ```
@@ -319,9 +319,18 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som ### Update the deployment utilities - The next `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. + The new `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. To be able to deploy it, we need to modify some of the deployment utilities. + 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. + + ```sh + git clone https://github.com/ethereum-optimism/superchainerc20-starter.git + cd superchainerc20-starter + pnpm install + pnpm init:env + ``` + 1. Replace `packages/contracts/package.json` with this code: ```json filename="packages/contracts/package.json" @@ -329,7 +338,9 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som "name": "@superchainerc20-starter/contracts", "main": "index.js", "scripts": { - "deploy:dev": "env-cmd -f .env cross-env-shell 'wait-port http://:8420/ready && forge script scripts/Superc> "deploy:token": "env-cmd -f .env cross-env-shell 'forge script scripts/LockboxDeployer.s.sol --broadcast --> "update:rpcs": "cd ../.. && ./scripts/fetch-superchain-rpc-urls.sh", + "deploy:dev": "env-cmd -f .env cross-env-shell 'wait-port http://:8420/ready && forge script scripts/SuperchainERC20Deployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", + "deploy:token": "env-cmd -f .env cross-env-shell 'forge script scripts/LockboxDeployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", + "update:rpcs": "cd ../.. && ./scripts/fetch-superchain-rpc-urls.sh", "install": "forge install", "build": "forge build", "test": "forge test", @@ -349,7 +360,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som
Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the [SuperchainERC20 starter kit](./deploy-superchain-erc20). + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. Some functions are modified, as explained below. ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a @@ -382,8 +393,137 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som
- 3. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: + ### Create and deploy the new contract + + 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb ``` + +
+ + Explanation + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` + + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 + ``` + + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. + + - Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + - Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + - The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + - The original owner (`msg.sender`) does not have enough tokens to transfer. + + If the tests are successful, mint the requested amount for `msg.sender`. + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 + ``` + + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. + + - Again, check chain ID. + - Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + - Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. + +
+ + 1. Actually deploy the contract. + + ```sh + pnpm contracts:deploy:token + ``` + + 1. Get the new token address and store it in an environment variable. + + ```sh + NEW_TOKEN_ADDRESS=`cat packages/contracts/broadcast/multi/LockboxDeployer.s.sol-latest/run.json | awk '/contractAddress/ {print $2}' | head -1 | sed 's/[",]//g'` + ``` + + +
+ Sanity check + + 1. Check that the user has a single token of the original ERC-20. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 1. Lock a quarter token in the lockbox ERC-20 contract. + To do this we first need to give the lockbox ERC-20 contract an allowance and then call it. + + ```sh + QUARTER_TOKEN=`echo 0.25 | cast to-wei` + cast send $ERC20_ADDRESS "approve(address,uint256)" $NEW_TOKEN_ADDRESS $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + cast send $NEW_TOKEN_ADDRESS "lockAndMint(uint256)" $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + ``` + + 1. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 1. Transfer 0.1 token to chain B. + + ```sh + TENTH_TOKEN=`echo 0.1 | cast to-wei` + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS $TENTH_TOKEN 902 + ``` + + 1. See the user's balances on both chains. + + ```sh + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 1. Specify the configuration for another user. + + ```sh + USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + ``` + + 1. Transfer new tokens to the new user (on chain B) and see that they were actually tansferred. + + ```sh + cast send $NEW_TOKEN_ADDRESS "transfer(address,uint256)" $USER_ADDRESS_2 $TENTH_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 1. As the new user, transfer tokens back to chain A and redeem them. + + ```sh + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY_2 "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS_2 $TENTH_TOKEN 901 + cast send $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY_2 "redeemAndBurn(uint256)" $TENTH_TOKEN + ``` + + 1. See that the second user does not have any more of the new tokens, but does have the original token. + + ```sh + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei + ``` + +
\ No newline at end of file From a93acb35ce7cdca0c00cc38ba8b41bfe72f92a3d Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 29 Mar 2025 19:04:25 -0500 Subject: [PATCH 12/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../tutorials/upgrade-to-superchain-erc20.mdx | 108 +++++++++--------- words.txt | 2 + 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 77fdf30c2..fd17b6ae6 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -322,7 +322,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som The new `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. To be able to deploy it, we need to modify some of the deployment utilities. - 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. + 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. ```sh git clone https://github.com/ethereum-optimism/superchainerc20-starter.git @@ -331,7 +331,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som pnpm init:env ``` - 1. Replace `packages/contracts/package.json` with this code: + 2. Replace `packages/contracts/package.json` with this code: ```json filename="packages/contracts/package.json" { @@ -352,7 +352,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som } ``` - 2. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: + 3. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` @@ -400,82 +400,79 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb ``` -
- - Explanation - - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` +
+ Explanation - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 - ``` + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 + ``` - - Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - - Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - - The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - - The original owner (`msg.sender`) does not have enough tokens to transfer. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - If the tests are successful, mint the requested amount for `msg.sender`. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 - ``` + If the tests are successful, mint the requested amount for `msg.sender`. - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 + ``` - - Again, check chain ID. - - Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - - Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
- 1. Actually deploy the contract. + 2. Actually deploy the contract. ```sh pnpm contracts:deploy:token ``` - 1. Get the new token address and store it in an environment variable. + 3. Get the new token address and store it in an environment variable. ```sh NEW_TOKEN_ADDRESS=`cat packages/contracts/broadcast/multi/LockboxDeployer.s.sol-latest/run.json | awk '/contractAddress/ {print $2}' | head -1 | sed 's/[",]//g'` ``` -
Sanity check - 1. Check that the user has a single token of the original ERC-20. + 1. Check that the user has a single token of the original ERC-20. ```sh cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei ``` - 1. Lock a quarter token in the lockbox ERC-20 contract. - To do this we first need to give the lockbox ERC-20 contract an allowance and then call it. + 2. Lock a quarter token in the lockbox ERC-20 contract. + To do this we first need to give the lockbox ERC-20 contract an allowance and then call it. - ```sh - QUARTER_TOKEN=`echo 0.25 | cast to-wei` - cast send $ERC20_ADDRESS "approve(address,uint256)" $NEW_TOKEN_ADDRESS $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A - cast send $NEW_TOKEN_ADDRESS "lockAndMint(uint256)" $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A - ``` + ```sh + QUARTER_TOKEN=`echo 0.25 | cast to-wei` + cast send $ERC20_ADDRESS "approve(address,uint256)" $NEW_TOKEN_ADDRESS $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + cast send $NEW_TOKEN_ADDRESS "lockAndMint(uint256)" $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + ``` - 1. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. + 3. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. ```sh cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei @@ -483,28 +480,28 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som cast call $ERC20_ADDRESS "balanceOf(address)" $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei ``` - 1. Transfer 0.1 token to chain B. + 4. Transfer 0.1 token to chain B. ```sh TENTH_TOKEN=`echo 0.1 | cast to-wei` cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS $TENTH_TOKEN 902 ``` - 1. See the user's balances on both chains. + 5. See the user's balances on both chains. ```sh cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei ``` - 1. Specify the configuration for another user. + 6. Specify the configuration for another user. ```sh USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a ``` - 1. Transfer new tokens to the new user (on chain B) and see that they were actually tansferred. + 7. Transfer new tokens to the new user (on chain B) and see that they were actually tansferred. ```sh cast send $NEW_TOKEN_ADDRESS "transfer(address,uint256)" $USER_ADDRESS_2 $TENTH_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B @@ -512,18 +509,17 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_B | cast from-wei ``` - 1. As the new user, transfer tokens back to chain A and redeem them. + 8. As the new user, transfer tokens back to chain A and redeem them. ```sh cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY_2 "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS_2 $TENTH_TOKEN 901 cast send $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY_2 "redeemAndBurn(uint256)" $TENTH_TOKEN ``` - 1. See that the second user does not have any more of the new tokens, but does have the original token. + 9. See that the second user does not have any more of the new tokens, but does have the original token. ```sh cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei ``` - -
\ No newline at end of file + diff --git a/words.txt b/words.txt index cfbb67766..a41c33f55 100644 --- a/words.txt +++ b/words.txt @@ -373,6 +373,7 @@ smartcard snapshotlog Snapsync snapsync +solady Solana Soneium soyboy @@ -404,6 +405,7 @@ syncmode SYNCTARGET synctarget syscalls +tansferred thirdweb threadcreate timeseries From 0c49dfef4461612174132474e140744162c5931b Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 1 Apr 2025 16:22:17 -0500 Subject: [PATCH 13/34] WIP --- .../interop/tutorials/upgrade-to-superchain-erc20.mdx | 8 +++++++- public/tutorials/setup-for-erc20-upgrade.sh | 0 2 files changed, 7 insertions(+), 1 deletion(-) mode change 100755 => 100644 public/tutorials/setup-for-erc20-upgrade.sh diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index fd17b6ae6..e6abbd066 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -16,7 +16,7 @@ import { Steps } from 'nextra/components' ## Overview -This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can then teleport across the Superchain interop cluster quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the Superchain interop cluster quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20).
About this tutorial @@ -523,3 +523,9 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei ```
+ +## Next steps + +* Review the [Superchain Interop Explainer](/stack/interop/explainer) for answers to common questions about interoperability. +* Read the [Message Passing Explainer](/stack/interop/message-passing) to understand what happens "under the hood". +* Read the [Superchain ERC20 Explainer](/stack/interop/superchain-erc20) diff --git a/public/tutorials/setup-for-erc20-upgrade.sh b/public/tutorials/setup-for-erc20-upgrade.sh old mode 100755 new mode 100644 From fdc7a7fd16e6d7ebd9a2b4ed8c37b9568d44ab0c Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 1 Apr 2025 18:00:26 -0500 Subject: [PATCH 14/34] Ready for review --- .../stack/interop/tutorials/upgrade-to-superchain-erc20.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index e6abbd066..ec80fd730 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -457,7 +457,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som
Sanity check - 1. Check that the user has a single token of the original ERC-20. + 1. Check that the user has a single token of the original ERC-20 (or 0.9 if you did the proxy upgrade steps on the same token). ```sh cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei @@ -501,7 +501,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a ``` - 7. Transfer new tokens to the new user (on chain B) and see that they were actually tansferred. + 7. Transfer new tokens to the new user (on chain B) and see that they were actually transferred. ```sh cast send $NEW_TOKEN_ADDRESS "transfer(address,uint256)" $USER_ADDRESS_2 $TENTH_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B @@ -528,4 +528,4 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som * Review the [Superchain Interop Explainer](/stack/interop/explainer) for answers to common questions about interoperability. * Read the [Message Passing Explainer](/stack/interop/message-passing) to understand what happens "under the hood". -* Read the [Superchain ERC20 Explainer](/stack/interop/superchain-erc20) +* Read the [Superchain ERC20 Explainer](/stack/interop/superchain-erc20). From 33b49ed8ecc6d7ebea1228803f6c16e1661c0546 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 1 Apr 2025 18:00:56 -0500 Subject: [PATCH 15/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- words.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/words.txt b/words.txt index a41c33f55..630490861 100644 --- a/words.txt +++ b/words.txt @@ -405,7 +405,6 @@ syncmode SYNCTARGET synctarget syscalls -tansferred thirdweb threadcreate timeseries From cdebe0fb50a6b0d699def56c6df47a83aa732249 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 1 Apr 2025 20:16:35 -0500 Subject: [PATCH 16/34] Lint --- .../tutorials/upgrade-to-superchain-erc20.mdx | 196 +++++++++--------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index ec80fd730..ddd59f10f 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -153,27 +153,27 @@ Luckily, we should be able to do it in this case. Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+ If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. @@ -201,45 +201,45 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- Detailed explanation +
+ Detailed explanation - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - Most of the code is identical to the original `MyToken`. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - This function is identical to the one in `MyToken`. + This function is identical to the one in `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+ Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). @@ -357,41 +357,41 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
- Explanation of the modified functions +
+ Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
### Create and deploy the new contract @@ -400,46 +400,46 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb ``` -
- Explanation +
+ Explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 + ``` - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - If the tests are successful, mint the requested amount for `msg.sender`. + If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 + ``` - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
2. Actually deploy the contract. From 11e0b328b46522192019e0caff676ba971e89d6c Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 1 Apr 2025 20:24:52 -0500 Subject: [PATCH 17/34] Code rabbit suggestions Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- public/tutorials/LockboxSuperchainERC20.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/tutorials/LockboxSuperchainERC20.sol b/public/tutorials/LockboxSuperchainERC20.sol index 94eb379f3..2bc96d825 100644 --- a/public/tutorials/LockboxSuperchainERC20.sol +++ b/public/tutorials/LockboxSuperchainERC20.sol @@ -17,6 +17,8 @@ contract LockboxSuperchainERC20 is SuperchainERC20 { uint8 decimals_, address originalTokenAddress_, uint256 originalChainId_) { + require(originalTokenAddress_ != address(0), "Invalid token address"); + require(originalChainId_ != 0, "Invalid chain ID"); _name = name_; _symbol = symbol_; _decimals = decimals_; From e059449ab89f80298706aeb30bb368b64db6203d Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 2 Apr 2025 17:00:52 -0500 Subject: [PATCH 18/34] WIP, some @zainbacchu comments --- .../tutorials/upgrade-to-superchain-erc20.mdx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index ddd59f10f..1b251a977 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -1,5 +1,5 @@ --- -title: Upgrading ERC-20 tokens for interop support +title: Upgrading ERC20 to SuperchainERC20 lang: en-US description: What to do if you already have an ERC-20 token, and now you need to support Interop. --- @@ -12,18 +12,18 @@ import { Steps } from 'nextra/components' Please note that the OP Stack interoperability upgrade, required for crosschain messaging, is currently still in active development.
-# Upgrading ERC-20 tokens for interop support +# Upgrading ERC20 to SuperchainERC20 ## Overview -This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the Superchain interop cluster quickly and safely using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](../explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20).
About this tutorial **What you'll learn** - * How do upgrade an ERC-20 token to enable Superchain interoperability under various conditions. + * How to upgrade an ERC-20 token to enable Superchain interoperability under various conditions. **Prerequisite knowlege** @@ -52,6 +52,8 @@ show what is possible, it does the exact job needed. ## Setup +These initial setup steps are required for both the [proxy upgrade](#upgrade-a-proxied-contract) and the [lockbox solution](#lockboxes-for-permissionless-interop). + ### Install and run Supersim @@ -181,7 +183,7 @@ Luckily, we should be able to do it in this case. There are two alternative solutions: * [Lockboxes](#lockboxes-for-permissionless-interop), which require users to exchange their tokens for tokens that can go through interop. - * Writing your own bridge, which allows for ERC-20 tokens on different addresses to be exchanged through interop. + * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. ### Deploy ERC-7802 contracts @@ -297,10 +299,8 @@ There are many cases in which the previous technique is not usable: * The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. For those cases there is an alternative solution, the lockbox. -We create a separate ERC-20 contract, which we control. -Users can transfer the original ERC-20 to the new ERC-20 contract, and in return get an equal number of new tokens. -Those tokens, because we control them, are interoperable. -Eventually, the owner of new ERC-20 tokens (either the original depositor or somebody else) can redeem them back for the original tokens. +The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. +Users can unwrap their Superchain interop token at any time by returning it to the contract, which burns the Superchain interop tokens and releases the corresponding original ERC-20 from the lockbox. ### Setup environment variables @@ -397,7 +397,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: - ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=7fd49a23ddf10481be66dde8b0b23ccb + ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf ```
@@ -409,7 +409,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som The lockbox contract needs to know the contract for which it is a lockbox. This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=ba2c333c4d581d79d4b80d918b183dc8 + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 ``` Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. @@ -426,7 +426,7 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=4ae43aa03ebf68f5c3a6d08dfcb0a524 + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 ``` Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. @@ -526,6 +526,6 @@ Eventually, the owner of new ERC-20 tokens (either the original depositor or som ## Next steps -* Review the [Superchain Interop Explainer](/stack/interop/explainer) for answers to common questions about interoperability. -* Read the [Message Passing Explainer](/stack/interop/message-passing) to understand what happens "under the hood". -* Read the [Superchain ERC20 Explainer](/stack/interop/superchain-erc20). +* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* [Learn more about SuperchainERC20](../superchain-erc20) +* Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain \ No newline at end of file From 4b8033b8bef1ce634b3410f2cf35d0a536bcb06c Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 2 Apr 2025 17:01:29 -0500 Subject: [PATCH 19/34] WIP --- pages/stack/interop/tutorials/_meta.json | 2 +- .../tutorials/upgrade-to-superchain-erc20.mdx | 204 +++++++++--------- 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/pages/stack/interop/tutorials/_meta.json b/pages/stack/interop/tutorials/_meta.json index ce86c95c7..3f704edd7 100644 --- a/pages/stack/interop/tutorials/_meta.json +++ b/pages/stack/interop/tutorials/_meta.json @@ -1,9 +1,9 @@ { "message-passing": "Interop message passing", "deploy-superchain-erc20": "Deploying a SuperchainERC20", + "upgrade-to-superchain-erc20": "Upgrading ERC20 to SuperchainERC20", "transfer-superchainERC20": "Transferring a SuperchainERC20", "custom-superchain-erc20": "Custom SuperchainERC20 tokens", - "upgrade-to-superchain-erc20": "Upgrading ERC-20 tokens for interop support", "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`", diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 1b251a977..4db8cace4 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -155,27 +155,27 @@ Luckily, we should be able to do it in this case. Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+ If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. @@ -203,45 +203,45 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- Detailed explanation +
+ Detailed explanation - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - Most of the code is identical to the original `MyToken`. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - This function is identical to the one in `MyToken`. + This function is identical to the one in `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+ Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). @@ -299,7 +299,7 @@ There are many cases in which the previous technique is not usable: * The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. For those cases there is an alternative solution, the lockbox. -The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. +The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. Users can unwrap their Superchain interop token at any time by returning it to the contract, which burns the Superchain interop tokens and releases the corresponding original ERC-20 from the lockbox. @@ -357,41 +357,41 @@ Users can unwrap their Superchain interop token at any time by returning it to t ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
- Explanation of the modified functions +
+ Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
### Create and deploy the new contract @@ -400,46 +400,46 @@ Users can unwrap their Superchain interop token at any time by returning it to t ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf ``` -
- Explanation +
+ Explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ``` - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - If the tests are successful, mint the requested amount for `msg.sender`. + If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ``` - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
2. Actually deploy the contract. @@ -526,6 +526,6 @@ Users can unwrap their Superchain interop token at any time by returning it to t ## Next steps -* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain -* [Learn more about SuperchainERC20](../superchain-erc20) -* Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain \ No newline at end of file +* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* [Learn more about SuperchainERC20](../superchain-erc20) +* Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain From 921101d7faac575a1a9c0e2715c281a1b2f3bb73 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 2 Apr 2025 20:14:25 -0500 Subject: [PATCH 20/34] WIP --- pages/stack/interop/tutorials/_meta.json | 2 +- .../stack/interop/tutorials/custom-superchain-erc20.mdx | 4 ++-- .../interop/tutorials/upgrade-to-superchain-erc20.mdx | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pages/stack/interop/tutorials/_meta.json b/pages/stack/interop/tutorials/_meta.json index 3f704edd7..b863a6375 100644 --- a/pages/stack/interop/tutorials/_meta.json +++ b/pages/stack/interop/tutorials/_meta.json @@ -3,7 +3,7 @@ "deploy-superchain-erc20": "Deploying a SuperchainERC20", "upgrade-to-superchain-erc20": "Upgrading ERC20 to SuperchainERC20", "transfer-superchainERC20": "Transferring a SuperchainERC20", - "custom-superchain-erc20": "Custom SuperchainERC20 tokens", + "custom-superchain-erc20": "Creating a custom SuperchainERC20", "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`", diff --git a/pages/stack/interop/tutorials/custom-superchain-erc20.mdx b/pages/stack/interop/tutorials/custom-superchain-erc20.mdx index ccd268ff8..fe290453e 100644 --- a/pages/stack/interop/tutorials/custom-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/custom-superchain-erc20.mdx @@ -1,5 +1,5 @@ --- -title: Creating custom SuperchainERC20 tokens +title: Creating a custom SuperchainERC20 lang: en-US description: Create SuperchainERC20 tokens with custom behaviors --- @@ -12,7 +12,7 @@ import { Steps } from 'nextra/components' Please note that the OP Stack interoperability upgrade, required for crosschain messaging, is currently still in active development.
-# Custom SuperchainERC20 tokens +# Creating a custom SuperchainERC20 ## Overview diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 4db8cace4..54621e525 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -1,7 +1,7 @@ --- title: Upgrading ERC20 to SuperchainERC20 lang: en-US -description: What to do if you already have an ERC-20 token, and now you need to support Interop. +description: Tutorial on how to take an existing ERC20 and upgrade it to SuperchainERC20. --- import { Callout } from 'nextra/components' @@ -16,7 +16,7 @@ import { Steps } from 'nextra/components' ## Overview -This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](../explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](/stack/interop/explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20).
About this tutorial @@ -182,7 +182,8 @@ Luckily, we should be able to do it in this case. There are two alternative solutions: - * [Lockboxes](#lockboxes-for-permissionless-interop), which require users to exchange their tokens for tokens that can go through interop. + * [Lockboxe](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. + The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH. * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. @@ -527,5 +528,5 @@ Users can unwrap their Superchain interop token at any time by returning it to t ## Next steps * Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain -* [Learn more about SuperchainERC20](../superchain-erc20) +* [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) * Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain From ca937f71f933fa0904244722c6131958d28c89d6 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 2 Apr 2025 20:15:04 -0500 Subject: [PATCH 21/34] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx | 2 +- words.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 54621e525..9688086ee 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -182,7 +182,7 @@ Luckily, we should be able to do it in this case. There are two alternative solutions: - * [Lockboxe](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. + * [Lockboxe](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH. * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. diff --git a/words.txt b/words.txt index 630490861..810074409 100644 --- a/words.txt +++ b/words.txt @@ -190,6 +190,7 @@ knowlege leveldb lightkdf Lisk +Lockboxe logfile logfmt MAXAGE From 36859448cb9e893729f32790ecccfd756f26cd1b Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 2 Apr 2025 20:20:04 -0500 Subject: [PATCH 22/34] lint --- .../tutorials/upgrade-to-superchain-erc20.mdx | 196 +++++++++--------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 9688086ee..49bbf7fed 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -155,27 +155,27 @@ Luckily, we should be able to do it in this case. Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+ If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. @@ -204,45 +204,45 @@ Luckily, we should be able to do it in this case. ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- Detailed explanation +
+ Detailed explanation - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - Most of the code is identical to the original `MyToken`. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - This function is identical to the one in `MyToken`. + This function is identical to the one in `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+ Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). @@ -358,41 +358,41 @@ Users can unwrap their Superchain interop token at any time by returning it to t ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
- Explanation of the modified functions +
+ Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
### Create and deploy the new contract @@ -401,46 +401,46 @@ Users can unwrap their Superchain interop token at any time by returning it to t ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf ``` -
- Explanation +
+ Explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ``` - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - If the tests are successful, mint the requested amount for `msg.sender`. + If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ``` - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
2. Actually deploy the contract. From 523dbbcf3b81e84fa6117ba4a7ffff8a4cdaa2e7 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Thu, 3 Apr 2025 22:29:58 -0500 Subject: [PATCH 23/34] Multiple changes --- .../tutorials/deploy-superchain-erc20.mdx | 5 + .../tutorials/upgrade-to-superchain-erc20.mdx | 504 +----------------- .../upgrade-to-superchain-erc20/_meta.json | 4 + .../upgrade-to-superchain-erc20/lockbox.mdx | 364 +++++++++++++ .../proxy-upgrade.mdx | 277 ++++++++++ public/tutorials/setup-for-erc20-upgrade.sh | 24 +- 6 files changed, 680 insertions(+), 498 deletions(-) create mode 100644 pages/stack/interop/tutorials/upgrade-to-superchain-erc20/_meta.json create mode 100644 pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx create mode 100644 pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx diff --git a/pages/stack/interop/tutorials/deploy-superchain-erc20.mdx b/pages/stack/interop/tutorials/deploy-superchain-erc20.mdx index 4b12c6e42..e7b14f847 100644 --- a/pages/stack/interop/tutorials/deploy-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/deploy-superchain-erc20.mdx @@ -45,6 +45,11 @@ This requirement abstracts away the complexity of cross-chain validation. Achieving this requires deterministic deployment methods. There are multiple ways to do this. Here we will use the [SuperchainERC20 Starter Kit](/app-developers/starter-kit). + + This tutorial teaches you how to deploy a *new* `SuperchainERC20`. + If you're looking to migrate an existing ERC20 visit [this tutorial](./upgrade-to-superchain-erc20). + + ### What you'll do * Use the [SuperchainERC20 Starter Kit](/app-developers/starter-kit) to deploy an `SuperchainERC20` token on the devnet. diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 49bbf7fed..9b9c32498 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -18,18 +18,6 @@ import { Steps } from 'nextra/components' This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](/stack/interop/explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). -
- About this tutorial - - **What you'll learn** - - * How to upgrade an ERC-20 token to enable Superchain interoperability under various conditions. - - **Prerequisite knowlege** - - * You should already know how to [deploy SuperchainERC20 tokens with custom code](./custom-superchain-erc20). -
- The code on the documentation site is sample code, *not* production code. This means that we ran it, and it works as advertised. @@ -45,485 +33,19 @@ show what is possible, it does the exact job needed. */} -### What you'll do - -* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). -* Create a "lockbox" SuperchainERC20 contract to permissionlessly enable interop. - -## Setup - -These initial setup steps are required for both the [proxy upgrade](#upgrade-a-proxied-contract) and the [lockbox solution](#lockboxes-for-permissionless-interop). - - - ### Install and run Supersim - - [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. - - - Make sure to run Supersim with autorelay on. - - ```sh - ./supersim --interop.autorelay true - ``` - - - ### Setup the ERC-20 token on chain A - - Download and run the setup script. - - ```sh - curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh - chmod +x setup-for-erc20-upgrade.sh - ./setup-for-erc20-upgrade.sh - ``` - - ### Store the addresses - - Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. - - ```sh - BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 - ``` - - ### Specify environment variables - - Specify these variables, which will be useful later. - - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - ``` - - -## Upgrade a proxied contract - -There are several requirements for an ERC-20 to use Superchain interop: - -* The ERC-20 contract has to implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - We need to modify the ERC-20 implementation for this support, which is the reason this particular technique requires a proxied contract where we control the proxy. -* The equivalent ERC-20 contracts need to be on the same address on the other chains. -* The storage layout of the ERC-20 contract needs to be the same as the one of `SuperchainERC20`. - -The exact method to get the same address depends on how the proxy contract was initially deployed. -In this case we can see it in the upgrade script. - -We need to follow the same steps, from the same address, with the same nonce. -Luckily, we should be able to do it in this case. - - - ### Create a Foundry project - - We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. - - ```sh - mkdir proxy-upgrade - cd proxy-upgrade - forge init - forge install OpenZeppelin/openzeppelin-contracts - forge install OpenZeppelin/openzeppelin-contracts-upgradeable - forge install ethereum-optimism/interop-lib - ``` - - ### Create and run the deployment script - - 1. Create an `script/LabSetup.s.sol` file with this content: - - ```solidity file=/public/tutorials/setup-for-erc20-upgrade.sh#L17-L56 hash=8c54bec9989ae4e1734825d3828ae220 filename="script/LabSetup.s.sol" - ``` - - This is the same deployment script used for the original deployment on chain A. - - 2. Run this command to deploy the same contracts on chain B. - - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` - - Scroll up and see the Logs section of the output: - - ``` - == Logs == - Token address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 - msg.sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - UpgradeableBeacon: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - Proxy: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 - ``` - - Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. - -
- What to do when the values are not the same - - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - - You can see the nonce values using these commands: - - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` - - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` - - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
- - - Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. - - There are two alternative solutions: - - * [Lockboxe](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. - The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH. - * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. - - - ### Deploy ERC-7802 contracts - - We need to replace the ERC-20 contracts with contracts that: - - * Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - * Have the same storage layout as the ERC-20 contracts they replace. - - - These contracts do *not* need to be deployed to the same address. - The address that needs to be the same is not the address of the ERC-20 contract itself, but of the proxy. - - - 1. Create a file, `src/InteropToken.sol`: - - ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" - ``` - -
- Detailed explanation - - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` - - Most of the code is identical to the original `MyToken`. - - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` - - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` - - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` - - This function is identical to the one in `MyToken`. - - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` - - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` - - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
- - - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - - - 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). - - ```sh - ERC7802_A=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A --broadcast | awk '/Deployed to:/ {print $3}'` - ERC7802_B=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B --broadcast | awk '/Deployed to:/ {print $3}'` - ``` - - ### Update proxies - - Notify the beacon contracts of the new implementation contracts. - - ```sh - cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_A --rpc-url $URL_CHAIN_A - cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_B --rpc-url $URL_CHAIN_B - ``` -
- -
- Sanity check - - 1. See your balance on chain A. - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - ``` - - 2. See your balance on chain B. - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - ``` - - 3. Transfer 0.1 token. - - ```sh - AMOUNT=`echo 0.1 | cast to-wei` - cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 - ``` - - 4. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - ``` -
- -## Lockboxes for permissionless interop - -There are many cases in which the previous technique is not usable: - -* The original ERC-20 contract was not deployed behind a proxy, so there is no way to make it implement [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802). -* The original ERC-20 contract was a proxy, but it was deployed either directly or using the [`CREATE`](https://www.evm.codes/?fork=cancun#f0) opcode and that nonce value has already been used by that address on one of the chains you need to interoperate with. -* The private key for the address that originally deployed the ERC-20 contract is not available. This could happen, for example, because the organization that deployed the ERC-20 contract does not wish to cooperate. - -For those cases there is an alternative solution, the lockbox. -The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. -Users can unwrap their Superchain interop token at any time by returning it to the contract, which burns the Superchain interop tokens and releases the corresponding original ERC-20 from the lockbox. - - - ### Setup environment variables - - We need a few more environment variables to make token information available for the new deployment. - - ```sh - export ERC20_ADDRESS - export ERC20_CHAINID=`cast chain-id --rpc-url $URL_CHAIN_A` - ORIGINAL_TOKEN_NAME=`cast call $ERC20_ADDRESS "name()" --rpc-url $URL_CHAIN_A | cast to-ascii` - export NEW_TOKEN_NAME="$ORIGINAL_TOKEN_NAME Lockbox" - ORIGINAL_TOKEN_SYMBOL=`cast call $ERC20_ADDRESS "symbol()" --rpc-url $URL_CHAIN_A | cast to-ascii` - export NEW_TOKEN_SYMBOL="$ORIGINAL_TOKEN_SYMBOL-L" - export TOKEN_DECIMALS=`cast call $ERC20_ADDRESS "decimals()" --rpc-url $URL_CHAIN_A | cast to-dec` - ``` - - ### Update the deployment utilities - - The new `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. - To be able to deploy it, we need to modify some of the deployment utilities. - - 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. - - ```sh - git clone https://github.com/ethereum-optimism/superchainerc20-starter.git - cd superchainerc20-starter - pnpm install - pnpm init:env - ``` - - 2. Replace `packages/contracts/package.json` with this code: - - ```json filename="packages/contracts/package.json" - { - "name": "@superchainerc20-starter/contracts", - "main": "index.js", - "scripts": { - "deploy:dev": "env-cmd -f .env cross-env-shell 'wait-port http://:8420/ready && forge script scripts/SuperchainERC20Deployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", - "deploy:token": "env-cmd -f .env cross-env-shell 'forge script scripts/LockboxDeployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", - "update:rpcs": "cd ../.. && ./scripts/fetch-superchain-rpc-urls.sh", - "install": "forge install", - "build": "forge build", - "test": "forge test", - "init:env": "cp .env.example .env" - }, - "dependencies": { - "viem": "^2.21.37" - } - } - ``` - - 3. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: - - ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 - ``` - -
- Explanation of the modified functions - - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. - - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` - - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. - - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` - - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. - - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` - - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. - - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
- - ### Create and deploy the new contract - - 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: - - ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf - ``` - -
- Explanation - - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` - - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. - - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 - ``` - - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. - - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. - - If the tests are successful, mint the requested amount for `msg.sender`. - - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 - ``` - - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. - - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
- - 2. Actually deploy the contract. - - ```sh - pnpm contracts:deploy:token - ``` - - 3. Get the new token address and store it in an environment variable. - - ```sh - NEW_TOKEN_ADDRESS=`cat packages/contracts/broadcast/multi/LockboxDeployer.s.sol-latest/run.json | awk '/contractAddress/ {print $2}' | head -1 | sed 's/[",]//g'` - ``` -
- -
- Sanity check - - 1. Check that the user has a single token of the original ERC-20 (or 0.9 if you did the proxy upgrade steps on the same token). - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - ``` - - 2. Lock a quarter token in the lockbox ERC-20 contract. - To do this we first need to give the lockbox ERC-20 contract an allowance and then call it. - - ```sh - QUARTER_TOKEN=`echo 0.25 | cast to-wei` - cast send $ERC20_ADDRESS "approve(address,uint256)" $NEW_TOKEN_ADDRESS $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A - cast send $NEW_TOKEN_ADDRESS "lockAndMint(uint256)" $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A - ``` - - 3. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. - - ```sh - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - cast call $ERC20_ADDRESS "balanceOf(address)" $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - ``` - - 4. Transfer 0.1 token to chain B. - - ```sh - TENTH_TOKEN=`echo 0.1 | cast to-wei` - cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS $TENTH_TOKEN 902 - ``` - - 5. See the user's balances on both chains. - - ```sh - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - ``` - - 6. Specify the configuration for another user. - - ```sh - USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC - PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a - ``` - - 7. Transfer new tokens to the new user (on chain B) and see that they were actually transferred. - - ```sh - cast send $NEW_TOKEN_ADDRESS "transfer(address,uint256)" $USER_ADDRESS_2 $TENTH_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_B | cast from-wei - ``` - - 8. As the new user, transfer tokens back to chain A and redeem them. - - ```sh - cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY_2 "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS_2 $TENTH_TOKEN 901 - cast send $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY_2 "redeemAndBurn(uint256)" $TENTH_TOKEN - ``` - - 9. See that the second user does not have any more of the new tokens, but does have the original token. - - ```sh - cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei - cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei - ``` -
+There are several ways to upgrade an existing ERC20 for interop, depending on the exact circumstances. Click any of the green rectangles for directions. + +```mermaid +graph TB + classDef link fill:#f96 + Deployed{Is there
already an
ERC20 contract
in use?} + Deployed -- No --> New[New SuperchainERC20 contract]:::link + Deployed -- Yes --> Proxy{Is the
existing contract
behind a
proxy?} + Proxy -- No --> Lockbox[Lockbox contract]:::link + Proxy -- Yes --> Proxy2{Can you
deploy proxies on
the same address
on the other chains?} + Proxy2 -- No --> Bridge[Specialized Bridge
Coming soon] + Proxy2 -- Yes --> Upgrade[Contract upgrade]:::link +``` ## Next steps diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/_meta.json b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/_meta.json new file mode 100644 index 000000000..626bef1be --- /dev/null +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/_meta.json @@ -0,0 +1,4 @@ +{ + "lockbox": "Lockboxes for permissionless interop", + "proxy-upgrade": "Contract upgrade" +} diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx new file mode 100644 index 000000000..2f0e911c6 --- /dev/null +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -0,0 +1,364 @@ +--- +title: Lockboxes for permissionless interop +lang: en-US +description: Tutorial on how to take permissionlessly create a lockbox contract to enable Superchain interoperability. +--- + +import { Steps, Callout, Tabs } from 'nextra/components' + + + 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. + + +# Lockboxes for permissionless interop + +## Overview + +The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. +Users can unwrap their Superchain interop token at any time by returning it to the contract, which burns the Superchain interop tokens and releases the corresponding original ERC-20 from the lockbox. + +
+ About this tutorial + + **What you'll learn** + + * how to take permissionlessly create a lockbox contract to enable Superchain interoperability. + + **Prerequisite knowledge** + + * You should already know how to [deploy SuperchainERC20 tokens with custom code](/stack/interop/tutorials/). +
+ + + The code on the documentation site is sample code, *not* production code. + This means that we ran it, and it works as advertised. + However, it did not pass through the rigorous audit process that most Optimism code undergoes. + You're welcome to use it, but if you need it for production purposes you should get it audited first. + + +{/* + +I put this warning here, when we don't have it on most pages, because this tutorial +has, IMHO, code that is a lot more likely to be used in production. It doesn't just +show what is possible, it does the exact job needed. + +*/} + +### What you'll do + +Create a lockbox `SuperchainERC20` contract to permissionlessly enable interop. + +## Step by step + +The setup steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). + + + ### Install and run Supersim + + If you are going to use Supersim, [follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + + + Make sure to run Supersim with autorelay on. + + ```sh + ./supersim --interop.autorelay true + ``` + + + ### Setup the ERC-20 token on chain A + + Download and run the setup script. + + ```sh + curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh + chmod +x setup-for-erc20-upgrade.sh + ./setup-for-erc20-upgrade.sh + ``` + + If you want to deploy to the [development networks](/stack/interop/tools/devnet), provide `setup-for-erc20-upgrade.sh` with the private key of an address with ETH on both devnets. + + ```sh + ./setup-for-erc20-upgrade.sh + ``` + + ### Store the addresses + + Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. + + ```sh + BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + ``` + + ### Specify environment variables + + 1. Specify these variables, which we use later: + + + + Set these parameters for Supersim. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + ``` + + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + ``` + + + + 1. Regardless of whether you use Supersim or Devnet, specify these variables. + + ```sh + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + export ERC20_CHAINID=`cast chain-id --rpc-url $URL_CHAIN_A` + ORIGINAL_TOKEN_NAME=`cast call $ERC20_ADDRESS "name()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_NAME="$ORIGINAL_TOKEN_NAME Lockbox" + ORIGINAL_TOKEN_SYMBOL=`cast call $ERC20_ADDRESS "symbol()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_SYMBOL="$ORIGINAL_TOKEN_SYMBOL-L" + export TOKEN_DECIMALS=`cast call $ERC20_ADDRESS "decimals()" --rpc-url $URL_CHAIN_A | cast to-dec` + ``` + + ### Update the deployment utilities + + The new `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. + To be able to deploy it, we need to modify some of the deployment utilities. + + 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. + + ```sh + git clone https://github.com/ethereum-optimism/superchainerc20-starter.git + cd superchainerc20-starter + pnpm install + pnpm init:env + ``` + + 2. Replace `packages/contracts/package.json` with this code: + + ```json filename="packages/contracts/package.json" + { + "name": "@superchainerc20-starter/contracts", + "main": "index.js", + "scripts": { + "deploy:dev": "env-cmd -f .env cross-env-shell 'wait-port http://:8420/ready && forge script scripts/SuperchainERC20Deployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", + "deploy:token": "env-cmd -f .env cross-env-shell 'forge script scripts/LockboxDeployer.s.sol --broadcast --private-key $DEPLOYER_PRIVATE_KEY'", + "update:rpcs": "cd ../.. && ./scripts/fetch-superchain-rpc-urls.sh", + "install": "forge install", + "build": "forge build", + "test": "forge test", + "init:env": "cp .env.example .env" + }, + "dependencies": { + "viem": "^2.21.37" + } + } + ``` + + 3. Create a new file, `packages/contracts/scripts/LockboxDeployer.s.sol`: + + ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 + ``` + +
+ Explanation of the modified functions + + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` + + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. + + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` + + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. + + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` + + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. + + + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
+ + ### Create and deploy the new contract + + 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: + + ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf + ``` + +
+ Explanation + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` + + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ``` + + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. + + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. + + If the tests are successful, mint the requested amount for `msg.sender`. + + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ``` + + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. + + * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
+ + 2. Actually deploy the contract. + + + + ```sh + pnpm contracts:deploy:token + ``` + + + To deploy to the [development networks](/stack/interop/tools/devnet), follow the steps [in the tutorial](/stack/interop/tutorials/deploy-superchain-erc20#prepare-for-deployment) before you deploy the contract. + + Then, update `packages/contracts/.env` and deploy the token. + + ```sh + echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env + pnpm contracts:deploy:token + ``` + + + + + 3. Get the new token address and store it in an environment variable. + + ```sh + NEW_TOKEN_ADDRESS=`cat packages/contracts/broadcast/multi/LockboxDeployer.s.sol-latest/run.json | awk '/contractAddress/ {print $2}' | head -1 | sed 's/[",]//g'` + ``` + + ### Verification + + 1. Check that the user has a single token of the original ERC-20. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 2. Lock a quarter token in the lockbox ERC-20 contract. + To do this we first need to give the lockbox ERC-20 contract an allowance and then call it. + + ```sh + QUARTER_TOKEN=`echo 0.25 | cast to-wei` + cast send $ERC20_ADDRESS "approve(address,uint256)" $NEW_TOKEN_ADDRESS $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + cast send $NEW_TOKEN_ADDRESS "lockAndMint(uint256)" $QUARTER_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + ``` + + 3. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 4. Transfer 0.1 token to chain B. + + ```sh + TENTH_TOKEN=`echo 0.1 | cast to-wei` + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS $TENTH_TOKEN `cast chain-id --rpc-url $URL_CHAIN_B` + ``` + + 5. See the user's balances on both chains. + + ```sh + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 6. Specify the configuration for another user. + + + + ```sh + USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + ``` + + + Specify the private key (`PRIVATE_KEY_2`) and user address (`USER_ADDRESS_2`) of another user that has ETH on both devnets. + + + + + 7. Transfer new tokens to the new user (on chain B) and see that they were actually transferred. + + ```sh + cast send $NEW_TOKEN_ADDRESS "transfer(address,uint256)" $USER_ADDRESS_2 $TENTH_TOKEN --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 8. As the new user, transfer tokens back to chain A and redeem them. + + ```sh + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY_2 "sendERC20(address,address,uint256,uint256)" $NEW_TOKEN_ADDRESS $USER_ADDRESS_2 $TENTH_TOKEN `cast chain-id --rpc-url $URL_CHAIN_A` + cast send $NEW_TOKEN_ADDRESS --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY_2 "redeemAndBurn(uint256)" $TENTH_TOKEN + ``` + + 9. See that the second user does not have any more of the new tokens, but does have the original token. + + ```sh + cast call $NEW_TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS_2 --rpc-url $URL_CHAIN_A | cast from-wei + ``` +
+ +## Next steps + +* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) +* Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx new file mode 100644 index 000000000..a1164ff6c --- /dev/null +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -0,0 +1,277 @@ +--- +title: Contract upgrade +lang: en-US +description: Tutorial on how to upgrade a proxied ERC20 contract for use with Superchain interop. +--- + +import { Callout } from 'nextra/components' +import { Steps } from 'nextra/components' + + + 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. + + +# Contract upgrade + +## Overview + +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](/stack/interop/explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). + +
+ About this tutorial + + **What you'll learn** + + * How to upgrade an ERC20 token to enable Superchain interoperability when it was deployed with a proxy. + + **Prerequisite knowledge** + + * You should already know how to [deploy SuperchainERC20 tokens with custom code](/stack/interop/tutorials/). +
+ + + The code on the documentation site is sample code, *not* production code. + This means that we ran it, and it works as advertised. + However, it did not pass through the rigorous audit process that most Optimism code undergoes. + You're welcome to use it, but if you need it for production purposes you should get it audited first. + + +{/* + +I put this warning here, when we don't have it on most pages, because this tutorial +has, IMHO, code that is a lot more likely to be used in production. It doesn't just +show what is possible, it does the exact job needed. + +*/} + +### What you'll do + +* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). + +## Step by step + + + ### Install and run Supersim + + [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + + + Make sure to run Supersim with autorelay on. + + ```sh + ./supersim --interop.autorelay true + ``` + + + ### Setup the ERC-20 token on chain A + + Download and run the setup script. + + ```sh + curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh + chmod +x setup-for-erc20-upgrade.sh + ./setup-for-erc20-upgrade.sh + ``` + + ### Store the addresses + + Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. + + ```sh + BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + ``` + + ### Specify environment variables + + Specify these variables, which will be useful later. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` + + ### Create a Foundry project + + We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. + + ```sh + mkdir proxy-upgrade + cd proxy-upgrade + forge init + forge install OpenZeppelin/openzeppelin-contracts + forge install OpenZeppelin/openzeppelin-contracts-upgradeable + forge install ethereum-optimism/interop-lib + ``` + + ### Create and run the deployment script + + 1. Create an `script/LabSetup.s.sol` file with this content: + + ```solidity file=/public/tutorials/setup-for-erc20-upgrade.sh#L26-L66 hash=83e951ca27872311bd7ae734c24cdbc2 filename="script/LabSetup.s.sol" + ``` + + This is the same deployment script used for the original deployment on chain A. + + 2. Run this command to deploy the same contracts on chain B. + + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` + + Scroll up and see the Logs section of the output: + + ``` + == Logs == + Token address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 + msg.sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + UpgradeableBeacon: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + Proxy: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + ``` + + Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. + +
+ What to do when the values are not the same + + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + + You can see the nonce values using these commands: + + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` + + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` + + If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
+ + + Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. + + There are two alternative solutions: + + * [Lockboxes](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. + The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH. + * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. + + + ### Deploy ERC-7802 contracts + + We need to replace the ERC-20 contracts with contracts that: + + * Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + * Have the same storage layout as the ERC-20 contracts they replace. + + + These contracts do *not* need to be deployed to the same address. + The address that needs to be the same is not the address of the ERC-20 contract itself, but of the proxy. + + + 1. Create a file, `src/InteropToken.sol`: + + ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" + ``` + +
+ Detailed explanation + + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` + + Most of the code is identical to the original `MyToken`. + + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` + + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` + + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` + + This function is identical to the one in `MyToken`. + + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` + + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` + + Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
+ + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + + + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). + + ```sh + ERC7802_A=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A --broadcast | awk '/Deployed to:/ {print $3}'` + ERC7802_B=`forge create InteropToken --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B --broadcast | awk '/Deployed to:/ {print $3}'` + ``` + + ### Update proxies + + Notify the beacon contracts of the new implementation contracts. + + ```sh + cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_A --rpc-url $URL_CHAIN_A + cast send $BEACON_ADDRESS --private-key $PRIVATE_KEY "upgradeTo(address)" $ERC7802_B --rpc-url $URL_CHAIN_B + ``` + + ### Verification + + 1. See your balance on chain A. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + ``` + + 2. See your balance on chain B. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + + 3. Transfer 0.1 token. + + ```sh + AMOUNT=`echo 0.1 | cast to-wei` + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 + ``` + + 4. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. + + ```sh + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei + ``` + +
+ +## Next steps + +* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) +* Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain diff --git a/public/tutorials/setup-for-erc20-upgrade.sh b/public/tutorials/setup-for-erc20-upgrade.sh index b7f3801c0..6748d6c5e 100644 --- a/public/tutorials/setup-for-erc20-upgrade.sh +++ b/public/tutorials/setup-for-erc20-upgrade.sh @@ -4,11 +4,20 @@ rm -rf upgrade-erc20 mkdir upgrade-erc20 cd upgrade-erc20 -PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -URL_CHAIN_A=http://127.0.0.1:9545 -URL_CHAIN_B=http://127.0.0.1:9546 - +if [ -z $1 ] +then + echo Supersim + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + URL_CHAIN_A=http://localhost:9545 + URL_CHAIN_B=http://localhost:9546 +else + echo Devnet + PRIVATE_KEY=$1 + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io +fi + +USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` forge init forge install OpenZeppelin/openzeppelin-contracts-upgradeable @@ -47,7 +56,8 @@ contract LabSetup is Script { console.log("UpgradeableBeacon:", address(beacon)); BeaconProxy proxy = new BeaconProxy(address(beacon), - abi.encodeCall(MyToken.initialize, ("Test", "TST", block.chainid == 901 ? 10**18 : 0)) + abi.encodeCall(MyToken.initialize, ("Test", "TST", + (block.chainid == 901) || (block.chainid == 420120000) ? 10**18 : 0)) ); console.log("Proxy:", address(proxy)); @@ -63,4 +73,4 @@ ERC20_ADDRESS=`cat setup_output | awk '/Proxy:/ {print $2}'` echo Run these commands to store the configuration: echo BEACON_ADDRESS=$BEACON_ADDRESS -echo ERC20_ADDRESS=$ERC20_ADDRESS +echo export ERC20_ADDRESS=$ERC20_ADDRESS From 5b92cb7361bd5b612aff19e0f61ed2987a990bf5 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 07:06:57 -0500 Subject: [PATCH 24/34] WIP --- .../upgrade-to-superchain-erc20/lockbox.mdx | 215 +++++++++--------- .../proxy-upgrade.mdx | 72 ++++-- words.txt | 2 - 3 files changed, 156 insertions(+), 133 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index 2f0e911c6..03f1143cf 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -51,7 +51,7 @@ Create a lockbox `SuperchainERC20` contract to permissionlessly enable interop. ## Step by step -The setup steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). +Some steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). ### Install and run Supersim @@ -81,7 +81,7 @@ The setup steps depend on whether you want to deploy on [supersim](/stack/intero ```sh ./setup-for-erc20-upgrade.sh ``` - + ### Store the addresses Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. @@ -93,31 +93,32 @@ The setup steps depend on whether you want to deploy on [supersim](/stack/intero ### Specify environment variables - 1. Specify these variables, which we use later: + 1. Specify these variables, which we use later: - - - Set these parameters for Supersim. + + + Set these parameters for Supersim. - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - ``` - - - For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + ``` + + + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. - ```sh - USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` - URL_CHAIN_A=https://interop-alpha-0.optimism.io - URL_CHAIN_B=https://interop-alpha-1.optimism.io - ``` - - + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + ``` + + - 1. Regardless of whether you use Supersim or Devnet, specify these variables. + 2. Regardless of whether you use Supersim or Devnet, specify these variables. ```sh INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 @@ -169,41 +170,41 @@ The setup steps depend on whether you want to deploy on [supersim](/stack/intero ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
- Explanation of the modified functions +
+ Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
### Create and deploy the new contract @@ -212,67 +213,67 @@ The setup steps depend on whether you want to deploy on [supersim](/stack/intero ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf ``` -
- Explanation +
+ Explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ``` - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - If the tests are successful, mint the requested amount for `msg.sender`. + If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ``` - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
2. Actually deploy the contract. - - - ```sh - pnpm contracts:deploy:token - ``` - - - To deploy to the [development networks](/stack/interop/tools/devnet), follow the steps [in the tutorial](/stack/interop/tutorials/deploy-superchain-erc20#prepare-for-deployment) before you deploy the contract. + + + ```sh + pnpm contracts:deploy:token + ``` + - Then, update `packages/contracts/.env` and deploy the token. + + To deploy to the [development networks](/stack/interop/tools/devnet), follow the steps [in the tutorial](/stack/interop/tutorials/deploy-superchain-erc20#prepare-for-deployment) before you deploy the contract. - ```sh - echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env - pnpm contracts:deploy:token - ``` - - + Then, update `packages/contracts/.env` and deploy the token. + ```sh + echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env + pnpm contracts:deploy:token + ``` + + 3. Get the new token address and store it in an environment variable. @@ -321,18 +322,18 @@ The setup steps depend on whether you want to deploy on [supersim](/stack/intero 6. Specify the configuration for another user. - - - ```sh - USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC - PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a - ``` - - - Specify the private key (`PRIVATE_KEY_2`) and user address (`USER_ADDRESS_2`) of another user that has ETH on both devnets. - - - + + + ```sh + USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + ``` + + + + Specify the private key (`PRIVATE_KEY_2`) and user address (`USER_ADDRESS_2`) of another user that has ETH on both devnets. + + 7. Transfer new tokens to the new user (on chain B) and see that they were actually transferred. diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index a1164ff6c..afdfcb38e 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -4,8 +4,7 @@ lang: en-US description: Tutorial on how to upgrade a proxied ERC20 contract for use with Superchain interop. --- -import { Callout } from 'nextra/components' -import { Steps } from 'nextra/components' +import { Callout, Steps, Tabs } from 'nextra/components' The SuperchainERC20 standard is ready for production deployments. @@ -51,10 +50,12 @@ show what is possible, it does the exact job needed. ## Step by step +Some steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). + ### Install and run Supersim - [Follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. + If you are going to use Supersim, [follow these instructions](/app-developers/tutorials/supersim/getting-started/installation) to install and run Supersim. Make sure to run Supersim with autorelay on. @@ -69,31 +70,64 @@ show what is possible, it does the exact job needed. Download and run the setup script. ```sh - curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh + curl https://docs.optimism.io/tutorials/setup-for-erc20-upgrade.sh > setup-for-erc20-upgrade.sh chmod +x setup-for-erc20-upgrade.sh ./setup-for-erc20-upgrade.sh ``` + If you want to deploy to the [development networks](/stack/interop/tools/devnet), provide `setup-for-erc20-upgrade.sh` with the private key of an address with ETH on both devnets. + + ```sh + ./setup-for-erc20-upgrade.sh + ``` + ### Store the addresses Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. ```sh BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` ### Specify environment variables - Specify these variables, which will be useful later. + 1. Specify these variables, which we use later: - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - ``` + + + Set these parameters for Supersim. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + ``` + + + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + ``` + + + + 2. Regardless of whether you use Supersim or Devnet, specify these variables. + + ```sh + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + export ERC20_CHAINID=`cast chain-id --rpc-url $URL_CHAIN_A` + ORIGINAL_TOKEN_NAME=`cast call $ERC20_ADDRESS "name()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_NAME="$ORIGINAL_TOKEN_NAME Lockbox" + ORIGINAL_TOKEN_SYMBOL=`cast call $ERC20_ADDRESS "symbol()" --rpc-url $URL_CHAIN_A | cast to-ascii` + export NEW_TOKEN_SYMBOL="$ORIGINAL_TOKEN_SYMBOL-L" + export TOKEN_DECIMALS=`cast call $ERC20_ADDRESS "decimals()" --rpc-url $URL_CHAIN_A | cast to-dec` + ``` ### Create a Foundry project @@ -157,16 +191,6 @@ show what is possible, it does the exact job needed. If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`.
- - Note that if we couldn't redeploy on chain A (because the ERC-20 was already in use, not just a test system) and the nonce on chain B was already higher than what it had been on chain A when the proxy contract was deployed, it would have been impossible to create on chain B an ERC-20 account with the same address as the ERC-20 on chain A. - - There are two alternative solutions: - - * [Lockboxes](#lockboxes-for-permissionless-interop) allows any existing ERC20 to become compatible with Superchain interop. - The Lockbox is just a simple wrapper contract, analogous to Wrapped ETH. - * Creating a custom bridge using the [L2toL2CrossDomainMessenger](./message-passing) to facilitate cross-chain mint/burns that does not require a deterministic address across chains but does require the token issuer to manage a token registry per chain. - - ### Deploy ERC-7802 contracts We need to replace the ERC-20 contracts with contracts that: @@ -272,6 +296,6 @@ show what is possible, it does the exact job needed. ## Next steps -* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* Deploy a [SuperchainERC20](/stack/interop/tutorials/deploy-superchain-erc20) to the Superchain * [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) * Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain diff --git a/words.txt b/words.txt index 119da5130..fbb81dc1a 100644 --- a/words.txt +++ b/words.txt @@ -185,11 +185,9 @@ JSPATH jspath jwtsecret Keccak -knowlege leveldb lightkdf Lisk -Lockboxe logfile logfmt MAXAGE From 73f1f9f27df520ad9f7cd98102b601181566ec9e Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 07:10:28 -0500 Subject: [PATCH 25/34] WIP --- .../upgrade-to-superchain-erc20/lockbox.mdx | 4 +- .../proxy-upgrade.mdx | 83 +++++++++---------- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index 03f1143cf..0aaa5c28a 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -135,7 +135,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too The new `SuperchainERC20` variant is called `LockboxSuperchainERC20`, and it requires different constructor parameters. To be able to deploy it, we need to modify some of the deployment utilities. - 1. Download [the SuperchainERC20 starter kit](./deploy-superchain-erc20), and install libraries, etc. + 1. Download [the SuperchainERC20 starter kit](/stack/interop/tutorials/deploy-superchain-erc20), and install libraries, etc. ```sh git clone https://github.com/ethereum-optimism/superchainerc20-starter.git @@ -360,6 +360,6 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ## Next steps -* Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain +* Deploy a [SuperchainERC20](/stack/interop/tutorials/deploy-superchain-erc20) to the Superchain * [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) * Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index afdfcb38e..31ef4c91d 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -169,27 +169,27 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. -
+ If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. +
### Deploy ERC-7802 contracts @@ -208,45 +208,45 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- Detailed explanation +
+ Detailed explanation - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - Most of the code is identical to the original `MyToken`. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - This function is identical to the one in `MyToken`. + This function is identical to the one in `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+ Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). @@ -291,7 +291,6 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei ``` - ## Next steps From 3968ad1b1fec351490393d3ad729bf556f124841 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 07:51:10 -0500 Subject: [PATCH 26/34] Almost ready --- .../proxy-upgrade.mdx | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index 31ef4c91d..9d3562ff5 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -92,42 +92,32 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ### Specify environment variables - 1. Specify these variables, which we use later: + Specify these variables, which we use later: - - - Set these parameters for Supersim. + + + Set these parameters for Supersim. - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - ``` - - - - For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. - - ```sh - USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` - URL_CHAIN_A=https://interop-alpha-0.optimism.io - URL_CHAIN_B=https://interop-alpha-1.optimism.io - ``` - - + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` + - 2. Regardless of whether you use Supersim or Devnet, specify these variables. + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - export ERC20_CHAINID=`cast chain-id --rpc-url $URL_CHAIN_A` - ORIGINAL_TOKEN_NAME=`cast call $ERC20_ADDRESS "name()" --rpc-url $URL_CHAIN_A | cast to-ascii` - export NEW_TOKEN_NAME="$ORIGINAL_TOKEN_NAME Lockbox" - ORIGINAL_TOKEN_SYMBOL=`cast call $ERC20_ADDRESS "symbol()" --rpc-url $URL_CHAIN_A | cast to-ascii` - export NEW_TOKEN_SYMBOL="$ORIGINAL_TOKEN_SYMBOL-L" - export TOKEN_DECIMALS=`cast call $ERC20_ADDRESS "decimals()" --rpc-url $URL_CHAIN_A | cast to-dec` ``` + + ### Create a Foundry project @@ -172,7 +162,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too
What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` (or your address in the case of using devnet) on chain A and chain B are different. You can see the nonce values using these commands: @@ -189,6 +179,8 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ``` If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. + + If the nonce on chain B is already higher than the nonce was on chain A when the original proxy contract was deployed this method is not available and you have to either create a special bridge or [use a lockbox](./lockbox).
### Deploy ERC-7802 contracts From cad3ad97153620fddfad2fa15c16395ed855b4bc Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 09:37:11 -0500 Subject: [PATCH 27/34] Comments addressed --- .../upgrade-to-superchain-erc20/lockbox.mdx | 2 +- .../proxy-upgrade.mdx | 39 +++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index 0aaa5c28a..0454e0a99 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -49,7 +49,7 @@ show what is possible, it does the exact job needed. Create a lockbox `SuperchainERC20` contract to permissionlessly enable interop. -## Step by step +## Instructions Some steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index 9d3562ff5..64c599ee2 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -15,7 +15,7 @@ import { Callout, Steps, Tabs } from 'nextra/components' ## Overview -This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](/stack/interop/explainer#superchain-interop-cluster) using the [`SuperchainTokenBridge`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol) contract. For more information on how it works, [see the explainer](/stack/interop/superchain-erc20). +This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainERC20.sol) that can teleport across the [Superchain interop cluster](/stack/interop/explainer#superchain-interop-cluster) when the original ERC20 contract was placed behind a proxy to enable future upgrades.
About this tutorial @@ -48,7 +48,40 @@ show what is possible, it does the exact job needed. * Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). -## Step by step + +## How beacon proxies work + +```mermaid +sequenceDiagram + Actor User + User->>BeaconProxy: transfer(
, ) + BeaconProxy->>UpgradeableBeacon: What is the implementation address? + UpgradeableBeacon->>BeaconProxy: It is 0xBAD0...60A7 + BeaconProxy->>0xBAD0...60A7: transfer(
, ) +``` + +A [beacon proxy](https://docs.openzeppelin.com/contracts/3.x/api/proxy#BeaconProxy) uses two contracts. +The [`UpgradeableBeacon`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/beacon/UpgradeableBeacon.sol) contract holds the address of the implementation contract. +The [`BeaconProxy`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/beacon/BeaconProxy.sol) contract is the one called for the functionality, the one that holds the storage. +When a user (or another contract) calls `BeaconProxy`, it asks `UpgradeableBeacon` for the implementation address and then uses [`delegatecall`](https://www.evm.codes/?fork=cancun#f4) to call that contract. + +```mermaid +sequenceDiagram + Actor User + Actor Owner + Participant BeaconProxy + Participant 0x600D...60A7 + Owner->>UpgradeableBeacon: Your new implementation address is 0x600D...60A7 + User->>BeaconProxy: transfer(
, ) + BeaconProxy->>UpgradeableBeacon: What is the implementation address? + UpgradeableBeacon->>BeaconProxy: It is 0x600D...60A7 + BeaconProxy->>0x600D...60A7: transfer(
, ) +``` + +To upgrade the contract, an authorized address (typically the `Owner`) calls `UpgradeableBeacon` directly to specify the new implementation contract address. +After that happens, all new calls are sent to the new implementation. + +## Instructions Some steps depend on whether you want to deploy on [supersim](/stack/interop/tools/supersim) or on the [development network](/stack/interop/tools/devnet). @@ -274,7 +307,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```sh AMOUNT=`echo 0.1 | cast to-wei` - cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT 902 + cast send $INTEROP_BRIDGE --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,address,uint256,uint256)" $ERC20_ADDRESS $USER_ADDRESS $AMOUNT `cast chain-id --rpc-url $URL_CHAIN_B` ``` 4. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. From b0b71a2ea769f68ec2e24a9fa3a2e0e245e13f45 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:00:10 -0500 Subject: [PATCH 28/34] Lint --- .../upgrade-to-superchain-erc20/lockbox.mdx | 212 +++++++++--------- .../proxy-upgrade.mdx | 85 ++++--- 2 files changed, 148 insertions(+), 149 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index 0454e0a99..d7dd30eeb 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -95,28 +95,28 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too 1. Specify these variables, which we use later: - - - Set these parameters for Supersim. - - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - ``` - - - - For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. - - ```sh - USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` - URL_CHAIN_A=https://interop-alpha-0.optimism.io - URL_CHAIN_B=https://interop-alpha-1.optimism.io - ``` - - + + + Set these parameters for Supersim. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + ``` + + + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + ``` + + 2. Regardless of whether you use Supersim or Devnet, specify these variables. @@ -170,41 +170,41 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 ``` -
- Explanation of the modified functions +
+ Explanation of the modified functions - For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. - Some functions are modified, as explained below. + For the most part, this is the standard `SuperchainERC20Deployer.s.sol` that comes with the SuperchainERC20 starter kit. + Some functions are modified, as explained below. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a + ``` - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + Get the majority of the configuration from the environment. + Mostly of it is derived from the configuration of the original ERC-20 token. - Note that there is no `owner` here. - This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). + Note that there is no `owner` here. + This `SuperchainERC20` contract does not need an owner, because minting and burning are handled by the users themselves (by locking and unlocking the original tokens). - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ``` - "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ - If there is already a contract there, we have a problem. - Otherwise, deploy `LockboxSuperchainERC20`. + "Manually" calculate the address that [`CREATE2`](https://www.evm.codes/?fork=cancun#f5) will give us.\ + If there is already a contract there, we have a problem. + Otherwise, deploy `LockboxSuperchainERC20`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 - ``` + ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ``` - I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). - This is not necessary, but I consider it a developer experience improvement. - During development you redeploy slightly modified code a lot of times. - It is easier if you don't need to manually change the salt every time. + I modified this salt function to include a timestamp (obtained using `vm.unixTime()` in the constructor). + This is not necessary, but I consider it a developer experience improvement. + During development you redeploy slightly modified code a lot of times. + It is easier if you don't need to manually change the salt every time. - - Remove this before deploying to production. - Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. - -
+ + Remove this before deploying to production. + Otherwise, as new blockchains join the Interop cluster, you may not be able to deploy your contract at the same address. + +
### Create and deploy the new contract @@ -213,67 +213,67 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf ``` -
- Explanation +
+ Explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ``` - The lockbox contract needs to know the contract for which it is a lockbox. - This requires not just the address, but also to know what chain has it. + The lockbox contract needs to know the contract for which it is a lockbox. + This requires not just the address, but also to know what chain has it. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ``` - Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. - This function has several tests to make sure it can be called. + Users call this function to transfer original tokens to the contract and mint themselves an equivalent number of lockbox tokens. + This function has several tests to make sure it can be called. - * Check the chain ID. - Locking and redeeming tokens can only be done on the original token's chain. - * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. - This call typically reverts when it fails, but it can also return `false`. - In that case, we revert. - There are two reasons it may fail. - * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). - * The original owner (`msg.sender`) does not have enough tokens to transfer. + * Check the chain ID. + Locking and redeeming tokens can only be done on the original token's chain. + * Use [`transferFrom`](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#transferFrom) to transfer the tokens to ourselves. + This call typically reverts when it fails, but it can also return `false`. + In that case, we revert. + There are two reasons it may fail. + * The user (in this case, the `LockboxSuperchainERC20` contract) does not have [the allowance](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#_approve) to spend that amount of tokens from the original owner (`msg.sender`). + * The original owner (`msg.sender`) does not have enough tokens to transfer. - If the tests are successful, mint the requested amount for `msg.sender`. + If the tests are successful, mint the requested amount for `msg.sender`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 - ``` + ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ``` - Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. - It also has multiple tests. + Users call this function to redeem their existing lockbox tokens and replace them with the original tokens. + It also has multiple tests. - * Again, check chain ID. - * Try to `_burn` the amount of lockbox tokens. - [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. - * Transfer the amount of the original ERC-20 redeemed to - the caller. - This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. - However, if it does fail for some reason, revert. -
+ * Again, check chain ID. + * Try to `_burn` the amount of lockbox tokens. + [The solady `_burn` function](https://github.com/Vectorized/solady/blob/main/src/tokens/ERC20.sol#L539-L542), the one we inherit from `SuperchainERC20`, reverts if the user does not have enough tokens to burn. + * Transfer the amount of the original ERC-20 redeemed to + the caller. + This should never fail, because lockbox ERC-20 tokens are supposed to always be backed by an equal number of the original tokens. + However, if it does fail for some reason, revert. +
2. Actually deploy the contract. - - - ```sh - pnpm contracts:deploy:token - ``` - + + + ```sh + pnpm contracts:deploy:token + ``` + - - To deploy to the [development networks](/stack/interop/tools/devnet), follow the steps [in the tutorial](/stack/interop/tutorials/deploy-superchain-erc20#prepare-for-deployment) before you deploy the contract. + + To deploy to the [development networks](/stack/interop/tools/devnet), follow the steps [in the tutorial](/stack/interop/tutorials/deploy-superchain-erc20#prepare-for-deployment) before you deploy the contract. - Then, update `packages/contracts/.env` and deploy the token. + Then, update `packages/contracts/.env` and deploy the token. - ```sh - echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env - pnpm contracts:deploy:token - ``` - - + ```sh + echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env + pnpm contracts:deploy:token + ``` + + 3. Get the new token address and store it in an environment variable. @@ -322,18 +322,18 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too 6. Specify the configuration for another user. - - - ```sh - USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC - PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a - ``` - - - - Specify the private key (`PRIVATE_KEY_2`) and user address (`USER_ADDRESS_2`) of another user that has ETH on both devnets. - - + + + ```sh + USER_ADDRESS_2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + PRIVATE_KEY_2=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + ``` + + + + Specify the private key (`PRIVATE_KEY_2`) and user address (`USER_ADDRESS_2`) of another user that has ETH on both devnets. + + 7. Transfer new tokens to the new user (on chain B) and see that they were actually transferred. diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index 64c599ee2..d1c5c88b8 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -48,7 +48,6 @@ show what is possible, it does the exact job needed. * Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). - ## How beacon proxies work ```mermaid @@ -192,29 +191,29 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. -
- What to do when the values are not the same +
+ What to do when the values are not the same - This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` (or your address in the case of using devnet) on chain A and chain B are different. + This can happen when the nonce values of `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` (or your address in the case of using devnet) on chain A and chain B are different. - You can see the nonce values using these commands: + You can see the nonce values using these commands: - ```sh - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B - ``` + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` - The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. + The easiest solution is to send transactions to the chain with the lower nonce until the nonces are equal, and then deploy to both chains. - ```sh - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup - forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup - ``` + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_A --broadcast --private-key $PRIVATE_KEY --tc LabSetup + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup + ``` - If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. + If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_ADDRESS`. - If the nonce on chain B is already higher than the nonce was on chain A when the original proxy contract was deployed this method is not available and you have to either create a special bridge or [use a lockbox](./lockbox). -
+ If the nonce on chain B is already higher than the nonce was on chain A when the original proxy contract was deployed this method is not available and you have to either create a special bridge or [use a lockbox](./lockbox). +
### Deploy ERC-7802 contracts @@ -233,45 +232,45 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ``` -
- Detailed explanation +
+ Detailed explanation - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ``` - Most of the code is identical to the original `MyToken`. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC-7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e + ``` - This function is identical to the one in `MyToken`. + This function is identical to the one in `MyToken`. - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. -
+ Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- - Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. - Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). - + + Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html). + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). From 933095862ec5dcc631624185dc6df73553f88eab Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:10:12 -0500 Subject: [PATCH 29/34] breadcrumbs --- .../interop/tutorials/upgrade-to-superchain-erc20.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 9b9c32498..e3205575e 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -47,6 +47,14 @@ graph TB Proxy2 -- Yes --> Upgrade[Contract upgrade]:::link ``` + + + + } /> + } /> + + + ## Next steps * Deploy a [SuperchainERC20](./deploy-superchain-erc20) to the Superchain From 25ce3e20540336fc6b3106fefecbe1cad75c8d35 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:11:54 -0500 Subject: [PATCH 30/34] Update pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index d7dd30eeb..3378d3ac2 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -2,6 +2,10 @@ title: Lockboxes for permissionless interop lang: en-US description: Tutorial on how to take permissionlessly create a lockbox contract to enable Superchain interoperability. +topic: Interoperability +personas: [Developer] +categories: [Tutorial, Interop] +content_type: article --- import { Steps, Callout, Tabs } from 'nextra/components' From 4640c8a71ea2caba0b103cba14a8da7974a06beb Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:13:28 -0500 Subject: [PATCH 31/34] Update proxy-upgrade.mdx --- .../tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index d1c5c88b8..76be343c6 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -2,6 +2,10 @@ title: Contract upgrade lang: en-US description: Tutorial on how to upgrade a proxied ERC20 contract for use with Superchain interop. +topic: Interoperability +personas: [Developer] +categories: [Tutorial, Interop] +content_type: article --- import { Callout, Steps, Tabs } from 'nextra/components' From 1eb56579dcc34cd8f7df236fba2c66d51718dc4d Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:36:33 -0500 Subject: [PATCH 32/34] Import Card(s) --- pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index e3205575e..4b4849e2f 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -4,8 +4,7 @@ lang: en-US description: Tutorial on how to take an existing ERC20 and upgrade it to SuperchainERC20. --- -import { Callout } from 'nextra/components' -import { Steps } from 'nextra/components' +import { Callout, Steps, Card, Cards } from 'nextra/components' The SuperchainERC20 standard is ready for production deployments. From 384c99dedd7a6ff9e6a34e80abe7e905c3131301 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:41:49 -0500 Subject: [PATCH 33/34] Update pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx index 4b4849e2f..57f7e5f2c 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20.mdx @@ -2,6 +2,10 @@ title: Upgrading ERC20 to SuperchainERC20 lang: en-US description: Tutorial on how to take an existing ERC20 and upgrade it to SuperchainERC20. +topic: Interoperability +personas: [Developer] +categories: [Tutorial, Interop] +content_type: article --- import { Callout, Steps, Card, Cards } from 'nextra/components' From 9bcaaebcb6249176541c99dd86c4ec9d20fd6b7c Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 4 Apr 2025 12:58:41 -0500 Subject: [PATCH 34/34] Code rabbit --- .../upgrade-to-superchain-erc20/lockbox.mdx | 6 ++-- .../proxy-upgrade.mdx | 32 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx index 3378d3ac2..d4f36d813 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/lockbox.mdx @@ -19,7 +19,7 @@ import { Steps, Callout, Tabs } from 'nextra/components' ## Overview -The lockbox is smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. +The lockbox is a smart contract that accepts deposits of the original ERC-20 and issues an equivalent amount of tokens that are Superchain interop compatible. Users can unwrap their Superchain interop token at any time by returning it to the contract, which burns the Superchain interop tokens and releases the corresponding original ERC-20 from the lockbox.
@@ -44,14 +44,14 @@ Users can unwrap their Superchain interop token at any time by returning it to t {/* I put this warning here, when we don't have it on most pages, because this tutorial -has, IMHO, code that is a lot more likely to be used in production. It doesn't just +has code that is a lot more likely to be used in production. It doesn't just show what is possible, it does the exact job needed. */} ### What you'll do -Create a lockbox `SuperchainERC20` contract to permissionlessly enable interop. +Create a lockbox `SuperchainERC20` contract to enable interoperability for an ERC20 contract without permission from the original ERC20 deployer. ## Instructions diff --git a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx index 76be343c6..ccd4fa5bf 100644 --- a/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx +++ b/pages/stack/interop/tutorials/upgrade-to-superchain-erc20/proxy-upgrade.mdx @@ -43,14 +43,14 @@ This guide explains how to upgrade an ERC20 to a [`SuperchainERC20`](https://git {/* I put this warning here, when we don't have it on most pages, because this tutorial -has, IMHO, code that is a lot more likely to be used in production. It doesn't just +has code that is a lot more likely to be used in production. It doesn't just show what is possible, it does the exact job needed. */} ### What you'll do -* Upgrade an existing ERC-20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). +* Upgrade an existing ERC20 that uses [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/proxies) to comply with interop requirements (with the proper authority). ## How beacon proxies work @@ -101,7 +101,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ``` - ### Setup the ERC-20 token on chain A + ### Setup the ERC20 token on chain A Download and run the setup script. @@ -119,7 +119,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ### Store the addresses - Execute the bottom two lines of the setup script output to store the ERC-20 address and the address of the beacon contract. + Execute the bottom two lines of the setup script output to store the ERC20 address and the address of the beacon contract. ```sh BEACON_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 @@ -157,7 +157,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ### Create a Foundry project - We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC-20 and proxy deployment. + We create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts, which were used for the original ERC20 and proxy deployment. ```sh mkdir proxy-upgrade @@ -219,16 +219,16 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too If the nonce on chain B is already higher than the nonce was on chain A when the original proxy contract was deployed this method is not available and you have to either create a special bridge or [use a lockbox](./lockbox).
- ### Deploy ERC-7802 contracts + ### Deploy ERC7802 contracts - We need to replace the ERC-20 contracts with contracts that: + We need to replace the ERC20 contracts with contracts that: - * Support [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC-165](https://eips.ethereum.org/EIPS/eip-165). - * Have the same storage layout as the ERC-20 contracts they replace. + * Support [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC165](https://eips.ethereum.org/EIPS/eip-165). + * Have the same storage layout as the ERC20 contracts they replace. These contracts do *not* need to be deployed to the same address. - The address that needs to be the same is not the address of the ERC-20 contract itself, but of the proxy. + The address that needs to be the same is not the address of the ERC20 contract itself, but of the proxy. 1. Create a file, `src/InteropToken.sol`: @@ -247,13 +247,13 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 ``` - These are the imports needed for ERC-7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC-7802 events. + These are the imports needed for ERC7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC7802 events. ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce ``` - We also implement [ERC-165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + We also implement [ERC165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e ``` @@ -263,16 +263,16 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 ``` - Standard [ERC-7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + Standard [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 ``` - Standard [ERC-165](https://eips.ethereum.org/EIPS/eip-165) behavior. + Standard [ERC165](https://eips.ethereum.org/EIPS/eip-165) behavior.
- Copying the original ERC-20 token code with minimal differences is one method to keep the storage layout identical. + Copying the original ERC20 token code with minimal differences is one method to keep the storage layout identical. Alternatively, if you want to use a different contract, such as `SuperchainERC20`, you can modify the storage layout to match the old one using [the Solidity docs](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html).