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

# Integrate asset bridging

> Bridge supported assets, receive USDCx confidentially, and connect browser or embedded wallets.

This guide shows how to add bridging to your application, choose a supported pair, and connect the wallets that authorize each transfer.

## Choose how funds arrive

Your application can send funds to a public balance or, for USDCx, directly to a balance held in token records. The [Fund guide](../trading/fund-and-bridge#how-funding-works) explains which balances you can use for trading.

You authorize the transfer with the source wallet. Some routes also need the receiving wallet to finish delivery. A confirmed source transaction means the transfer has started; check destination delivery before treating the funds as available.

## Supported pairs

The following **mainnet** pairs support transfers in both directions. Each row connects the external asset to its Aleo counterpart. The interface column distinguishes executable SDK routes from direct Hyperlane routes exposed by the wallet services API.

| External asset | External network | Aleo asset | Bridge | Interface |
| - | - | - | - | - |
| USDC | Ethereum or Arc | USDCx | Circle xReserve | TypeScript, Python. |
| ETH | Ethereum | ETH | Hyperlane | TypeScript, Python, REST. |
| WBTC | Ethereum | WBTC | Hyperlane | TypeScript, Python, REST. |
| USDT | Ethereum | USDT | Hyperlane | TypeScript, Python, REST. |
| USDT | BNB Smart Chain | USDT | Hyperlane | REST. |
| SOL | Solana | SOL | Hyperlane | TypeScript, Python, REST. |
| BAT | Ethereum or Solana | BAT | Hyperlane | TypeScript, Python. |
| USDG | Ethereum or Solana | USDG | Hyperlane | TypeScript, Python. |
| ZEC | Solana | ZEC | Hyperlane | TypeScript, Python. |
| ALEO | Ethereum, Base, or Solana | ALEO | Hyperlane | REST. |

USDC from Base or Arbitrum can also reach USDCx through a composed SDK route: CCTP moves USDC to Arc, then xReserve bridges it to Aleo. The reverse route returns USDC through Arc. The [packaged examples](#run-packaged-examples) cover these transfers. ZEC in the table is a Solana token, not a transfer from the Zcash network.

Check [route discovery](../trading/fund-and-bridge#2-discover-assets-and-routes) before requesting a quote. SDK routes marked `metadata-required` cannot execute. The REST catalog can also return providers that exchange different assets; those quotes are separate from the direct bridge pairs above. A listed route still needs a valid quote for your amount and addresses.

## Bridge SOL between Solana and Aleo

**Prerequisites:** Your mainnet Aleo account from [Setup](../trading/configure), a Solana wallet, and a Solana mainnet RPC endpoint. Keep enough SOL for the transfer, network fees, delivery payment, and any account rent shown in the quote. The service examples use Node.js 22 or later or Python 3.11 or later.

### 1. Connect the accounts

For a service or agent, supply credentials through your application's configuration or secret manager. These examples read them from environment variables. A [browser application](#integrate-bridging-with-a-frontend) or [embedded wallet](#use-embedded-wallets-for-bridging) supplies a wallet connection instead of a source private key.

| Input | Value supplied by your application |
| - | - |
| `ALEO_PRIVATE_KEY` | Your mainnet trading account's private key. |
| `SOLANA_RPC_URL` | Your Solana mainnet RPC endpoint. |
| `SOLANA_KEYPAIR_JSON` | TypeScript: a JSON array containing the Solana wallet's 64-byte keypair. |
| `SOLANA_PRIVATE_KEY` | Python: the Solana wallet's base58-encoded private key. |
| `SOURCE_ADDRESS`, `DESTINATION_ADDRESS` | REST: the sending Solana address and receiving Aleo address. |

<Tabs>
  <Tab title="TypeScript">
    Install the bridge packages and Solana signing dependency:

    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    npm install --legacy-peer-deps @provablehq/aleo-bridge-sdk@0.12.1 @provablehq/veil-aleo-sdk@0.12.1 @provablehq/veil-aleo-devnode@0.12.1 @solana/kit@8
    ```

    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import { loadNetwork } from "@provablehq/veil-aleo-sdk"
    import {
      createAleoClient, createBridgeClient, createSolanaClient,
      solanaHttp, solanaKeyPair,
    } from "@provablehq/aleo-bridge-sdk"

    const network = await loadNetwork("mainnet")
    const nativeAleo = network.createAleoClient({ privateKey: process.env.ALEO_PRIVATE_KEY! })
    const solana = createSolanaClient({
      transport: solanaHttp(process.env.SOLANA_RPC_URL!),
      account: solanaKeyPair(Uint8Array.from(JSON.parse(process.env.SOLANA_KEYPAIR_JSON!))),
    })
    const solanaBridge = createBridgeClient({
      environment: "mainnet",
      clients: {
        solana,
        aleo: createAleoClient({ publicClient: nativeAleo.publicClient, account: nativeAleo.walletClient }),
      },
    })
    ```

    `solanaBridge` can request quotes and authorize transfers with either account. Creating it does not move funds.
  </Tab>

  <Tab title="Python">
    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    python -m pip install "aleo-bridge-sdk==0.6.1"
    ```

    Register the Aleo record scanner so the client can find token records when you later unshield funds for a withdrawal. Registration shares your account's view key with the hosted scanner, which can read records belonging to that account.

    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import os
    from aleo import Aleo, HTTPProvider
    from aleo_bridge import Bridge, FileCheckpointStore, Solana

    aleo = Aleo(HTTPProvider("https://edge.provable.com/api", network="mainnet"))
    aleo.default_account = aleo.account.from_private_key(os.environ["ALEO_PRIVATE_KEY"])
    registration = aleo.records.register(aleo.default_account)
    if not registration.get("ok"):
        raise RuntimeError(registration.get("error", "Record scanner registration failed"))
    solana = Solana(os.environ["SOLANA_RPC_URL"], private_key=os.environ["SOLANA_PRIVATE_KEY"])
    solana_bridge = Bridge(
        aleo, solana=solana, checkpoints=FileCheckpointStore(".bridge/checkpoints"),
    )
    ```

    `solana_bridge` uses your accounts to authorize transfers. Its checkpoint store retains transfer progress so you can recover after a restart.
  </Tab>

  <Tab title="REST">
    The API prepares the transfer; your wallet signs it. Use `curl` and `jq` with the source and destination addresses supplied by your application:

    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    export WALLET_API=https://wallet.api.provable.com
    : "${SOURCE_ADDRESS:?Set the Solana sender address}"
    : "${DESTINATION_ADDRESS:?Set the Aleo recipient address}"
    ```

    Keep the source private key in your wallet. The API needs the addresses, not the private key.
  </Tab>
</Tabs>

### 2. Review a SOL quote

**Prerequisites:** The Solana connections from [Connect the accounts](#1-connect-the-accounts). These examples request **0.01 SOL in display units**, not lamports. The recipient is your configured Aleo account.

The quote identifies the route, expected delivery, and fees before you authorize a transfer. The SDK examples select Hyperlane explicitly; review the provider in the REST response before choosing a quote.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    const solanaQuote = await solanaBridge.quote({
      source: { chain: "solana", asset: "sol" },
      destination: { chain: "aleo", asset: "sol" },
      bridgeProtocol: "hyperlane",
      amount: "0.01",
      sender: await solana.walletClient!.getAddress(),
      recipient: String(nativeAleo.account.address),
    })
    ```

    Review `solanaQuote.plan` before submitting it. For a withdrawal, reverse the source and destination, use your Aleo address as `sender`, and set `recipient` to the receiving Solana address.
  </Tab>

  <Tab title="Python">
    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    solana_quote = solana_bridge.quote(
        source_chain="solana", source_asset="sol",
        destination_chain="aleo", destination_asset="sol",
        bridge_protocol="hyperlane", amount="0.01",
        sender=solana.address, recipient=solana_bridge.aleo_address(),
    )
    ```

    Review `solana_quote.amount_out` and `solana_quote.fees`. For a withdrawal, reverse the chains, use `solana_bridge.aleo_address()` as `sender`, and supply the receiving Solana address as `recipient`.
  </Tab>

  <Tab title="REST">
    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    QUOTES=$(curl --fail-with-body --silent --show-error --get "$WALLET_API/bridge/quotes" \
      -H 'X-Client-Type: programmatic' \
      --data-urlencode 'srcChain=SOLANA' --data-urlencode 'srcAsset=SOL_SOLANA' \
      --data-urlencode 'destChain=ALEO' --data-urlencode 'destAsset=WSOL_ALEO' \
      --data-urlencode 'amountIn=0.01' --data-urlencode 'slippageBps=50' \
      --data-urlencode "fromAddress=$SOURCE_ADDRESS" \
      --data-urlencode "refundAddress=$SOURCE_ADDRESS" \
      --data-urlencode "recipientAddress=$DESTINATION_ADDRESS")
    printf '%s\n' "$QUOTES" | jq '.data | to_entries[] | {index: .key, quote: .value}'
    ```

    Each result contains the provider, output amount, and fees. For a withdrawal, reverse the chain and asset identifiers and use the Aleo wallet as the source. `WSOL_ALEO` is the API code for the Aleo SOL asset.
  </Tab>
</Tabs>

The SDK field names and REST parameters follow the [quote parameter tables](../trading/fund-and-bridge#3-request-and-review-a-quote). Before withdrawing, [unshield the withdrawal amount](../trading/fund-and-bridge#shield-public-balances) and wait for acceptance. Hyperlane withdrawals spend a public balance.

### 3. Submit and check delivery

**Prerequisites:** Your reviewed quote from [Review a SOL quote](#2-review-a-sol-quote). For TypeScript, supply `saveCheckpoint`, your application callback that durably saves each `BridgeCheckpoint` before returning. It may return `void` or `Promise<void>`.

<Warning>
  **Recover the original transfer after an uncertain submission.** A timeout does not prove that funds stayed in your source wallet; sending again can create a second transfer.
</Warning>

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    const solanaExecution = await solanaBridge.execute({
      plan: solanaQuote.plan, onCheckpoint: saveCheckpoint,
    })
    const solanaProgress = await solanaBridge.wait({
      progress: { next: "wait", plan: solanaQuote.plan, receipt: solanaExecution.receipt },
    })
    ```

    `solanaProgress.next === "done"` confirms delivery. A pending result needs monitoring or [recovery](../trading/fund-and-bridge#6-recover-an-interrupted-transfer), not a new transfer.
  </Tab>

  <Tab title="Python">
    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    solana_progress = solana_bridge.execute(solana_quote.plan, mode="signer")
    if solana_progress.next == "wait":
        solana_progress = solana_bridge.wait(solana_progress)
    ```

    `solana_progress.next == "done"` confirms delivery. `mode="signer"` also supports the return transfer by spending your public Aleo balance. The checkpoint store retains the original transfer if monitoring stops.
  </Tab>

  <Tab title="REST">
    Continue with `QUOTES` in [Submit the reviewed transfer](../trading/fund-and-bridge#4-submit-the-reviewed-transfer), then [Check delivery](../trading/fund-and-bridge#5-check-delivery). The same order flow supports Solana. Signed Solana transactions use base64; if the wallet broadcasts, report its original transaction signature instead of submitting the bytes again.

    Fund the Solana source account before fetching unsigned transactions. The API simulates the transfer and can reject an unfunded, newly generated address with `AccountNotFound`, even when quote and order creation succeeded.
  </Tab>
</Tabs>

After inbound delivery, [shield the received SOL](../trading/fund-and-bridge#shield-public-balances), using `sol` instead of `eth` in those examples. After outbound delivery, the receiving Solana wallet holds native SOL.

## Receive USDCx confidentially

**Prerequisites:** The mainnet Ethereum and Aleo clients from [Configure access](../trading/fund-and-bridge#1-configure-access), USDC plus ETH for source fees, and the recipient's Aleo wallet with destination fee payment configured. The examples below use that section's TypeScript `bridge` and `account`, or Python `bridge` and `ethereum`.

Circle xReserve offers two ways to receive USDCx directly in token records. They differ in whether the Ethereum deposit exposes the Aleo recipient and who completes delivery.

| Delivery mode | What stays public | How you receive USDCx |
| - | - | - |
| `record` | The Ethereum sender, amount, and Aleo recipient. | A provider delivers the token record without a separate recipient claim. |
| `private` | The Ethereum sender, amount, and timing. The deposit contains a commitment instead of the Aleo recipient address. | The recipient uses the original secret nonce to authorize a destination mint after Circle attests the deposit. |

Both modes avoid a separate shielding transaction. Neither conceals the source transaction. Destination fee payment can also be public; the TypeScript completion example below uses a public fee. See [public data and confidentiality boundaries](../confidentiality/public-data) for trading disclosures.

### 1. Keep the secret needed for completion

The `private` flow commits to the recipient and a secret nonce, a random value needed again to receive the funds. Generate a nonzero Aleo scalar once and save it in your application's secret store **before** depositing. Load that same value for quoting, submission, and completion. Bridge checkpoints omit it.

These examples load the saved scalar string, such as a decimal integer followed by `scalar`, from `BRIDGE_MINT_SECRET_NONCE`. Use a cryptographically random value, not a fixed sample or `0scalar`.

### 2. Review a private-mint quote

**Prerequisites:** The clients named in [Receive USDCx confidentially](#receive-usdcx-confidentially) and the saved nonce from [Keep the secret needed for completion](#1-keep-the-secret-needed-for-completion). Each example requests **2 USDC in display units**; the quote supplies the current constraints and fees.

Your Ethereum account must already hold the requested USDC amount. Both SDKs check that balance while quoting, before any deposit is submitted.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    const nonce = process.env.BRIDGE_MINT_SECRET_NONCE
    if (!nonce || nonce === "0scalar") throw new Error("Load the saved nonzero mint nonce")
    const privateQuote = await bridge.quote({
      source: { chain: "ethereum", asset: "usdc" },
      destination: { chain: "aleo", asset: "usdcx" },
      bridgeProtocol: "xreserve", amount: "2",
      recipient: String(account.address),
      mintMode: "private", privateMintSecretNonce: nonce,
    })
    ```

    The plan selects `private` delivery for the configured recipient. Review the amount, allowance, approval requirement, and fees before execution.
  </Tab>

  <Tab title="Python">
    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import os
    from aleo import mainnet

    nonce = os.environ["BRIDGE_MINT_SECRET_NONCE"]
    if str(mainnet.Scalar.from_string(nonce)) == "0scalar":
        raise ValueError("Load the saved nonzero mint nonce")
    private_quote = bridge.quote(
        source_chain="ethereum", source_asset="usdc",
        destination_chain="aleo", destination_asset="usdcx",
        bridge_protocol="xreserve", amount="2",
        sender=ethereum.require_address(), recipient=bridge.aleo_address(),
        mint_mode="private", secret_nonce=nonce,
    )
    ```

    Review `private_quote.amount_out` and `private_quote.fees`. The configured Aleo account is the recipient that must later complete this mint.
  </Tab>
</Tabs>

`mintMode` in TypeScript and `mint_mode` in Python are optional strings with a `public` default; select `private` explicitly for this flow. The corresponding nonce fields are scalar strings required by this tutorial. To use provider-delivered records instead, select `record`, omit the nonce, and follow the [packaged record-delivery example](#run-packaged-examples). A Circle attestation alone does not confirm that the provider has delivered the record.

The wallet services REST examples do not expose these xReserve mint-mode controls. Use an SDK for this flow; a REST quote for USDC to USDCx may select a different provider.

### 3. Deposit and complete the mint

**Prerequisites:** The quote and saved nonce from [Review a private-mint quote](#2-review-a-private-mint-quote). TypeScript also needs the durable `saveCheckpoint` callback described in [Submit and check delivery](#3-submit-and-check-delivery). Python uses the checkpoint store configured in the Fund guide.

<Warning>
  **Keep the original nonce and checkpoint until delivery completes.** Losing the nonce prevents this flow from completing the mint. If submission is interrupted, recover the saved transfer instead of making another deposit.
</Warning>

Run the source submission after reviewing the quote:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    const privateExecution = await bridge.execute({
      plan: privateQuote.plan, privateMintSecretNonce: nonce,
      onCheckpoint: saveCheckpoint,
    })
    let privateProgress = await bridge.wait({
      progress: { next: "wait", plan: privateQuote.plan, receipt: privateExecution.receipt },
    })
    ```

    `privateProgress.next === "complete"` means Circle's attestation is ready and the recipient can authorize the destination mint. It does not mean USDCx has arrived.
  </Tab>

  <Tab title="Python">
    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    private_progress = bridge.execute(private_quote.plan, secret_nonce=nonce)
    if private_progress.next == "wait":
        private_progress = bridge.wait(private_progress)
    ```

    `private_progress.next == "complete"` means the recipient can authorize the destination mint with the saved nonce.
  </Tab>
</Tabs>

If the result requests `resume`, recover and resume the remaining source action with the same nonce; an approval may have confirmed before the deposit was sent. A `wait` result needs further monitoring. The [recovery guide](../trading/fund-and-bridge#6-recover-an-interrupted-transfer) explains these states.

When the result is `complete`, approve the destination mint with the recipient's Aleo wallet:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    if (privateProgress.next === "complete") {
      const mint = await bridge.complete({
        progress: privateProgress, privateMintSecretNonce: nonce,
        privateFee: false, onCheckpoint: saveCheckpoint,
      })
      privateProgress = await bridge.wait({
        progress: { next: "wait", plan: privateQuote.plan, receipt: mint.receipt },
      })
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    if private_progress.next == "complete":
        private_progress = bridge.complete(private_progress, secret_nonce=nonce)
        if private_progress.next == "wait":
            private_progress = bridge.wait(private_progress)
    ```
  </Tab>
</Tabs>

Only `next: "done"` confirms destination delivery. The resulting USDCx record can be used for trading without another shielding step.

## Integrate bridging with a frontend

**Prerequisites:** A TypeScript browser application, the connected Aleo `client` from [Browser client setup](../trading/configure#browser-client-setup), an Ethereum wallet provider, and a connected Solana Wallet Standard account when offering Solana routes. Each wallet and RPC endpoint must target mainnet.

Your frontend requests quotes through public RPC connections and asks a connected wallet to authorize fund movements. Private keys stay in the wallets. Your application still needs to retain bridge progress so a page reload does not lose the transfer reference.

### 1. Connect the bridge to browser wallets

Install the bridge and Solana packages alongside the wallet packages from Setup:

```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
npm install --legacy-peer-deps @provablehq/aleo-bridge-sdk@0.12.1 @provablehq/veil-core@0.12.1 @solana/kit@8
```

The function below accepts connections from your wallet selection UI. The Solana wallet must expose `solana:signAndSendTransaction`; an Ethereum connector supplies an EIP-1193 provider after account access is approved.

| Argument | Type | Required or default |
| - | - | - |
| `ethereumProvider` | EIP-1193 provider from your Ethereum wallet connector. | Required; no default. |
| `solanaAccount` | Object containing the connected Wallet Standard `wallet`, selected `account`, and `chain: "solana:mainnet"`. | Required; no default. |
| `aleoWalletClient` | The connected Aleo `client` from Setup. | Required; no default. |
| `ethereumRpcUrl`, `solanaRpcUrl` | Public mainnet RPC URL strings. | Required; no default. |

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
import { createPublicClient, http } from "@provablehq/veil-core"
import {
  createAleoClient, createBridgeClient, createEvmClient, createSolanaClient,
  evmHttp, evmProvider, solanaHttp, solanaWallet,
} from "@provablehq/aleo-bridge-sdk"

function createBrowserBridge(options: {
  ethereumProvider: Parameters<typeof evmProvider>[0]
  solanaAccount: Parameters<typeof solanaWallet>[0]
  aleoWalletClient: NonNullable<Parameters<typeof createAleoClient>[0]["account"]>
  ethereumRpcUrl: string
  solanaRpcUrl: string
}) {
  const publicClient = createPublicClient({
    transport: http("https://edge.provable.com/api/v2", { network: "mainnet" }),
  })
  return createBridgeClient({
    environment: "mainnet",
    clients: {
      ethereum: createEvmClient({
        transport: evmHttp(options.ethereumRpcUrl),
        account: evmProvider(options.ethereumProvider),
      }),
      solana: createSolanaClient({
        transport: solanaHttp(options.solanaRpcUrl),
        account: solanaWallet(options.solanaAccount),
      }),
      aleo: createAleoClient({ publicClient, account: options.aleoWalletClient }),
    },
  })
}
```

The returned bridge supports the quote and execution steps on this page. Remove an unused source-chain client if your application offers only Ethereum or Solana. Recreate the bridge and request a fresh quote when a selected account or network changes.

### 2. Separate quote review from authorization

Use the returned bridge to request a quote when the amount or destination changes. Show the recipient, source amount, expected output, and fees before asking for approval. For USDCx, also show the selected delivery mode and whether a destination claim is required.

Call this function from your confirmation handler with the reviewed `BridgePlan`. Supply `saveCheckpoint` from your application's durable storage; it receives a serializable `BridgeCheckpoint` and resolves only after saving it.

```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
import type { BridgeCheckpoint, BridgePlan } from "@provablehq/aleo-bridge-sdk"

async function submitReviewedPlan(
  bridge: ReturnType<typeof createBrowserBridge>,
  plan: BridgePlan,
  saveCheckpoint: (checkpoint: BridgeCheckpoint) => Promise<void>,
  privateMintSecretNonce?: string,
) {
  if (plan.mintMode === "private" && (!privateMintSecretNonce || privateMintSecretNonce === "0scalar")) {
    throw new Error("Load the saved nonzero mint nonce before submitting")
  }
  const execution = await bridge.execute({ plan, onCheckpoint: saveCheckpoint, privateMintSecretNonce })
  return bridge.wait({ progress: { next: "wait", plan, receipt: execution.receipt } })
}
```

This handler depends on `createBrowserBridge` from [Connect the bridge to browser wallets](#1-connect-the-bridge-to-browser-wallets). The optional nonce is required for a `private` USDCx mint and must come from the saved secret, not from a new generation on each click.

Disable repeat submission while the request is in progress. The returned `next` value tells your UI what the transfer needs:

| `next` | UI action |
| - | - |
| `wait` | Show the transfer as pending and keep monitoring. |
| `resume` | Request authorization for the remaining source action. |
| `complete` | Request authorization for destination completion. |
| `done` | Show confirmed delivery. |
| `failed` | Show the recorded error and check the saved transfer before offering another submission. |

After a reload, load the checkpoint and call `bridge.recover({ checkpoint })` before enabling another transfer. Wallet transaction history does not replace your application's bridge checkpoint.

For a frontend built around REST, keep the same review and confirmation boundaries while following [the order flow](../trading/fund-and-bridge#4-submit-the-reviewed-transfer). The connected wallet signs the returned transactions. A wallet that broadcasts must report the original transaction to the API rather than submit it again.

## Use Embedded Wallets for bridging

**Prerequisites:** A Node.js 22 or later service or Python 3.11 or later service, the bridge packages and Aleo client from [Connect the accounts](#1-connect-the-accounts), and mainnet RPC endpoints for your source networks. Reuse the Aleo initialization from that setup: TypeScript's `nativeAleo` or Python's `aleo`. Your chosen embedded wallet supplies the source signer below.

Dynamic and Privy embedded wallets can authorize bridge transfers for your service or agent without an interactive browser prompt. The source wallet signs the Ethereum or Solana transfer; your Aleo account handles shielding, withdrawals, and USDCx destination claims.

Choose one provider below. Each example connects Ethereum and Solana; keep only the connections your application uses. Supply credentials through your service's configuration or secret manager. These examples use environment variables.

For a frontend, pass the connected wallet provider from Dynamic or Privy to the [browser bridge](#integrate-bridging-with-a-frontend). Server API tokens, app secrets, and signing passwords stay in your service.

### Dynamic

Dynamic's [server wallet setup](https://www.dynamic.xyz/docs/node/wallets/server-wallets/overview) covers creating wallets and API credentials. Reconnect the existing wallets when your service restarts so their addresses stay the same.

These examples use encrypted key shares backed up to Dynamic. The wallet password unlocks those shares for signing. For TypeScript, retain the complete metadata objects returned by wallet creation as `evmWalletMetadata` and `solanaWalletMetadata`; an address alone does not include the required backup information. Dynamic's [storage guide](https://www.dynamic.xyz/docs/node/wallets/server-wallets/storage-best-practices) describes what to retain. The Python signers resolve the existing wallets by address.

<Tabs>
  <Tab title="TypeScript">
    Install the Dynamic clients alongside the bridge packages. The server clients need native addon support; run them in your Node.js service. Use `module: "ESNext"` and `moduleResolution: "Bundler"` in TypeScript.

    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    npm install --legacy-peer-deps @dynamic-labs-wallet/node-evm@1.1.24 @dynamic-labs-wallet/node-svm@1.1.24
    ```

    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import { DynamicEvmWalletClient } from "@dynamic-labs-wallet/node-evm"
    import { DynamicSvmWalletClient } from "@dynamic-labs-wallet/node-svm"
    import { createAleoClient, createBridgeClient, evmHttp, solanaHttp } from "@provablehq/aleo-bridge-sdk"
    import { createDynamicEvmClient, createDynamicSolanaClient } from "@provablehq/aleo-bridge-sdk/dynamic"

    const dynamicEvm = new DynamicEvmWalletClient({ environmentId: process.env.DYNAMIC_ENVIRONMENT_ID! })
    const dynamicSolana = new DynamicSvmWalletClient({ environmentId: process.env.DYNAMIC_ENVIRONMENT_ID! })
    await dynamicEvm.authenticateApiToken(process.env.DYNAMIC_API_TOKEN!)
    await dynamicSolana.authenticateApiToken(process.env.DYNAMIC_API_TOKEN!)
    const ethereum = await createDynamicEvmClient({
      client: dynamicEvm, walletMetadata: evmWalletMetadata,
      password: process.env.DYNAMIC_EVM_WALLET_PASSWORD!,
      transport: evmHttp(process.env.ETHEREUM_RPC_URL!),
    })
    const solana = await createDynamicSolanaClient({
      client: dynamicSolana, walletMetadata: solanaWalletMetadata,
      password: process.env.DYNAMIC_SOLANA_WALLET_PASSWORD!,
      chainId: "101", transport: solanaHttp(process.env.SOLANA_RPC_URL!),
    })
    const bridge = createBridgeClient({
      environment: "mainnet",
      clients: {
        ethereum, solana,
        aleo: createAleoClient({ publicClient: nativeAleo.publicClient, account: nativeAleo.walletClient }),
      },
    })
    ```

    The `bridge` now uses the selected Dynamic wallets for source authorization. `"101"` is Dynamic's Solana mainnet identifier and must match your Solana RPC endpoint.
  </Tab>

  <Tab title="Python">
    The bridge package includes Dynamic's Python SDK. Resolve each signer before quoting to check its credentials and wallet identity:

    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import os
    from aleo_bridge import Bridge, Ethereum, FileCheckpointStore, Solana
    from aleo_bridge.dynamic import DynamicEvmSigner, DynamicSolanaSigner
    from dynamic_wallet_sdk import DynamicEvmWalletClient, DynamicSvmWalletClient

    evm_signer = DynamicEvmSigner(
        DynamicEvmWalletClient(os.environ["DYNAMIC_ENVIRONMENT_ID"]),
        address=os.environ["DYNAMIC_EVM_ADDRESS"],
        api_token=os.environ["DYNAMIC_API_TOKEN"],
        password=os.environ["DYNAMIC_EVM_WALLET_PASSWORD"],
    )
    solana_signer = DynamicSolanaSigner(
        DynamicSvmWalletClient(os.environ["DYNAMIC_ENVIRONMENT_ID"]),
        address=os.environ["DYNAMIC_SOLANA_ADDRESS"],
        api_token=os.environ["DYNAMIC_API_TOKEN"],
        password=os.environ["DYNAMIC_SOLANA_WALLET_PASSWORD"],
    )
    evm_signer.resolve()
    solana_signer.resolve()
    ethereum = Ethereum(os.environ["ETHEREUM_RPC_URL"], signer=evm_signer)
    solana = Solana(os.environ["SOLANA_RPC_URL"], signer=solana_signer)
    bridge = Bridge(
        aleo, ethereum=ethereum, solana=solana,
        checkpoints=FileCheckpointStore(".bridge/checkpoints"),
    )
    ```

    Both connections now authorize transfers through Dynamic. Call `evm_signer.close()` and `solana_signer.close()` when your service shuts down.
  </Tab>
</Tabs>

### Privy

Privy's [wallet creation guide](https://docs.privy.io/wallets/wallets/create/create-a-wallet) covers provisioning a wallet and choosing who controls it. Retain each wallet's ID and address; they must identify the same wallet when you reconnect it.

Your app credentials identify the service. Permission to sign depends on the wallet's owner and signer configuration. If that configuration requires an authorization key, supply it with the request. This key authorizes requests to Privy and is separate from a chain private key. Privy's [request-signing guide](https://docs.privy.io/controls/authorization-keys/using-owners/sign/overview) explains those permissions.

<Tabs>
  <Tab title="TypeScript">
    Install Privy's server client alongside the bridge packages:

    ```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    npm install --legacy-peer-deps @privy-io/node@0.35.0 viem@2.54.6
    ```

    ```typescript theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import { PrivyClient } from "@privy-io/node"
    import { getAddress } from "viem"
    import { createAleoClient, createBridgeClient, evmHttp, solanaHttp } from "@provablehq/aleo-bridge-sdk"
    import { createPrivyEvmClient, createPrivySolanaClient } from "@provablehq/aleo-bridge-sdk/privy"

    const privy = new PrivyClient({
      appId: process.env.PRIVY_APP_ID!, appSecret: process.env.PRIVY_APP_SECRET!,
    })
    const authorizationKey = process.env.PRIVY_AUTHORIZATION_PRIVATE_KEY
    const authorizationContext = authorizationKey
      ? { authorization_private_keys: [authorizationKey] }
      : undefined
    const ethereum = await createPrivyEvmClient({
      client: privy, walletId: process.env.PRIVY_EVM_WALLET_ID!,
      address: getAddress(process.env.PRIVY_EVM_ADDRESS!), authorizationContext,
      transport: evmHttp(process.env.ETHEREUM_RPC_URL!),
    })
    const solana = await createPrivySolanaClient({
      client: privy, walletId: process.env.PRIVY_SOLANA_WALLET_ID!,
      address: process.env.PRIVY_SOLANA_ADDRESS!, authorizationContext,
      transport: solanaHttp(process.env.SOLANA_RPC_URL!),
    })
    const bridge = createBridgeClient({
      environment: "mainnet",
      clients: {
        ethereum, solana,
        aleo: createAleoClient({ publicClient: nativeAleo.publicClient, account: nativeAleo.walletClient }),
      },
    })
    ```

    The `bridge` uses the saved wallet IDs to request source signatures from Privy.
  </Tab>

  <Tab title="Python">
    The bridge package includes Privy's Python SDK. Resolve the signers to verify that the wallet IDs, chains, and addresses match:

    ```python theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
    import os
    from privy import PrivyClient
    from aleo_bridge import Bridge, Ethereum, FileCheckpointStore, Solana
    from aleo_bridge.privy import PrivyEvmSigner, PrivySolanaSigner

    privy = PrivyClient(
        app_id=os.environ["PRIVY_APP_ID"], app_secret=os.environ["PRIVY_APP_SECRET"],
    )
    authorization_key = os.environ.get("PRIVY_AUTHORIZATION_PRIVATE_KEY")
    authorization_keys = [authorization_key] if authorization_key else None
    evm_signer = PrivyEvmSigner(
        privy, wallet_id=os.environ["PRIVY_EVM_WALLET_ID"],
        address=os.environ["PRIVY_EVM_ADDRESS"], authorization_private_keys=authorization_keys,
    )
    solana_signer = PrivySolanaSigner(
        privy, wallet_id=os.environ["PRIVY_SOLANA_WALLET_ID"],
        address=os.environ["PRIVY_SOLANA_ADDRESS"], authorization_private_keys=authorization_keys,
    )
    evm_signer.resolve()
    solana_signer.resolve()
    ethereum = Ethereum(os.environ["ETHEREUM_RPC_URL"], signer=evm_signer)
    solana = Solana(os.environ["SOLANA_RPC_URL"], signer=solana_signer)
    bridge = Bridge(
        aleo, ethereum=ethereum, solana=solana,
        checkpoints=FileCheckpointStore(".bridge/checkpoints"),
    )
    ```

    Both connections can now request authorized signatures from Privy. The optional authorization key is passed through only when supplied.
  </Tab>
</Tabs>

### Continue with the bridge flow

Use the provider's `bridge` in the [SOL quote and submission steps](#2-review-a-sol-quote), replacing `solanaBridge` in TypeScript or `solana_bridge` in Python. For Ethereum assets, use the [ETH example](../trading/fund-and-bridge#3-request-and-review-a-quote) or the [USDCx flow](#receive-usdcx-confidentially). Use your configured Aleo account as the USDCx recipient so it can authorize destination completion.

For REST, use the embedded wallet's Ethereum or Solana address as `SOURCE_ADDRESS` and your Aleo recipient as `DESTINATION_ADDRESS`. The [order flow](../trading/fund-and-bridge#4-submit-the-reviewed-transfer) returns transactions for your service to sign through Dynamic or Privy. Submit signed bytes only if the wallet has not broadcast them; otherwise report the original transaction ID. Provider credentials stay with the signing client.

## Next Steps

### Run packaged examples

The [TypeScript bridge examples](https://github.com/ProvableHQ/veil/tree/main/packages/bridge/examples) cover Solana transfers, USDCx delivery modes, and USDC routes through Arc. From that example package, after its documented setup, these commands preview a quote:

```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
npx tsx sol-to-aleo.ts
USDCX_MINT_MODE=private USDCX_SECRET_NONCE="${BRIDGE_MINT_SECRET_NONCE:?Set the saved mint nonce}" npx tsx usdc-to-usdcx.ts
```

The private USDCx example needs your saved nonzero nonce as `USDCX_SECRET_NONCE` and the matching Aleo recipient. Previewing a quote does not authorize a deposit. Follow the example's execution acknowledgement only after review.

The [Python bridge examples](https://github.com/ProvableHQ/python-sdk/tree/master/bridge-sdk/examples) include `bridge_sol`, `bridge_usdc_private_balance`, `bridge_usdc_private_recipient`, and `l2_arc_aleo_roundtrip`. Their help lists the required account and route inputs:

```bash theme={"languages":{"custom":["/languages/leo.tmLanguage.json"]}}
python -m aleo_bridge.examples.bridge_sol --help
python -m aleo_bridge.examples.bridge_usdc_private_recipient --help
python -m aleo_bridge.examples.dynamic_wallets --help
```

The [TypeScript Dynamic and Privy examples](https://github.com/ProvableHQ/veil/tree/main/packages/bridge/examples/remote-wallets) show how to reconnect existing embedded wallets. Python's `dynamic_wallets` and `privy_wallets` examples include quoting and optional execution. Run without execution flags first to review the transfer.

### Prepare funds for trading

After Hyperlane delivery, [shield your public balance](../trading/fund-and-bridge#shield-public-balances). After USDCx record delivery, continue to [Quote](../trading/quote).

### Recover a transfer

Use the saved checkpoint or REST order ID to [recover an interrupted transfer](../trading/fund-and-bridge#6-recover-an-interrupted-transfer) without starting another one.


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