Cloak

Private invoicing for freelancers — get paid on-chain without exposing your income or clients.

Cloak

Created At

ETHGlobal Cannes 2026

Project Description

Cloak is a private invoicing platform for freelancers built on Base Sepolia. Freelancers connect their wallet via Dynamic, which derives a deterministic privacy account through Unlink's ZK pool — no wallet addresses or secrets are ever stored server-side.

When a freelancer creates an invoice, they share a payment link with their client. The client can pay with USDC directly, or with ETH/WETH which is automatically swapped to USDC via Uniswap V3's SwapRouter02 using a disposable burner wallet. The swap happens inside the privacy pool, and the exact invoice amount is deposited to the freelancer's anonymous Unlink address. Excess funds and gas are refunded to the payer.

On-chain, there is zero connection between the payer and the freelancer. The platform's database stores only invoice IDs, anonymous Unlink addresses, and amounts — no wallet addresses, no transaction hashes, no seeds. The freelancer can withdraw their private balance to any wallet at any time, completely unlinkable to the original payments.

The privacy model ensures that even the platform operator cannot link payers to freelancers, and a full database breach reveals nothing about user identities.

How it's Made

Built with Next.js 15 App Router as a single monorepo — API routes handle all backend logic, no separate server.

Unlink SDK is the core privacy layer. Freelancer accounts are derived deterministically from an EOA wallet signature — the user signs a fixed message via Dynamic, we keccak256 hash the signature to produce a seed, and pass it to unlinkAccount.fromSeed(). This means the same wallet always recovers the same privacy account, and the seed never needs to be stored server-side. Deposits use the two-phase Permit2 witness flow — our server calls /transactions/deposit/prepare to get the EIP-712 typed data, the payer signs it in their browser wallet, and we submit the signature back to Unlink. The payer's private key never leaves their browser.

Uniswap V3 powers cross-token payments. When a payer selects ETH or WETH for a USDC invoice, we generate a disposable burner wallet server-side, the payer sends funds to it in a single transaction, then the server wraps ETH to WETH, calls exactOutputSingle on SwapRouter02 to get the exact invoice amount in USDC, deposits the output to the freelancer's Unlink address via Permit2, and refunds leftover ETH back to the payer. We use QuoterV2 on-chain to show a live quote before the payer commits. The burner is destroyed after use.

Dynamic JS SDK handles wallet authentication. We restrict to EOA wallets only because MPC wallets produce non-deterministic signatures which would break our seed derivation model — a key architectural decision we hit during development.

Turso (libSQL) stores only invoices — ID, anonymous Unlink address, amount, status. No wallet addresses, no seeds, no transaction hashes. We deliberately removed tx hash storage mid-build when we realised it would let the freelancer identify the payer's on-chain address, breaking the privacy model.

Notably hacky: the entire cross-token swap flow runs through an ephemeral burner wallet that lives only in server memory for the duration of one API call, handles 5+ on-chain transactions (wrap, approve, swap, approve Permit2, deposit), then self-destructs and refunds the payer.

background image mobile

Join the mailing list

Get the latest news and updates