From c3f920eccfc4276ca58a151ec14b9d1c3ae0099d Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Thu, 18 Apr 2024 10:46:02 +1000 Subject: [PATCH 1/5] Document manual bridging process --- README.md | 3 ++ docs/manual-bridging.md | 108 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 docs/manual-bridging.md diff --git a/README.md b/README.md index 3bf1a1d1..c8161708 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The Immutable token bridge facilitates the transfer of assets between two chains * [Contract Deployment](#contract-deployment) * [Deployed Contract Addresses](#deployed-contract-addresses) * [Flow Rate Parameters](#flow-rate-parameters) +* [Manual Bridging Guide](#manual-bridging-guide) * [Audits](#audits) @@ -186,6 +187,8 @@ Below are the [flow rate](https://github.com/immutable/zkevm-bridge-contracts/bl | ETH | 10^18 | 10.08 | 0.0028 | 5.04 | | [IMX](https://sepolia.etherscan.io/address/0xe2629e08f4125d14e446660028bd98ee60ee69ff) | 10^18 | 68,976 | 19.16 | 34,488 | +## Manual Bridging Guide +The process to manually bridge funds from Ethereum to Immutable zkEVM by directly interacting with the bridge contracts is documented [here](docs/manual-bridging.md). However, the recommended method for bridging to and from the Immutable zkEVM is to use the [Immutable Toolkit](https://toolkit.immutable.com/bridge/) user interface. ## Audits The Immutable token bridge has been audited by [Trail of Bits](https://www.trailofbits.com/). The audit report can be found [here](./audits/Trail-of-Bits-2023-12-14.pdf). diff --git a/docs/manual-bridging.md b/docs/manual-bridging.md new file mode 100644 index 00000000..a88a868d --- /dev/null +++ b/docs/manual-bridging.md @@ -0,0 +1,108 @@ +# Manual Bridging Guide + + * [Overview](#overview) + * [Mapping ERC-20 Token](#mapping-erc-20-token) + * [Checking if a token is mapped](#checking-if-a-token-is-mapped) + * [Mapping Token](#mapping-token) + * [Depositing ERC-20 Token](#depositing-erc-20-token) + * [ERC-20 Approval](#erc-20-approval) + * [Depositing Token](#depositing-token) + * [Depositing ETH](#depositing-eth) + * [Estimating Bridge Fee](#estimating-bridge-fee) + * [Resources](#resources) + * [Bridge Addresses](#bridge-addresses) + * [Supporting Tools](#supporting-tools) + + +## Overview +This document outlines the procedure for manually transferring funds to Immutable zkEVM from Ethereum through direct interaction with the bridge contracts. This method serves as an alternative to the recommended [Immutable Toolkit](https://toolkit.immutable.com/bridge/) user interface. The document also provides details about registering (mapping) new tokens. + +This guide involves interacting directly with contracts using Etherscan. It assumes the reader has a basic familiarity with how to interact with contracts, including reading state and performing transactions, via Etherscan. + +## Mapping ERC-20 Token +For a token to be bridged through the Immutable zkEVM bridge, it first needs to be mapped (registered). The mapping process creates a representative token contract on L2 for each token contract on L1. Mapping is a permissionless process that anyone can perform. Although many tokens have already been mapped on the bridge, the token you want to bridge may not have been. Therefore, it's important to check first. + +### Checking if a token is mapped +- Navigate to the Root bridge contract on Etherscan ([mainnet](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6), [testnet](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5)) +- Select Contract → “Read as Proxy”. +- Invoke the `rootTokenToChildToken()` function by providing the L1 ERC-20 token's address. +- If the returned value isn't the `0x0` address, the token has already been mapped and you can proceed to depositing funds. If it hasn't been mapped yet, see below for how to map a token. + +### Mapping Token +- Navigate to the Root bridge contract on Etherscan ([mainnet](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6), [testnet](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5)) +- Select Contract → “Write as Proxy”. +- Execute the `mapToken()` function by providing: + - `payableAmount` : Bridge fee estimate in ETH (see [Estimating Bridge Fee](#estimating-bridge-fee) for details) + - `rootToken`: L1 ERC-20 token address +- The transaction will perform a cross-chain call to the L2, where a representative token will be created for the given L1 token. +- This process can take ~20minutes to be completed on the destination chain. You can track the progress of this cross-chain call by going to Axelarscan ([mainnet](https://axelarscan.io/), [testnet](https://testnet.axelarscan.io/)) and providing the transaction hash in the search field at the top right. + +Note: The bridge only supports standard ERC-20 tokens, so make sure your token adheres to this interface + +## Depositing ERC-20 Token +Depositing ERC-20, requires two separate steps: 1) Approving the bridge contract to transfer funds on-behalf of the user, for the given ERC-20 contract and 2) Depositing funds on the bridge + +### ERC-20 Approval +- For the ERC-20 token that you would like to deposit, navigate to its address on Etherscan +- Select Contract → “Write Contract” + - If the contract is behind a proxy (e.g. USDC), you’ll see “Write as Proxy” as an option. If so, choose this option instead + - This process assumes the ERC-20 contract is verified. If you don’t see any of the above options, it means the contract is not verified on Etherscan and should be verified before proceeding. This process is not covered in this document. +- Execute `approve()` function providing the following parameters: + - `spender` : The address of the Root bridge. For mainnet you’d use [`0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6`](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6) for testnet [`0x0d3c59c779fd552c27b23f723e80246c840100f5`](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5) + - `amount`: The amount to approve in the base unit of the token. This would be equal to or higher than the amount you intend to deposit in the next step. + e.g. 10IMX would be represented as `10000000000000000000`, because IMX uses 18 decimals, whereas 10USDC would be represented as `10000000`, because USDC uses 6 decimals. + +### Depositing Token +- Navigate to the Root bridge contract on Etherscan ([mainnet](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6), [testnet](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5)) +- Select Contract → “Write as Proxy”. +- Execute the `deposit()` function, providing the following parameters: + - `payableAmount` : Bridge fee estimate in ETH (see [Estimating Bridge Fee](#estimating-bridge-fee) for details) + - `rootToken` : Address of ERC-20 contract on L1 + - `amount`: Amount to transfer in the base unit of the token e.g. 10IMX would be represented as `10000000000000000000`, because IMX uses 18 decimals, whereas 10USDC would be represented as `10000000`, because USDC uses 6 decimals. + - **Note:** The `deposit()` function will transfer funds to the same address on L2 as the sender executing this transaction on L1. If however, you’d like to deposit to a different address use the `depositTo` function, which will take an additional parameter `receiver` which is the address of the receiver on L2. +- The transaction will first transfer the specified amount of ERC-20 from the user to the bridge and perform a cross-chain call to the L2 to mint a corresponding amount of tokens to the intended receiver address. +- This process can take ~20minutes to be completed on the destination chain. You can track the progress of this cross-chain call by going to Axelarscan ([mainnet](https://axelarscan.io/), [testnet](https://testnet.axelarscan.io/)) and providing the transaction hash in the search field at the top right. +- Once completed, the funds will be available in the recipient's address on L2. + +Note: The bridge only supports standard ERC-20 tokens, so make sure your token adheres to this interface + +## Depositing ETH +- Navigate to the Root bridge contract on Etherscan ([mainnet](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6), [testnet](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5)) +- Select Contract → “Write as Proxy”. +- Execute the `depositETH()` function, providing the following parameters: + - `payableAmount` : This amount should be the sum of two things + - Amount to transfer in ETH + - Bridge fee estimate in ETH (see [Estimating Bridge Fee](#estimating-bridge-fee) for details) + - `amount`: Amount to transfer in wei e.g. 1 ETH would be represented as `100000000000000` + - **Note:** The `depositETH()` function will transfer funds to the same address on L2 as the sender executing this transaction on L1. If however, you’d like to deposit to a different address use the `depositToETH`function, which will take an additional parameter `receiver` which is the address of the receiver on L2. +- The transaction will first transfer the specified amount of ERC-20 from the user to the bridge and perform a cross-chain call to the L2 to mint a corresponding amount of tokens to the intended receiver address. +- This process can take ~20minutes to be completed on the destination chain. You can track the progress of this cross-chain call by going to Axelarscan ([mainnet](https://axelarscan.io/), [testnet](https://testnet.axelarscan.io/)) and providing the transaction hash in the search field at the top right. +- Once completed, the funds will be available in the recipient's address on L2. + +## Estimating Bridge Fee +Estimates for bridge fees are obtained through Axelar's API. These fees encompass the costs for Axelar validators to validate transactions, as well as the gas costs incurred when executing a transaction on the destination chain. + +- Go to Axelar’s API docs [here](https://docs.axelarscan.io/gmp#estimateGasFee) +- Select the environment that you’d like a quote for (Testnet or Mainnet) +- Execute the `estimateGasFee` endpoint, providing the following parameter details + - Source Chain: if mainnet, use `Ethereum` if testnet use `Sepolia` + - Destination Chain: `immutable` + - Gas Limit: `200000` + - Gasa Multiplier: `1.1` +- The result will be the gas cost in wei. If you need the amount in ETH, use the [Wei to Eth converter](https://www.eth-to-wei.com/) to convert the value. + + +## Resources +### Bridge Addresses +- Mainnet: + - Ethereum (Root Bridge): [0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6](https://etherscan.io/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6) + - Immutable zkEVM (Child Bridge): [0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6](https://explorer.immutable.com/address/0xBa5E35E26Ae59c7aea6F029B68c6460De2d13eB6) +- Testnet: + - Sepolia (Root Bridge): [0x0d3c59c779fd552c27b23f723e80246c840100f5](https://sepolia.etherscan.io/address/0x0d3c59c779fd552c27b23f723e80246c840100f5) + - Immutable zkEVM Testnet (Child Bridge): [0x0D3C59c779Fd552C27b23F723E80246c840100F5](https://explorer.testnet.immutable.com/address/0x0D3C59c779Fd552C27b23F723E80246c840100F5) + +### Supporting Tools +- [Convert Ether to Wei](https://www.eth-to-wei.com/) +- [Axelar API](https://docs.axelarscan.io/gmp#estimateGasFee) +- [Axelarscan Mainnet](https://axelarscan.io/) +- [Axelarscan Testnet](https://testnet.axelarscan.io/) \ No newline at end of file From b36fa99c697836d377caf7fc52c34e1d8b7934cc Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Thu, 18 Apr 2024 11:12:56 +1000 Subject: [PATCH 2/5] Configure file permissions for Foundry cheatcodes --- foundry.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/foundry.toml b/foundry.toml index 6c372780..be2a47fa 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,3 +6,4 @@ solc-version = "0.8.19" # Reason for why this flag is required can be found here: https://github.com/foundry-rs/foundry/issues/7607 unchecked_cheatcode_artifacts = true # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +fs_permissions = [{ access = "read", path = "./"}] From 6d0104586ffffa60adf0eeef279c9d3a02f88bfc Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Thu, 18 Apr 2024 14:31:34 +1000 Subject: [PATCH 3/5] Fix Foundry version to avoid latest nightly bug --- .github/workflows/e2e.yml | 2 +- .github/workflows/test.yml | 2 +- foundry.toml | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 099f4a7b..554b9640 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -21,7 +21,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly + version: nightly-e4ab9f460e92586fc4d4f6c9e00d8cda0c2dabf0 - name: Run install uses: borales/actions-yarn@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c46f5c72..2126db2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly + version: nightly-e4ab9f460e92586fc4d4f6c9e00d8cda0c2dabf0 - name: Run Forge build run: | diff --git a/foundry.toml b/foundry.toml index be2a47fa..0e7d2ae8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,5 +5,4 @@ libs = ["lib"] solc-version = "0.8.19" # Reason for why this flag is required can be found here: https://github.com/foundry-rs/foundry/issues/7607 unchecked_cheatcode_artifacts = true -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options -fs_permissions = [{ access = "read", path = "./"}] +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options \ No newline at end of file From 4c0cf52c539b99e5f45ba55bd603713d69378efa Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Thu, 18 Apr 2024 16:40:39 +1000 Subject: [PATCH 4/5] Use fine-grained token with single scope --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c1ba6d25..38b7523e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -5,7 +5,7 @@ on: env: FOUNDRY_PROFILE: ci - GITHUB_TOKEN: ${{ secrets.PLATFORM_SA_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.ZKEVM_BRIDGE_CONTRACTS_GITHUB_TOKEN }} jobs: check: From 05edfca318f3a87f58bbd4ab195c3a252ebbcc7f Mon Sep 17 00:00:00 2001 From: Ermyas Abebe Date: Fri, 19 Apr 2024 16:01:36 +1000 Subject: [PATCH 5/5] Mention that native ETH and WETH deposits are equivalent --- docs/manual-bridging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual-bridging.md b/docs/manual-bridging.md index a88a868d..e3476611 100644 --- a/docs/manual-bridging.md +++ b/docs/manual-bridging.md @@ -77,7 +77,7 @@ Note: The bridge only supports standard ERC-20 tokens, so make sure your token a - **Note:** The `depositETH()` function will transfer funds to the same address on L2 as the sender executing this transaction on L1. If however, you’d like to deposit to a different address use the `depositToETH`function, which will take an additional parameter `receiver` which is the address of the receiver on L2. - The transaction will first transfer the specified amount of ERC-20 from the user to the bridge and perform a cross-chain call to the L2 to mint a corresponding amount of tokens to the intended receiver address. - This process can take ~20minutes to be completed on the destination chain. You can track the progress of this cross-chain call by going to Axelarscan ([mainnet](https://axelarscan.io/), [testnet](https://testnet.axelarscan.io/)) and providing the transaction hash in the search field at the top right. -- Once completed, the funds will be available in the recipient's address on L2. +- Once completed, the funds will be available in the recipient's address on L2. Note that whether you deposit native ETH using this method or wrapped ETH via the ERC-20 token deposit method, the wrapped ETH token received on Layer 2 will be the same. ## Estimating Bridge Fee Estimates for bridge fees are obtained through Axelar's API. These fees encompass the costs for Axelar validators to validate transactions, as well as the gas costs incurred when executing a transaction on the destination chain.