LiquiFlow

Cross-chain liquidity rewards solved. Earn on ETH/Base, claim on any chain via Arc+CCTP.

LiquiFlow

Created At

HackMoney 2026

Project Description

LiquiFlow solves DeFi's liquidity fragmentation problem by letting LPs earn rewards on one chain and claim them on another.

Users provide liquidity to USDC/WETH pools on Ethereum Sepolia or Base Sepolia via custom Uniswap v4 hooks. The protocol tracks their positions in real-time and calculates time-weighted rewards (liquidity × time held) to ensure fair distribution. All rewards accumulate in a central treasury on Arc Testnet, where USDC is the native gas token—making reward storage virtually free.

When claiming, users choose their destination chain (Ethereum or Base), and Circle's CCTP bridges USDC from Arc to their wallet in minutes. The entire process is automated: approve, burn on Arc, wait for attestation, mint on destination chain.

This architecture delivers three key benefits: (1) LPs earn rewards across multiple chains from a single interface, (2) treasury gas costs are 99% lower on Arc vs. Ethereum, and (3) users receive USDC on whichever chain they're actively using—no manual bridging required.

Built with Node.js backend tracking hook events, Next.js frontend for wallet interaction, and Circle's Bridge Kit SDK handling all cross-chain transfers.

How it's Made

LiquiFlow uses a five-layer architecture combining Uniswap v4, Arc Testnet, and Circle CCTP:

1. Uniswap v4 Custom Hooks (Event Layer) Deployed identical monitoring hooks on Ethereum Sepolia and Base Sepolia that emit LiquidityAdded/LiquidityRemoved events whenever LPs interact with USDC/WETH pools. These hooks don't modify pool logic—they purely observe and broadcast position changes. Contract addresses: 0xABa7EC...1Cc500 (Ethereum) and 0x4d85A0...a14500 (Base).

2. Node.js Event Aggregator (Calculation Layer) An Express server with ethers.js v6 listens to both hook contracts simultaneously using WebSocket providers. Every event gets stored in data.json with metadata (provider address, liquidity delta, timestamp, tx hash, block number). The calculateRewards() function runs on each API call, computing time-weighted rewards: for each LP, it multiplies their liquidity by seconds held, sums across all LPs, then distributes the treasury balance proportionally. Currently tracking 12 events with zero downtime.

3. Arc Testnet Treasury (Storage Layer) Chose Arc because USDC is its native gas token (like ETH on Ethereum). The treasury wallet holds 19.97 USDC at address 0xfE6887...EDD93. Storing and transferring USDC costs nearly zero since gas is paid in USDC itself. Arc's USDC implementation is unique: the contract at 0x3600...0000 is a system-level contract with dual decimals—18 for native gas, 6 for ERC-20 interface.

4. Circle CCTP Bridge (Transfer Layer) Initially tried manual CCTP integration using depositForBurn() but hit a critical error: "missing revert data" during gas estimation. Arc's native USDC system contract doesn't behave like standard ERC-20, causing manual CCTP calls to fail. Switched to @circle-fin/bridge-kit and @circle-fin/adapter-viem-v2 packages, which abstract Arc's quirks. The createAdapterFromPrivateKey() factory method handles Arc's unique USDC perfectly.

Bridge flow: (1) Approve USDC on Arc, (2) Burn via CCTP's depositForBurn (domain 26), (3) Poll Circle's attestation API, (4) Mint on destination chain (domain 0 for Sepolia, domain 6 for Base). Successful transactions: 0xeb0bb3f7... (Sepolia) and 0x8fe7679b... (Base).

5. Next.js Frontend (Interface Layer) TypeScript UI with ethers.js for wallet connection (MetaMask), TailwindCSS for styling, react-hot-toast for notifications. Fetches positions and rewards via REST API (/api/rewards/:address, /api/positions/:address), displays treasury stats, and lets users select destination chain + claim amount. The claim button triggers the backend bridge flow and shows real-time progress.

Notable Technical Challenges:

  • BigInt Serialization: Node.js can't serialize BigInt to JSON by default. Added BigInt.prototype.toJSON = function() { return this.toString(); } globally to fix Express JSON responses containing reward calculations.

  • Arc's Dual-Decimal USDC: Arc USDC uses 18 decimals for native balance but 6 decimals for ERC-20 operations. Bridge Kit's amount parameter expects decimal strings (e.g., "0.99"), which automatically handles conversion.

  • Destination Chain Gas Requirements: Discovered that the treasury needs ETH on destination chains (Sepolia, Base) to execute the mint transaction. First claim succeeded because treasury had 0.01 ETH leftover; subsequent claims failed with "Insufficient token balance on Base Sepolia." Solution: funded treasury with 0.1 ETH on both chains via Alchemy faucets.

  • Manual CCTP Failure: Spent hours debugging why messenger.depositForBurn(amount, domain, mintRecipient, burnToken) reverted. The issue: Arc's USDC at 0x3600...0000 is a special system contract that CCTP's TokenMessenger doesn't recognize as valid burnToken. Bridge Kit uses internal logic to handle this edge case.

Tech Stack Summary: Node.js, Express.js, ethers.js v6, Circle Bridge Kit SDK, Viem v2 adapters, Uniswap v4 hooks, Next.js 14, TypeScript, TailwindCSS, react-hot-toast, dotenv for env management, fs module for JSON persistence.

The system is fully operational with 100% success rate on claims after resolving gas and adapter issues. Ready for mainnet migration.

background image mobile

Join the mailing list

Get the latest news and updates

LiquiFlow | ETHGlobal