|
| 1 | +# Bridged USDC standard implementation for Orbit chains |
| 2 | + |
| 3 | +## Background |
| 4 | + |
| 5 | +Circle’s Bridged USDC Standard is a specification and process for deploying a bridged form of USDC on EVM blockchains with optionality for Circle to seamlessly upgrade to native issuance in the future. |
| 6 | + |
| 7 | +We provide custom USDC gateway implementation (for parent and child chain) that follows Bridged USDC Standard. These contracts can be used by new Orbit chains. This solution will NOT be used in existing Arbitrum chains. On parent chain contract `L1USDCGateway` is used in case child chain uses ETH as native currency, or `L1OrbitUSDCGateway` in case child chain uses custom fee token. On child chain `L2USDCGateway` is used. For the USDC token contracts, Circle's referent implementation is used. |
| 8 | + |
| 9 | +This doc describes how to deploy USDC bridge compatible with both Arbitrum's token bridge and Circle’s Bridged USDC Standard.Also steps for transition to native USDC issuance are provided. |
| 10 | + |
| 11 | +## Assumptions |
| 12 | + |
| 13 | +It is assumed there is already USDC token deployed and used on the parent chain. |
| 14 | + |
| 15 | +Also, it is assumed the standard Orbit chain ownership system is used, ie. UpgradeExecutor is the owner of the ownable contracts and there is an EOA or multisig which has executor role on the UpgradeExecutor. |
| 16 | + |
| 17 | +Note: throughout the docs and code, terms `L1` and `L2` are used interchangeably with `parent chain` and `child chain`. They have the same meaning, ie. if an Orbit chain is deployed on top of ArbitrumOne then ArbitrumOne is `L1`/`parent chain`, while Orbit is `L2`/`child chain` |
| 18 | + |
| 19 | +## Deployment steps |
| 20 | + |
| 21 | +Checkout target code, install dependencies and build |
| 22 | + |
| 23 | +``` |
| 24 | +cd token-bridge-contracts |
| 25 | +yarn install |
| 26 | +yarn build |
| 27 | +``` |
| 28 | + |
| 29 | +Populate .env based on `env.example` in this directory |
| 30 | + |
| 31 | +``` |
| 32 | +PARENT_RPC= |
| 33 | +PARENT_DEPLOYER_KEY= |
| 34 | +CHILD_RPC= |
| 35 | +CHILD_DEPLOYER_KEY= |
| 36 | +L1_ROUTER= |
| 37 | +L2_ROUTER= |
| 38 | +INBOX= |
| 39 | +L1_USDC= |
| 40 | +## OPTIONAL arg. If set, script will register the gateway, otherwise it will store TX payload in a file |
| 41 | +ROLLUP_OWNER_KEY= |
| 42 | +``` |
| 43 | + |
| 44 | +Run the script |
| 45 | + |
| 46 | +``` |
| 47 | +yarn deploy:usdc-token-bridge |
| 48 | +``` |
| 49 | + |
| 50 | +Script will do the following: |
| 51 | + |
| 52 | +- load deployer wallets for L1 and L2 |
| 53 | +- register L1 and L2 networks in SDK |
| 54 | +- deploy new L1 and L2 proxy admins |
| 55 | +- deploy bridged (L2) USDC using the Circle's implementation |
| 56 | +- init L2 USDC |
| 57 | +- deploy L1 USDC gateway |
| 58 | +- deploy L2 USDC gateway |
| 59 | +- init both gateways |
| 60 | +- if `ROLLUP_OWNER_KEY` is provided, register the gateway in the router through the UpgradeExecutor |
| 61 | +- if `ROLLUP_OWNER_KEY` is not provided, prepare calldata and store it in `registerUsdcGatewayTx.json` file |
| 62 | +- set minter role to L2 USDC gateway with max allowance |
| 63 | + |
| 64 | +Now new USDC gateways can be used to deposit/withdraw USDC. And everything is in place to support transtition to native USDC issuance, in case Circle and Orbit chain owner agree to it. |
| 65 | + |
| 66 | +## Transition to native USDC |
| 67 | + |
| 68 | +Once transition to native USDC is agreed on, following steps are required: |
| 69 | + |
| 70 | +- L1 gateway owner pauses deposits on parent chain by calling `pauseDeposits()` |
| 71 | +- L2 gateway owner pauses withdrawals on child chain by calling `pauseWithdrawals()` |
| 72 | +- master minter removes the minter role from the child chain gateway |
| 73 | + - NOTE: there should be no in-flight deposits when minter role is revoked. If there are any, they should be finalized first. That can be done by anyone by claiming the claimable failed retryable tickets which do the USDC depositing |
| 74 | +- L1 gateway owner sets Circle's account as burner on the parent chain gateway using `setBurner(address)` |
| 75 | +- L1 gateway owner reads the total supply of USDC on the child chain, and then invokes `setBurnAmount(uint256)` on the parent child gateway where the amount matches the total supply |
| 76 | +- USDC masterMinter gives minter role with 0 allowance to L1 gateway, so the burn can be executed |
| 77 | +- on the child chain, L2 gateway owner calls the `setUsdcOwnershipTransferrer(address)` to set the account (provided and controlled by Circle) which will be able to transfer the bridged USDC ownership and proxy admin |
| 78 | +- if not already owned by gateway, L2 USDC owner transfers ownership to gateway, and proxy admin transfers admin rights to gateway |
| 79 | +- Circle uses `usdcOwnershipTransferrer` account to trigger `transferUSDCRoles(address)` which will set caller as USDC proxy admin and will transfer USDC ownership to the provided address |
| 80 | +- Circle calls `burnLockedUSDC()` on the L1 gateway using `burner` account to burn the `burnAmount` of USDC |
| 81 | + - remaining USDC will be cleared off when remaining in-flight USDC withdrawals are executed, if any |
| 82 | + - L1 gateway owner is trusted to not frontrun this TX to modify the burning amount |
0 commit comments