Permit claiming web app for Ubiquity Rewards.
- Frontend: React + TypeScript + Vite in
src/. - Server:
serve.ts(Deno Deploy) serves the built SPA fromdist/and exposes a small API to record permit claims in Supabase.
- Install dependencies:
bun install
- Create a
.envfile:cp .env.example .env
- Fill in required variables (see
.env.example):- Backend (
serve.ts):SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY - Frontend (Vite):
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY - CI build convenience:
SUPABASE_ANON_KEYis used to setVITE_SUPABASE_ANON_KEYin.github/workflows/deno-deploy.yml
- Backend (
All frontend variables must be prefixed with VITE_ to be exposed to the app.
RPC uses /rpc only on *.ubq.fi hostnames; all other hostnames use https://rpc.ubq.fi.
-
Two‑port dev with HMR:
bun run dev
- Vite runs on
http://localhost:5173 serve.tsruns onhttp://localhost:8000- Vite proxies
/api/*and top‑level numeric routes to:8000.
- Vite runs on
-
Production‑like single‑port (no HMR):
bun run start
Builds the frontend and serves it + API on
http://localhost:8000.
bun run buildOutputs static files to dist/.
Deployment is handled by GitHub Actions via .github/workflows/deno-deploy.yml.
- Builds
dist/with Bun/Vite. - Deploys
serve.tsto Deno Deploy and includesdist/**. - Required Deploy secrets:
DENO_DEPLOY_TOKEN,SUPABASE_URL,SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY.
serve.ts exposes POST /api/permits/record-claim which accepts { "transactionHash": "0x...", "networkId": 100 } and derives the permit signature(s) by decoding Permit2 calldata before updating Supabase.
Note: this endpoint uses SUPABASE_SERVICE_ROLE_KEY (bypasses RLS), so security comes from on-chain transaction verification before writing.
One-off tooling lives in scripts/ (requires .env with Supabase + RPC vars).
- Audit permits against both Permit2 contracts (includes “nonce used but DB transaction missing” mismatches):
bun run permit2:audit -- --owner 0x... --include-permits --out /tmp/permit2-audit.json
- Build an invalidation plan (no transactions sent):
bun run permit2:invalidate -- --owner 0x... --target old --only-db-unclaimed --out /tmp/permit2-invalidate-plan.json
- Execute invalidations (sends transactions; use
--max-txsas a safety brake):INVALIDATOR_PRIVATE_KEY=0x... bun run permit2:invalidate -- --owner 0x... --target old --only-db-unclaimed --execute --max-txs 50 --out /tmp/permit2-invalidate-executed.json
- Backfill missing
permits.transactionvalues for claim txs:bun run permit2:backfill -- --owner 0x... --out /tmp/permit2-backfill.json- For a more complete one-off backfill on Gnosis, add
--match-mode beneficiary --scan-new-permit2-txlist. - Add
--executeto write to Supabase (use--max-updatesas a safety brake).
- Seed small test permits for QA (default: 1e18 UUSD on Gnosis; plan-only unless
--execute):INVALIDATOR_PRIVATE_KEY=0x... bun run permit2:seed-test-permits -- --beneficiary 0x... --beneficiary-user-id <githubUserId> --execute --out /tmp/test-permits.json
- Sign/broadcast test permit claims (sign-only unless
--execute):BENEFICIARY_PRIVATE_KEY=0x... bun run permit2:claim-test-permits -- --beneficiary 0x... --limit 10 --execute --batch --out /tmp/test-claims.json
bun run format / bun run format:check run Prettier across the repo (including serve.ts).
Permit2 and PermitAggregator addresses used by the UI are in src/constants/config.ts.