Skip to content

Latest commit

 

History

History
164 lines (118 loc) · 7 KB

File metadata and controls

164 lines (118 loc) · 7 KB

AGENTS.md

This file is for AI assistants and human contributors. It documents the project layout, contract interface, and key conventions so you can orient quickly without reading every file.


What this is

Genjury is a platform of on-chain mini-games built on GenLayer. Every game outcome is settled by an Intelligent Contract — a Python smart contract that can call an LLM and have multiple validator nodes independently verify the result before it finalises.

Mistrial is the first playable game. Two truths and a lie. The AI Judge decides which statement is false.


Repo layout

genjury/
├── contracts/
│   └── mistrial.py          # The Intelligent Contract — all game logic lives here
├── frontend/                # React 18 + Vite app (workspace: @workspace/genjury)
│   ├── src/
│   │   ├── pages/           # One file per game phase (Lobby, Writing, Voting, …)
│   │   ├── components/      # Shared UI components
│   │   └── lib/
│   │       ├── genlayer.js  # GenLayer SDK wrapper, wallet connect, contract reads/writes
│   │       ├── store.js     # Zustand game store — polls contract, shapes state for UI
│   │       ├── profile.js   # Local profile cache (localStorage)
│   │       ├── joinedRooms.js  # Joined-rooms registry (localStorage)
│   │       └── ens.js       # ENS name resolution cache
│   ├── .env.example         # All required env vars documented here
│   └── vite.config.ts
├── api/                     # Vercel serverless functions (Express-style)
│   ├── chat.js              # Socket.io chat proxy
│   ├── username.js          # Username claim/check
│   └── profile/             # Profile CRUD + ENS + avatar endpoints
├── lib/
│   └── db/                  # Drizzle ORM schema + Neon Postgres client
├── vercel.json              # Vercel build config (outputDirectory, rewrites)
└── pnpm-workspace.yaml      # Monorepo workspace — includes frontend, lib/*, artifacts/*

The Mistrial contract (contracts/mistrial.py)

The contract is a singleton — deployed once by the platform owner, reused by every player. All rooms live inside one contract instance.

Key state

class Genjury(gl.Contract):
    rooms: dict[str, Room]          # roomCode → Room
    player_xp: dict[str, int]       # address → cumulative XP
    prize_pool: dict[str, int]      # roomCode → wei balance

Key methods (callable from frontend)

Method Who calls it What it does
create_room(rounds, entry_fee) Host Creates a new room, returns 6-char room code
join_room(room_code) Player Joins an open room, locks in entry fee
submit_statement(room_code, s1, s2, s3, lie_index) Current writer Submits three statements + declares which is the lie
submit_vote(room_code, vote_index, confidence) Voters Votes on which statement is the lie
run_ai_judge(room_code) Host (after voting) Calls the LLM via gl.eq_principle.strict_eq, returns AI verdict
call_objection(room_code) Any player Opens an objection vote window
submit_objection_vote(room_code, sustain) Players Votes to sustain or overrule the AI verdict
finalize_round(room_code) Host Distributes XP, advances to next round or ends game
get_room_state(room_code) Frontend poll Returns full room state for UI rendering
get_leaderboard() Frontend Returns top players by cumulative XP

Game flow

create_room → join_room (×N players) → [for each round:]
  submit_statement → submit_vote (×N) → run_ai_judge
  → [optional: call_objection → submit_objection_vote (×N)]
  → finalize_round → [next round or end]

AI judging

The contract calls gl.eq_principle.strict_eq with a structured prompt that includes all three statements. GenLayer's validator network runs the LLM call independently on each node and reaches consensus before the result is committed on-chain. There is no off-chain oracle.


Frontend architecture

Contract singleton

The deployed contract address is set via VITE_MISTRIAL_CONTRACT. The frontend never asks users to deploy anything — it reads and writes to this single address.

genlayer.js exports two main helpers:

  • readContract(method, args) — read-only call, no wallet needed
  • callContract(method, args, value) — write call, requires connected wallet

Game state

store.js is a Zustand store that:

  1. Polls get_room_state(roomCode) every ~1.5 seconds
  2. Normalises the contract response into UI-friendly state
  3. Exposes actions that call contract write methods

Wallet

Multi-wallet support via EIP-6963 (Rabby, MetaMask, Coinbase Wallet, etc). Wallet state is managed in genlayer.js. The chosen provider is stored in memory; the address is persisted in localStorage as genjury_injected_address.


Environment variables

All variables go in frontend/.env (copy from frontend/.env.example).

Variable Required Description
VITE_MISTRIAL_CONTRACT Yes Deployed Mistrial contract address (0x…)
VITE_GENLAYER_NETWORK Yes Target network: studionet / asimov / bradbury
DATABASE_URL Yes Neon Postgres connection string (profiles + ENS cache)
SESSION_SECRET Yes Random string for session signing
VITE_GENLAYER_RPC No Override RPC URL (defaults to network's public RPC)

Deploying the contract

# Install GenLayer CLI
pip install genlayer

# Deploy to Bradbury testnet
genlayer deploy contracts/mistrial.py --network bradbury --args 3 0
# args: default_rounds(3), entry_fee_wei(0)

# Paste the returned address into frontend/.env
VITE_MISTRIAL_CONTRACT=0x…

Running locally

git clone https://github.com/Jeephoenix/genjury.git
cd genjury
pnpm install

cp frontend/.env.example frontend/.env
# fill in VITE_MISTRIAL_CONTRACT and VITE_GENLAYER_NETWORK

pnpm --filter @workspace/genjury run dev
# → http://localhost:5173

Key conventions

  • Never add game logic to the frontend. All rules, XP math, and verdicts live in contracts/mistrial.py. The frontend only displays state and collects input.
  • [genjury] log prefix — used in console.warn throughout the frontend. This is the platform name, not game-specific.
  • localStorage key prefixesgenjury_* keys are platform-level (wallet, network, profile, rooms). mistrial_* keys are game-specific (contract address override).
  • Room codes — 6-char uppercase alphanumeric (e.g. TRIAL9). Generated by the contract, validated by isValidRoomCode() in genlayer.js.
  • pnpm only — the preinstall script blocks npm and yarn. Always use pnpm.