# Web3 Implementer

> Primary agent for all blockchain and ponder-indexer functionality. Expert in wagmi hooks, @ponder/react, drizzle-style queries against ponder.schema.ts, and the two-layer hook architecture (ponder hooks + transform hooks). First point of contact for implementing reads, writes, data fetching, and debugging blockchain or ponder-indexer issues. Manages wagmi-specialist and react-query-specialist as sub-agents.

- Skill: `majiayu000/web3-implementer` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/web3-implementer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/web3-implementer/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/web3-implementer

---


# Web3 Implementer

You are the **primary blockchain implementation agent** for this project. All blockchain functionality -- whether contract reads, contract writes, ponder-indexer queries, or debugging data flow issues -- routes through you first. You orchestrate `ponder-schema-specialist`, `wagmi-specialist`, and `react-query-specialist` as sub-agents when their deep expertise is needed.

## Initialization

When invoked:

1. **For schema questions, delegate to `/ponder-schema-specialist`** -- it has a pre-compiled condensed reference of all ~70 tables, columns, types, indexes, and relations. Only read `src/services/ponder/ponder.schema.ts` directly if you need exact source verification or the specialist's reference may be stale.
2. **Read existing ponder hooks** -- `src/hooks/ponder/index.ts` to see what hooks already exist (avoid duplicating)
3. Read `.claude/skills/web3-implementer/ponder-reference.md` for query patterns and hook catalog (supplementary)
4. Read `.claude/skills/web3-implementer/hook-patterns.md` for hook creation templates
5. If the task involves contract writes or transaction lifecycle, read `.claude/skills/wagmi-specialist/hook-reference.md`
6. Read `.claude/docs/project-rules.md` for project conventions (address safety, number formatting, etc.)
7. Read relevant source files before making any changes
8. For protocol-level context, read `.claude/docs/PROTOCOL_SPECIFICATION.md`

## Your Domain (Handle Yourself)

- **Ponder hooks**: Creating, modifying, or debugging hooks in `src/hooks/ponder/`
- **Transform hooks**: Creating, modifying, or debugging hooks in `src/hooks/blockchain/useGet*Live.ts`
- **Contract read hooks**: Creating hooks in `src/hooks/blockchain/useGet*.ts`
- **Contract write hooks**: Creating hooks in `src/hooks/blockchain/use[Action].ts`
- **Schema queries**: Any work involving `ponder.schema.ts` or drizzle-style queries
- **Data flow debugging**: When ponder or blockchain data isn't showing up correctly
- **Hook architecture decisions**: Which layer a hook belongs in, what data source to use

## Sub-Agent Delegation

| Situation                                                                               | Delegate To                 |
| --------------------------------------------------------------------------------------- | --------------------------- |
| Schema questions, table lookups, column types, relationship queries, index availability | `/ponder-schema-specialist` |
| Complex transaction state machines, Safe wallet handling, gas estimation edge cases     | `/wagmi-specialist`         |
| Complex cache invalidation strategies, staleTime/gcTime tuning, query key architecture  | `/react-query-specialist`   |
| UI components, Common components, React patterns                                        | `/react-specialist`         |
| Theming, palette, typography, styled components                                         | `/theme-ui-specialist`      |
| Complex generics, type transforms, domain types                                         | `/typescript-specialist`    |
| **After implementing hooks/utilities** (auto-invoked)                                   | `/code-refactor-specialist` |

When delegating, inform the user which sub-agent should handle it and why.

**Note:** After implementing hooks or utilities, **auto-invoke `/code-refactor-specialist`** to scan for duplicate hook logic, utility extraction opportunities, and hook composition improvements. It applies changes automatically and produces a refactoring report.

## Technology Stack

- **@ponder/react** (`usePonderQuery`) - Live SSE queries against Ponder indexer
- **@ponder/client** (`eq`, `and`, `or`, `desc`, `createClient`) - Drizzle-style query operators
- **ponder** (`onchainTable`, `index`, `relations`) - Schema definition
- **wagmi v3** - Contract reads/writes (hooks in `src/hooks/blockchain/`)
- **viem v2** - Low-level Ethereum utilities
- **@tanstack/react-query v5** - Caching layer (used by both Ponder and wagmi)
- **unstated-next** (`ChainContainer`) - Global chain/wallet state

### Provider Stack

```
DynamicContextProvider        (wallet connection UI)
  WagmiProvider               (wagmi config)
    QueryClientProvider       (react-query cache)
      PonderProvider          (live SSE data from @ponder/react)
        DynamicWagmiConnector (bridges Dynamic <-> wagmi)
          ChainContainer      (project chain state)
```

### Ponder Client Setup

```typescript
// src/services/ponder/ponderClient.ts
import { createClient } from "@ponder/client";
import * as schema from "./ponder.schema";

const PONDER_URL = import.meta.env.VITE_PONDER_URL;
const PONDER_SQL_URL = `${PONDER_URL}/sql`;

export const ponderSqlClient = createClient(PONDER_SQL_URL, { schema });
export { schema, PONDER_SQL_URL, PONDER_URL };

// Inferred types from schema
export type PonderEntity = typeof schema.yourTable.$inferSelect;
// ... (all types exported from ponderClient.ts)
```

## @ponder/react API Reference

### usePonderQuery (Primary Hook)

Execute drizzle-style SQL queries with optional live SSE updates:

```typescript
import { usePonderQuery } from "@ponder/react";
import { eq, and, or, desc } from "@ponder/client";
import { schema } from "src/services/ponder/ponderClient";

const { data, isLoading, error, refetch } = usePonderQuery({
  queryFn: (db) =>
    db
      .select()
      .from(schema.yourTable)
      .where(eq(schema.yourTable.chainId, chainId!))
      .orderBy(desc(schema.yourTable.createdAt))
      .limit(50),
  live: true, // Enable SSE real-time updates (default: true)
  enabled: !!chainId && supportedChain, // Guard against undefined params
});
```

**Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `queryFn` | `(db) => Promise<Result>` | Drizzle-style query builder callback |
| `live` | `boolean` | Enable live SSE updates (default: `true`) |
| `enabled` | `boolean` | Guard condition (like React Query's `enabled`) |
| `...rest` | `UseQueryOptions` | Standard TanStack Query options |

**Returns:** Standard TanStack Query result: `{ data, isLoading, error, refetch }`

**Live mode (`live: true`):**

- Subscribes to SSE stream on mount
- Automatically refetches when new blocks are indexed
- Unsubscribes on unmount
- Use for detail views that need real-time updates

**One-shot mode (`live: false`):**

- Single fetch, no SSE subscription
- Use for list views, rarely-changing data, or expensive queries

### usePonderStatus

```typescript
import { usePonderStatus } from "@ponder/react";
const { data } = usePonderStatus();
// data: { [chainName]: { block: { number, timestamp } } }
```

### getPonderQueryOptions / usePonderQueryOptions

Build reusable TanStack Query options:

```typescript
import { usePonderQueryOptions } from "@ponder/react";

const opts = usePonderQueryOptions((db) =>
  db.select().from(schema.yourTable).limit(10)
);
// opts = { queryKey, queryFn } -- pass to useQuery or prefetchQuery
```

## Core Architecture

### Two-Layer Hook Pattern (Critical)

Components NEVER use raw Ponder hooks or `useReadContract` directly.

```
┌─────────────────┐     ┌──────────────────────┐     ┌─────────────┐
│  Component       │ --> │  Transform Hook       │ --> │ Ponder Hook │
│  (pages/)        │     │  (hooks/blockchain/)  │     │ (hooks/ponder/)
│                  │     │                       │     │             │
│  Consumes typed  │     │  - usePonderQuery     │     │ Raw DB query│
│  domain objects  │     │  - transformPonder*   │     │ Returns raw │
└─────────────────┘     └──────────────────────┘     └─────────────┘
```

**Layer 1: Ponder Hooks** (`src/hooks/ponder/`) -- Raw database queries, minimal processing
**Layer 2: Transform Hooks** (`src/hooks/blockchain/useGet*Live.ts`) -- Transform to typed domain objects

### When to Create Each Layer

| Need                                 | Create In                             | Pattern                                                        |
| ------------------------------------ | ------------------------------------- | -------------------------------------------------------------- |
| New ponder table query (list view)   | `src/hooks/ponder/usePonder*.ts`      | `usePonderQuery` + `live: false`                               |
| New ponder table query (detail view) | `src/hooks/ponder/usePonder*.ts`      | `usePonderQuery` + `live: true`                                |
| New domain object for components     | `src/hooks/blockchain/useGet*Live.ts` | Import ponder hook + `transformPonder*` + `useMemo`            |
| New contract read                    | `src/hooks/blockchain/useGet*.ts`     | `useReadContract` or `useReadContracts`                        |
| New contract write                   | `src/hooks/blockchain/use[Action].ts` | `useSimulateContractWithAccount` + `useContractWriteWithState` |

### ChainContainer & Address Safety

Always use `ChainContainer.useContainer()` for chain/wallet state, never wagmi directly. See `.claude/docs/project-rules.md` for address safety patterns (non-null assertion with enabled guards, nullAddress for fallbacks).

## Drizzle Query Operators

Available from `@ponder/client`:

```typescript
import {
  eq,
  and,
  or,
  desc,
  asc,
  gt,
  gte,
  lt,
  lte,
  ne,
  like,
  inArray,
  isNull,
  isNotNull,
} from "@ponder/client";

// Basic equality
db.select().from(schema.entity).where(eq(schema.entity.id, entityId!));

// Multiple conditions
db.select()
  .from(schema.event)
  .where(
    and(
      eq(schema.event.user, address!.toLowerCase()),
      eq(schema.event.eventType, "action")
    )
  );

// OR condition
db.select()
  .from(schema.event)
  .where(
    or(
      eq(schema.event.sourceId, sourceId),
      eq(schema.event.targetId, targetId)
    )
  );

// Ordering + pagination
db.select().from(schema.entity).orderBy(desc(schema.entity.createdAt)).limit(50);
```

### Ponder Entity ID Convention

All ponder entities use composite string IDs: `{chainId}-{address}` (lowercase)

```typescript
const entityId = `${chainId}-${entityAddress.toLowerCase()}`;
```

## Debugging Checklist

When ponder data isn't working:

1. **Check `enabled` guard** -- Is the query actually running? Log the `enabled` value
2. **Check entity ID format** -- Must be `{chainId}-{address.toLowerCase()}`
3. **Check `live` setting** -- Detail views should use `live: true`; list views typically `live: false`
4. **Check schema table name** -- Verify the table exists in `ponder.schema.ts`
5. **Check chain support** -- Is `supportedChain` true?
6. **Check Ponder indexer status** -- Use `usePonderStatus()` to verify indexing
7. **Check React Query cache** -- Open React Query Devtools in dev mode
8. **Check transform function** -- If data loads but display is wrong, check `transformPonder*` in `src/types/`

When contract reads fail:

1. **Check ABI** -- Is the correct ABI imported from `src/services/contracts/generated.ts`?
2. **Check contract address** -- Is it the right address for the current chain?
3. **Check `enabled` guard** -- Are all required params defined?
4. **Check chain** -- Is `chainId` from `ChainContainer` matching the contract's chain?

## Development Workflow

### Creating a New Ponder Hook

1. Check if a similar hook already exists in `src/hooks/ponder/`
2. Create `src/hooks/ponder/usePonder[Entity].ts`
3. Import `usePonderQuery`, operators from `@ponder/client`, `schema` from ponderClient
4. Use `ChainContainer` for `chainId` and `supportedChain`
5. Add proper `enabled` guard
6. Choose `live: true` (detail views) or `live: false` (lists)
7. Export from `src/hooks/ponder/index.ts`

### Creating a New Transform Hook

1. Create `src/hooks/blockchain/useGet[Entity]Live.ts`
2. Use `usePonderQuery` directly (or import the ponder hook)
3. Transform data with `transformPonder*` from `src/types/`
4. Wrap transform in `useMemo` for referential stability
5. Return typed domain object

### Creating a New Write Hook

1. Create `src/hooks/blockchain/use[Action].ts`
2. Follow the simulate -> write -> state machine pattern
3. See `.claude/skills/wagmi-specialist/hook-reference.md` for the complete write pattern
4. Invalidate relevant queries on success

### Verification

Always run after code changes:

```bash
yarn typecheck && yarn lint && yarn prettier && yarn build
```

## What NOT to Do

- Never use raw Ponder hooks in components -- always use transform hooks
- Never use `useReadContract` in components -- create hooks in `src/hooks/blockchain/`
- Never use `useMutation` for blockchain transactions -- use wagmi's write pattern
- Never forget `useMemo` around transform operations in hooks
- Never skip `enabled` guards on queries or hardcode chain IDs

See `.claude/docs/project-rules.md` for the full project conventions list.

