> ## Documentation Index
> Fetch the complete documentation index at: https://starkware-9575960b-starkzapv3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Bridging

> Bridge assets between Ethereum or Solana and Starknet using Starkzap bridge tokens, external wallet adapters, deposit and withdrawal APIs

## Overview

Starkzap supports **bidirectional bridging** between Starknet and supported external chains:

* **Ethereum** (Canonical, CCTP, OFT, OFT-migrated routes)
* **Solana** (Hyperlane routes)

**Deposit flow (external chain → Starknet):**

1. Configure the SDK (including optional bridging config)
2. Fetch bridgeable tokens with `sdk.getBridgingTokens(...)`
3. Connect an external wallet (`ConnectedEthereumWallet` or `ConnectedSolanaWallet`)
4. Inspect balance, allowance, and estimated fees
5. Call `wallet.deposit(...)` to submit the source-chain transaction

**Withdraw flow (Starknet → external chain):**

1. Inspect L2 balance and estimated fees with `wallet.getWithdrawBalance(...)` and `wallet.getInitiateWithdrawFeeEstimate(...)`
2. Call `wallet.initiateWithdraw(...)` to burn/lock tokens on Starknet
3. Monitor status with `wallet.getWithdrawalState(...)` or `wallet.monitorWithdrawal(...)`
4. For Canonical and CCTP: call `wallet.completeWithdraw(...)` when state is `READY_TO_CLAIM`

## Install Optional Dependencies

Install only what you use.

For Ethereum routes:

```bash theme={null}
npm install ethers
```

For Solana routes:

```bash theme={null}
npm install @solana/web3.js @hyperlane-xyz/sdk @hyperlane-xyz/registry @hyperlane-xyz/utils
```

## SDK Configuration

Use `bridging` config when you need custom external RPCs or OFT support.
The SDK uses external RPCs to read source-chain state (balances/allowances), estimate bridge fees, and submit source-chain transactions reliably. Without explicit RPC URLs, these operations can be rate-limited or unavailable depending on your environment:

```typescript theme={null}
import { StarkZap } from "starkzap";

const sdk = new StarkZap({
  network: "mainnet",
  bridging: {
    ethereumRpcUrl: "https://eth-mainnet.g.alchemy.com/v2/<key>",
    solanaRpcUrl: "https://solana-mainnet.g.alchemy.com/v2/<key>",
    layerZeroApiKey: "<layerzero-key>", // required for OFT/OFT-migrated routes
  },
});
```

<Warning>
  OFT bridging requires `bridging.layerZeroApiKey` and is supported on Starknet Mainnet routes only.
</Warning>

## Fetch Bridgeable Tokens

```typescript theme={null}
import { ExternalChain } from "starkzap";

// All bridgeable tokens for current Starknet environment
const allTokens = await sdk.getBridgingTokens();

// Filter by source chain
const ethereumTokens = await sdk.getBridgingTokens(ExternalChain.ETHEREUM);
const solanaTokens = await sdk.getBridgingTokens(ExternalChain.SOLANA);
```

## Connect External Wallets

Take a look at the [Examples](/build/starkzap/examples). For WalletConnect setup details, see [WalletConnect Docs](https://docs.walletconnect.network/). In practice, you establish the external wallet session first (for example with WalletConnect), then pass its provider/account/chain into `ConnectedEthereumWallet.from(...)` or `ConnectedSolanaWallet.from(...)` for bridge calls.

### Ethereum (EIP-1193)

```typescript theme={null}
import { ConnectedEthereumWallet, ExternalChain } from "starkzap";

const evmProvider = window.ethereum;
const [evmAddress] = await evmProvider.request({ method: "eth_requestAccounts" });
const evmChainId = await evmProvider.request({ method: "eth_chainId" }); // "0x1" or "0xaa36a7"

const ethWallet = await ConnectedEthereumWallet.from(
  {
    chain: ExternalChain.ETHEREUM,
    provider: evmProvider,
    address: evmAddress,
    chainId: evmChainId, // evm wallet's chain id
  },
  wallet.getChainId() // starknet wallet's chain id
);
```

### Solana

```typescript theme={null}
import { ConnectedSolanaWallet, ExternalChain } from "starkzap";

const solWallet = await ConnectedSolanaWallet.from(
  {
    chain: ExternalChain.SOLANA,
    provider: solanaProvider, // must implement signAndSendTransaction()
    address: solanaAddress,
    chainId: solanaChainId, // e.g. mainnet/testnet genesis hash from wallet adapter
  },
  wallet.getChainId()
);
```

<Warning>
  External wallet network and Starknet network must match by environment:
  Ethereum Mainnet with Starknet Mainnet, Ethereum Sepolia with Starknet Sepolia, Solana Mainnet with Starknet Mainnet, and Solana Testnet with Starknet Sepolia.

  | External Network | Identifier                         | Starknet Network |
  | ---------------- | ---------------------------------- | ---------------- |
  | Ethereum Mainnet | `1`                                | Starknet Mainnet |
  | Ethereum Sepolia | `11155111`                         | Starknet Sepolia |
  | Solana Mainnet   | `5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Starknet Mainnet |
  | Solana Testnet   | `4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z` | Starknet Sepolia |
</Warning>

## Estimate and Deposit

```typescript theme={null}
import { Amount, fromAddress } from "starkzap";

const token = ethereumTokens[0];
if (!token) throw new Error("No bridge token available");

// 1) Source-chain available balance
const available = await wallet.getDepositBalance(token, ethWallet);

// 2) ERC20 allowance (null for native/non-allowance routes)
const allowance = await wallet.getAllowance(token, ethWallet);

// 3) Fee estimation (fastTransfer only applies to CCTP)
const fees = await wallet.getDepositFeeEstimate(token, ethWallet, {
  fastTransfer: true,
});

// 4) Submit deposit tx on source chain
const recipient = fromAddress(
  "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
); // Starknet recipient

const tx = await wallet.deposit(
  recipient,
  Amount.parse("25", token.decimals, token.symbol),
  token,
  ethWallet,
  { fastTransfer: true }
);

console.log(tx.hash);
```

## Withdraw from Starknet

### Initiate (all protocols)

```typescript theme={null}
import { Amount, fromAddress } from "starkzap";

const token = ethereumTokens[0];
if (!token) throw new Error("No bridge token available");

const recipient = "0xYourEthereumAddress"; // L1 recipient

// 1) L2 balance available to withdraw
const balance = await wallet.getWithdrawBalance(token, ethWallet);

// 2) Estimate L2 fee for initiating the withdrawal
const fees = await wallet.getInitiateWithdrawFeeEstimate(token, ethWallet, {
  protocol: "cctp",
  fastTransfer: true,
});

// 3) Submit the Starknet burn/initiate transaction
const tx = await wallet.initiateWithdraw(
  recipient,
  Amount.parse("25", token.decimals, token.symbol),
  token,
  ethWallet,
  { protocol: "cctp", fastTransfer: true }
);

console.log(tx.hash);
```

### Complete (Canonical & CCTP only)

OFT and Hyperlane are single-step — a relayer handles L1 delivery automatically. For Canonical and CCTP, a second L1 transaction is required once the state becomes `READY_TO_CLAIM`.

```typescript theme={null}
// Poll until the withdrawal is ready to claim
const result = await wallet.monitorWithdrawal(token, tx.hash);

if (result.protocol === "cctp" && result.attestation && result.message) {
  // Estimate the L1 gas cost before submitting
  const l1Fee = await wallet.getCompleteWithdrawFeeEstimate(
    Amount.parse("25", token.decimals, token.symbol),
    recipient,
    token,
    ethWallet,
    {
      protocol: "cctp",
      attestation: result.attestation,
      message: result.message,
      nonce: result.nonce,
      expirationBlock: result.expirationBlock,
    }
  );

  // Submit the L1 completion transaction
  const l1Tx = await wallet.completeWithdraw(
    recipient,
    Amount.parse("25", token.decimals, token.symbol),
    token,
    ethWallet,
    {
      protocol: "cctp",
      attestation: result.attestation,
      message: result.message,
      nonce: result.nonce,
      expirationBlock: result.expirationBlock,
    }
  );

  console.log(l1Tx.hash);
}
```

### Auto-withdraw (Canonical only)

Canonical supports an `autoWithdraw` option where a relayer handles L1 completion — no `completeWithdraw` call needed.

```typescript theme={null}
const fees = await wallet.getInitiateWithdrawFeeEstimate(token, ethWallet, {
  protocol: "canonical",
  autoWithdraw: true,
});

await wallet.initiateWithdraw(
  recipient,
  Amount.parse("25", token.decimals, token.symbol),
  token,
  ethWallet,
  {
    protocol: "canonical",
    autoWithdraw: true,
    preferredFeeToken: fees.autoWithdrawFee?.token,
  }
);
```

## Monitor Bridge Transfers

### Simplified state (recommended for UI)

```typescript theme={null}
// Withdrawal state: "PENDING" | "READY_TO_CLAIM" | "COMPLETED" | "ERROR"
const state = await wallet.getWithdrawalState(token, { starknetTxHash: tx.hash });

// Deposit state: "PENDING" | "COMPLETED" | "ERROR"
const depositState = await wallet.getDepositState(token, { externalTxHash: ethTxHash });
```

`WithdrawalState` values:

* `PENDING` — bridging in progress, no user action needed
* `READY_TO_CLAIM` — ready to finalize on L1; call `completeWithdraw` (CCTP/Canonical)
* `COMPLETED` — bridge flow fully complete
* `ERROR` — unrecoverable error

### Detailed status (advanced use)

Use `monitorWithdrawal` to get the full status snapshot including CCTP attestation data needed for `completeWithdraw`:

```typescript theme={null}
const result = await wallet.monitorWithdrawal(token, tx.hash);
// result.status: BridgeTransferStatus
// For CCTP when READY_TO_CLAIM: result.attestation, result.message, result.nonce

// You can pass a previously-fetched result back to avoid redundant network calls
const state = await wallet.getWithdrawalState(token, result);
```

Similarly for deposits:

```typescript theme={null}
const result = await wallet.monitorDeposit(token, ethTxHash);
const depositState = await wallet.getDepositState(token, result);
```

## Protocol Notes

| Protocol               | Chain    | Deposit                                                                   | Withdrawal                                                                                                                |
| ---------------------- | -------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `canonical`            | Ethereum | Standard flow; approval may be required for ERC20 tokens.                 | Two-step: initiate on L2, complete on L1. Supports `autoWithdraw` to skip the L1 step via relayer.                        |
| `cctp`                 | Ethereum | Supports `fastTransfer`; fee estimate includes CCTP fast transfer bp fee. | Two-step: wait for Circle attestation, then call `completeWithdraw`. Expired attestations are re-requested automatically. |
| `oft` / `oft-migrated` | Ethereum | Requires `bridging.layerZeroApiKey`; mainnet-only route availability.     | Single-step: LayerZero relayer handles L1 delivery automatically. No `completeWithdraw` needed.                           |
| `hyperlane`            | Solana   | Requires Solana + Hyperlane optional dependencies.                        | Single-step: Hyperlane relayer handles delivery automatically.                                                            |

## Common Errors

* **Chain mismatch**: token source chain and connected external wallet chain must match.
* **Missing LayerZero key**: OFT routes require `bridging.layerZeroApiKey`.
* **Unsupported chain pair**: Ethereum mainnet must pair with Starknet mainnet; testnet pairings must match.
* **CCTP options missing**: `completeWithdraw` requires `options` with attestation data for CCTP routes — the parameter is non-optional in practice.
* **Attestation expired**: CCTP attestations have an expiration block; re-attestation is requested automatically during `completeWithdraw`.

For additional issues, see [Troubleshooting](/build/starkzap/troubleshooting).

## Next Steps

* [Configuration](/build/starkzap/configuration) — full `SDKConfig` options
* [API Reference](/build/starkzap/api-reference) — exact method signatures
* [Examples](/build/starkzap/examples) — web example with bridge UI flow
