Imported from MoonletLabs/dz-fee-tool (
AGENTS.md). Install upstream withnpx skills add MoonletLabs/dz-fee-tool. Copyright stays with the author.
AGENTS.md - DoubleZero Economic Hub
This document contains essential information for AI coding agents working in this repository.
Project Overview
The DoubleZero Economic Hub is a Next.js 15 application providing transparent visibility into DoubleZero's network economics. It connects validator fee obligations with contributor reward distributions using real data from S3 snapshots and Solana blockchain.
Tech Stack: Next.js 15.5.5, React 19, TypeScript 5 (strict mode), Tailwind CSS v4, shadcn/ui, Vitest, Playwright
Build, Test & Lint Commands
Development
pnpm dev # Start dev server with Turbopack
pnpm start # Start production server
Build
pnpm build # Production build with Turbopack
Code Quality
pnpm lint # Run ESLint
pnpm type-check # TypeScript type checking (tsc --noEmit)
Testing
# Unit tests (Vitest)
pnpm test # Run all tests once
pnpm test:watch # Run tests in watch mode
pnpm test:coverage # Run with coverage report
# Run a single test file
pnpm vitest run path/to/test.test.ts
# Run a single test by name pattern
pnpm vitest run -t "test name pattern"
# E2E tests (Playwright) - not yet configured
npx playwright test
Code Style Guidelines
Imports
- Path Aliases: Always use
@/for absolute imports (e.g.,import { cn } from "@/lib/utils") - Type Imports: Use
import typefor type-only imports (e.g.,import type { ClassValue } from "clsx") - Order: Group imports logically: external packages → internal aliases → relative imports
- Named vs Default: Prefer named exports for utilities/components; default exports for pages/routes
Example:
import { NextResponse } from "next/server";
import type { EconomicSummary } from "@/types/economic";
import { logger } from "@/lib/logger";
import { fetchData } from "./utils";
Formatting
- Quotes: Double quotes for strings
- Semicolons: Always use semicolons
- Indentation: 2 spaces (no tabs)
- Line Length: No strict limit, but keep readable (~100 chars preferred)
- Trailing Commas: Use in multiline objects/arrays
- Arrow Functions: Prefer arrow functions for inline callbacks
TypeScript
- Strict Mode: Enabled - write fully typed code
- Any Types: Avoid
any; ESLint warns but allows them (useunknownor proper types instead) - Interfaces vs Types: Use
interfacefor object shapes,typefor unions/intersections - Zod Schemas: Co-locate Zod schemas with TypeScript interfaces for runtime validation
- Type Exports: Export both TypeScript types and Zod schemas from
types/directory
Example:
// types/example.ts
import { z } from "zod";
export interface User {
id: string;
name: string;
email: string;
}
export const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
Naming Conventions
- Files:
- Components: PascalCase (e.g.,
Header.tsx,EconomicSummaryCard.tsx) - Utilities: kebab-case (e.g.,
mock-data.ts,price-service.ts) - API Routes:
route.tsin directory structure (e.g.,api/prices/sol/route.ts) - Types: kebab-case (e.g.,
economic.ts,contributor.ts)
- Components: PascalCase (e.g.,
- Variables/Functions: camelCase (e.g.,
fetchEconomicData,totalRewards) - Components: PascalCase (e.g.,
Header,EconomicSummaryCard) - Constants: UPPER_SNAKE_CASE for true constants (e.g.,
MAX_RETRIES,API_BASE_URL) - Types/Interfaces: PascalCase (e.g.,
EconomicSummary,ContributorReward)
Component Patterns
- React Server Components: Default for all components (Next.js 15 App Router)
- Client Components: Add
"use client"directive only when needed (hooks, interactivity) - Component Structure:
"use client"; // Only if needed import { ComponentProps } from "react"; import { cn } from "@/lib/utils"; interface MyComponentProps { title: string; className?: string; } export function MyComponent({ title, className }: MyComponentProps) { return ( <div className={cn("base-classes", className)}> {title} </div> ); } - Styling: Use Tailwind utility classes with
cn()helper for conditional/merged classes - Variants: Use
cva(class-variance-authority) for component variants
Error Handling
- API Routes: Return proper HTTP status codes with descriptive error messages
- Try-Catch: Always wrap async operations in try-catch blocks
- Logging: Use
@/lib/loggerfor structured logging (notconsole.log) - Error Objects: Check
error instanceof Errorbefore accessing.message
Example:
import { NextResponse } from "next/server";
import { logger } from "@/lib/logger";
export async function GET() {
try {
const data = await fetchData();
return NextResponse.json(data, { status: 200 });
} catch (error) {
logger.error("Failed to fetch data", { error });
return NextResponse.json(
{ error: error instanceof Error ? error.message : "Unknown error" },
{ status: 500 }
);
}
}
API Routes
- Dynamic Routes: Add
export const dynamic = "force-dynamic";to disable caching when needed - Response Format: Always use
NextResponse.json()for consistent responses - Status Codes: Use appropriate HTTP status codes (200, 400, 404, 500, 503)
- Validation: Use Zod schemas to validate request/response data
State Management
- Zustand: Use for global client state (stores in
lib/stores/) - React Hooks: Use
useState,useEffectfor local component state - Server State: Prefer React Server Components with direct data fetching when possible
Project Structure
app/ # Next.js App Router (pages, layouts, API routes)
components/ # React components
ui/ # shadcn/ui components (button, card, table, etc.)
[Feature].tsx # Feature-specific components
lib/ # Business logic, utilities, helpers
api/ # API data fetching/transformation
blockchain/ # Solana blockchain integration
s3/ # S3 bucket clients
calculations/ # Business logic calculations
formatters/ # Formatting utilities
stores/ # Zustand state stores
types/ # TypeScript types + Zod schemas
scripts/ # Utility scripts
Environment Variables
Create .env.local based on .env.example:
USE_REAL_DATA=false # Toggle between mock/real data
NEXT_PUBLIC_SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
# Optional: NEXT_PUBLIC_S3_BUCKET_URL (uses default if not set)
Important Notes
- No Prettier: Rely on ESLint for code style enforcement
- Testing: Test infrastructure configured but no tests written yet (create in
tests/directory) - Coverage Target: Aim for >80% code coverage
- Turbopack: Always enabled for dev and build commands
- Package Manager: Use
pnpmexclusively (not npm or yarn) - React 19: Ensure compatibility with React 19 patterns (no deprecated APIs)
Common Patterns
Conditional Styling
import { cn } from "@/lib/utils";
<div className={cn(
"base-class",
isActive && "active-class",
className
)}>
Data Fetching in API Routes
import { NextResponse } from "next/server";
import { EconomicSummarySchema } from "@/types/economic";
export async function GET() {
const data = await fetchData();
const validated = EconomicSummarySchema.parse(data);
return NextResponse.json(validated);
}
Component with Variants (using cva)
import { cva, type VariantProps } from "class-variance-authority";
const buttonVariants = cva("base-classes", {
variants: {
variant: {
default: "default-classes",
destructive: "destructive-classes",
},
},
defaultVariants: {
variant: "default",
},
});
interface ButtonProps extends VariantProps<typeof buttonVariants> {
children: React.ReactNode;
}
export function Button({ variant, children }: ButtonProps) {
return <button className={buttonVariants({ variant })}>{children}</button>;
}
Documentation
Refer to these project-specific docs for detailed information:
README.md- Setup and overviewREAL-DATA-INTEGRATION.md- S3 data integrationREAL-PRICE-INTEGRATION.md- Price service implementationPERFORMANCE-OPTIMIZATIONS.md- Caching strategiesSOLANA-INTEGRATION-STATUS.md- Blockchain integration status