Project guide for the Gnars DAO website. Keep terse, keep current.
docs/INDEX.mdis the canonical documentation map. Update it in the same change when adding/moving/removing docs.- All project docs live under
docs/. Root allows onlyREADME.md,CLAUDE.md,AGENTS.md. - No docs in
tasks/orsrc/**/README.md. Scripts go inscripts/. - Docs must reflect current code. Stale doc → update or delete.
AGENTS.mdcovers subagent routing only; do not duplicate these rules there.
Next.js 16 App Router site (React 19) for Gnars DAO on Base (chain ID 8453). Built on Nouns Builder architecture. Wallet layer is split:
- thirdweb v5 — login + writes + account abstraction (
sponsorGas: true) - wagmi v2 + viem — reads transport only (connectors array empty)
pnpm dev # dev server (Turbopack)
pnpm build # prod build (do not run unless asked)
pnpm start # prod server
pnpm lint # eslint — run before PR
pnpm format # prettier write
pnpm format:check # prettier check
pnpm test # vitest unit tests (src/**/*.test.ts)
pnpm exec playwright test # e2e tests (tests/e2e/)Unit tests via vitest (test, test:watch, test:coverage) — currently cover src/lib/proposal-*. Playwright e2e specs in tests/e2e/: propdates, gnars-gov, i18n, verify-nft-multiselect.
src/
├── app/ # App Router routes incl. /auctions /proposals /propose /tv /members /treasury /feed /propdates /droposals /installations /blogs /coin-proposal /community/bounties /map /mural /swap /rounds
│ ├── api/ # ~21 route groups (alchemy, coins, ens, og, pinata, propdates, proposals, treasury, tv, 0x, rounds, …)
│ └── md/ # markdown content-negotiation target (see proxy.ts)
├── components/ # ~24 feature dirs + ui/ (shadcn)
├── hooks/ # ~38 hooks — see naming note below
├── i18n/ # next-intl config (routing, request, navigation)
├── services/ # 17 data-layer modules (auctions, proposals, treasury, feed, members, farcaster, poidh, snapshot, rounds, …)
├── lib/ # config.ts, thirdweb.ts, wagmi.ts, subgraph.ts, ipfs.ts, zora-*, proposal-*, og-*, schemas/, types/
├── data/ # static JSON (installations.json)
├── types/ # shared TS interfaces
├── utils/abis/ # contract ABIs (erc20, …)
├── workers/ # client-side search workers (blog, proposal)
└── proxy.ts # Accept: text/markdown → rewrite to /md/* (Next.js 16 proxy convention, formerly middleware.ts)
Most files use use-kebab-case.ts; 9 use useCamelCase.ts (useCastVote, useCreateCoin, useDelegate, useMintDroposal, usePoidhBounties, useVotes, etc.). Prefer kebab-case for new hooks; don't rename existing ones speculatively.
src/lib/config.ts— single source of truth for DAO addresses, chain, Zora creator allowlist, subgraph URLs, treasury token allowlist. Do not duplicate addresses elsewhere.components.json— shadcn/ui (New York, RSC on)tsconfig.json— path alias@/* → src/*next.config.ts,eslint.config.mjs,postcss.config.mjs,playwright.config.ts
- Chain hardcoded to Base (8453).
- Login — thirdweb
useConnectModal(social, email OTP, MetaMask, Coinbase, Rainbow, WalletConnect). - Writes — every onchain write goes through thirdweb
sendTransaction({ account, transaction })dispatched viauseWriteAccount(). Signer matches the user's view mode (EOA direct vs SA via userop). - Reads — wagmi
useReadContract,useReadContracts,useBalance,useWaitForTransactionReceipt,usePublicClient. Works with no connectors. - Address — single source is
useUserAddress()→{ address, saAddress, adminAddress, isConnected, isInAppWallet, viewMode, canSwitchView }. Never call wagmi'suseAccount()— it's disconnected from thirdweb state. - View-mode toggle — external-wallet users can switch SA (sponsored) vs EOA (native prompt) via
WalletDrawer; persisted to localStorage. In-app wallets pinned to SA. - Governance pre-checks — write hooks that gate on voting power must pre-read
getPastVotes/getVotesand bail with a toast before prompting signatures. - Full provider tree + decision matrix:
docs/architecture/thirdweb-wallet-layer.md.
- Server Components for initial load (SEO, perf).
- Client Components only for interactive features (bidding, voting, wizard forms).
- Builder DAO subgraph (Goldsky) for historical data via
@buildeross/hooks/@buildeross/sdk. src/services/*is the canonical data-access layer — prefer it over inline fetches.
- Tailwind CSS v4, shadcn/ui components (New York).
- Mobile-first; all DAO features must work on mobile.
- Dark mode via
next-themes.
- Locales: EN + PT-BR. Messages in
messages/{en,pt-br}/*.json(one namespace per file); config insrc/i18n/. - Never mix EN and PT in one string ("Proposals recentes" → "Propostas recentes"). New UI strings go through next-intl, both locales, same change.
- PT-BR glossary: bid → lance, auction → leilão, treasury → tesouro (not "tesouraria"), governance → governança, proposal → proposta, claim → resgate, delegation → delegação ("delegate" as a person stays "delegate").
- Tone guide:
docs/i18n/tone-brief.md— update it in the same change as any glossary/tone decision.
Full list in env.example. Summary:
# Site
NEXT_PUBLIC_SITE_URL
NEXT_PUBLIC_BASE_URL
# RPC / chain
ALCHEMY_API_KEY
NEXT_PUBLIC_ALCHEMY_API_KEY
NEXT_PUBLIC_BASE_RPC_URL
BASE_RPC
BASESCAN_API_KEY
# Web3 auth (required for login + writes)
NEXT_PUBLIC_THIRDWEB_CLIENT_ID
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID
# Subgraph / data
NEXT_PUBLIC_GOLDSKY_PROJECT_ID
NEXT_PUBLIC_ZORA_COINS_SUBGRAPH_URL
# External APIs
NEXT_PUBLIC_ZORA_API_KEY
COINGECKO_API_KEY
PINATA_JWT
NEYNAR_API_KEY
ZEROX_API_KEY # 0x Swap API (server-only)
# Optional
USDC_BASE
Secrets never go in client code. NEXT_PUBLIC_* is public by definition.
- Web3:
thirdweb,wagmi,viem,@buildeross/hooks,@buildeross/sdk,@0xsplits/splits-sdk - Creator coins:
@zoralabs/coins-sdk - Social:
@farcaster/miniapp-sdk - 3D / viz:
three,@react-three/fiber,@react-three/drei,react-globe.gl,ogl,gsap,recharts - Maps:
leaflet,react-leaflet,leaflet-draw,leaflet.markercluster - Forms:
react-hook-form,zod - Data/UX:
@tanstack/react-query,next-intl,sonner(toasts),framer-motion,minisearch,pg(rounds) @rainbow-me/rainbowkitis inpackage.jsonbut unused insrc/— slated for removal.
- Single-DAO site (Gnars only), not a multi-DAO platform.
- Deploy target: Vercel.
- Do not run
pnpm buildunless explicitly asked. - Before PR:
pnpm lint+pnpm format:check.
- Verify UI changes at runtime. Any UI change: run
pnpm dev, exercise the changed flow (and its mobile layout) before declaring done or pushing. Reviewing a PR means running it, not just reading the diff. Past misses: missing cursor-pointer, broken media rendering, wrong links — all found by the user clicking around. - Address displays link to the internal profile
/members/[address], never Basescan. - Never invent user-facing status copy ("coming soon", "beta", "unstable") from code state — confirm the feature's real status with the user first.
- Format + lint before every commit, not after CI flags it:
pnpm formaton touched files, thenpnpm lint. Pre-existing lint errors outside your diff: report, don't fix. - End every change with the one-line report:
<projeto> · <sha curto> · <o que mudou> · deploy <success|building|failed>. A change is NOT done until its deploy issuccess(verify via GitHub deployments API, full sha). - Continuing an existing PR:
git fetch origin pull/<N>/head:pr-<N>and commit on that branch — don't create a new one. Fresh worktrees needpnpm installfirst (or invoke binaries from the main repo'snode_modules/.bin/).
Everything lands directly on main and in production. Do not open a PR and wait for review — the goal is visibility, not speed: work sitting in unreviewed PRs made it impossible to know what was live.
- After EVERY change, report in ONE line:
<projeto> · <sha curto> · <o que mudou> · deploy <success|building|failed>. - A change is not done until the deploy is
success. A commit onmainwithout a confirmed deploy is the old blindness, only faster. - Gates that still apply before pushing:
tscand the test suite green; PT-BR capture for any layout change; a failed read renders as a failure, never as zero. - Do NOT wait for approval on routine changes. Ask first ONLY for: changes touching power or money (governance, funds, permissions), hard-to-reverse actions, and anything touching credentials.
- PRs are the exception, used only when explicitly requested. Worktrees still apply when the main repo has unrelated work in progress.