Skip to content
Back to Home
Syntax Studios

ideologi

Next.js 15 Base Solidity OpenZeppelin viem Privy Smart Wallets merkletreejs Drizzle ORM PostgreSQL

ideologi is a debate platform on Base where creators sponsor an idea, participants stake their position, and rewards flow back through a signed Merkle-proof payout. The whole thing is one Next.js 15 app on App Router, with Postgres + Drizzle behind it, viem + wagmi against the chain, and Sentry instrumented across both edges.

The core contract is a UUPS-upgradeable Hyperlogue contract deployed on Base, gated by OpenZeppelin AccessControl with three roles: an admin role rotates the signer, treasury, and result-post relayer; a maintainer role controls fee tables and per-type lifecycle offsets; a moderator role handles content. State per hyperlogue lives in a single struct keyed by the platform's UUID, including participant count, fee splits, payout token, and the posted Merkle root. Three hyperlogue types share the same primitive: free (per-participant fee in ETH), funded crowdfund pool, and token-exchange where the initiator deposits one ERC-20 and participants contribute another.

Merkle construction runs in a single Postgres transaction. Each leaf hashes a participant's wallet and final reward amount with double keccak:

leaf = keccak256(keccak256(abi.encode(address, uint256)))

The tree is built with merkletreejs in sorted mode so the root is canonical regardless of insertion order. The same transaction writes every user's hex proof back into the database and the final root onto the hyperlogue row, gated by a precondition that the existing root is null, so two concurrent runs can't double-post. A backend wallet client then signs the EIP-712 root and submits it to the contract; claims verify the leaf against the stored root using OpenZeppelin's MerkleProof and release tokens. One bytes32 commits everyone's reward, individual amounts stay private until claimed, and anyone can rebuild and check the tree.

Pairing is deterministic, not a shuffle. With m submissions and round r, the closed-form formula is:

i = (k + floor(m/4)  - floor((r+1)/2)) mod m   (even rounds)
i = (k + floor(3m/4) - floor((r+1)/2)) mod m   (odd rounds)
j = (i + r + 1) mod m

The output passes a four-rule validator before insert: each submission evaluated exactly twice per match, no self-evaluation, no repeat opponent across history, no duplicate global pair. Mindshare math is just as concrete: skipped evaluation pairs add +50 to both submissions, then non-evaluators are zeroed out and their summed mindshare is split equally across the evaluators who actually showed up. That effective mindshare is what feeds the reward percentages.

Wallets come from Privy embedded wallets wrapped in a smart-wallets provider, so every user gets an ERC-4337 smart account on first login. Frontend transactions go through the smart-wallet client, which means a user can join a hyperlogue, post a payout root, or claim rewards without ever holding base-layer gas; permissionless handles the bundler/paymaster wiring.

Five cron routes sit under app/api/cron, all bearer-token gated and idempotent through database flags so a finalized hyperlogue is filtered out at the SELECT before any work runs. Drizzle indexes the hot paths and migrations are checked into the repo with a postinstall hook.