Skip to main content

Overview

Agora is built as a modern, scalable web application using Next.js 14, React, TypeScript, and PostgreSQL. The architecture is designed to support multiple DAOs on a single platform through a multi-tenant system with isolated data and configurations.

High-Level Architecture

Multi-Tenant Design

Agora’s core architectural pattern is multi-tenancy, allowing multiple DAOs to run on a single codebase and infrastructure while maintaining complete isolation.

Tenant Configuration

Each DAO is identified by a tenant namespace (e.g., ens, optimism, uniswap). The tenant system is implemented in /src/lib/tenant/:
// Tenant singleton provides DAO-specific configuration
import Tenant from "@/lib/tenant/tenant";

const { namespace, contracts, token, ui } = Tenant.current();

console.log(namespace); // "ens"
console.log(token.symbol); // "ENS"
console.log(contracts.governor.address); // "0x323A76393544d5ecca80cd6ef2A560C6a395b7E3"
The tenant is determined by the NEXT_PUBLIC_AGORA_INSTANCE_NAME environment variable.

Tenant Components

Each tenant configuration includes:
Blockchain contract addresses and configurations:
type TenantContracts = {
  governor: TenantContract<IGovernorContract>;
  token: TenantContract<ITokenContract | IMembershipContract>;
  timelock?: TenantContract<BaseContract>;
  staker?: TenantContract<IStaker>;
  alligator?: TenantContract<IAlligatorContract>;
  treasury?: string[];
  votableSupplyOracle?: TenantContract<IVotableSupplyOracleContract>;
  // Additional configuration
  governorType?: GOVERNOR_TYPE;
  timelockType?: TIMELOCK_TYPE;
  delegationModel?: DELEGATION_MODEL;
};
Contract configurations are defined in /src/lib/tenant/configs/contracts/.

Adding a New Tenant

To add a new DAO to Agora:
1

Create contract configuration

Add a new file in /src/lib/tenant/configs/contracts/your-dao.ts with governor, token, and other contract addresses.
2

Create UI configuration

Add /src/lib/tenant/configs/ui/your-dao.ts with branding, colors, and feature toggles.
3

Update factories

Add your DAO namespace to:
  • TenantContractFactory in /src/lib/tenant/tenantContractFactory.ts
  • TenantUIFactory in /src/lib/tenant/tenantUIFactory.ts
  • TenantTokenFactory in /src/lib/tenant/tenantTokenFactory.ts
4

Create database schema

Run migrations to create a new schema in PostgreSQL for your DAO’s data.
5

Deploy with environment variables

Set NEXT_PUBLIC_AGORA_INSTANCE_NAME=your-dao when deploying.

Database Architecture

Agora uses PostgreSQL with Prisma ORM for data persistence. The database is organized into multiple schemas:

Schema Organization

PostgreSQL Database
├── agora (shared cross-tenant data)
│   ├── delegate_statements
│   ├── address_metadata
│   ├── citizens
│   └── badgeholders
├── config (configuration data)
│   └── contracts
├── ens (ENS-specific data)
│   ├── proposals
│   ├── votes
│   ├── delegates
│   └── proposal_lifecycle
├── optimism (Optimism-specific data)
│   ├── proposals
│   ├── votes
│   └── ...
└── uniswap (Uniswap-specific data)
    └── ...

Dual Database Pattern

Agora separates data into two logical databases:

Web2 Database

User-Generated Content
  • Delegate statements and profiles
  • Forum topics and posts
  • User settings and notifications
  • Email preferences
Read-write access for user modifications.

Web3 Database

Blockchain-Indexed Data
  • Proposals from governor contracts
  • Votes from onchain events
  • Delegate power calculations
  • Token transfers and balances
Read-only for application (written by indexer).
Configure separate database URLs with READ_WRITE_WEB2_DATABASE_URL_* and READ_ONLY_WEB3_DATABASE_URL_* environment variables.

Key Data Models

Agora’s data models are defined in the Prisma schema (/prisma/schema.prisma):
Governance proposals are stored per-tenant:
model Proposal {
  id                String
  proposer          String
  title             String?
  description       String?
  status            ProposalStatus
  created_at        DateTime
  updated_at        DateTime
  start_block       BigInt
  end_block         BigInt
  for_votes         Decimal
  against_votes     Decimal
  abstain_votes     Decimal
  // ... additional fields
}
Each tenant schema has its own proposals table.

Views and Materialized Views

For performance, Agora uses PostgreSQL views:
  • Views: Dynamic queries computed on read (e.g., citizens, badgeholders)
  • Materialized Views: Precomputed results refreshed periodically (e.g., delegate rankings)
The database schema is managed in a separate repository. Always pull the latest schema with npx prisma db pull before generating the client.

Application Structure

Agora follows Next.js 14 App Router conventions:

Directory Structure

agora-next/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── api/                # API routes
│   │   │   ├── common/         # Shared API utilities
│   │   │   ├── v1/             # API v1 endpoints
│   │   │   └── analytics/      # Analytics endpoints
│   │   ├── proposals/          # Proposals pages
│   │   ├── delegates/          # Delegates pages
│   │   ├── forums/             # Forum pages
│   │   ├── create/             # Proposal creation
│   │   ├── layout.tsx          # Root layout
│   │   └── page.tsx            # Homepage (proposals)
│   ├── components/             # React components
│   │   ├── Proposals/          # Proposal components
│   │   ├── Delegates/          # Delegate components
│   │   ├── Votes/              # Voting components
│   │   ├── Forum/              # Forum components
│   │   ├── Dialogs/            # Modal dialogs
│   │   ├── Layout/             # Layout components
│   │   └── ui/                 # Shared UI components
│   ├── lib/                    # Utilities and helpers
│   │   ├── tenant/             # Multi-tenant system
│   │   ├── contracts/          # Contract ABIs and types
│   │   ├── actions/            # Server actions
│   │   ├── utils.ts            # Utility functions
│   │   └── types.d.ts          # TypeScript types
│   └── styles/                 # Global styles
│       ├── globals.scss        # Global SCSS
│       ├── variables.scss      # SCSS variables (theme)
│       └── theme.js            # JS theme (legacy)
├── prisma/
│   └── schema.prisma           # Prisma schema
├── public/                     # Static assets
├── .env.local                  # Environment variables
├── next.config.js              # Next.js configuration
├── tailwind.config.js          # Tailwind configuration
└── package.json

Key Directories

Next.js App Router
  • File-based routing (no Router configuration)
  • Each folder is a route (e.g., /proposals/src/app/proposals)
  • page.tsx files define route content
  • layout.tsx defines shared layouts
  • Server and client components mixed
API Routes and Server Functions
  • REST API endpoints in /api/v1/
  • Data fetching functions in /api/common/
  • Server actions for mutations
  • All data access uses Prisma ORM
  • React cache() wrapper for request deduplication
React Components
  • Organized by feature (Proposals, Delegates, Votes, Forum)
  • Mix of .tsx (TypeScript) and .jsx (JavaScript)
  • Styled with SCSS modules (Component.module.scss)
  • Some use Tailwind utility classes
  • Legacy components use Emotion CSS-in-JS
Utilities and Business Logic
  • tenant/: Multi-tenant configuration system
  • contracts/: Contract ABIs, TypeChain types, and interfaces
  • actions/: Server actions for mutations
  • utils.ts: Shared utility functions
  • types.d.ts: TypeScript type definitions

Technology Stack

Frontend Technologies

Next.js 14

React framework with App Router, server components, and API routes. Deployed on Vercel.

React 18

UI library with server and client components, Suspense, and concurrent rendering.

TypeScript

Type-safe JavaScript with comprehensive interfaces and type definitions.

Tailwind CSS

Utility-first CSS framework. Also uses SCSS modules and legacy Emotion styles.

ConnectKit

Wallet connection UI with support for MetaMask, WalletConnect, Coinbase Wallet, etc.

Wagmi & Viem

React hooks and TypeScript library for Ethereum interactions.

Backend Technologies

Prisma ORM

Type-safe database client for PostgreSQL with multi-schema support.

PostgreSQL

Relational database with schemas, views, materialized views, and full-text search.

Ethers.js v6

Ethereum library for contract interactions, TypeChain for type generation.

OpenTelemetry

Distributed tracing, metrics, and observability integration.

External Services

  • Alchemy: Primary RPC provider for Ethereum, Optimism, Base, etc.
  • Etherscan: Contract verification and ABI fetching
  • Tenderly: Transaction simulation for proposal execution preview
  • EAS: Ethereum Attestation Service for delegate verification

Data Flow

Proposal Creation Flow

Voting Flow

Performance Optimizations

Agora implements several performance optimizations:
Uses React cache() to deduplicate data fetches within a single request:
import { cache } from 'react';

export const fetchDelegates = cache(async (filter) => {
  return prisma.delegate.findMany({ where: filter });
});
Multiple components requesting the same data get cached results.
Precomputed database views for expensive queries:
  • Delegate rankings and voting power
  • Proposal statistics and aggregations
  • Refreshed periodically by database triggers
Pages are statically generated and revalidated:
export const revalidate = 60; // Regenerate every 60 seconds
Balances static site speed with fresh data.
Next.js server components fetch data on the server, reducing client bundle size and improving initial load time.

Security Considerations

Critical: Never expose private keys or sensitive credentials in client-side code or environment variables prefixed with NEXT_PUBLIC_.

Security Best Practices

1

Environment variable separation

  • NEXT_PUBLIC_*: Safe for browser (API keys, public addresses)
  • Server-only: Private keys, database URLs, JWT secrets
2

API authentication

  • Use API keys for programmatic access
  • JWT tokens for user sessions
  • Rate limiting with Redis
3

Database access

  • Read-only connection for web3 data
  • Parameterized queries (Prisma prevents SQL injection)
  • Row-level security for multi-tenant isolation
4

Smart contract interactions

  • Validate all inputs before encoding
  • Simulate transactions with Tenderly before execution
  • Use TypeChain for type-safe contract calls

Observability

Agora includes comprehensive observability:
  • OpenTelemetry: Distributed tracing for request flows
  • DataDog: Application metrics and alerts (optional)
  • Vercel Analytics: Web vitals and performance monitoring
  • Prisma Logging: Database query logging in development
Configure with environment variables:
ENABLE_DD_METRICS=true
DD_API_KEY=your_datadog_key
DD_APP_KEY=your_datadog_app_key
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

Next Steps

Now that you understand Agora’s architecture:

Customization Guide

Learn how to customize UI, add features, and extend functionality.

Deployment Guide

Deploy Agora to production with Vercel or your own infrastructure.

API Reference

Explore the REST API for building integrations and tools.

Contributing

Contribute to Agora development on GitHub.

Build docs developers (and LLMs) love