Architecture
How Opentip is built — from the smart contract to the frontend.
System overview
┌─────────────────────────────────────────────────────────┐
│ Frontend │
│ Next.js 16 · Vercel │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Pages │ │ API │ │ Auth │ │
│ │ (SSR) │ │ Routes │ │NextAuth │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴──────────────┴──────────────┴─────┐ │
│ │ Prisma Client │ │
│ └────────────────┬───────────────────────┘ │
└───────────────────┼────────────────────────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───┴───┐ ┌──────┴──────┐ ┌───┴────────┐
│ DB │ │ Contract │ │ External │
│Postgres│ │ (Base) │ │ Services │
└───────┘ └─────────────┘ └────────────┘
External Services:
Groq (AI summaries) · Azure Blob (uploads)
Resend (emails) · GitHub API (repos, ownership)
┌─────────────────────────────────────────────────────────┐
│ Indexer │
│ Node.js · Azure Container Apps │
│ │
│ Polls contract events → writes to PostgreSQL │
│ Events: TipReceived, RepoRegistered, PayoutUpdated │
└─────────────────────────────────────────────────────────┘Tech stack
Next.js 16
React framework with Turbopack, SSR, App Router
Prisma
Type-safe ORM for PostgreSQL
PostgreSQL
Azure Database for PostgreSQL Flexible Server
viem
TypeScript Ethereum library for contract interaction
Reown AppKit
Wallet connection modal (WalletConnect, injected)
NextAuth.js
Authentication (GitHub OAuth + email/password)
Azure Blob Storage
Profile picture and header image uploads
Groq
AI-powered repo summaries (GPT-OSS)
Resend
Transactional email (password reset)
Monorepo structure
opentip/ ├── frontend/ Next.js app (Vercel) │ ├── app/ Pages, API routes, layouts │ ├── components/ UI components (motion-enhanced) │ ├── lib/ Utilities, auth, contract ABI │ ├── prisma/ Schema + migrations │ └── config/ Reown AppKit + wagmi setup │ ├── indexer/ Event indexer (Azure Container Apps) │ ├── src/index.ts Polling loop + event processing │ └── prisma/ Shared schema with frontend │ ├── contracts/ Solidity smart contracts (Foundry) │ ├── src/Opentip.sol │ ├── script/Deploy.s.sol │ └── test/ 36 tests │ └── package.json npm workspaces
Authentication flow
GitHub OAuth: Email/Password:
User clicks "GitHub" User enters email + password
→ signIn("github") → POST /api/auth/signup
→ GitHub OAuth popup → bcrypt hash, store user
→ Callback with code → signIn("credentials")
→ NextAuth creates session → bcrypt compare
→ JWT stored in cookie → JWT stored in cookie
Both flows:
→ JWT callback enriches token
with login, githubId, accessToken
→ Session callback exposes
user.login, user.id, etc.Sessions use JWT strategy (not database sessions). The JWT is stored in an HTTP-only cookie (next-auth.session-token). The custom Prisma adapter handles user creation, account linking, and githubId deduplication.
Indexer
A standalone Node.js process that polls the Base blockchain for smart contract events and writes them to PostgreSQL.
How it works
- Reads the last processed block from
IndexerStatetable - Fetches new events via
eth_getLogsin batches ofGETLOGS_RANGEblocks (default 10). With no checkpoint and noSTART_BLOCK, the first scan starts 10,000 blocks behind the chain head. - Processes each event and writes to the database
- Saves the new checkpoint
- Repeats every 12 seconds
Events indexed
TipReceived→ Creates/updates Tip recordRepoRegistered→ Upserts Repo recordPayoutAddressUpdated→ Updates Repo.payoutAddressClaimed→ Stores a Claim and notifies the payout account ("Tips paid out")TreasuryWithdrawn→ Advances checkpoint onlyWallet connection
Opentip has two wallet paths. The primary path is the Opentip Smart Wallet — a CDP Embedded smart wallet created with an email code, giving the same address on every device with no extension or seed phrase. The secondary path is external wallets via Reown AppKit, which supports injected wallets (MetaMask, Rabby, etc.) and the WalletConnect protocol for mobile wallets.
The wagmi library handles EVM reads and external-wallet transactions, while CDP hooks sign for the Smart Wallet. The wagmi config uses cookie-based storage for SSR support. See Smart Wallet for the user-facing guide.
Environment variables
Frontend and indexer environment variables, including GROQ_API_KEY for AI summaries, are listed in Contributing. The indexer reads CHAIN, not NEXT_PUBLIC_CHAIN.
Deployment
Frontend → Vercel
Auto-deployed from the main branch. Environment variables set in the Vercel dashboard. Uses Node 20+.
Indexer → Azure Container Apps
Docker image built from the indexer directory. Runs in a consumption plan on Azure. Polls every 12 seconds.
Contract → Base (Foundry)
Deployed via Foundry scripts. Verified on Basescan. Live on Base mainnet.