{"id":19096758,"url":"https://github.com/makerdao/arbitrum-token-bridge","last_synced_at":"2025-04-30T14:15:01.844Z","repository":{"id":245867088,"uuid":"792499858","full_name":"makerdao/arbitrum-token-bridge","owner":"makerdao","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-17T11:17:46.000Z","size":758,"stargazers_count":5,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2024-09-17T14:08:09.092Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/makerdao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":"audit/20240703-cantina-report-maker-arbitrum-token-bridge.pdf","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-26T19:52:05.000Z","updated_at":"2024-08-23T11:15:02.000Z","dependencies_parsed_at":"2024-07-18T13:46:09.508Z","dependency_job_id":"978ba766-ca37-4114-90d2-0f8a50987631","html_url":"https://github.com/makerdao/arbitrum-token-bridge","commit_stats":null,"previous_names":["makerdao/arbitrum-token-bridge"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makerdao%2Farbitrum-token-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makerdao%2Farbitrum-token-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makerdao%2Farbitrum-token-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/makerdao%2Farbitrum-token-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/makerdao","download_url":"https://codeload.github.com/makerdao/arbitrum-token-bridge/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249514474,"owners_count":21284555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T03:37:40.117Z","updated_at":"2025-04-18T15:33:59.901Z","avatar_url":"https://github.com/makerdao.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MakerDAO Arbitrum Token Bridge\n\n## Overview\n\nThe Arbitrum Token Bridge is a [custom Arbitrum bridge](https://docs.arbitrum.io/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway) that allows users to deposit a supported token to Arbitrum and withdraw it back to Ethereum. It operates similarly to the previously deployed [Arbitrum Dai Bridge](https://github.com/makerdao/arbitrum-dai-bridge) and relies on the same security model but allows MakerDAO governance to update the set of tokens supported by the bridge.\n\n## Contracts\n\n- `L1TokenGateway.sol` - L1 side of the bridge. Transfers the deposited tokens into an escrow contract. Transfer them back to the user upon receiving a withdrawal message from the `L2TokenGateway`.\n- `L2TokenGateway.sol` - L2 side of the bridge. Mints new L2 tokens after receiving a deposit message from `L1TokenGateway`. Burns L2 tokens when withdrawing them to L1.\n\nThe `L1TokenGateway` and `L2TokenGateway` contracts use the ERC-1822 UUPS pattern for upgradeability and the ERC-1967 proxy storage slots standard. It is important that the `TokenGatewayDeploy` library sequences be used for deploying.\n\n### External dependencies\n\n- The L2 implementations of the bridged tokens are not provided as part of this repository and are assumed to exist in external repositories. It is assumed that only simple, regular ERC20 tokens will be used with this bridge. In particular, the supported tokens are assumed to revert on failure (instead of returning false) and do not execute any hook on transfer.\n- The [escrow contract](https://etherscan.io/address/0xA10c7CE4b876998858b1a9E12b10092229539400#code) holds the bridged tokens on L1. This is assumed to be the same escrow as the one used by the Arbitrum Dai Bridge.\n- The [`L1GovernanceRelay`](https://etherscan.io/address/0x9ba25c289e351779E0D481Ba37489317c34A899d#code) \u0026 [`L2GovernanceRelay`](https://arbiscan.io/address/0x10E6593CDda8c58a1d0f14C5164B376352a55f2F#code) allow governance to exert admin control over the deployed L2 contracts. These contracts have been previously deployed to control the Arbitrum Dai Bridge.\n\n## User flows\n\n### L1 to L2 deposits\n\nTo deposit a given amount of a supported token into Arbitrum, Alice calls `outboundTransfer[CustomRefund]()` on the `L1TokenGateway`. This call locks Alice's tokens into an escrow contract and creates an [Arbitrum Retryable Ticket](https://docs.arbitrum.io/how-arbitrum-works/arbos/l1-l2-messaging#retryable-tickets) which instructs the Arbitrum sequencer to asynchroneously call `finalizeInboundTransfer()` on `L2TokenGateway`. That latter call mints an equivalent amount of L2 tokens for Alice.\n\nNote that the `outboundTransfer[CustomRefund]` payable function requires a number of gas parameters to be provided, and must be called with some corresponding amount of ETH as `msg.value`. An example of how to calculate these parameters is provided in `script/Deposit.s.sol`.\n\n### L2 to L1 withdrawals\n\nTo withdraw her tokens back to L1, Alice calls `outboundTransfer()` on the `L2TokenGateway`. This call burns Alice's tokens and performs a call to the [ArbSys](https://docs.arbitrum.io/how-arbitrum-works/arbos/l2-l1-messaging#client-flow) precompile contract, which enables anyone to call `finalizeInboundTransfer()` on `L1TokenGateway` after the ~7 days security period. That latter call releases an equivalent amount of L1 tokens from the escrow to Alice.\n\n## Upgrades\n\n### Upgrade the bridge implementation(s)\n\n`L1TokenGateway` and/or `L2TokenGateway` implementations can be upgraded by calling the `upgradeToAndCall` function of their inherited `UUPSUpgradeable` parent. Special care must be taken to ensure any deposit or withdrawal that is in transit at the time of the upgrade will still be able to get confirmed on the destination side.\n\n### Upgrade to a new bridge (and deprecate this bridge)\n\nAs an alternative upgrade mechanism, a new bridge can be deployed to be used with the escrow.\n\n1. Deploy the new token bridge and connect it to the same escrow as the one used by this bridge. The old and new bridges can operate in parallel.\n2. Optionally, deprecate the old bridge by closing it. This involves calling `close()` on both the `L1TokenGateway` and `L2TokenGateway` so that no new outbound message can be sent to the other side of the bridge. After all cross-chain messages are done processing (can take ~1 week), the bridge is effectively closed and governance can consider revoking the approval to transfer funds from the escrow on L1 and the token minting rights on L2.\n\n### Upgrade a single token to a new bridge\n\nTo migrate a single token to a new bridge, follow the steps below:\n\n1. Deploy the new token bridge and connect it to the same escrow as the one used by this bridge.\n2. Unregister the token on `L1TokenGateway`, removing the ability to initiate new L1 to L2 transfers for that token.\n3. Wait a few days to give a chance for any failed L1 to L2 transfer to be retried.\n4. Execute an L2 spell to unregister the token on `L2TokenGateway`, removing the ability to initiate new L2 to L1 transfers for that token.\n\nNote that step 3 is required because unregistering the token on `L2TokenGateway` not only removes the ability to initiate new L2 to L1 transfers but also causes the finalization of pending L1 to L2 transfers to revert. This is a point of difference with the implementation of the Arbitrum generic-custom gateway, where a missing L2 token triggers a withdrawal of the tokens back to L1 instead of a revert.\n\n## Tests\n\n### OZ upgradeability validations\n\nThe OZ validations can be run alongside the existing tests:  \n`VALIDATE=true forge test --ffi --build-info --extra-output storageLayout`\n\n## Deployment\n\n### Declare env variables\n\nAdd the required env variables listed in `.env.example` to your `.env` file, and run `source .env`.\n\nMake sure to set the `L1` and `L2` env variables according to your desired deployment environment.\n\nMainnet deployment:\n\n```\nL1=mainnet\nL2=arbitrum_one\n```\n\nTestnet deployment:\n\n```\nL1=sepolia\nL2=arbitrum_one_sepolia\n```\n\n### Deploy the bridge\n\nDeploy the L1 and L2 tokens (not included in this repo) that must be supported by the bridge then fill in the addresses of these tokens in `script/input/{chainId}/config.json` as two arrays of address strings under the `tokens` key for both the L1 and L2 domains. On testnet, if the `tokens` key is missing for a domain, mock tokens will automatically be deployed for that domain.\n\nThe following command deploys the L1 and L2 sides of the bridge:\n\n```\nforge script script/Deploy.s.sol:Deploy --slow --multi --broadcast --verify\n```\n\n### Initialize the bridge\n\nOn mainnet, the bridge should be initialized via the spell process. On testnet, the bridge initialization can be performed via the following command:\n\n```\nforge script script/Init.s.sol:Init --slow --multi --broadcast\n```\n\n### Test the deployment\n\nMake sure the L1 deployer account holds at least 10^18 units of the first token listed under `\"l1Tokens\"` in `script/output/{chainId}/deployed-latest.json`. To perform a test deposit of that token, use the following command:\n\n```\nforge script script/Deposit.s.sol:Deposit --slow --multi --broadcast\n```\n\nTo subsequently perform a test withdrawal, use the following command:\n\n```\nforge script script/Withdraw.s.sol:Withdraw --slow --multi --broadcast --skip-simulation\n```\n\nNote that the `--skip-simulation` flag is required due to usage of custom Arb OpCodes in ArbSys.\n\nThe message can be relayed manually to L1 using [this Arbitrum tool](https://retryable-dashboard.arbitrum.io/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakerdao%2Farbitrum-token-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmakerdao%2Farbitrum-token-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmakerdao%2Farbitrum-token-bridge/lists"}