Trustless cross-chain USDC transfers in seconds, powered by Arc L1 and Circle CCTP V2.
Built for ETHGlobal HackMoney 2026 — Arc Prize Track
Arc Router treats multiple blockchains as a single liquidity surface. Users deposit USDC on any supported chain, and it arrives on the destination chain — all routed through Arc L1 as a settlement hub.
No bridges. No wrapped tokens. Native USDC on every chain, powered by Circle's CCTP V2.
Source Chain Arc L1 Destination Chain
(Ethereum/Base/ (Settlement (Ethereum/Base/
Arbitrum) Hub) Arbitrum)
User burns ──► USDC settles ──► USDC minted
USDC via on Arc via on destination
CCTP V2 receiveMessage via CCTP V2
Hop 1 ArcRouter Hop 2
(depositForBurn) (routeTransfer) (receiveMessage)
- Hop 1 — User calls
depositForBurnon the source chain. CCTP V2 burns USDC and emits a message. - Settlement — The relayer picks up Circle's attestation via the Iris API and calls
receiveMessageon Arc, minting USDC there. - Hop 2 — The ArcRouter contract on Arc approves and burns USDC via
depositForBurntoward the destination chain. - Delivery — The relayer gets the second attestation and calls
receiveMessageon the destination chain. USDC is minted to the recipient.
Total time: ~60-90 seconds on testnet (limited by Circle attestation speed).
https://github.com/user-attachments/assets/demo-placeholder
Connect wallet → Pick source/destination chains → Enter amount and recipient → Send → Watch the animated transfer progress in real-time.
┌─────────────────────────────────────────────────────────┐
│ Frontend (Next.js) │
│ ┌──────────┐ ┌──────────┐ ┌────────────────────────┐ │
│ │ Transfer │ │ Transfer │ │ Wallet (RainbowKit + │ │
│ │ Form │ │ Animation│ │ Wagmi + MetaMask) │ │
│ └────┬─────┘ └────▲─────┘ └────────────────────────┘ │
│ │ │ │
│ ┌────▼─────┐ ┌────┴─────┐ │
│ │useTransfer│ │useTransfer│ │
│ │ (hooks) │ │ Status │ │
│ └────┬─────┘ └────▲─────┘ │
│ │ │ │
│ ┌────▼──────────────┴─────┐ │
│ │ API Routes │ │
│ │ POST /api/transfers │ │
│ │ GET /api/transfers/:id│ │
│ └────┬──────────────▲─────┘ │
│ │ │ │
│ ┌────▼──────────────┴─────┐ │
│ │ SQLite (Prisma) │ │
│ └────┬──────────────▲─────┘ │
└───────┼──────────────┼───────────────────────────────────┘
│ │
┌───────▼──────────────┴───────────────────────────────────┐
│ Relayer │
│ │
│ ┌─────────┐ ┌──────────┐ ┌─────────────────────┐ │
│ │ Hop 1 │───►│ ArcRouter│───►│ Hop 2 │ │
│ │ Attest │ │ on Arc │ │ Attest + Relay │ │
│ │ + Relay │ │ (burn) │ │ to Destination │ │
│ └─────────┘ └──────────┘ └─────────────────────┘ │
│ │
│ Polls Circle Iris API ←──── CCTP V2 Attestations │
└───────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
│ Smart Contracts │
│ │
│ ArcRouter.sol (Arc Testnet) │
│ ├── routeTransfer() — Approve + burn USDC via CCTP V2 │
│ ├── setRelayer() — Update authorized relayer │
│ └── withdrawUsdc() — Emergency withdrawal │
│ │
│ Uses Circle's deployed contracts: │
│ ├── TokenMessengerV2 (depositForBurn) │
│ └── MessageTransmitterV2 (receiveMessage) │
└───────────────────────────────────────────────────────────┘
| Chain | Chain ID | CCTP Domain | USDC Address |
|---|---|---|---|
| Ethereum Sepolia | 11155111 | 0 | 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 |
| Arbitrum Sepolia | 421614 | 3 | 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d |
| Base Sepolia | 84532 | 6 | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
| Arc Testnet | 5042002 | 26 | 0x3600000000000000000000000000000000000000 |
| Contract | Network | Address |
|---|---|---|
| ArcRouter | Arc Testnet | 0x3E56fdBc57231570f56A1FCbe02251D32A4B273c |
Circle CCTP V2 contracts (same on all testnets):
- TokenMessengerV2:
0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA - MessageTransmitterV2:
0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275
| Layer | Technology |
|---|---|
| Frontend | Next.js 14, React 18, TailwindCSS, Framer Motion, shadcn/ui |
| Wallet | RainbowKit, Wagmi v2, Viem |
| Backend / API | Next.js API Routes |
| Database | SQLite via Prisma ORM |
| Relayer | Node.js (tsx), Viem wallet/public clients |
| Smart Contracts | Solidity 0.8.24, Foundry (forge) |
| Monorepo | pnpm workspaces, Turborepo |
| Attestation API | Circle Iris V2 Sandbox |
arc-router/
├── apps/web/ # Next.js frontend + API + relayer
│ ├── src/
│ │ ├── app/ # Pages, layout, API routes
│ │ │ ├── page.tsx # Main transfer page (form + animation)
│ │ │ ├── api/transfers/ # REST API for transfer tracking
│ │ │ └── providers.tsx # Wagmi + RainbowKit + React Query
│ │ ├── components/
│ │ │ ├── transfer/ # TransferAnimation
│ │ │ ├── layout/ # Header (Arc logo + ConnectButton), Footer
│ │ │ └── ui/ # Button, Popover (shadcn/ui)
│ │ ├── hooks/ # React hooks (all the blockchain logic)
│ │ │ ├── useTransfer.ts # Approve → burn → submit orchestration
│ │ │ ├── useUsdcBalance.ts
│ │ │ ├── useUsdcApproval.ts
│ │ │ ├── useDepositForBurn.ts
│ │ │ └── useTransferStatus.ts
│ │ ├── relayer/ # Offchain relayer process
│ │ │ ├── index.ts # Main loop
│ │ │ ├── hop1.ts # Source → Arc attestation + relay
│ │ │ ├── hop2.ts # Arc → Destination burn + relay
│ │ │ ├── clients.ts # Viem wallet/public clients
│ │ │ └── utils.ts # Attestation polling, helpers
│ │ └── lib/ # Shared utilities
│ │ ├── chains.ts # Chain display info
│ │ ├── transferStatus.ts # Status → animation step mapping
│ │ ├── utils.ts # cn() utility
│ │ └── wagmi.ts # Wagmi config
│ └── prisma/
│ └── schema.prisma # Transfer database model
├── packages/
│ ├── contracts/ # Foundry project
│ │ ├── src/ArcRouter.sol # Main contract
│ │ ├── test/ArcRouter.t.sol # Forge tests
│ │ └── script/Deploy.s.sol # Deployment script
│ └── shared/ # Shared constants, types, ABIs
│ └── src/
│ ├── constants.ts # Contract addresses, CCTP domains
│ ├── types/ # Transfer type definitions
│ └── abis/ # Contract ABIs
└── turbo.json # Turborepo config
- Node.js >= 18
- pnpm >= 9
- Foundry (for contract deployment)
- A wallet with testnet ETH on the chains you want to test
git clone https://github.com/your-username/arc-router.git
cd arc-router
pnpm installcp .env.example apps/web/.envEdit apps/web/.env:
DATABASE_URL="file:./dev.db"
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID="" # Get from cloud.walletconnect.com
RELAYER_PRIVATE_KEY="0x..." # Private key for the relayer EOA
NEXT_PUBLIC_ARC_ROUTER_ADDRESS="0x3E56fdBc57231570f56A1FCbe02251D32A4B273c"
CCTP_ATTESTATION_API="https://iris-api-sandbox.circle.com"
ARC_TESTNET_RPC_URL="https://rpc.testnet.arc.network"cd apps/web
pnpm db:generate
pnpm db:pushcd packages/contracts
export RELAYER_ADDRESS=0xYourRelayerAddress
forge script script/Deploy.s.sol:DeployScript \
--rpc-url https://rpc.testnet.arc.network \
--private-key $DEPLOYER_PRIVATE_KEY \
--broadcast -vvvIn one terminal — start the web app:
cd apps/web
pnpm devIn another terminal — start the relayer:
cd apps/web
pnpm relayerOpen http://localhost:3000.
- Connect your wallet using the button in the top-right corner
- Select source and destination chains from the dropdowns
- Enter the USDC amount — your balance is shown automatically
- Enter the recipient address (can be your own address on the destination chain)
- Click Send — MetaMask will prompt for two transactions:
- Approve USDC spending
depositForBurn(burns USDC on source chain)
- Watch the animation as the transfer progresses through 4 stages:
- Depositing → Settling on Arc → Sending → Delivered
- The relayer handles everything else automatically
User Action Status What Happens
─────────────────────────────────────────────────────────────
Click "Send" → ATTESTING_HOP1 USDC burned on source chain
Relayer polls Iris API for attestation
→ RELAYING_TO_ARC Attestation received, relaying to Arc
→ SETTLED_ON_ARC USDC minted on Arc
→ BURNING_ON_ARC ArcRouter burns USDC toward destination
→ ATTESTING_HOP2 Relayer polls for second attestation
→ RELAYING_TO_DEST Attestation received, relaying to dest
→ COMPLETED USDC minted on destination chain
Deployed on Arc Testnet. Called by the relayer after hop 1 settlement.
function routeTransfer(
bytes32 transferId, // Unique transfer ID for tracking
uint256 amount, // USDC amount (6 decimals)
uint32 destinationDomain, // CCTP domain of destination
bytes32 recipient // Recipient address (bytes32-padded)
) external onlyRelayerFlow: Checks USDC balance → Approves TokenMessengerV2 → Calls depositForBurn → Emits TransferRouted event.
cd packages/contracts
forge test -vvvBest Chain Abstracted USDC Apps Using Arc as a Liquidity Hub ($5,000)
Arc Router uses Arc L1 as a central liquidity hub for USDC. Instead of point-to-point bridges between every chain pair, all transfers route through Arc — creating a single settlement layer that connects Ethereum, Base, and Arbitrum.
- Users don't need to know about Arc — they pick source and destination chains, and the routing is handled automatically
- USDC is never wrapped or synthetic — it's native Circle USDC on every chain via CCTP V2
- The ArcRouter contract on Arc acts as the hub, receiving USDC from any chain and forwarding it to any other supported chain
- The relayer automates the two-hop process, polling Circle's Iris API and submitting relay transactions
Built by Siddharth at ETHGlobal HackMoney 2026.
MIT