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

# Bridge USDC Cross-Chain with CCTP

> Use a Para wallet with Circle Bridge Kit to move USDC between EVM chains, Solana, and other CCTP networks such as Arc.

export const Card = ({imgUrl, compactImgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered && href ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={href ? handleClick : undefined} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full ${href ? 'cursor-pointer' : 'cursor-default'} rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <picture style={{
    display: 'contents'
  }}>
              {compactImgUrl && <source media="(max-width: 639px)" srcSet={compactImgUrl} />}
              <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            </picture>
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

export const Link = ({href, label, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  return <a href={href} target={newTab ? '_blank' : '_self'} rel={newTab ? 'noopener noreferrer' : undefined} className="not-prose inline-block relative text-black font-semibold cursor-pointer border-b-0 no-underline" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      {label}
      <span className={`absolute left-0 bottom-0 w-full rounded-sm bg-gradient-to-r from-orange-600 to-purple-600 transition-all duration-300 ${isHovered ? 'h-0.5' : 'h-px'}`} />
    </a>;
};

Bridge native USDC between chains with Circle's CCTP V2. CCTP burns USDC on the source chain, waits for Circle's attestation, and mints the same amount on the destination chain. Para authenticates the user and signs on both sides of the route. Bridge Kit handles the approval, burn, attestation, and mint steps.

This guide bridges from Ethereum to Arc. The same hooks cover any supported route: change the `from` and `to` chains to bridge between EVM chains, or between an EVM chain and Solana.

## Choose a route

Para wallets cover the main CCTP chain families. The route you pick determines which Para wallet signs each side and how you submit the transfer.

| Para wallet | CCTP networks | How to bridge |
| - | - | - |
| EVM | Ethereum, Base, Arbitrum, OP Mainnet, Polygon PoS, Avalanche, Arc, and other EVM chains | Bridge Kit with a Para viem adapter |
| Solana | Solana | Bridge Kit with a Para Solana adapter |
| Sui | Sui | Circle's Sui CCTP packages with the Para Sui signer |
| Stellar | Stellar | Circle's Stellar CCTP contracts with the Para Stellar signer |

Bridge Kit's `BridgeChain` enum lists every chain it can route. A route only needs both chains in that list and an adapter for each side.

<Note>
  Each CCTP network has a protocol domain ID, such as `0` for Ethereum, `5` for Solana, `8` for Sui, `26` for Arc, and `27` for Stellar. Domain IDs are not EVM chain IDs. Bridge Kit's chain definitions carry the domains and CCTP contract addresses, so the example does not hardcode them.
</Note>

## Before you start

Start with a Next.js app that has `ParaProvider`, a login flow, and a wallet for each chain family in your route. These Para guides cover that setup:

<CardGroup cols={2}>
  <Card title="Set up Para in Next.js" imgUrl="/images/v3/cards/framework-nextjs.png" description="Add ParaProvider and authentication to your app." href="/v3/react/setup/nextjs" />

  <Card title="Use Para with viem" imgUrl="/images/v3/cards/para-with-viem.png" description="Connect the signed-in wallet to a viem client." href="/v3/react/guides/web3-operations/evm/setup-libraries" />
</CardGroup>

The wallet needs native USDC on the source chain and gas on both chains. The source chain pays for the approval and burn, and the destination chain pays for the mint. For Ethereum to Arc, that means ETH on Ethereum and USDC on Arc, since Arc uses USDC as its gas token. A Solana destination needs SOL for fees and for the USDC token account if the wallet does not have one yet. Review the transfer amount and network fees with the user before submitting a mainnet transaction.

## Install the bridge packages

From your Para Next.js application, install Bridge Kit, Circle's viem adapter, and the Para viem integration. The versions below are the ones used for this guide.

```bash theme={null}
npm install @circle-fin/bridge-kit@1.15.2 @circle-fin/adapter-viem-v2@1.19.0 @getpara/viem-v2-integration@3.21.0 viem@2.57.3
```

For routes that start or end on Solana, also install Circle's Solana adapter and the Para Solana signer.

```bash theme={null}
npm install @circle-fin/adapter-solana@1.7.4 @getpara/solana-web3.js-v1-integration@3.21.0 @solana/web3.js@1.99.0
```

Set an RPC URL for each chain in your routes, such as `NEXT_PUBLIC_ETHEREUM_RPC_URL`, `NEXT_PUBLIC_BASE_RPC_URL`, `NEXT_PUBLIC_ARC_RPC_URL`, and `NEXT_PUBLIC_SOLANA_RPC_URL`. The values are public browser configuration, so do not put private RPC credentials in them.

## Connect Para to Bridge Kit

Use <Link label="useClient()" href="/v3/references/hooks/useClient" /> and <Link label="useAccount()" href="/v3/references/hooks/useAccount" /> inside your existing `ParaProvider` to check the signed-in Para wallet. Confirm the session with <Link label="isFullyLoggedIn()" href="/v3/references/core/isfullyloggedin" />.

For EVM chains, <Link label="createParaViemAccount()" href="/v3/references/signers/evm/viem-v2/create-para-viem-account" /> selects the EVM wallet, and <Link label="createParaViemClient()" href="/v3/references/signers/evm/viem-v2/create-para-viem-client" /> gives Circle's `ViemAdapter` a wallet client for each chain. Every EVM chain uses the same Para account. For Solana, <Link label="ParaSolanaWeb3Signer" href="/v3/references/signers/solana/web3js-v1/para-solana-web3-signer" /> signs the transactions Circle's Solana adapter builds. `createAdapter()` returns the adapter that matches a chain's type.

```tsx src/hooks/useCctpAdapter.ts theme={null}
"use client";

import { useAccount, useClient } from "@getpara/react-sdk";
import {
  createParaViemAccount,
  createParaViemClient,
} from "@getpara/viem-v2-integration";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { ViemAdapter } from "@circle-fin/adapter-viem-v2";
import { createSolanaAdapterFromProvider } from "@circle-fin/adapter-solana";
import type { ChainDefinition } from "@circle-fin/bridge-kit";
import { Arc, Base, Ethereum, Solana } from "@circle-fin/bridge-kit/chains";
import { Connection, Transaction, VersionedTransaction } from "@solana/web3.js";
import { createPublicClient, http } from "viem";

const evmChains = [Ethereum, Base, Arc];

const evmRpcUrls: Record<number, string | undefined> = {
  [Ethereum.chainId]: process.env.NEXT_PUBLIC_ETHEREUM_RPC_URL,
  [Base.chainId]: process.env.NEXT_PUBLIC_BASE_RPC_URL,
  [Arc.chainId]: process.env.NEXT_PUBLIC_ARC_RPC_URL,
};
const solanaRpcUrl = process.env.NEXT_PUBLIC_SOLANA_RPC_URL;

function evmRpcFor(chainId: number) {
  const rpc = evmRpcUrls[chainId];
  if (!rpc) throw new Error(`Missing RPC URL for chain ${chainId}`);
  return rpc;
}

export function useCctpAdapter() {
  const para = useClient();
  const { isConnected } = useAccount();

  async function requireSession() {
    if (!para || !isConnected || !(await para.isFullyLoggedIn())) {
      throw new Error("Sign in with Para before bridging USDC");
    }
    return para;
  }

  async function createEvmAdapter(): Promise<ViemAdapter> {
    const para = await requireSession();
    const account = createParaViemAccount({ para });

    return new ViemAdapter(
      {
        getPublicClient: ({ chain }) =>
          createPublicClient({ chain, transport: http(evmRpcFor(chain.id)) }),
        getWalletClient: ({ chain }) =>
          createParaViemClient({
            para,
            walletClientConfig: {
              account,
              chain,
              transport: http(evmRpcFor(chain.id)),
            },
          }),
      },
      { addressContext: "user-controlled", supportedChains: evmChains },
    );
  }

  async function createSolanaAdapter() {
    const para = await requireSession();
    if (!solanaRpcUrl) throw new Error("Missing Solana RPC URL");

    const connection = new Connection(solanaRpcUrl, "confirmed");
    const signer = new ParaSolanaWeb3Signer(para, connection);
    const publicKey = signer.sender;
    if (!publicKey) throw new Error("Create a Solana wallet before bridging");

    async function signTransaction(transaction: unknown) {
      if (transaction instanceof VersionedTransaction) {
        return signer.signVersionedTransaction(transaction);
      }
      if (transaction instanceof Transaction) {
        return signer.signTransaction(transaction);
      }
      throw new Error("Unsupported Solana transaction");
    }

    return createSolanaAdapterFromProvider({
      connection,
      provider: {
        isConnected: true,
        publicKey,
        connect: async () => ({ publicKey }),
        disconnect: async () => {},
        signTransaction,
        signAllTransactions: (transactions) =>
          Promise.all(transactions.map(signTransaction)),
      },
      capabilities: { addressContext: "user-controlled", supportedChains: [Solana] },
    });
  }

  function createAdapter(chain: ChainDefinition) {
    return chain.type === "solana" ? createSolanaAdapter() : createEvmAdapter();
  }

  return { createAdapter };
}
```

If every route stays on EVM chains, drop `createSolanaAdapter()` and the Solana packages. To add another EVM chain, import its definition from `@circle-fin/bridge-kit/chains`, add it to `evmChains`, and add its RPC URL to `evmRpcUrls`.

<Warning>
  Keep the authenticated Para session available until the bridge finishes. Do not replace the Para signer with a private-key viem account or Solana keypair: that would bypass Para signing.
</Warning>

## Bridge USDC

Call `bridgeUsdc()` in response to a user action. Bridge Kit uses CCTP V2 to burn USDC on the source chain, waits for Circle's attestation, then mints USDC on the destination chain. Setting `useForwarder: false` keeps the destination mint signed by Para. The result contains each step and the overall state. `amount` is a human-readable USDC amount.

```tsx src/hooks/useCctpBridge.ts theme={null}
"use client";

import {
  BridgeKit,
  type BridgeResult,
  type ChainDefinition,
} from "@circle-fin/bridge-kit";
import { useCctpAdapter } from "./useCctpAdapter";

type BridgeRoute = {
  from: ChainDefinition;
  to: ChainDefinition;
  amount: string;
};

function summarize(result: BridgeResult) {
  const burn = result.steps.find(
    (step) => step.name === "burn" || step.name === "depositForBurn",
  );
  const mint = result.steps.find((step) => step.name === "mint");

  return {
    status:
      result.state === "error"
        ? "needs_attention"
        : result.state === "success" &&
            burn?.state === "success" &&
            burn.txHash &&
            mint?.state === "success" &&
            mint.txHash
          ? "complete"
          : "pending",
    failedStep: result.steps.find((step) => step.state === "error"),
    sourceBurn: burn?.txHash,
    sourceExplorer: burn?.explorerUrl,
    destinationMint: mint?.txHash,
    destinationExplorer: mint?.explorerUrl,
  };
}

export function useCctpBridge() {
  const { createAdapter } = useCctpAdapter();

  async function bridgeUsdc({ from, to, amount }: BridgeRoute) {
    const kit = new BridgeKit();
    kit.on("*", (event) => console.info(event.method, event.values));

    const result = await kit.bridge({
      from: { adapter: await createAdapter(from), chain: from },
      to: { adapter: await createAdapter(to), chain: to, useForwarder: false },
      amount,
      token: "USDC",
    });
    return { result, ...summarize(result) };
  }

  async function resumeTransfer(result: BridgeResult) {
    if (result.state !== "error") return { result, ...summarize(result) };

    const resumed = await new BridgeKit().retry(result, {
      from: await createAdapter(result.source.chain),
      to: await createAdapter(result.destination.chain),
    });
    return { result: resumed, ...summarize(resumed) };
  }

  return { bridgeUsdc, resumeTransfer };
}
```

In a client component, pass the route from a transfer button. The chain definitions come from `@circle-fin/bridge-kit/chains`:

```tsx theme={null}
bridgeUsdc({ from: Ethereum, to: Arc, amount: "1.00" });
```

`bridgeUsdc()` returns the Bridge Kit `result` and a summary with `status`, `failedStep`, and the burn and mint transaction hashes and explorer links. Show a transfer as complete only when `status` is `complete`. For a failed transfer, read `failedStep?.name` and `failedStep?.errorMessage`. Handle errors thrown before Bridge Kit returns a result separately. The `fetchAttestation` event records progress between the burn and mint; it is not itself an onchain transaction.

## Change the route

The route is the only part of the call that changes. Each side uses the Para wallet for that chain's type, so a single signed-in user can bridge in any direction the app supports.

```tsx theme={null}
bridgeUsdc({ from: Base, to: Arc, amount: "1.00" });
bridgeUsdc({ from: Ethereum, to: Solana, amount: "1.00" });
bridgeUsdc({ from: Solana, to: Base, amount: "1.00" });
```

Between EVM chains, both sides use the same Para EVM address. Between EVM and Solana, the burn and mint use the user's Para EVM and Solana addresses, so the user needs both wallet types before starting the transfer.

## Bridge with Sui or Stellar

CCTP also supports Sui and Stellar, and Para signs on both. Bridge Kit does not include Sui or Stellar in its chain list, so these routes use Circle's chain-specific CCTP integration instead of `bridgeUsdc()`. The Para Sui and Stellar signers build and sign those transactions with the user's Para wallet.

<Note>
  On Stellar, USDC precision and address encoding differ from other CCTP networks. Follow Circle's Stellar guidance for incoming transfers so the minted USDC reaches the correct recipient.
</Note>

<CardGroup cols={2}>
  <Card title="Use Para with Sui" imgUrl="/images/v3/cards/chain-sui.png" description="Sign Sui transactions with the signed-in Para wallet." href="/v3/react/guides/web3-operations/sui/setup-libraries" />

  <Card title="Use Para with Stellar" imgUrl="/images/v3/cards/chain-stellar.png" description="Sign Stellar transactions with the signed-in Para wallet." href="/v3/react/guides/web3-operations/stellar/setup-libraries" />
</CardGroup>

Circle's <Link label="CCTP supported blockchains" href="https://developers.circle.com/cctp/cctp-supported-blockchains" newTab /> page lists the contracts and features for each network.

## Resume an interrupted transfer

If `result.state` is `error`, inspect `result.steps` before asking the user to try again. An approved or burned transfer may already have spent gas or destroyed the source USDC. Keep that result and pass it to `resumeTransfer(result)` from a separate Resume action. The hook reads the source and destination chains from the result, creates fresh authenticated Para adapters for both, and returns the updated result and status summary.

Do not start a second `bridge()` call to recover a transfer whose burn succeeded. If a retry still fails, keep the burn transaction hash and investigate the failed step before attempting another transaction. A `pending` result is not a confirmed destination mint.

## Check the route

For a production check, record both the source burn transaction and the destination mint transaction from the same Bridge Kit result. The source receipt alone confirms only the burn. The destination explorer receipt confirms the mint. For a testnet trial before using mainnet funds, swap in testnet chain definitions such as `EthereumSepolia`, `BaseSepolia`, `ArcTestnet`, and `SolanaDevnet`, or follow Circle's <Link label="Ethereum Sepolia to Arc Testnet quickstart" href="https://docs.arc.io/app-kit/quickstarts/bridge-tokens-across-blockchains" newTab />. Circle's <Link label="Bridge Kit documentation" href="https://docs.arc.io/app-kit/bridge" newTab /> covers fees, supported routes, and recovery behavior.

## Keep building with Para

Use the same signed-in Para wallets for other actions on each chain:

<CardGroup cols={3}>
  <Card title="Send EVM tokens" imgUrl="/images/v3/cards/sdk-evm-send-tokens.png" description="Move tokens with a Para-signed EVM transaction." href="/v3/react/guides/web3-operations/evm/send-tokens" />

  <Card title="Send Solana tokens" imgUrl="/images/v3/cards/sdk-solana-send-tokens.png" description="Move SPL tokens with a Para-signed Solana transaction." href="/v3/react/guides/web3-operations/solana/send-tokens" />

  <Card title="Explore Arc" imgUrl="/images/v3/cards/para-with-arc.png" description="Review Arc network setup and wallet details." href="/v3/walkthroughs/arc" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.