How Opentip is built — from the smart contract to the frontend.
┌─────────────────────────────────────────────────────────┐
│ Frontend │
│ Next.js 16 · Vercel │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Pages │ │ API │ │ Auth │ │
│ │ (SSR) │ │ Routes │ │NextAuth │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴──────────────┴──────────────┴─────┐ │
│ │ Prisma Client │ │
│ └────────────────┬───────────────────────┘ │
└───────────────────┼────────────────────────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───┴───┐ ┌──────┴──────┐ ┌───┴────────┐
│ DB │ │ Contract │ │ External │
│Postgres│ │ (Base) │ │ Services │
└───────┘ └─────────────┘ └────────────┘
External Services:
Groq (AI summaries) · Relay (ETH swaps) · 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 │
└─────────────────────────────────────────────────────────┘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)
Relay
Same-chain ETH→USDC swap for ETH tips
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
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.
A standalone Node.js process that polls the Base blockchain for smart contract events and writes them to PostgreSQL.
IndexerState tableeth_getLogs in batches of 10,000 blocksTipReceived→ Creates/updates Tip recordRepoRegistered→ Upserts Repo recordPayoutAddressUpdated→ Updates Repo.payoutAddressClaimed→ Advances checkpoint onlyTreasuryWithdrawn→ Advances checkpoint onlyOpentip uses Reown AppKit (formerly WalletConnect Web3Modal) for wallet connection. This provides a unified modal that supports:
The wagmi library handles all EVM interactions — reading contract state, sending transactions, and signing messages. The config uses cookie-based storage for SSR support.
The frontend requires 19 environment variables to run (including GROQ_API_KEY for AI summaries). The indexer requires 6.
Auto-deployed from the main branch. Environment variables set in the Vercel dashboard. Uses Node 20+.
Docker image built from the indexer directory. Runs in a consumption plan on Azure. Polls every 12 seconds.
Deployed via Foundry scripts. Verified on Basescan. Currently on Base Sepolia (testnet).