Skip to content

Commit bd7febd

Browse files
update: readme + docs
1 parent 49d10ea commit bd7febd

3 files changed

Lines changed: 102 additions & 27 deletions

File tree

README.md

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Let believers back you and doubters bet against you. Submit evidence — and let
1010

1111
Every doubt recorded · every receipt public · refereed by GenLayer validator consensus.
1212

13-
[Quickstart](#quickstart) · [How it works](#how-it-works) · [Architecture](#architecture) · [Deploy to GenLayer](#deploy-to-genlayer) · [Quality gates](#quality-gates)
13+
[Quickstart](#quickstart) · [How it works](#how-it-works) · [Sign-in](#sign-in--silent-signing) · [Architecture](#architecture) · [Deploy](#deploy-to-genlayer) · [Quality gates](#quality-gates)
1414

1515
</div>
1616

@@ -29,7 +29,7 @@ GRUDGE turns a personal commitment into a public, on-chain market.
2929

3030
The referee is a **subjective judgment**_"does this evidence actually prove the promise?"_ — that no deterministic EVM contract or single oracle can make trustlessly.
3131

32-
GenLayer's Intelligent Contracts run that judgment _inside consensus_: `gl.eq_principle_prompt_comparative` has the validator set each execute the judging prompt and agree on the verdict, so the outcome is a **consensus artifact**, not one model's opinion. Prompt-injection attempts inside submitted evidence are adjudicated by that same consensus and auto-`REJECTED`.
32+
GenLayer's Intelligent Contracts run that judgment _inside consensus_: `gl.eq_principle.prompt_comparative` has the validator set each execute the judging prompt and agree on the verdict, so the outcome is a **consensus artifact**, not one model's opinion. Prompt-injection attempts inside submitted evidence are adjudicated by that same consensus and auto-`REJECTED`.
3333

3434
> No other chain can settle _"did you actually do the thing?"_ without trusting a human.
3535
@@ -47,13 +47,33 @@ create_challenge ──▶ stake (believe / doubt) ──▶ submit_evidence ─
4747

4848
The chain is the single source of truth. The web app holds **no** authoritative state.
4949

50+
## Sign-in & silent signing
51+
52+
GRUDGE has **no wallet extension, no seed phrase, no transaction popups**. Auth is
53+
**email + a one-time code** (Privy), and on first login Privy provisions an
54+
**embedded wallet** whose key it custodies — so every write (`create`, `stake`,
55+
`submit_evidence`, `dispute`, `settle`, `claim`) signs **silently**, with no fee
56+
confirmation.
57+
58+
- On **studionet** the embedded wallet is **auto-funded** with simulated GEN the
59+
first time it's empty, so a new user can act seconds after signing in.
60+
- On **Bradbury** (real testnet GEN) there is **no auto-fund** — the UI surfaces
61+
the faucet, and value actions get an in-app confirm.
62+
63+
`lib/auth/` is the only place that touches Privy; the rest of the app consumes
64+
`useAuth()` / `useEmailLogin()`. See [`apps/web/AUTH.md`](apps/web/AUTH.md) for the
65+
full model (where the key lives, server token verification, the studionet-vs-Bradbury
66+
boundary). A CI guard (`pnpm guard:wallet`) fails the build if any
67+
`wagmi` / `@rainbow-me/rainbowkit` / `window.ethereum` reference returns — the
68+
embedded wallet is the only signer.
69+
5070
## Quickstart
5171

5272
Zero config — boots on an in-memory mock chain with seeded grudges, so the full
53-
loop is playable immediately.
73+
loop is playable immediately (no Privy, no wallet, no network).
5474

5575
```sh
56-
pnpm i && pnpm dev # http://localhost:3000
76+
pnpm i && pnpm dev # http://localhost:3000 (CHAIN_MODE=mock)
5777
```
5878

5979
**Requirements:** Node ≥ 20, pnpm 10.
@@ -66,23 +86,31 @@ without it a deterministic heuristic keeps everything zero-config.
6686
## Architecture
6787

6888
```
69-
apps/web/ Next.js 15 App Router · TS strict · Tailwind · Framer Motion
70-
app/(marketing)/ landing: hero (live ledger) → how-it-works → live feed
71-
app/docs/ in-app documentation page
89+
apps/web/ Next.js 15 App Router · React 19 · TS strict · Tailwind · Framer Motion
90+
app/(marketing)/ landing: hero (live market) → how-it-works → live feed
91+
app/explorer/ browse/search every grudge (paginated, all statuses)
92+
app/docs/ in-app documentation page (+ FAQ structured data)
7293
app/challenge/[id]/ challenge detail: tug-of-war, evidence tribunal, settle
7394
app/create/ create-challenge wizard (pre-flight LLM screening)
95+
app/api/judge/ stateless screening/judge proxy — SAME prompt as the contract
96+
97+
lib/auth/ ONLY Privy touchpoint: useAuth, useEmailLogin, server verify
7498
lib/chain/ ONE adapter interface (GrudgeClient): mock.ts ⇄ genlayer.ts
99+
authBridge.ts embedded-wallet provider → genlayer-js (plain TS, no React)
100+
fund.ts studionet-only auto-fund (balance-gated; Bradbury tripwire)
75101
lib/motion/ motion tokens + variants (single reduced-motion gate)
76102
lib/psychology/ pure, unit-tested copy engine (odds lines, nudges, captions)
77-
app/api/judge/ mock judge proxy — SAME prompt as the contract
103+
lib/seo.ts SEO/AEO config (metadata, OG, sitemap, JSON-LD)
78104
79105
contracts/
80106
grudge.py GenVM Intelligent Contract
81107
scripts/genvm_lint.py custom AST linter for GenVM contract rules
82108
```
83109

84110
Every chain access in the app goes through a `GrudgeClient`; components never
85-
touch `genlayer-js` or `viem` directly. Mock mode ships **no** wallet/chain JS.
111+
touch `genlayer-js` or `viem` directly, and the embedded signer reaches the
112+
adapter through `authBridge` (no React imports in the chain layer). Mock mode
113+
ships **no** auth/chain JS.
86114

87115
### Contract surface (`contracts/grudge.py`)
88116

@@ -94,7 +122,13 @@ touch `genlayer-js` or `viem` directly. Mock mode ships **no** wallet/chain JS.
94122
| `dispute_evidence(challenge_id, index, counter_evidence)` | `write` | Challenge a `VERIFIED` entry; consensus re-judges. |
95123
| `settle(challenge_id)` | `write` | After the deadline, resolve and credit winners' ledgers. |
96124
| `claim()` | `write` | Withdraw your settled winnings. |
97-
| `get_challenge`, `get_open_challenges`, `get_challenges_page`, `get_claimable` | `view` | Read-only chain queries. |
125+
| `get_challenge` · `get_challenge_summary` | `view` | Full / bounded single-challenge reads. |
126+
| `get_challenges_page(offset, limit)` | `view` | Paginated **summaries** (no nested arrays) — the only list read. |
127+
| `get_stakes_page` · `get_evidence_page` | `view` | Paginate a single challenge's stakes / evidence. |
128+
| `get_claimable(address)` · `get_solvency()` | `view` | Withdrawable balance · contract liability invariant. |
129+
130+
> All list/detail reads are **bounded** — there is no unbounded view, so views
131+
> never revert as the ledger grows.
98132
99133
## Deploy to GenLayer
100134

@@ -111,27 +145,36 @@ Then set `apps/web/.env.local` (see `apps/web/.env.example`):
111145

112146
```sh
113147
NEXT_PUBLIC_CHAIN_MODE=genlayer
114-
NEXT_PUBLIC_BRADBURY_CHAIN_ID=61999 # Studio (Bradbury: 4221)
115-
NEXT_PUBLIC_BRADBURY_RPC=https://studio.genlayer.com/api
116-
NEXT_PUBLIC_BRADBURY_EXPLORER=https://studio.genlayer.com
117-
NEXT_PUBLIC_GRUDGE_CONTRACT_ADDRESS=0x… # from the deploy step
118-
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID= # optional (injected-wallet dev)
148+
149+
# Auth (Privy) — create an app at https://dashboard.privy.io:
150+
# • enable Email login • embedded wallets: create on login • whitelist your origins
151+
NEXT_PUBLIC_PRIVY_APP_ID=...
152+
PRIVY_APP_SECRET=... # server-only — never NEXT_PUBLIC
153+
154+
# Network + chain (read by lib/chain/bradbury.ts)
155+
NEXT_PUBLIC_NETWORK=studionet # studionet (auto-fund) | bradbury (faucet)
156+
NEXT_PUBLIC_STUDIO_RPC=https://studio.genlayer.com/api
157+
NEXT_PUBLIC_CHAIN_ID=61999 # Studio (Bradbury: 4221)
158+
NEXT_PUBLIC_RPC=https://studio.genlayer.com/api
159+
NEXT_PUBLIC_EXPLORER=https://explorer-studio.genlayer.com
160+
NEXT_PUBLIC_GRUDGE_CONTRACT_ADDRESS=0x… # from the deploy step
119161
```
120162

121-
In `genlayer` mode the header shows a RainbowKit **Connect Wallet** button;
122-
writes sign through the connected wallet, and tx toasts link to the explorer.
123-
On Studio, fund your account from the simulator before a value-carrying write:
163+
In `genlayer` mode the header shows an **email Sign-in** chip; after sign-in the
164+
embedded wallet signs every write silently and tx toasts link to the explorer.
165+
On studionet the wallet auto-funds when empty; to fund manually:
124166

125167
```sh
126168
curl -X POST https://studio.genlayer.com/api -H "Content-Type: application/json" \
127-
-d '{"jsonrpc":"2.0","method":"sim_fundAccount","params":["0xYOUR_ADDRESS", 500000000000000000000],"id":1}'
128-
# └ amount is in wei (500 GEN)
169+
-d '{"jsonrpc":"2.0","method":"sim_fundAccount","params":["0xYOUR_ADDRESS", 1000000000000000000000],"id":1}'
170+
# └ amount is a wei NUMBER (1000 GEN)
129171
```
130172

131173
## Quality gates
132174

133175
```sh
134-
pnpm typecheck && pnpm lint && pnpm test # web: TS strict, ESLint, vitest
176+
pnpm typecheck && pnpm lint && pnpm test # web: TS strict, ESLint, vitest, wallet-lib guard
177+
pnpm guard:wallet # fail if wagmi/rainbowkit/window.ethereum return
135178
pnpm e2e # Playwright core-loop (mock mode)
136179
make -C contracts lint # ruff --select ALL, mypy --strict, genvm_lint.py
137180
make -C contracts test # settle-math units + gltest --network studionet
@@ -146,7 +189,8 @@ storage writes in nondet blocks, no banned imports, and
146189
## Tech stack
147190

148191
- **Frontend** — Next.js 15 (App Router), React 19, TypeScript (strict), Tailwind CSS, Framer Motion
149-
- **Wallet / chain**`genlayer-js`, wagmi, RainbowKit, viem
192+
- **Auth / wallet** — Privy (`@privy-io/react-auth` + `@privy-io/server-auth`) email login + embedded wallet
193+
- **Chain**`genlayer-js`, viem (signing only, via the embedded provider)
150194
- **Data** — TanStack Query, Zod (schema-validated chain reads)
151195
- **Contract** — Python GenVM Intelligent Contract on GenLayer
152196
- **Tooling** — pnpm workspaces, Vitest, Playwright, ESLint, ruff, mypy

apps/web/app/docs/DocsView.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { CodeBlock } from "./CodeBlock";
1414
const SECTIONS = [
1515
{ id: "overview", label: "Overview" },
1616
{ id: "how-it-works", label: "How it works" },
17+
{ id: "signing-in", label: "Signing in" },
1718
{ id: "why-genlayer", label: "Why GenLayer" },
1819
{ id: "lifecycle", label: "Grudge lifecycle" },
1920
{ id: "verdicts", label: "Verdicts & disputes" },
@@ -149,6 +150,23 @@ export function DocsView() {
149150
</ol>
150151
</Section>
151152

153+
<Section id="signing-in" title="Signing in">
154+
<Lead>
155+
No wallet extension, no seed phrase, no transaction popups. You sign in with an
156+
email and a one-time code — and an on-chain wallet is created for you automatically.
157+
</Lead>
158+
<ul className="ml-5 list-disc space-y-2 marker:text-gold">
159+
<li><span className="text-paper">Email login.</span> Enter your email, then the code we send you. That’s it — no app to install.</li>
160+
<li><span className="text-paper">Embedded wallet.</span> On first login an embedded wallet is provisioned and its key is securely custodied, so every action signs <span className="text-paper">silently</span> — no confirmation dialog, no fee prompt.</li>
161+
<li><span className="text-paper">Funded to start.</span> On GenLayer Studio your wallet is auto-funded with test GEN the first time it’s empty, so you can act within seconds.</li>
162+
<li><span className="text-paper">You stay in control.</span> Export your wallet any time from the account menu — you’re never locked in.</li>
163+
</ul>
164+
<p className="text-mut">
165+
Browsing the ledger needs no account; only actions that put GEN on-chain (create,
166+
stake, evidence, dispute, settle, claim) require signing in.
167+
</p>
168+
</Section>
169+
152170
<Section id="why-genlayer" title="Why GenLayer">
153171
<Lead>
154172
The referee is a <span className="text-paper">subjective judgment</span> - “does this
@@ -246,7 +264,10 @@ result = gl.eq_principle_prompt_comparative(
246264
["dispute_evidence(challenge_id, index, counter)", "write", "Challenge a VERIFIED entry; consensus re-judges."],
247265
["settle(challenge_id)", "write", "After the deadline, resolve and credit winners."],
248266
["claim()", "write", "Withdraw your settled winnings."],
249-
["get_challenge / get_open_challenges / get_challenges_page / get_claimable", "view", "Read-only chain queries."],
267+
["get_challenges_page(offset, limit)", "view", "Paginated summaries — the only list read (bounded, no nested arrays)."],
268+
["get_challenge / get_challenge_summary", "view", "Full or bounded single-challenge read."],
269+
["get_stakes_page / get_evidence_page", "view", "Paginate one challenge's stakes / evidence."],
270+
["get_claimable(address) / get_solvency()", "view", "Withdrawable balance · contract liability invariant."],
250271
].map(([m, k, p]) => (
251272
<tr key={m} className="align-top">
252273
<td className="px-4 py-3 font-mono text-[12px] text-paper/90">{m}</td>
@@ -262,10 +283,12 @@ result = gl.eq_principle_prompt_comparative(
262283
<Section id="faq" title="FAQ">
263284
<div className="space-y-5">
264285
{[
286+
["How do I sign in?", <>With your email. Enter it, type the one-time code we send you, and an on-chain wallet is created for you automatically — no extension, no seed phrase.</>],
287+
["Do I have to confirm every transaction?", <>No. Your embedded wallet signs silently, so creating, staking, submitting evidence, settling, and claiming all happen without a popup or fee prompt.</>],
265288
["Who decides if I kept my promise?", <>No one person. The GenLayer validator set each runs the contract’s judging prompt and reaches consensus on the verdict. The result is on-chain and auditable.</>],
266289
["What stops someone faking evidence?", <>The judging prompt evaluates the proof against the grudge’s evidence policy, and prompt-injection attempts are adjudicated by the same consensus and rejected. A verified entry can still be disputed.</>],
267-
["Do I need GEN to play?", <>To create a grudge or stake, yes - those calls carry value. On GenLayer Studio the network is feeless, so reading and browsing cost nothing. Fund a Studio account with the simulator’s <Term>sim_fundAccount</Term> method.</>],
268-
["Can I try it without a wallet?", <>Yes. The app ships a zero-config mock mode with seeded grudges so you can play the whole loop locally before connecting to a real network.</>],
290+
["Do I need GEN to play?", <>To create a grudge or stake, yes - those calls carry value. On GenLayer Studio your wallet is auto-funded with test GEN, so you start ready. Reading and browsing the ledger cost nothing and need no sign-in.</>],
291+
["Can I try it without signing in?", <>Yes. The app ships a zero-config mock mode with seeded grudges, so you can play the whole loop locally before connecting to a real network.</>],
269292
].map(([q, a], i) => (
270293
<div key={i} className="rounded-card border border-ink-line bg-ink-soft p-5">
271294
<p className="font-display uppercase italic tracking-wide text-paper">{q}</p>

apps/web/app/docs/page.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ const FAQ: { q: string; a: string }[] = [
2020
q: "What is GRUDGE?",
2121
a: "GRUDGE is a social-accountability game on GenLayer. You stake GEN on a concrete, time-boxed promise; believers stake that you'll keep it and doubters stake that you won't; you submit evidence; and GenLayer validator-LLM consensus settles the verdict. The contract pays the winning side automatically.",
2222
},
23+
{
24+
q: "How do I sign in to GRUDGE?",
25+
a: "With your email. You enter your email address, type the one-time code that's sent to you, and an on-chain wallet is created for you automatically. There's no browser extension and no seed phrase to manage.",
26+
},
27+
{
28+
q: "Do I have to confirm every transaction?",
29+
a: "No. GRUDGE provisions an embedded wallet whose key is securely custodied, so it signs transactions silently. Creating a grudge, staking, submitting evidence, settling, and claiming all happen without a wallet popup or fee confirmation.",
30+
},
2331
{
2432
q: "Who decides if I kept my promise?",
2533
a: "No single person. The GenLayer validator set each runs the contract's judging prompt and reaches consensus on the verdict. The result is recorded on-chain and is publicly auditable.",
@@ -30,10 +38,10 @@ const FAQ: { q: string; a: string }[] = [
3038
},
3139
{
3240
q: "Do I need GEN to play?",
33-
a: "To create a grudge or stake you need GEN, since those calls carry value. On GenLayer Studio the network is feeless, so reading and browsing cost nothing. You can fund a Studio account with the simulator's sim_fundAccount method.",
41+
a: "To create a grudge or stake you need GEN, since those calls carry value. On GenLayer Studio your embedded wallet is auto-funded with simulated GEN the first time it's empty, so you start ready to act. Reading and browsing the ledger cost nothing and require no sign-in.",
3442
},
3543
{
36-
q: "Can I try GRUDGE without a wallet?",
44+
q: "Can I try GRUDGE without signing in?",
3745
a: "Yes. The app ships a zero-config mock mode with seeded grudges so you can play the entire loop locally before connecting to a real network.",
3846
},
3947
{

0 commit comments

Comments
 (0)