> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celo.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdrawing CELO to Ethereum

> Programmatically withdraw CELO from Celo back to Ethereum through the native bridge using the viem OP Stack

This guide is for developers who want to withdraw CELO from Celo back to Ethereum programmatically with the [viem OP Stack](https://viem.sh/op-stack). If you just want to bridge with a UI, use [Superbridge](/home/bridged-tokens/bridges).

The example below runs against the testnets, withdrawing CELO from Celo Sepolia to Ethereum Sepolia.

## Steps to withdraw CELO

Withdrawals require the user to submit three transactions:

1. [Withdrawal initiating a transaction](https://viem.sh/op-stack/actions/initiateWithdrawal), which the user submits on L2.
2. [Withdrawal proving transaction](https://viem.sh/op-stack/actions/proveWithdrawal), which the user submits on L1 to prove that the withdrawal is legitimate.
3. [Withdrawal finalizing transaction](https://viem.sh/op-stack/actions/finalizeWithdrawal), which the user submits on L1 after the fault challenge period has passed, to actually run the transaction on L1.

## Code example

The following example demonstrates how to configure a file with all the details you need for interacting with Celo Sepolia.

<CodeGroup>
  ```js theme={null}
  import {
    createPublicClient,
    createWalletClient,
    http,
    parseEther,
  } from "viem";
  import { privateKeyToAccount } from "viem/accounts";
  import { celoSepolia, sepolia } from "viem/chains";
  import {
    publicActionsL1,
    walletActionsL2,
    walletActionsL1,
    publicActionsL2,
  } from "viem/op-stack";

  const account = privateKeyToAccount(
    "[PRIVATE_KEY]",
  );

  const value = parseEther("0.0001"); // Amount to Withdraw

  export const publicClientL1 = createPublicClient({
    chain: sepolia,
    transport: http(),
  }).extend(publicActionsL1());

  export const publicClientL2 = createPublicClient({
    chain: celoSepolia,
    transport: http(),
  }).extend(publicActionsL2());

  export const walletClientL1 = createWalletClient({
    chain: sepolia,
    transport: http(),
    account,
  }).extend(walletActionsL1());

  export const walletClientL2 = createWalletClient({
    chain: celoSepolia,
    transport: http(),
    account,
  }).extend(walletActionsL2());

  export default async function main() {
    console.log("Building Initiate Withdrawal...");
    const args = await publicClientL1.buildInitiateWithdrawal({
      account,
      to: account.address, // Receive on the same address on L1.
      value,
    });

    console.log("Initiaiting Withdrawal...");
    const hash = await walletClientL2.initiateWithdrawal(args);

    const initiateWithdrawalReceipt = await publicClientL2.waitForTransactionReceipt({
      hash
    });
    console.log(`Withdrawal Initiated: ${initiateWithdrawalReceipt}`);

    /**
     * The below step can take upto 2 hours!
     *
     * Hence, you may want to use viem's `getTimeToProve`.
     *
     * https://viem.sh/op-stack/actions/getTimeToProve
     *
     * Store the wait time in a database
     * and let the user know to come back later.
     *
     * */
    console.log("Waiting to prove...");
    const { output, withdrawal } = await publicClientL1.waitToProve({
      receipt: initiateWithdrawalReceipt,
      targetChain: walletClientL2.chain,
    });

    console.log("Building Prove Withdrawal...");
    const proveArgs = await publicClientL2.buildProveWithdrawal({
      output,
      withdrawal,
    });

    console.log("Proving Withdrawal...");
    const proveHash = await walletClientL1.proveWithdrawal(proveArgs);

    const proveReceipt = await publicClientL1.waitForTransactionReceipt({
      hash: proveHash,
    });
    console.log(`Withdrawal Proved: ${proveReceipt}`);

    /**
     * The fault challenge period is 7 days (proofMaturityDelaySeconds
     * is 604800 on Celo mainnet and Celo Sepolia).
     *
     * Hence, you may want to use viem's `getTimeToFinalize`.
     *
     * https://viem.sh/op-stack/actions/getTimeToFinalize
     *
     * Store the wait time in a database
     * and let the user know to come back later.
     *
     *
    */
    console.log("Waiting To Finalize...");
    await publicClientL1.waitToFinalize({
      targetChain: walletClientL2.chain,
      withdrawalHash: withdrawal.withdrawalHash,
    });

    console.log("Finalizing Withdrawal...");
    const finalizeWithdrawalHash = await walletClientL1.finalizeWithdrawal({
      targetChain: walletClientL2.chain,
      withdrawal,
    });

    const finalizeWithdrawalReceipt = await publicClientL1.waitForTransactionReceipt({
      hash: finalizeWithdrawalHash,
    });
    console.log(`Withdrawal Finalized: ${finalizeWithdrawalReceipt}`)
  }


  ```
</CodeGroup>

## Related

* [Bridging CELO from Ethereum](/home/bridged-tokens/bridging-celo-from-ethereum) - the deposit direction
* [Bridges](/home/bridged-tokens/bridges) - bridge UIs, including Superbridge for mainnet
* [L1 contracts](/tooling/contracts/l1-contracts) - the portal and token addresses for mainnet and Celo Sepolia
