> For the complete documentation index, see [llms.txt](https://docs.carbondefi.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.carbondefi.xyz/contracts-and-functions/carbonvortexbridge-functions/write-functions/bridge.md).

# Bridge()

{% code overflow="wrap" lineNumbers="true" %}

```solidity
    function bridge(uint256 amount) external payable override nonReentrant returns (uint256) {
```

{% endcode %}

This function allows anyone to trigger a bridge transaction where the consolidated fees (typically in the `finalTargetToken` format) will be sent to the Ethereum blockchain.

### **Function Arguments**

<table><thead><tr><th width="196">Name</th><th width="168.33333333333331">Type</th><th>Description</th></tr></thead><tbody><tr><td>amount</td><td>uint256</td><td>This value represents the amount of tokens to bridge over from the contract on the selected chain to Ethereum</td></tr><tr><td>bridge</td><td>uint256</td><td>This value represents the bridge fee in native gas token units</td></tr></tbody></table>

This function requires 2 values to successfully work:

1. Amount

   This value represents the amount of `finalTargetToken` you would like to bridge over. When passing the value 0, it will try to bridge the entire available amount.\
   **How to identify the available amount?**

   1. Visit the CarbonVortex contract ([follow the address in the relevant chain](/contracts-and-functions/contracts.md))
   2. `Read` `finalTargetToken`  to find the address of the token to bridge over (if the address is null, check `targetToken` address instead)
   3. Check the contract balance for the token address<br>
2. Bridge&#x20;

   This value represents the **bridge fee** which is required in order to pay for the 2nd transaction to claim the bridged amount on the ethereum blockchain. \
   **How to identify the required bridge fee?**\
   Different chains have different bridge implementation. Follow this step closely to find the correct value to use

#### Overview

The prove/finalize script lives in the [bancorprotocol/carbon-vortex-bridge](https://github.com/bancorprotocol/carbon-vortex-bridge) repo. Clone it and install dependencies:

{% tabs %}
{% tab title="Celo" %}

#### Completing a Celo → Ethereum Vortex Bridge Withdrawal

> <sub>Operator guide for the canonical OP-stack Celo Vortex bridge (</sub><sub>VortexOpStackBridge</sub><sub>).</sub>

#### Overview

Celo is an **OP-stack L2**, and the Vortex bridges WETH to Ethereum over the **canonical Standard Bridge**. A canonical L2→L1 withdrawal has **three on-chain steps** and an unavoidable **\~7-day** delay:

```
(L2: Celo)            (L1: Ethereum)                         (L1: Ethereum)
  1. bridge()    ──►    2. prove        ── wait ~7 days ──►    3. finalize
  burns WETH,          submits a Merkle proof of the          releases WETH to
  queues message       withdrawal to OptimismPortal2          the vault
```

* **Step 1** is a single contract write on Celo (a Celoscan "Write").
* **Steps 2 & 3** happen on Ethereum L1 and are driven by a script in the repo,&#x20;
* scripts/withdraw\.mjs (pnpm withdraw \<tx>) — the proof construction is far&#x20;
* too complex to do by hand in a block explorer.

The bridge is **permissionless** to prove/finalize: any funded EOA can run steps 2 and 3, and the WETH still lands in the vault regardless of who submits them.

***

#### Setup

The prove/finalize script lives in the [bancorprotocol/carbon-vortex-bridge](https://github.com/bancorprotocol/carbon-vortex-bridge) repo. Clone it and install dependencies:

```
 git clone https://github.com/bancorprotocol/carbon-vortex-bridge
cd carbon-vortex-bridge
pnpm install
```

Requires **Node ≥ 18** and **pnpm**. Nothing else — scripts/withdraw\.mjs is a self-contained [viem](https://viem.sh) script (no Hardhat/Foundry needed for the withdrawal flow).

***

#### Prerequisites

* A signing key for the L1 transactions, passed explicitly as PRIVATE\_KEY on the command line&#x20;
* (see the commands below).
* That EOA needs **ETH on Ethereum mainnet** for gas (prove ≈ 350k gas; finalize is similar or smaller).
* For Step 1, just **CELO** for gas on Celo.

***

#### Step 1 – Initiate the bridge on Celo

1. Open the VortexOpStackBridge proxy on Celoscan → **Contract → Write as Proxy**:&#x20;
2. [0x753425e27aE996f0DB75C759C6E5EBa5c74D6aAF](https://celoscan.io/address/0x753425e27aE996f0DB75C759C6E5EBa5c74D6aAF#writeProxyContract)
3. Connect your wallet.
4. Find the bridge function. It takes one argument, amount:
   1. payableAmount (CELO) – **leave** 0 (this bridge does not take a native fee; sending any value reverts).
   2. amount – 0 bridges the entire WETH balance held by the Vortex; or pass a specific amount (wei).
5. Click **Write** and sign. This burns the L2 WETH and queues the canonical withdrawal message.

6\. **Copy the transaction hash** – you need it for steps 2 and 3.

> <sub>The L2 token being withdrawn is the bridged WETH</sub> <sub></sub><sub>0xD221812de1BD094f35587EE8E174B07B6167D9Af</sub><sub>; on L1 it is released as canonical WETH</sub> <sub></sub><sub>0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</sub> <sub></sub><sub>to the vault.</sub>

***

#### Step 2 – Prove the withdrawal on Ethereum

The withdrawal can only be proven once a **dispute game** covering its L2 block has been posted to L1 by Celo's proposer (OP-Succinct validity proofs). This usually takes **tens of minutes to a few hours**.

From the repo root, check status any time (read-only, no key):

```
pnpm withdraw 0x6a5069be8e8f8a17107cacfb1502b3532d6b40eb85ce23e17eab0de7edffe7fe
```

When it reads ready-to-prove, run the **same command with your key** to send the prove tx:

```
PRIVATE_KEY=0x<your-key> pnpm withdraw 0x6a5069be8e8f8a17107cacfb1502b3532d6b40eb85ce23e17eab0de7edffe7fe
```

The script is **status-aware** — with a key set, the *same command* takes the correct next action at each stage; without a key it only reports status:

| Status it prints    | What it does (with PRIVATE\_KEY)                                    |
| ------------------- | ------------------------------------------------------------------- |
| waiting-to-prove    | No game covers the L2 block yet. **Re-run later.**                  |
| ready-to-prove      | Builds the proof and send the prove tx, then waits for the receipt. |
| waiting-to-finalize | Proven – prints the remaining time in the last 7-day window.        |
| ready-to-finalize   | Sends the finalize tx (see Step 4).                                 |
| finalized           | Done – WETH is in the vault.                                        |

On ready-to-prove it submits proveWithdrawalTransaction on the L1 portal and reports:

```
status: ready-to-prove
→ ready to prove against game index=35063 (l2Block=69633498)
   prove tx sent: 0x3812198829...
   prove success in block 25323683. 7-day finalize window starts now.
```

**The 7-day proof-maturity timer starts at the moment the prove tx confirms.**

***

#### Step 3 — Wait \~7 days

Re-running the read-only command during this period prints the countdown:

```
status: waiting-to-finalize
→ proven, in the 7-day window. ~168.0h left (ready at 2026-06-22T15:04:56.784Z).
```

Nothing to do but wait — you don't need to keep a process running, just re-run later.

***

#### Step 4 — Finalize on Ethereum

After the window opens, run the command with your key again:

```
PRIVATE_KEY=0x<your-key> pnpm withdraw 0x6a5069be8e8f8a17107cacfb1502b3532d6b40eb85ce23e17eab0de7edffe7fe
```

It detects ready-to-finalize, submits finalizeWithdrawalTransaction, and the **WETH is released to the vault** 0x60917e542aDdd13bfd1a7f81cD654758052dAdC4:

```
status: ready-to-finalize
   finalize tx sent: 0x...
   finalize success in block .... L1 WETH released to the vault.
```

That completes the bridge.

***

#### Script Configuration

scripts/withdraw\.mjs reads these from the environment (pass them on the command line; nothing is loaded from .env):

| Env var            | Purpose                                                                       | Default                               |
| ------------------ | ----------------------------------------------------------------------------- | ------------------------------------- |
| PRIVATE\_KEY       | Signer key for the L1 prove/finalize tx (0x-prefixed). Omit to run read-only. | —                                     |
| L1\_RPC            | Ethereum mainnet RPC (reads + sends txs)                                      | <https://ethereum-rpc.publicnode.com> |
| CELO\_RPC          | Celo RPC (receipt + status)                                                   | <https://forno.celo.org>              |
| CELO\_ARCHIVE\_RPC | Celo **archive** RPC for eth\_getProof (prove step)                           | <https://celo.drpc.org>               |
|                    |                                                                               |                                       |

Passing no PRIVATE\_KEY runs read-only — it just prints the current status without sending anything.

***

#### Reference addresses

| Contract                        | Network | Address                                    |
| ------------------------------- | ------- | ------------------------------------------ |
| VortexOpStackBridge (proxy)     | Celo    | 0x753425e27aE996f0DB75C759C6E5EBa5c74D6aAF |
| Vortex                          | Celo    | 0xD9D89e8A0dfE549e5B424D5b511cB3b84A764857 |
| WETH (withdraw token, L2)       | Celo    | 0xD221812de1BD094f35587EE8E174B07B6167D9Af |
| L2 Standard Bridge (predeploy)  | Celo    | 0x4200000000000000000000000000000000000010 |
| L2ToL1MessagePasser (predeploy) | Celo    | 0x4200000000000000000000000000000000000016 |
| OptimismPortal2                 | L1      | 0xc5c5D157928BDBD2ACf6d0777626b6C75a9EAEDC |
| DisputeGameFactory              | L1      | 0xFbAC162162f4009Bb007C6DeBC36B1dAC10aF683 |
| L1 Standard Bridge              | L1      | 0x9C4955b92F34148dbcfDCD82e9c9eCe5CF2badfe |
| WETH (released to vault, L1)    | L1      | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
| Vault (final recipient)         | L1      | 0x60917e542aDdd13bfd1a7f81cD654758052dAdC4 |

***

#### Troubleshooting

* eth\_getProof → **"Missing or invalid parameters"** – forno does not serve eth\_getProof. Use an&#x20;
* archive endpoint (the default celo.drpc.org, or rpc.ankr.com/celo). This is why the prove step uses&#x20;
* CELO\_ARCHIVE\_RPC separately from CELO\_RPC.
* **"Dispute game not found" / stuck on waiting-to-prove** – the proposer hasn't posted a game covering&#x20;
* your L2 block yet. Wait and re-run; this is normal for the first tens of minutes to a couple of hours.
* **Prove/finalize tx fails or never sends** – confirm the signing EOA has mainnet ETH for gas, and&#x20;
* that you passed PRIVATE\_KEY (without it the script is read-only by design).
* **Want to check status without spending gas** – run the command with no PRIVATE\_KEY; it stays&#x20;
* read-only and just prints the current status.

***

#### Doing it manually (raw contract calls)

To drive the flow yourself without the script — every L1/L2 function call, on which contract, and how to source each parameter — see the contract-level reference: [celo-bridge-withdrawal-technical.md](https://celo-bridge-withdrawal-technical.md).<br>
{% endtab %}

{% tab title="Coti" %}
**On Coti**

1. View the wETH token page on [readProxy on cotiscan](https://mainnet.cotiscan.io/address/0x639aCc80569c5FC83c6FBf2319A6Cc38bBfe26d1?tab=read_proxy)
2. Find the read function called `quoteGasPayment`\
   ![](/files/PdMuVGtJCrMTn3PHtFdU)
3. Input `1` to indicate Ethereum as the destination chain
4. Click `Read` to discover the required gas cost\
   ![](/files/dCalJcUV08AsrgwN4Vi6)

Once you have this information available, paste it on the `Send native COTI` field for the `bridge` function\
![](/files/Z4lis9uiEjHEnWsk36yb)<br>
{% endtab %}

{% tab title="BNB" %}
**On BNB**\
Bridge is utilizing the "across" bridge infrastructure which knows how to calculate all required fees during execution.&#x20;

1. **bridge fee** value should be set to zero (0)
2. Click "write" and initiate the briding process.

Thats it.

{% endtab %}

{% tab title="All other chains" %}
**On Sei, Base, Tac, Fantom, Mantle, Linea, Blast, Telos and Iota:**\
Bridge supports automatic "claim" and as such you are required to include the gas fee needed for the following transaction on the ethereum blockchain. In order for the transaction to go through successfully, you must identify the required gas fee by following this process:

1. Visit [StarGate interface](https://stargate.finance/bridge)
2. Connect wallet
3. Attempt to bridge any amount of tokens (you want to see the transaction details on your wallet signed page DO NOT SIGN THE TRANSACTION)\
   ![](/files/zE6zuENNnD1zL08PM8YB)
4. In the wallet confirmation popup, find the value under "Param #2" as outlined in the image (you can "Cancel" the wallet popup after coping the required value)\
   ![](/files/iJL2fsVAjAjw68wIUOjq)
5. Copy the indicated value and paste it into the `bridge` field of the transaction. Make sure you normalize the decimal correctly to the gas token of the chain. For example, on Base, the input value should be in "ETH" units i.e. 0.000666588492010473 and not as indicated in WEI.

### Example

<pre class="language-solidity" data-overflow="wrap"><code class="lang-solidity">bridge(
0, //zero will try to bridge the full available amount
<strong>0.000666588492010473 //bridge fee (some bridge require this value in WEI)
</strong></code></pre>

{% endtab %}
{% endtabs %}
