Build a cross-chain USDC bridge using Squid Router API with Para SDK for seamless wallet management across Ethereum, Base, and Solana networks. The key integration pattern: Squid Router provides the cross-chain route and signable transaction data, while Para SDK handles the wallet management and transaction signing across multiple networks.

Para Setup Requirements

Before integrating with Squid Router, ensure your Para configuration supports all required networks:
  • Para API key with Ethereum, Base, and Solana networks enabled
  • Squid Router integrator ID from Squid Router
  • Each enabled network will create wallets for your users on that chain
Important: Your Para API key must have all three networks (Ethereum, Base, Solana) enabled in your Developer Portal for the multi-network signers to work properly.

Prerequisites

Before integrating Para with Squid Router, ensure you have:
  • Para API key with all three networks enabled: Ethereum, Base, and Solana
  • Squid Router integrator ID from Squid Router
  • Node.js 18+ and Next.js development environment

Installation

Install the required dependencies:
npm install @getpara/react-sdk @0xsquid/sdk
npm install @getpara/ethers-v6-integration @getpara/solana-web3.js-v1-integration
npm install @tanstack/react-query ethers@^6 @solana/web3.js lucide-react

Environment Variables

Configure your environment variables:
# .env.local
NEXT_PUBLIC_PARA_API_KEY=your_para_api_key
NEXT_PUBLIC_PARA_ENVIRONMENT=BETA
NEXT_PUBLIC_SQUID_INTEGRATOR_ID=your_squid_integrator_id

Configuration Setup

Create your constants file with network configurations and validation:
// src/constants.ts
import { Environment } from "@getpara/react-sdk";

export const PARA_API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? "";
export const PARA_ENVIRONMENT = (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA;

if (!PARA_API_KEY) {
  throw new Error("API key is not defined. Please set NEXT_PUBLIC_PARA_API_KEY in your environment variables.");
}

export const SQUID_INTEGRATOR_ID = process.env.NEXT_PUBLIC_SQUID_INTEGRATOR_ID ?? "";

if (!SQUID_INTEGRATOR_ID) {
  throw new Error(
    "Squid integrator ID is not defined. Please set NEXT_PUBLIC_SQUID_INTEGRATOR_ID in your environment variables."
  );
}

export const SUPPORTED_NETWORKS = ["ethereum", "base", "solana"] as const;
export type SupportedNetwork = (typeof SUPPORTED_NETWORKS)[number];

type NetworkConfig = {
  name: string;
  icon: string;
  chainId: number | string;
  usdcContractAddress: string;
  rpcUrl: string;
  networkType: "mainnet" | "testnet" | "devnet";
  networkCategory: "evm" | "svm";
};

export const NETWORK_CONFIG: Record<SupportedNetwork, NetworkConfig> = {
  ethereum: {
    name: "Ethereum",
    icon: "/ethereum.png",
    chainId: 1,
    usdcContractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    rpcUrl: process.env.NEXT_PUBLIC_ETHEREUM_RPC_URL ?? "https://ethereum-rpc.publicnode.com",
    networkType: "mainnet",
    networkCategory: "evm",
  },
  base: {
    name: "Base",
    icon: "/base.png",
    chainId: 8453,
    usdcContractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    rpcUrl: process.env.NEXT_PUBLIC_BASE_RPC_URL ?? "https://base-rpc.publicnode.com",
    networkType: "mainnet",
    networkCategory: "evm",
  },
  solana: {
    name: "Solana",
    icon: "/solana.png",
    chainId: "solana-mainnet-beta",
    usdcContractAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    rpcUrl: process.env.NEXT_PUBLIC_SOLANA_RPC_URL ?? "https://solana-rpc.publicnode.com",
    networkType: "mainnet",
    networkCategory: "svm",
  },
};

export const SUPPORTED_ASSETS = ["usdc"] as const;
export type SupportedAsset = (typeof SUPPORTED_ASSETS)[number];

export const ASSET_DETAILS: Record<SupportedAsset, {
  id: string;
  name: string;
  symbol: string;
  icon: string;
}> = {
  usdc: {
    id: "usdc",
    name: "USD Coin",
    symbol: "USDC",
    icon: "/usdc.png",
  },
};

Squid Client Hook

Initialize the Squid Router SDK:
// src/hooks/useSquidClient.tsx
import { useEffect, useState } from "react";
import { Squid } from "@0xsquid/sdk";
import { SQUID_INTEGRATOR_ID } from "@/constants";

let squidInstance: Squid | null = null;

export function useSquidClient() {
  const [client, setClient] = useState<Squid | null>(squidInstance);

  useEffect(() => {
    if (squidInstance) {
      setClient(squidInstance);
      return;
    }

    const initSquid = async () => {
      const squid = new Squid({
        baseUrl: "https://apiplus.squidrouter.com",
        integratorId: SQUID_INTEGRATOR_ID,
      });
      await squid.init();
      squidInstance = squid;
      setClient(squid);
    };

    initSquid();
  }, []);

  return client;
}

Multi-Network Signers

Para creates separate signers for each network, allowing you to sign transactions on Ethereum, Base, and Solana:
// src/hooks/useSigners.tsx
import { useQuery } from "@tanstack/react-query";
import { useAccount, useClient } from "@getpara/react-sdk";
import { ParaEthersSigner } from "@getpara/ethers-v6-integration";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { ethers } from "ethers";
import { Connection } from "@solana/web3.js";
import { NETWORK_CONFIG } from "@/constants";

async function initializeSigners(para, account) {
  // Initialize Ethereum signer
  const ethereumProvider = new ethers.JsonRpcProvider(NETWORK_CONFIG.ethereum.rpcUrl);
  const ethereumSigner = new ParaEthersSigner(para, ethereumProvider);
  
  // Initialize Base signer  
  const baseProvider = new ethers.JsonRpcProvider(NETWORK_CONFIG.base.rpcUrl);
  const baseSigner = new ParaEthersSigner(para, baseProvider);
  
  // Initialize Solana signer
  const solanaConnection = new Connection(NETWORK_CONFIG.solana.rpcUrl);
  const solanaSigner = new ParaSolanaWeb3Signer(para, solanaConnection);

  return {
    ethereumEthers: { provider: ethereumProvider, signer: ethereumSigner, address: "...", isInitialized: true },
    baseEthers: { provider: baseProvider, signer: baseSigner, address: "...", isInitialized: true },
    solanaSvm: { signer: solanaSigner, connection: solanaConnection, address: "...", isInitialized: true },
  };
}

export function useSigners() {
  const para = useClient();
  const { data: account } = useAccount();

  const { data: signers } = useQuery({
    queryKey: ["globalSigners", account?.isConnected],
    queryFn: () => initializeSigners(para, account),
    enabled: !!para && !!account?.isConnected,
    staleTime: Infinity,
  });

  // Returns initialized signers for all three networks
  return signers || defaultSignerState;
}
Key Point: Para automatically creates wallets for each enabled network in your API key configuration. Each signer can then sign transactions for its respective blockchain. See the full implementation for complete error handling and initialization logic.

Bridge Operations Hook

The integration follows a clear pattern: Squid Router provides the route and signable transaction data, Para SDK handles the signing:
// src/hooks/useSquidBridge.tsx
import { useQuery, useMutation } from "@tanstack/react-query";
import { useSquidClient } from "./useSquidClient";
import { useSigners } from "./useSigners";

export function useSquidBridge() {
  const squid = useSquidClient();
  const { ethereumEthers, baseEthers, solanaSvm } = useSigners();

  const useQuote = (params: QuoteParams) => {
    return useQuery({
      queryKey: ["squidQuote", params],
      queryFn: () => fetchQuote(params), // Squid generates route and transaction data
      enabled: !!(squid && params.originNetwork && params.destNetwork && params.amount),
      staleTime: 20000,
      refetchInterval: 20000,
    });
  };

  const executeMutation = useMutation({
    mutationFn: async ({ quote, originNetwork, onProgress }) => {
      // 1. Squid provides the signable route object
      const route = quote.route;
      
      // 2. Para signer signs the transaction for the appropriate network
      const signer = originNetwork === "ethereum" ? ethereumEthers.signer : 
                     originNetwork === "base" ? baseEthers.signer : 
                     solanaSvm.signer;
      
      // 3. Execute with Squid client + Para signer
      const tx = await squid.executeRoute({ signer, route });
      
      // 4. Monitor transaction status
      return tx;
    },
  });

  return {
    useQuote,
    executeBridge: executeMutation.mutate,
    isExecuting: executeMutation.isPending,
  };
}
See the full implementation for complete quote fetching, transaction execution, and status monitoring logic.

Main Application Component

Create the core bridge interface structure:
// src/app/page.tsx
"use client";

import { useState } from "react";
import { useAccount, useModal, ParaModal } from "@getpara/react-sdk";
import { useSquidBridge } from "@/hooks/useSquidBridge";
import { useSigners } from "@/hooks/useSigners";

export default function Home() {
  const { openModal } = useModal();
  const { data: account } = useAccount();
  const { useQuote, executeBridge, isExecuting } = useSquidBridge();
  const { ethereumEthers, baseEthers, solanaSvm } = useSigners();
  
  const [originNetwork, setOriginNetwork] = useState(null);
  const [destNetwork, setDestNetwork] = useState(null);
  const [amount, setAmount] = useState("");

  const { data: quote } = useQuote({
    originNetwork,
    destNetwork,
    amount,
    originAddress: getNetworkAddress(originNetwork),
    destAddress: getNetworkAddress(destNetwork),
  });

  const handleBridge = () => {
    executeBridge({
      quote,
      originNetwork,
      onProgress: (progressData) => {
        // Handle transaction progress updates
      },
    });
  };

  return (
    <div className="min-h-screen flex items-center justify-center">
      {/* Network selection, amount input, bridge button */}
      <ParaModal />
    </div>
  );
}
See the full implementation for complete UI components and transaction processing logic.

Para Provider Setup

Wrap your application with the Para and QueryClient providers:
// src/providers/providers.tsx
"use client";

import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@getpara/react-sdk";
import { PARA_API_KEY, PARA_ENVIRONMENT } from "@/constants";

const queryClient = new QueryClient();

export function Providers({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <QueryClientProvider client={queryClient}>
      <ParaProvider
        paraClientConfig={{
          apiKey: PARA_API_KEY,
          env: PARA_ENVIRONMENT,
        }}>
        {children}
      </ParaProvider>
    </QueryClientProvider>
  );
}

Key Features

This integration provides:
  • Multi-network support: Seamless bridging between Ethereum, Base, and Solana
  • Unified wallet management: Single Para authentication for all networks
  • Real-time quotes: Automatic quote updates with optimal routing
  • Transaction monitoring: Status tracking with error handling and recovery options
  • Production-ready: Comprehensive error handling and user feedback

Next Steps