From 071da34a2fa9e7a7cfcccca9c0c92a01e4ce1c95 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 12 Apr 2025 16:04:31 +0300 Subject: [PATCH 01/21] WIP --- .../custom-bridge.mdx | 378 ++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx new file mode 100644 index 000000000..362ccc906 --- /dev/null +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -0,0 +1,378 @@ +--- +title: Custom interoperability bridges +lang: en-US +description: Tutorial on how to create a custom interoperability bridge. The example is a bridge when the addresses of the ERC20 contracts are not the same. +topic: Interoperability +personas: [Developer] +categories: [Tutorial, Interop] +content_type: article +--- + +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. + + +# Custom interoperability bridges + +## Overview + +Sometimes the address of an ERC20 contract is not available on a different chain. +This means that the [standard bridge](/interop/superchain-erc20) is not an option. +However, if the original ERC20 contract is behind a proxy (so we can add [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) support), we can still use interop bridging. + + +
+ About this tutorial + + **What you'll learn** + + * How to use [interop message passing](/interop/tutorials/message-passing) to create a custom bridge. + + **Prerequisite knowledge** + + * How to [deploy SuperchainERC20 tokens with custom code](/stack/interop/tutorials/custom-superchain-erc20). + * How to [transfer interop messages](/interop/tutorials/message-passing). + + +
+ + + 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 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 enable interoperability for an ERC20 contract without permission from the original ERC20 deployer. + +## 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). + + + ### 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 + ``` + + + + 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` + ``` + + ### 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](/stack/interop/tutorials/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](/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 + + + + From f9c3040067ec599177920014aba538592083fb4d Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sat, 12 Apr 2025 16:43:33 +0300 Subject: [PATCH 02/21] WIP --- .../custom-bridge.mdx | 388 +++++++++--------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index 362ccc906..ef737fe6c 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -56,7 +56,83 @@ show what is possible, it does the exact job needed. ### What you'll do -Create a lockbox `SuperchainERC20` contract to enable interoperability for an ERC20 contract without permission from the original ERC20 deployer. +Create a custom bridge and use it to transfer tokens between two ERC20 contracts running on different addresses. + + + + + 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) when the original ERC20 contract was placed behind a proxy to enable future upgrades. + +
+ 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 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 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 + +```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 @@ -75,7 +151,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. @@ -93,7 +169,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 @@ -102,268 +178,196 @@ 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` ``` + + - ### Update the deployment utilities + ### Create a Foundry project - 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. + 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. - 1. Download [the SuperchainERC20 starter kit](/stack/interop/tutorials/deploy-superchain-erc20), and install libraries, etc. + ```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 + ``` - ```sh - git clone https://github.com/ethereum-optimism/superchainerc20-starter.git - cd superchainerc20-starter - pnpm install - pnpm init:env - ``` + ### Create and run the deployment script + + 1. Create an `script/LabSetup.s.sol` file with this content: - 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" - } - } + ```solidity file=/public/tutorials/setup-for-erc20-upgrade.sh#L26-L66 hash=83e951ca27872311bd7ae734c24cdbc2 filename="script/LabSetup.s.sol" ``` - 3. Create a new file, `packages/contracts/scripts/LockboxDeployer.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. - ```solidity filename="packages/contracts/scripts/LockboxDeployer.s.sol" file=/public/tutorials/LockboxDeployer.s.sol hash=534b543709be173d87508a53322d8c59 + ```sh + forge script script/LabSetup.s.sol --rpc-url $URL_CHAIN_B --broadcast --private-key $PRIVATE_KEY --tc LabSetup ``` -
- Explanation of the modified functions + Scroll up and see the Logs section of the output: + + ``` + == Logs == + Token address: 0x5FbDB2315678afecb367f032d93F642f64180aa3 + msg.sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + UpgradeableBeacon: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + Proxy: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + ``` - 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. + Verify that the proxy address is the same as `$ERC20_ADDRESS`, and that the beacon address is the same as `$BEACON_ADDRESS`. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L46-L52 hash=302d02c3895f109e5e64d265b0473e6a - ``` +
+ What to do when the values are not the same - Get the majority of the configuration from the environment. - Mostly of it is derived from the configuration of the original ERC-20 token. + 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. - 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). + You can see the nonce values using these commands: - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L54-L69 hash=c45855080dc554cece35ed87e2d68f68 + ```sh + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B ``` - "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`. + 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. - ```solidity file=/public/tutorials/LockboxDeployer.s.sol#L80-L84 hash=5d1f71b16a6f02d52a79b1a9e7588f87 + ```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 ``` - 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. + If you do this, remember to update `$ERC20_ADDRESS` and `$BEACON_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. - + 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).
- ### Create and deploy the new contract + ### Deploy ERC7802 contracts + + We need to replace the ERC20 contracts with contracts that: + + * 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. - 1. Create this file in `packages/contracts/src/LockboxSuperchainERC20.sol`: + + 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 ERC20 contract itself, but of the proxy. + + + 1. Create a file, `src/InteropToken.sol`: - ```solidity filename="packages/contracts/src/LockboxSuperchainERC20.sol" file=/public/tutorials/LockboxSuperchainERC20.sol hash=d326f0e1c26904b844263274914951cf + ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" ```
- Explanation + Detailed explanation - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L11-L12 hash=45d211a19533f9b0dee310743b25459f + ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b ``` - 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. + Most of the code is identical to the original `MyToken`. - ```solidity file=/public/tutorials/LockboxSuperchainERC20.sol#L47-L57 hash=20f6aa15d113dcaf992875184173cb47 + ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 ``` - 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. + These are the imports needed for ERC7802 support. + We need `IERC165` for documentation purposes, and `IERC7802` for the ERC7802 events. - * 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/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce + ``` - If the tests are successful, mint the requested amount for `msg.sender`. + 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/LockboxSuperchainERC20.sol#L59-L67 hash=2e63a9cd1ac1114c3fb2110e28b60924 + ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e ``` - 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. -
+ This function is identical to the one in `MyToken`. - 2. Actually deploy the contract. + ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 + ``` - - - ```sh - pnpm contracts:deploy:token - ``` - + Standard [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. - - 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. + ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 + ``` - Then, update `packages/contracts/.env` and deploy the token. + Standard [ERC165](https://eips.ethereum.org/EIPS/eip-165) behavior. +
- ```sh - echo DEPLOYER_PRIVATE_KEY=$PRIVATE_KEY > packages/contracts/.env - pnpm contracts:deploy:token - ``` - - + + 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). + - 3. Get the new token address and store it in an environment variable. + 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). ```sh - NEW_TOKEN_ADDRESS=`cat packages/contracts/broadcast/multi/LockboxDeployer.s.sol-latest/run.json | awk '/contractAddress/ {print $2}' | head -1 | sed 's/[",]//g'` + 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}'` ``` - ### Verification + ### Update proxies - 1. Check that the user has a single token of the original ERC-20. + Notify the beacon contracts of the new implementation contracts. - ```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 + 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 + ``` - ```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 - ``` + ### Verification - 3. See the balances of the user, both original and lockbox, and the balance of the lockbox contract itself. + 1. See your balance on chain A. ```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. + 2. See your balance on 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` + cast call $ERC20_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei ``` - 5. See the user's balances on both chains. + 3. Transfer 0.1 token. ```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 + 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 `cast chain-id --rpc-url $URL_CHAIN_B` ``` - 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. + 4. See the new balances. The A chain should have 0.9 tokens, and the B chain should have 0.1 tokens. ```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 + 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 ``` @@ -372,7 +376,3 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too * 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 - - - - From 75b3ce27a810f697c4139c603cb8b447e2d095bf Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 14 Apr 2025 22:23:30 +0300 Subject: [PATCH 03/21] Working 1st draft --- .../custom-bridge.mdx | 247 +++++++----------- public/tutorials/CustomBridge.sol | 101 +++++++ 2 files changed, 201 insertions(+), 147 deletions(-) create mode 100644 public/tutorials/CustomBridge.sol diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index ef737fe6c..d52f036f5 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -1,5 +1,5 @@ --- -title: Custom interoperability bridges +title: Custom interoperability bridge lang: en-US description: Tutorial on how to create a custom interoperability bridge. The example is a bridge when the addresses of the ERC20 contracts are not the same. topic: Interoperability @@ -21,8 +21,7 @@ import { Steps, Callout, Tabs } from 'nextra/components' Sometimes the address of an ERC20 contract is not available on a different chain. This means that the [standard bridge](/interop/superchain-erc20) is not an option. -However, if the original ERC20 contract is behind a proxy (so we can add [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) support), we can still use interop bridging. - +However, if the original ERC20 contract is behind a proxy (so we can add [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) support), we can still use interop by writing our own bridge.
About this tutorial @@ -54,53 +53,7 @@ show what is possible, it does the exact job needed. */} -### What you'll do - -Create a custom bridge and use it to transfer tokens between two ERC20 contracts running on different addresses. - - - - - 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) when the original ERC20 contract was placed behind a proxy to enable future upgrades. - -
- 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 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 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 @@ -176,6 +129,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` + ### Specify environment variables Specify these variables, which we use later: @@ -189,7 +143,6 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 URL_CHAIN_A=http://127.0.0.1:9545 URL_CHAIN_B=http://127.0.0.1:9546 - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 ``` @@ -200,175 +153,173 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too 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 ``` + ### Advance the user's nonoce on chain B + + This solution is necessary when the nonce on chain B is higher than it was on chain A when the proxy for the ERC-20 contract was installed. + To simulate this situation we send a few meaningless transactions on chain B and then see that the nonce on B is higher than the nonce on A. + + ```sh + cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + echo -n Nonce on chain A: + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A + echo -n Nonce on chain B: + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + ``` + ### 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 ERC20 and proxy deployment. ```sh - mkdir proxy-upgrade - cd proxy-upgrade + mkdir custom-bridge + cd custom-bridge 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" - ``` + ### Deploy proxies - This is the same deployment script used for the original deployment on chain A. + We need two contracts on each chain: an ERC20 and a bridge, and to enable future upgrades, we want to install each of those contracts behind a proxy. + We already have one contract, the ERC20 on chain A, but we need to create the other three. - 2. Run this command to deploy the same contracts on chain B. + We have an interesting [chicken-and-egg](https://en.wikipedia.org/wiki/Chicken_or_the_egg) issue here. + To create a proxy, we need the address of the implementation contract, the one with the actual code. + However, the bridge and ERC20 code needs to have the proxy addresses. + One possible solution is to choose a pre-existing contract, and use that as the implementation contract until we can upgrade. + Every OP Stack chain has [predeploys](https://specs.optimism.io/protocol/predeploys.html) we can use for this purpose. - ```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` (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: - - ```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`. - - 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). -
+ ```sh + DUMMY_ADDRESS=0x4200000000000000000000000000000000000000 + UPGRADE_BEACON_CONTRACT=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon + PROXY_CONTRACT=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol:BeaconProxy + BRIDGE_BEACON_ADDRESS_A=`forge create $UPGRADE_BEACON_CONTRACT --broadcast --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --constructor-args $DUMMY_ADDRESS $USER_ADDRESS | awk '/Deployed to:/ {print $3}'` + BRIDGE_ADDRESS_A=`forge create $PROXY_CONTRACT --broadcast --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --constructor-args $BRIDGE_BEACON_ADDRESS_A 0x | awk '/Deployed to:/ {print $3}'` + BRIDGE_BEACON_ADDRESS_B=`forge create $UPGRADE_BEACON_CONTRACT --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $DUMMY_ADDRESS $USER_ADDRESS | awk '/Deployed to:/ {print $3}'` + BRIDGE_ADDRESS_B=`forge create $PROXY_CONTRACT --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $BRIDGE_BEACON_ADDRESS_B 0x | awk '/Deployed to:/ {print $3}'` + ERC20_BEACON_ADDRESS_A=$BEACON_ADDRESS + ERC20_ADDRESS_A=$ERC20_ADDRESS + ERC20_BEACON_ADDRESS_B=`forge create $UPGRADE_BEACON_CONTRACT --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $DUMMY_ADDRESS $USER_ADDRESS | awk '/Deployed to:/ {print $3}'` + ERC20_ADDRESS_B=`forge create $PROXY_CONTRACT --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $ERC20_BEACON_ADDRESS_B 0x | awk '/Deployed to:/ {print $3}'` + ``` ### Deploy ERC7802 contracts We need to replace the ERC20 contracts with contracts that: * 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. + * Allow the bridge address to mint and burn tokens. + Normally this is `PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE`, but in our case it would be the bridge proxy address, which we'll store in `bridgeAddress`. + * Have the same storage layout as the ERC20 contracts they replace (in the case of chain A). - - 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 ERC20 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" - ```solidity file=/public/tutorials/InteropToken.sol hash=007791836635608fdeb9c70c1b368f25 filename="src/InteropToken.sol" - ``` + 1. This `src/InteropToken.sol` is used for contract upgrades. + Edit it to allow our custom bridge to mint and burn tokens instead of the predeployed superchain token bridge. + + - On lines 20 and 31 replace ~`PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE`~ with `bridgeAddress`. -
- Detailed explanation - - ```solidity file=/public/tutorials/InteropToken.sol#L1-L5 hash=36b9b9d0fb1ff680dc0eaa1c48b7c56b + ```solidity + require(msg.sender == bridgeAddress, "Unauthorized"); ``` - Most of the code is identical to the original `MyToken`. + - Add these lines anywhere in the contract: + + ```solidity + address public immutable bridgeAddress; - ```solidity file=/public/tutorials/InteropToken.sol#L6-L7 hash=f06f3bd72be73dbd754008da7dd00d48 + constructor(address bridgeAddress_) { + bridgeAddress = bridgeAddress_; + } ``` - These are the imports needed for ERC7802 support. - We need `IERC165` for documentation purposes, and `IERC7802` for the ERC7802 events. + 1. Deploy `InteropToken` on both chains, with the bridge address. - ```solidity file=/public/tutorials/InteropToken.sol#L9 hash=ca402292e7551621669ef1a59b85d7ce - ``` + ```sh + INTEROP_TOKEN_A=`forge create InteropToken --private-key $PRIVATE_KEY --broadcast --rpc-url $URL_CHAIN_A --constructor-args $BRIDGE_ADDRESS_A | awk '/Deployed to:/ {print $3}'` + INTEROP_TOKEN_B=`forge create InteropToken --private-key $PRIVATE_KEY --broadcast --rpc-url $URL_CHAIN_B --constructor-args $BRIDGE_ADDRESS_B | awk '/Deployed to:/ {print $3}'` + ``` - We also implement [ERC165](https://eips.ethereum.org/EIPS/eip-165), but we don't need to import anything from there. + 1. Update the proxies to the new implementations. - ```solidity file=/public/tutorials/InteropToken.sol#L10-L14 hash=37e9b49f50a8b70971ce5d0112bd934e - ``` + ```sh + cast send $ERC20_BEACON_ADDRESS_A "upgradeTo(address)" $INTEROP_TOKEN_A --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + cast send $ERC20_BEACON_ADDRESS_B "upgradeTo(address)" $INTEROP_TOKEN_B --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B + ``` - This function is identical to the one in `MyToken`. + ### Deploy the actual bridge - ```solidity file=/public/tutorials/InteropToken.sol#L16-L36 hash=448a7e21e094b3fd961f2b8ee15bc6c7 - ``` + 1. Create a file, `src/CustomBridge.sol`. - Standard [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) behavior. + ```solidity file=/public/tutorials/CustomBridge.sol hash=df5685626f96af100d608b5b94477888 filename="src/CustomBridge.sol" - ```solidity file=/public/tutorials/InteropToken.sol#L38-L42 hash=abb2093e9681984f25afa6f9d8b237a3 - ``` +
+ + Explanation + + blockchains - Standard [ERC165](https://eips.ethereum.org/EIPS/eip-165) behavior.
- - 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). - + 1. Get the chainID values. + + ```sh + CHAINID_A=`cast chain-id --rpc-url $URL_CHAIN_A` + CHAINID_B=`cast chain-id --rpc-url $URL_CHAIN_B` + ``` - 2. Deploy this contract on both chains, and store the addresses (which may or may not be the same). + 1. Deploy the bridges with the correct configuration. ```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}'` + BRIDGE_IMPLEMENTATION_ADDRESS_A=`forge create CustomBridge --broadcast --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --constructor-args $ERC20_ADDRESS_A $ERC20_ADDRESS_B $CHAINID_B $BRIDGE_ADDRESS_B | awk '/Deployed to:/ {print $3}'` + BRIDGE_IMPLEMENTATION_ADDRESS_B=`forge create CustomBridge --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $ERC20_ADDRESS_B $ERC20_ADDRESS_A $CHAINID_A $BRIDGE_ADDRESS_A | awk '/Deployed to:/ {print $3}'` ``` - ### Update proxies - - Notify the beacon contracts of the new implementation contracts. + 1. Inform the proxy beacons about the new addresses of the bridge 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 - ``` + ```sh + cast send $BRIDGE_BEACON_ADDRESS_A "upgradeTo(address)" $BRIDGE_IMPLEMENTATION_ADDRESS_A --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A + cast send $BRIDGE_BEACON_ADDRESS_B "upgradeTo(address)" $BRIDGE_IMPLEMENTATION_ADDRESS_B --private-key $PRIVATE_KEY --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 + cast call $ERC20_ADDRESS_A "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 + cast call $ERC20_ADDRESS_B "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 `cast chain-id --rpc-url $URL_CHAIN_B` + cast send $BRIDGE_ADDRESS_A --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY "sendERC20(address,uint256)" $USER_ADDRESS $AMOUNT ``` 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 + cast call $ERC20_ADDRESS_A "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei + cast call $ERC20_ADDRESS_B "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei ``` + ## Next steps @@ -376,3 +327,5 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too * 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/public/tutorials/CustomBridge.sol b/public/tutorials/CustomBridge.sol new file mode 100644 index 000000000..406bf15a6 --- /dev/null +++ b/public/tutorials/CustomBridge.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Libraries +import { PredeployAddresses } from "interop-lib/src/libraries/PredeployAddresses.sol"; + +// Interfaces +import { IERC7802, IERC165 } from "interop-lib/src/interfaces/IERC7802.sol"; +import { IL2ToL2CrossDomainMessenger } from "interop-lib/src/interfaces/IL2ToL2CrossDomainMessenger.sol"; + +/// @custom:proxied true +/// @title CustomBridge +contract CustomBridge { + // Immutable configuration + address public immutable tokenAddressHere; + address public immutable tokenAddressThere; + uint256 public immutable chainIdThere; + address public immutable bridgeAddressThere; + + error ZeroAddress(); + error Unauthorized(); + + /// @notice Thrown when attempting to relay a message and the cross domain message sender is not the + /// SuperchainTokenBridge. + error InvalidCrossDomainSender(); + + /// @notice Emitted when tokens are sent from one chain to another. + /// @param token Address of the token sent. + /// @param from Address of the sender. + /// @param to Address of the recipient. + /// @param amount Number of tokens sent. + /// @param destination Chain ID of the destination chain. + event SendERC20( + address indexed token, address indexed from, address indexed to, uint256 amount, uint256 destination + ); + + /// @notice Emitted whenever tokens are successfully relayed on this chain. + /// @param token Address of the token relayed. + /// @param from Address of the msg.sender of sendERC20 on the source chain. + /// @param to Address of the recipient. + /// @param amount Amount of tokens relayed. + /// @param source Chain ID of the source chain. + event RelayERC20(address indexed token, address indexed from, address indexed to, uint256 amount, uint256 source); + + /// @notice Address of the L2ToL2CrossDomainMessenger Predeploy. + address internal constant MESSENGER = PredeployAddresses.L2_TO_L2_CROSS_DOMAIN_MESSENGER; + + // Setup the configuration + constructor( + address tokenAddressHere_, + address tokenAddressThere_, + uint256 chainIdThere_, + address bridgeAddressThere_ + ) { + tokenAddressHere = tokenAddressHere_; + tokenAddressThere = tokenAddressThere_; + chainIdThere = chainIdThere_; + bridgeAddressThere = bridgeAddressThere_; + } + + /// @notice Sends tokens to a target address on another chain. + /// @dev Tokens are burned on the source chain. + /// @param _to Address to send tokens to. + /// @param _amount Amount of tokens to send. + /// @return msgHash_ Hash of the message sent. + function sendERC20( + address _to, + uint256 _amount + ) + external + returns (bytes32 msgHash_) + { + if (_to == address(0)) revert ZeroAddress(); + + IERC7802(tokenAddressHere).crosschainBurn(msg.sender, _amount); + + bytes memory message = abi.encodeCall(this.relayERC20, (msg.sender, _to, _amount)); + msgHash_ = IL2ToL2CrossDomainMessenger(MESSENGER).sendMessage(chainIdThere, bridgeAddressThere, message); + + emit SendERC20(tokenAddressHere, msg.sender, _to, _amount, chainIdThere); + } + + /// @notice Relays tokens received from another chain. + /// @dev Tokens are minted on the destination chain. + /// @param _from Address of the msg.sender of sendERC20 on the source chain. + /// @param _to Address to relay tokens to. + /// @param _amount Amount of tokens to relay. + function relayERC20(address _from, address _to, uint256 _amount) external { + if (msg.sender != MESSENGER) revert Unauthorized(); + + (address crossDomainMessageSender, uint256 source) = + IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); + + if (crossDomainMessageSender != bridgeAddressThere) revert InvalidCrossDomainSender(); + if (source != chainIdThere) revert InvalidCrossDomainSender(); + + IERC7802(tokenAddressHere).crosschainMint(_to, _amount); + + emit RelayERC20(tokenAddressHere, _from, _to, _amount, chainIdThere); + } +} \ No newline at end of file From b3535ad33992cc3852c741c37ec100d22df3236b Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 14 Apr 2025 22:24:20 +0300 Subject: [PATCH 04/21] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- .../custom-bridge.mdx | 69 +++--- public/tutorials/InteropToken.sol | 44 ++++ public/tutorials/setup-for-erc20-upgrade.sh | 76 ++++++ words.txt | 229 +++++++++--------- 4 files changed, 267 insertions(+), 151 deletions(-) create mode 100644 public/tutorials/InteropToken.sol create mode 100644 public/tutorials/setup-for-erc20-upgrade.sh diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index d52f036f5..9406f088b 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -20,7 +20,7 @@ import { Steps, Callout, Tabs } from 'nextra/components' ## Overview Sometimes the address of an ERC20 contract is not available on a different chain. -This means that the [standard bridge](/interop/superchain-erc20) is not an option. +This means that the [standard bridge](/interop/superchain-erc20) is not an option. However, if the original ERC20 contract is behind a proxy (so we can add [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) support), we can still use interop by writing our own bridge.
@@ -32,10 +32,8 @@ However, if the original ERC20 contract is behind a proxy (so we can add [ERC780 **Prerequisite knowledge** - * How to [deploy SuperchainERC20 tokens with custom code](/stack/interop/tutorials/custom-superchain-erc20). + * How to [deploy SuperchainERC20 tokens with custom code](/interop/tutorials/custom-superchain-erc20). * How to [transfer interop messages](/interop/tutorials/message-passing). - -
@@ -53,8 +51,6 @@ show what is possible, it does the exact job needed. */} - - ## How beacon proxies work ```mermaid @@ -89,7 +85,7 @@ 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). +Some steps depend on whether you want to deploy on [supersim](/interop/tools/supersim) or on the [development network](/interop/tools/devnet). ### Install and run Supersim @@ -114,7 +110,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ./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. + If you want to deploy to the [development networks](/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 @@ -129,7 +125,6 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too export ERC20_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 ``` - ### Specify environment variables Specify these variables, which we use later: @@ -159,7 +154,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ### Advance the user's nonoce on chain B - This solution is necessary when the nonce on chain B is higher than it was on chain A when the proxy for the ERC-20 contract was installed. + This solution is necessary when the nonce on chain B is higher than it was on chain A when the proxy for the ERC-20 contract was installed. To simulate this situation we send a few meaningless transactions on chain B and then see that the nonce on B is higher than the nonce on A. ```sh @@ -223,34 +218,35 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too 1. Create a file, `src/InteropToken.sol`. ```solidity file=/public/tutorials/InteropToken.sol hash=5e728534c265028c94d60dcb6550699d filename="src/InteropToken.sol" + ``` + + 2. This `src/InteropToken.sol` is used for contract upgrades. + Edit it to allow our custom bridge to mint and burn tokens instead of the predeployed superchain token bridge. - 1. This `src/InteropToken.sol` is used for contract upgrades. - Edit it to allow our custom bridge to mint and burn tokens instead of the predeployed superchain token bridge. - - - On lines 20 and 31 replace ~`PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE`~ with `bridgeAddress`. + * On lines 20 and 31 replace ~~`PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE`~~ with `bridgeAddress`. - ```solidity - require(msg.sender == bridgeAddress, "Unauthorized"); - ``` + ```solidity + require(msg.sender == bridgeAddress, "Unauthorized"); + ``` - - Add these lines anywhere in the contract: + * Add these lines anywhere in the contract: - ```solidity - address public immutable bridgeAddress; + ```solidity + address public immutable bridgeAddress; - constructor(address bridgeAddress_) { - bridgeAddress = bridgeAddress_; - } - ``` + constructor(address bridgeAddress_) { + bridgeAddress = bridgeAddress_; + } + ``` - 1. Deploy `InteropToken` on both chains, with the bridge address. + 3. Deploy `InteropToken` on both chains, with the bridge address. ```sh INTEROP_TOKEN_A=`forge create InteropToken --private-key $PRIVATE_KEY --broadcast --rpc-url $URL_CHAIN_A --constructor-args $BRIDGE_ADDRESS_A | awk '/Deployed to:/ {print $3}'` INTEROP_TOKEN_B=`forge create InteropToken --private-key $PRIVATE_KEY --broadcast --rpc-url $URL_CHAIN_B --constructor-args $BRIDGE_ADDRESS_B | awk '/Deployed to:/ {print $3}'` ``` - 1. Update the proxies to the new implementations. + 4. Update the proxies to the new implementations. ```sh cast send $ERC20_BEACON_ADDRESS_A "upgradeTo(address)" $INTEROP_TOKEN_A --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A @@ -259,7 +255,7 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too ### Deploy the actual bridge - 1. Create a file, `src/CustomBridge.sol`. + 1. Create a file, `src/CustomBridge.sol`. ```solidity file=/public/tutorials/CustomBridge.sol hash=df5685626f96af100d608b5b94477888 filename="src/CustomBridge.sol" @@ -271,26 +267,28 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too
- 1. Get the chainID values. + ``` + + 2. Get the chainID values. ```sh CHAINID_A=`cast chain-id --rpc-url $URL_CHAIN_A` CHAINID_B=`cast chain-id --rpc-url $URL_CHAIN_B` - ``` + ``` - 1. Deploy the bridges with the correct configuration. + 3. Deploy the bridges with the correct configuration. ```sh BRIDGE_IMPLEMENTATION_ADDRESS_A=`forge create CustomBridge --broadcast --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --constructor-args $ERC20_ADDRESS_A $ERC20_ADDRESS_B $CHAINID_B $BRIDGE_ADDRESS_B | awk '/Deployed to:/ {print $3}'` BRIDGE_IMPLEMENTATION_ADDRESS_B=`forge create CustomBridge --broadcast --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY --constructor-args $ERC20_ADDRESS_B $ERC20_ADDRESS_A $CHAINID_A $BRIDGE_ADDRESS_A | awk '/Deployed to:/ {print $3}'` ``` - 1. Inform the proxy beacons about the new addresses of the bridge implementation contracts. + 4. Inform the proxy beacons about the new addresses of the bridge implementation contracts. ```sh cast send $BRIDGE_BEACON_ADDRESS_A "upgradeTo(address)" $BRIDGE_IMPLEMENTATION_ADDRESS_A --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A cast send $BRIDGE_BEACON_ADDRESS_B "upgradeTo(address)" $BRIDGE_IMPLEMENTATION_ADDRESS_B --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B - ``` + ``` ### Verification @@ -319,13 +317,10 @@ Some steps depend on whether you want to deploy on [supersim](/stack/interop/too cast call $ERC20_ADDRESS_A "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_A | cast from-wei cast call $ERC20_ADDRESS_B "balanceOf(address)" $USER_ADDRESS --rpc-url $URL_CHAIN_B | cast from-wei ``` - ## Next steps -* Deploy a [SuperchainERC20](/stack/interop/tutorials/deploy-superchain-erc20) to the Superchain -* [Learn more about SuperchainERC20](/stack/interop/superchain-erc20) +* Deploy a [SuperchainERC20](/interop/tutorials/deploy-superchain-erc20) to the Superchain +* [Learn more about SuperchainERC20](/interop/superchain-erc20) * Build a [revolutionary app](/app-developers/get-started) that uses multiple blockchains within the Superchain - - 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 100644 index 000000000..6748d6c5e --- /dev/null +++ b/public/tutorials/setup-for-erc20-upgrade.sh @@ -0,0 +1,76 @@ +#! /bin/sh + +rm -rf upgrade-erc20 +mkdir upgrade-erc20 +cd upgrade-erc20 + +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 + +cat > script/LabSetup.s.sol < Date: Thu, 17 Apr 2025 22:08:27 +0300 Subject: [PATCH 05/21] Full version --- .../custom-bridge.mdx | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index 9406f088b..a000a4ffb 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -220,8 +220,8 @@ Some steps depend on whether you want to deploy on [supersim](/interop/tools/sup ```solidity file=/public/tutorials/InteropToken.sol hash=5e728534c265028c94d60dcb6550699d filename="src/InteropToken.sol" ``` - 2. This `src/InteropToken.sol` is used for contract upgrades. - Edit it to allow our custom bridge to mint and burn tokens instead of the predeployed superchain token bridge. + 2. This `src/InteropToken.sol` is used for contract upgrades when the ERC20 contracts are at the same address. + Here we need to edit it to allow our custom bridge to mint and burn tokens instead of the predeployed superchain token bridge. * On lines 20 and 31 replace ~~`PredeployAddresses.SUPERCHAIN_TOKEN_BRIDGE`~~ with `bridgeAddress`. @@ -256,18 +256,47 @@ Some steps depend on whether you want to deploy on [supersim](/interop/tools/sup ### Deploy the actual bridge 1. Create a file, `src/CustomBridge.sol`. + This file is based on the standard `SuperchainERC20` [`SuperchainTokenBridge.sol`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol). ```solidity file=/public/tutorials/CustomBridge.sol hash=df5685626f96af100d608b5b94477888 filename="src/CustomBridge.sol" + ```
Explanation - blockchains + These are the main differences between the generic bridge and our implementation. -
+ ```solidity file=/public/tutorials/CustomBridge.sol#L14-L18 hash=fd42623685f26046337ea6d105f27e4a + ``` - ``` + The configuration is [`immutable`](https://docs.soliditylang.org/en/latest/contracts.html#immutable). + We are deploying the contract behind a proxy, so if we need to change it we can deploy a different contract. + + These parameters assume there are only two chains in the interop cluster. + If there are more, change the `There` variables to array. + + ```solidity file=/public/tutorials/CustomBridge.sol#L48-L59 hash=f85b1508d8fe5d75fd27842f41b66240 + ``` + + The constructor writes the configuration parameters. + + ```solidity file=/public/tutorials/CustomBridge.sol#L66-L69 hash=01ac207764b0d944ec4a005c7c3f01a8 + ``` + + We don't need to specify the token address, or the chain ID on the other side, because they are hardwired in this bridge. + + ```solidity file=/public/tutorials/CustomBridge.sol#L80 hash=d4ccbac2c6ce01bce65bc75242741f6e + ``` + + Emit the same log entry that would be emitted by the standard bridge. + + ```solidity file=/public/tutorials/CustomBridge.sol#L94-L96 hash=a35005bdb47c73ac667f1b4b33079776 + ``` + + Make sure any relay requests come from the correct contract on the correct chain. + +
2. Get the chainID values. From 29bf26a8f213527a5c63a3d916f653d0e90195db Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Thu, 17 Apr 2025 22:38:16 +0300 Subject: [PATCH 06/21] @zainbacchus comments --- .../tutorials/upgrade-to-superchain-erc20/_meta.json | 3 +++ .../upgrade-to-superchain-erc20/custom-bridge.mdx | 11 +++-------- 2 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 pages/interop/tutorials/upgrade-to-superchain-erc20/_meta.json diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/_meta.json b/pages/interop/tutorials/upgrade-to-superchain-erc20/_meta.json new file mode 100644 index 000000000..4bada62e7 --- /dev/null +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/_meta.json @@ -0,0 +1,3 @@ +{ + "custom-bridge": "Building a custom bridge" +} diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index a000a4ffb..faf3a6b0f 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -1,5 +1,5 @@ --- -title: Custom interoperability bridge +title: Building a custom bridge lang: en-US description: Tutorial on how to create a custom interoperability bridge. The example is a bridge when the addresses of the ERC20 contracts are not the same. topic: Interoperability @@ -10,17 +10,12 @@ content_type: article 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. - - -# Custom interoperability bridges +# Building a custom bridge ## Overview Sometimes the address of an ERC20 contract is not available on a different chain. -This means that the [standard bridge](/interop/superchain-erc20) is not an option. +This means that the [SuperchainTokenBridge](/interop/superchain-erc20) is not an option. However, if the original ERC20 contract is behind a proxy (so we can add [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) support), we can still use interop by writing our own bridge.
From 8e8648e1655b6dda3255c86ddae61214c254388f Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 18 Apr 2025 19:09:24 +0300 Subject: [PATCH 07/21] Update custom-bridge.mdx --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index faf3a6b0f..c29aa8f69 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -147,10 +147,10 @@ Some steps depend on whether you want to deploy on [supersim](/interop/tools/sup - ### Advance the user's nonoce on chain B + ### Advance the user's nonce on chain B This solution is necessary when the nonce on chain B is higher than it was on chain A when the proxy for the ERC-20 contract was installed. - To simulate this situation we send a few meaningless transactions on chain B and then see that the nonce on B is higher than the nonce on A. + To simulate this situation, we send a few meaningless transactions on chain B and then see that the nonce on B is higher than the nonce on A. ```sh cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B From 28b44967bc46fb93344faffe012cecf7688ed816 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 08:19:32 -0500 Subject: [PATCH 08/21] Maybe now it'll build --- pages/interop/tutorials/message-passing.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index 4ab7f54ce..aa275cb3f 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -1,4 +1,4 @@ ---- +=-- title: Interop message passing tutorial description: >- Learn to implement cross-chain communication in the Superchain by building a @@ -238,7 +238,7 @@ For development purposes, we'll first use autorelay mode to handle message execu 6. Create `src/GreetingSender.sol`. - ```solidity file=/public/tutorials/GreetingSender.sol#L1-L28 hash=75d197d1e1da112421785c2160f6a55a + ```solidity file=/public/tutorials/GreetingSender.sol#L1-L28 hash=9ed77001810caf52bbaa94da8b0dc5c6 ```
From fec0467ccc8c34636c0876af65913ecfe24a93bf Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 08:20:07 -0500 Subject: [PATCH 09/21] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- pages/interop/tutorials/message-passing.mdx | 38 +++++++++++---------- words.txt | 1 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index aa275cb3f..00b2c0cfb 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -1,26 +1,28 @@ -=-- +\=-- title: Interop message passing tutorial description: >- - Learn to implement cross-chain communication in the Superchain by building a - message passing system using the L2ToL2CrossDomainMessenger contract. +Learn to implement cross-chain communication in the Superchain by building a +message passing system using the L2ToL2CrossDomainMessenger contract. lang: en-US -content_type: tutorial +content\_type: tutorial topic: interop-message-passing-tutorial personas: - - protocol-developer - - chain-operator - - app-developer -categories: - - protocol - - interoperability - - cross-chain-messaging - - message-relaying - - cross-domain-messenger - - smart-contracts - - testnet - - superchain -is_imported_content: 'false' ---- + +* protocol-developer +* chain-operator +* app-developer + categories: +* protocol +* interoperability +* cross-chain-messaging +* message-relaying +* cross-domain-messenger +* smart-contracts +* testnet +* superchain + is\_imported\_content: 'false' + +*** import { Callout } from 'nextra/components' import { Steps } from 'nextra/components' diff --git a/words.txt b/words.txt index d0793a111..186d6b4ac 100644 --- a/words.txt +++ b/words.txt @@ -95,7 +95,6 @@ Devnet devnet Devnets devnets -Devs direnv DISABLETXPOOLGOSSIP disabletxpoolgossip From babb2b62d0a63ebd44caf564189b8889391c66d0 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 08:28:33 -0500 Subject: [PATCH 10/21] Maybe now? --- pages/interop/tutorials/message-passing.mdx | 38 ++++++++++----------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index 00b2c0cfb..5214c4597 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -1,28 +1,26 @@ -\=-- +--- title: Interop message passing tutorial description: >- -Learn to implement cross-chain communication in the Superchain by building a -message passing system using the L2ToL2CrossDomainMessenger contract. + Learn to implement cross-chain communication in the Superchain by building a + message passing system using the L2ToL2CrossDomainMessenger contract. lang: en-US -content\_type: tutorial +content_type: tutorial topic: interop-message-passing-tutorial personas: - -* protocol-developer -* chain-operator -* app-developer - categories: -* protocol -* interoperability -* cross-chain-messaging -* message-relaying -* cross-domain-messenger -* smart-contracts -* testnet -* superchain - is\_imported\_content: 'false' - -*** + - protocol-developer + - chain-operator + - app-developer +categories: + - protocol + - interoperability + - cross-chain-messaging + - message-relaying + - cross-domain-messenger + - smart-contracts + - testnet + - superchain +is_imported_content: 'false' +--- import { Callout } from 'nextra/components' import { Steps } from 'nextra/components' From 98340fe09d3855c01e0c0bb4092ff5fadde3ac93 Mon Sep 17 00:00:00 2001 From: Bradley Camacho <42678939+bradleycamacho@users.noreply.github.com> Date: Mon, 21 Apr 2025 09:35:51 -0700 Subject: [PATCH 11/21] Fix nav --- pages/interop/tutorials/_meta.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/interop/tutorials/_meta.json b/pages/interop/tutorials/_meta.json index 67816f137..169ee9cd7 100644 --- a/pages/interop/tutorials/_meta.json +++ b/pages/interop/tutorials/_meta.json @@ -8,5 +8,6 @@ "relay-messages-viem": "Relaying interop messages using `viem`", "contract-calls": "Making crosschain contract calls (ping pong)", "event-reads": "Making crosschain event reads (tic-tac-toe)", - "event-contests": "Deploying crosschain event composability (contests)" + "event-contests": "Deploying crosschain event composability (contests)", + "upgrade-to-superchain-erc20": "Upgrade to SuperchainERC20" } From 2fcc9d72802a13f871b25da7b23d9f35d2cee89e Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:02:38 -0500 Subject: [PATCH 12/21] Uppercase Supersim Co-authored-by: Bradley Camacho <42678939+bradleycamacho@users.noreply.github.com> --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index c29aa8f69..9ae3a34a8 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -80,7 +80,7 @@ After that happens, all new calls are sent to the new implementation. ## Instructions -Some steps depend on whether you want to deploy on [supersim](/interop/tools/supersim) or on the [development network](/interop/tools/devnet). +Some steps depend on whether you want to deploy on [Supersim](/interop/tools/supersim) or on the [development network](/interop/tools/devnet). ### Install and run Supersim From 4fd1d18cd9300f51616003233a438311dfa669be Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:36:54 -0500 Subject: [PATCH 13/21] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- public/tutorials/CustomBridge.sol | 6 ++++++ public/tutorials/setup-for-erc20-upgrade.sh | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/tutorials/CustomBridge.sol b/public/tutorials/CustomBridge.sol index 406bf15a6..a9c47996a 100644 --- a/public/tutorials/CustomBridge.sol +++ b/public/tutorials/CustomBridge.sol @@ -52,6 +52,12 @@ contract CustomBridge { uint256 chainIdThere_, address bridgeAddressThere_ ) { + if ( + tokenAddressHere_ == address(0) || + tokenAddressThere_ == address(0) || + bridgeAddressThere_ == address(0) + ) revert ZeroAddress(); + tokenAddressHere = tokenAddressHere_; tokenAddressThere = tokenAddressThere_; chainIdThere = chainIdThere_; diff --git a/public/tutorials/setup-for-erc20-upgrade.sh b/public/tutorials/setup-for-erc20-upgrade.sh index 6748d6c5e..fdfcd0c37 100644 --- a/public/tutorials/setup-for-erc20-upgrade.sh +++ b/public/tutorials/setup-for-erc20-upgrade.sh @@ -1,12 +1,13 @@ #! /bin/sh -rm -rf upgrade-erc20 -mkdir upgrade-erc20 -cd upgrade-erc20 +rm -rf upgrade-erc20 || { echo "Failed to remove directory"; exit 1; } +mkdir upgrade-erc20 || { echo "Failed to create directory"; exit 1; } +cd upgrade-erc20 || { echo "Failed to change directory"; exit 1; } if [ -z $1 ] then echo Supersim + # This is a well-known development private key – never use in production PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 URL_CHAIN_A=http://localhost:9545 URL_CHAIN_B=http://localhost:9546 From bb44dfde1bc60126bf7c0515768fa2f27f657182 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:43:54 -0500 Subject: [PATCH 14/21] Fixed hashed and code lines --- .../upgrade-to-superchain-erc20/custom-bridge.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index 9ae3a34a8..fab3186f3 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -253,7 +253,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup 1. Create a file, `src/CustomBridge.sol`. This file is based on the standard `SuperchainERC20` [`SuperchainTokenBridge.sol`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol). - ```solidity file=/public/tutorials/CustomBridge.sol hash=df5685626f96af100d608b5b94477888 filename="src/CustomBridge.sol" + ```solidity file=/public/tutorials/CustomBridge.sol hash=fc6cb08b40b7cf5bfb2b0c5793e4795e filename="src/CustomBridge.sol" ```
@@ -271,22 +271,22 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup These parameters assume there are only two chains in the interop cluster. If there are more, change the `There` variables to array. - ```solidity file=/public/tutorials/CustomBridge.sol#L48-L59 hash=f85b1508d8fe5d75fd27842f41b66240 + ```solidity file=/public/tutorials/CustomBridge.sol#L48-L65 hash=87edfdbb21fe11f573eff932d6f36d82 ``` The constructor writes the configuration parameters. - ```solidity file=/public/tutorials/CustomBridge.sol#L66-L69 hash=01ac207764b0d944ec4a005c7c3f01a8 + ```solidity file=/public/tutorials/CustomBridge.sol#L72-L75 hash=01ac207764b0d944ec4a005c7c3f01a8 ``` We don't need to specify the token address, or the chain ID on the other side, because they are hardwired in this bridge. - ```solidity file=/public/tutorials/CustomBridge.sol#L80 hash=d4ccbac2c6ce01bce65bc75242741f6e + ```solidity file=/public/tutorials/CustomBridge.sol#L86 hash=d4ccbac2c6ce01bce65bc75242741f6e ``` Emit the same log entry that would be emitted by the standard bridge. - ```solidity file=/public/tutorials/CustomBridge.sol#L94-L96 hash=a35005bdb47c73ac667f1b4b33079776 + ```solidity file=/public/tutorials/CustomBridge.sol#L100-L101 hash=142a487e877cc2cbeba8e07fce451c88 ``` Make sure any relay requests come from the correct contract on the correct chain. From 63cb35f5ef84015cf98bbb59843e8af38048eaa0 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:51:48 -0500 Subject: [PATCH 15/21] Update pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index fab3186f3..a23a9babd 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -179,9 +179,9 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ### Deploy proxies We need two contracts on each chain: an ERC20 and a bridge, and to enable future upgrades, we want to install each of those contracts behind a proxy. - We already have one contract, the ERC20 on chain A, but we need to create the other three. + You already have one contract—the ERC20 on chain A—but need to create the other three. - We have an interesting [chicken-and-egg](https://en.wikipedia.org/wiki/Chicken_or_the_egg) issue here. + There's an interesting [chicken-and-egg](https://en.wikipedia.org/wiki/Chicken_or_the_egg) issue here. To create a proxy, we need the address of the implementation contract, the one with the actual code. However, the bridge and ERC20 code needs to have the proxy addresses. One possible solution is to choose a pre-existing contract, and use that as the implementation contract until we can upgrade. From 6e200536d346da67141876c787d9d6105387807b Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:52:44 -0500 Subject: [PATCH 16/21] Update pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index a23a9babd..d62310cb0 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -95,7 +95,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ``` - ### Setup the ERC20 token on chain A + ### Set up the ERC20 token on chain A Download and run the setup script. From e578ccddf721e8f0adc8d24273209cfa382fea0e Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:53:00 -0500 Subject: [PATCH 17/21] Update pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index d62310cb0..de3f8c41c 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -203,7 +203,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ### Deploy ERC7802 contracts - We need to replace the ERC20 contracts with contracts that: + Replace the ERC20 contracts with contracts that: * Support [ERC7802](https://eips.ethereum.org/EIPS/eip-7802) and [ERC165](https://eips.ethereum.org/EIPS/eip-165). * Allow the bridge address to mint and burn tokens. From b9b74d93cdb093bf54caf012b3c4cccf7463e1ac Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 12:54:22 -0500 Subject: [PATCH 18/21] Update pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index de3f8c41c..e62aeae34 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -165,8 +165,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ### 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 ERC20 and proxy deployment. - + Create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts used for the original ERC20 and proxy deployment. ```sh mkdir custom-bridge cd custom-bridge From 21595582265fddd407294d69d82d0cbec0c7a3ec Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 14:44:53 -0500 Subject: [PATCH 19/21] lint --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index e62aeae34..2c5e3b5af 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -159,8 +159,8 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup cast send $USER_ADDRESS --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B echo -n Nonce on chain A: cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_A - echo -n Nonce on chain B: - cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B + echo -n Nonce on chain B: + cast nonce $USER_ADDRESS --rpc-url $URL_CHAIN_B ``` ### Create a Foundry project From 3efad2fcc22745d5a0789f78b30802b2cf4edf17 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 14:47:15 -0500 Subject: [PATCH 20/21] lint --- .../custom-bridge.mdx | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index 2c5e3b5af..1ed183811 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -166,6 +166,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ### Create a Foundry project Create a [Foundry](https://book.getfoundry.sh/) project and import the [OpenZeppelin](https://www.openzeppelin.com/solidity-contracts) contracts used for the original ERC20 and proxy deployment. + ```sh mkdir custom-bridge cd custom-bridge @@ -178,7 +179,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ### Deploy proxies We need two contracts on each chain: an ERC20 and a bridge, and to enable future upgrades, we want to install each of those contracts behind a proxy. - You already have one contract—the ERC20 on chain A—but need to create the other three. + You already have one contract—the ERC20 on chain A—but need to create the other three. There's an interesting [chicken-and-egg](https://en.wikipedia.org/wiki/Chicken_or_the_egg) issue here. To create a proxy, we need the address of the implementation contract, the one with the actual code. @@ -255,42 +256,40 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ```solidity file=/public/tutorials/CustomBridge.sol hash=fc6cb08b40b7cf5bfb2b0c5793e4795e filename="src/CustomBridge.sol" ``` -
- - Explanation +
+ Explanation - These are the main differences between the generic bridge and our implementation. + These are the main differences between the generic bridge and our implementation. - ```solidity file=/public/tutorials/CustomBridge.sol#L14-L18 hash=fd42623685f26046337ea6d105f27e4a - ``` - - The configuration is [`immutable`](https://docs.soliditylang.org/en/latest/contracts.html#immutable). - We are deploying the contract behind a proxy, so if we need to change it we can deploy a different contract. + ```solidity file=/public/tutorials/CustomBridge.sol#L14-L18 hash=fd42623685f26046337ea6d105f27e4a + ``` - These parameters assume there are only two chains in the interop cluster. - If there are more, change the `There` variables to array. + The configuration is [`immutable`](https://docs.soliditylang.org/en/latest/contracts.html#immutable). + We are deploying the contract behind a proxy, so if we need to change it we can deploy a different contract. - ```solidity file=/public/tutorials/CustomBridge.sol#L48-L65 hash=87edfdbb21fe11f573eff932d6f36d82 - ``` + These parameters assume there are only two chains in the interop cluster. + If there are more, change the `There` variables to array. - The constructor writes the configuration parameters. + ```solidity file=/public/tutorials/CustomBridge.sol#L48-L65 hash=87edfdbb21fe11f573eff932d6f36d82 + ``` - ```solidity file=/public/tutorials/CustomBridge.sol#L72-L75 hash=01ac207764b0d944ec4a005c7c3f01a8 - ``` + The constructor writes the configuration parameters. - We don't need to specify the token address, or the chain ID on the other side, because they are hardwired in this bridge. + ```solidity file=/public/tutorials/CustomBridge.sol#L72-L75 hash=01ac207764b0d944ec4a005c7c3f01a8 + ``` - ```solidity file=/public/tutorials/CustomBridge.sol#L86 hash=d4ccbac2c6ce01bce65bc75242741f6e - ``` + We don't need to specify the token address, or the chain ID on the other side, because they are hardwired in this bridge. - Emit the same log entry that would be emitted by the standard bridge. + ```solidity file=/public/tutorials/CustomBridge.sol#L86 hash=d4ccbac2c6ce01bce65bc75242741f6e + ``` - ```solidity file=/public/tutorials/CustomBridge.sol#L100-L101 hash=142a487e877cc2cbeba8e07fce451c88 - ``` + Emit the same log entry that would be emitted by the standard bridge. - Make sure any relay requests come from the correct contract on the correct chain. + ```solidity file=/public/tutorials/CustomBridge.sol#L100-L101 hash=142a487e877cc2cbeba8e07fce451c88 + ``` -
+ Make sure any relay requests come from the correct contract on the correct chain. +
2. Get the chainID values. From f33ceff1a54106af4c9e7046e4172c7131fb3119 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 21 Apr 2025 14:50:40 -0500 Subject: [PATCH 21/21] lint, yet another issue --- .../tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx index 1ed183811..4262d38d6 100644 --- a/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx +++ b/pages/interop/tutorials/upgrade-to-superchain-erc20/custom-bridge.mdx @@ -256,7 +256,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ```solidity file=/public/tutorials/CustomBridge.sol hash=fc6cb08b40b7cf5bfb2b0c5793e4795e filename="src/CustomBridge.sol" ``` -
+
Explanation These are the main differences between the generic bridge and our implementation. @@ -289,7 +289,7 @@ Some steps depend on whether you want to deploy on [Supersim](/interop/tools/sup ``` Make sure any relay requests come from the correct contract on the correct chain. -
+
2. Get the chainID values.