Search projects, hackathons, sponsors and tech

    ← All projects

    Pact

    Pact: Mutual B2B escrow on ENS — both parties fund USDC, terms are on-chain.

    Demo

    About

    Pact is a mutual B2B engagement layer, not a one-sided clientflow tool. Two businesses — e.g. a dev studio and a design agency co-pitching a client — propose work using one of six templates (Fixed, Milestone 2-5 phases, Retainer, Time & Materials, Recurring, Split Delivery + a guided custom builder), countersign, and fund USDC escrow before work starts. Both have skin in the game. Terms don't live in a PDF. They are written as ENSIP-5 text records (pact:type, pact:scope, pact:amount, pact:terms-hash, per-milestone records) under eng-<hex>.pact-hack.eth and *.pact-hack.eth business subnames. Either party can click "Verify on ENS" to resolve the name live in the browser — no backend call — and compare the on-chain hash to what was shown. Scope ambiguity, the #1 cause of B2B payment disputes, becomes impossible. Completion is backend-optional: PactEscrow.releaseMilestone() and autoRelease() are callable directly by the accepting party's wallet. If Pact's server disappears, the other party still gets paid. The final release emits PactCompleted — both ENS subnames, template, total value, on-time and disputed flags — a permanent, portable reputation event. Anyone can reproduce a profile from Etherscan. World Selfie Check proves a live human behind each business at signup and again for high-value (> $5k) accepts. Privy provides email-only login, embedded wallets, and gas sponsorship — no seed phrase, no chain selector.

    How it's made

    Contracts (Foundry, Solidity ^0.8.24, Sepolia): PactRegistry.sol handles business registration, createEngagement/signEngagement (2-of-2 -> ACTIVE), and setEscrow (one-time wiring). PactEscrow.sol holds MilestoneTerms per milestone (deadline, grace, evidenceHash, acceptanceWindow) and enforces lateness on-chain, high-value World attestation via attestWorldVerification, N-way releaseSplit with pull-payment claimShare fallback, and optimistic disputes (proposeResolution -> challenge window -> executeResolution or pre-agreed default). PactScore.sol attests score tiers [200,550,800]. MockUSDC (6 dec) is the test USDC. Custom errors only; PactCompleted is guarded by completedEmitted. We hit the 24kb limit at 32kb — enabled optimizer=true, runs=200 in contracts/foundry.toml to ship at 19.6kb. Backend (Node/Express/TS, ESM, Pact/backend): Supabase Postgres is a mirror only — on-chain is authoritative. 9 migrations (0001 core + 0007 M4 obligations + 0008 bonds + 0009 optimistic proposals, etc., all pgcrypto). Routes: POST /businesses/register, POST /proposals + GET /:token (410 after 14d) + POST /:token/accept, POST /engagements + submit/release/world-check, dispute/resolve + resolve-propose/resolve-challenge, GET /businesses/:subname/reputation (now returns score/tier), POST /scheduler/sweep (supports Authorization: Bearer <CRON_SECRET> for external cron, otherwise Privy). Auth is PrivyClient ( @privy-io/node ) verifyAccessToken -> resolveIdentity (prefers embedded wallet), dev fallback via x-wallet-address only when ALLOW_DEV_AUTH=true. New Supabase sb_secret_... is aliased to SUPABASE_SERVICE_KEY in config/env.ts. Frontend (Next.js 16 App Router, Tailwind v4, Pact/frontend): Warp-inspired dark canvas #2b2622 + turquoise #2DD4BF, Inter 400 tight tracking, DM Mono/Instrument Serif. Privy PrivyProvider (@privy-io/react-auth + @stripe/stripe-js peer) wraps AuthProvider (lib/auth.tsx) which fetches a fresh getAccessToken() per request (not cached) and derives the embedded wallet from linkedAccounts. lib/api.ts is a typed client for all 18 endpoints with async getAuth. Canvas-UI effects are vendored (components/canvas/DecryptReveal + GlyphRain + ParticleReveal + rect-cache) — all html-in-canvas WebGL with plain-HTML fallback and prefers-reduced-motion crisp path. Partner benefit: ENSv2 makes terms verifiable without trusting our DB; Privy removes seed-phrase friction and lets auto-release work via session signers; World ID gives sybil resistance without KYC. The hack is that every critical fund movement has a direct on-chain path and the UI just offers a one-click wrapper for it.