Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

blockful/delegation-incentives-system

Open more actions menu

Repository files navigation

ENS Delegation Incentives System

Monorepo for computing and distributing monthly ENS delegation incentive rewards. Active voters and their token holders earn pro-rata shares of a tier-based reward pool determined by month-over-month growth in the total voting power held by active voters.

Architecture

Single-service: Ponder indexes on-chain events and serves REST API endpoints from the same process.

packages/
└── domain/          — Pure incentives logic (no framework deps, fully tested)

apps/
├── backend/         — Ponder indexer + Hono REST API
└── frontend/        — Web dashboard (placeholder)

Quick Start

# 1. Copy env and fill in values
cp .env.example .env

# 2. Install dependencies
pnpm install

# 3. Run tests
pnpm --filter @ens-dis/domain test
pnpm --filter @ens-dis/backend test

# 4. Start the backend (requires RPC_URL and DATABASE_URL)
pnpm --filter @ens-dis/backend dev

Environment Variables

Variable Description
RPC_URL Ethereum mainnet RPC (required)
DATABASE_URL PostgreSQL connection string (required)
BACKEND_PORT API port (default: 42069)
ROUND_MONTHS Comma-separated configured round months, e.g. 2026-03,2026-04,2026-05
BLOCKFUL_API_TOKEN Bearer token forwarded by the backend to the Gateful relayer (required when relayer is enabled)
GATEFUL_UPSTREAM_URL Upstream Gateful relayer base URL (required when relayer is enabled)
ALLOWED_ORIGINS Comma-separated frontend origins permitted to call this backend cross-origin (required in production when the SPA hits the backend on a different origin)

API Endpoints

Routes are served at the root (no /api prefix on the backend). Frontends configure a base via VITE_API_BASE_URL if they want one.

Scalar API reference at GET /docs. OpenAPI 3.0 spec at GET /openapi.json.

Method Path Description
GET /health Ponder's built-in liveness check (use this for Railway/uptime probes)
GET /voters/active Addresses that voted on ≥ 7 of the last 10 proposals
GET /eligibility/{address} Is this address an active voter or a token holder of one?
GET /tiers/progression Current tier, VP needed for next tier, all tier thresholds
GET /apy/{address} Estimated monthly reward and annualized APY
GET /distributions/{month} Fetch a stored distribution (JSON)
GET /distributions/{month}/csv Download distribution as CSV

The backend also starts an automatic distribution scheduler in normal runs. It waits for Ponder readiness, then scans every minute and computes any configured ROUND_MONTHS that ended at least one minute ago and are not already cached.

Documentation

Document Contents
docs/algorithm.md Full pipeline spec: each step, all formulas, pool tiers, TWB, cap redistribution, lottery
docs/integrations.md Protocol integrations: contracts indexed, events handled, how data feeds the pipeline
OPERATOR.md Operator guide: running a distribution round, CSV export, monitoring

Tech Stack

  • Indexer & API: Ponder 0.16, Hono, Zod OpenAPI, Drizzle ORM
  • Domain logic: Pure TypeScript, zero framework dependencies
  • Arithmetic: BigInt throughout — no floating point
  • Testing: Vitest, fast-check (property-based)
  • Package manager: pnpm workspaces

Gasless Delegation (Relayer)

The frontend delegates voting power via a gasless relayer (when eligible) or falls back to a direct on-chain delegate(address) call. Backend routes at /api/gateful/:dao/relay/* proxy the upstream Gateful relayer with the bearer token injected server-side.

Eligibility is the only gate: if the relayer's /balance reports hasEnoughBalance: false, the upstream is unreachable, or the user is over their rate limit / under minVotingPower, the modal switches to direct-tx automatically. To take gasless offline cluster-wide without a redeploy, make /balance return hasEnoughBalance: false.

Resources

Morty Proxy This is a proxified and sanitized view of the page, visit original site.