This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Coda is an automated client generation tool for Solana programs. Built on top of Codama, Coda provides a CLI that transforms Anchor IDLs into modern TypeScript clients with full type safety and ES modules support.
The monorepo contains:
- Coda CLI - The main tool for generating TypeScript clients from Anchor IDLs
- Codama utilities - Custom visitors and renderers for enhanced code generation
- Generated clients - Pre-built clients for popular Solana programs
- Package Manager: Bun (v1.3.13)
- Build System: Turbo for monorepo orchestration
- Language: TypeScript with ES modules
- Code Generation: Codama for AST transformations
- Code Quality: oxlint for linting (type-aware + type-checking via oxlint-tsgolint), oxfmt for formatting
- Testing: Bun test runner
The repo ships a Nix flake (flake.nix) that provides the runtimes — Bun
1.3.13, Node 24, git, and nixfmt. Everything else (oxlint, oxfmt,
oxlint-tsgolint, TypeScript, turbo) is pinned in package.json and resolved
from node_modules/.bin, so there is exactly one copy of each on PATH and it
is the same one CI installs from the lockfile.
nix develop # enter the dev shell manually
direnv allow # or let direnv enter it automatically (uses .envrc).envrc runs use flake, which auto-watches flake.nix and flake.lock, and
dotenv_if_exists to load a local .env. Nix is optional — if you already
have Bun 1.3.13+ and Node 24+ on PATH, bun install works without it.
engines in the root package.json records the expected versions.
# Development
bun install # Install all dependencies
bun run build # Build all packages
bun run build:watch # Watch mode for all packages
bun run build:watch:packages # Watch mode for packages only
# Code Generation
bun run codegen # Run code generation for all clients
coda generate # Generate client with Coda CLI
coda init # Initialize coda.config.ts
# Code Quality
bun run lint # Run oxlint (lint + type-check) and check formatting
bun run lint:fix # Apply oxlint autofixes and format with oxfmt
bun run format # Format with oxfmt
# Testing
bun run test # Run all tests
bun test # Run tests directly
# Package Publishing
bun run changeset # Create changeset for version bumps
bun run ci:publish # Publish packages to npm
# IMPORTANT: After making code changes
bun run build # Build to check for TypeScript errors
bun run lint:fix # Fix linting and formatting issuescoda/
├── packages/ # Core packages
│ ├── coda/ # Main CLI tool (@macalinao/coda)
│ ├── codama-instruction-accounts-dedupe-visitor/ # Flattens nested accounts
│ └── codama-renderers-js-esm/ # ESM-native renderer
├── clients/ # Generated client libraries
│ └── token-metadata/ # Metaplex Token Metadata client
├── docs/ # Documentation site (Fumadocs + Next.js)
├── scripts/ # Build and CI scripts
└── vendor/ # Vendored dependencies for reference
└── fumadocs/ # Fumadocs source for configuration reference
- Works out of the box (looks for
./idls/*.jsonby default) - Configurable via
coda.config.ts - Generates TypeScript clients with full type safety
- Built on Codama for extensibility
- Supports glob patterns for IDL discovery
- Flattens nested account structures from Anchor IDL
- Preserves relationships through naming conventions
- Updates PDA seeds when accounts are flattened
- ESM-native TypeScript renderer for Codama
- Adds
.jsextensions to all imports - Removes Node.js-specific environment checks
- Ensures compatibility with
"type": "module" - Emits only erasable syntax (no
enum, no angle-bracket assertions), so generated clients compile undererasableSyntaxOnly
- Pre-generated client for Metaplex Token Metadata program
- Includes custom PDAs and type definitions
- Ready-to-use TypeScript client
- Parse IDL: Reads Anchor IDL file (JSON format)
- Create AST: Converts to Codama's node structure
- Apply Visitors: Transforms the AST (custom PDAs, flattening, etc.)
- Generate Code: Renders TypeScript with ESM support
- Output Files: Creates organized file structure with types, instructions, accounts, etc.
Coda automatically discovers IDLs without any configuration:
- Looks for
./idls/*.jsonby default - Place your IDL files in the
idls/directory - No config file needed for basic usage!
For projects with a single program (like token-metadata):
import { defineConfig } from "@macalinao/coda";
import { addPdasVisitor } from "codama";
export default defineConfig({
// Optional: Custom path for single IDL
idlPath: "./idls/my_program.json",
outputDir: "./src/generated",
// Optional: Codama visitors for customization
visitors: [
addPdasVisitor({
// Add custom PDAs
}),
],
});For projects with multiple programs (like quarry):
import { defineConfig } from "@macalinao/coda";
export default defineConfig({
// Use glob pattern to match all IDLs
idlPath: "./idls/*.json",
outputDir: "./src/generated",
// Optional: Add PDAs and other visitors for each program
visitors: [
// Custom visitors for each program
],
});You can also explicitly list IDL files:
import { defineConfig } from "@macalinao/coda";
export default defineConfig({
// Array of specific IDL paths
idlPath: [
"./idls/program1.json",
"./idls/program2.json",
"./custom/path/program3.json",
],
outputDir: "./src/generated",
});Use specific patterns to match only certain IDLs:
import { defineConfig } from "@macalinao/coda";
export default defineConfig({
// Match only IDLs starting with "quarry_"
idlPath: "./idls/quarry_*.json",
// Or combine multiple patterns
// idlPath: ["./idls/quarry_*.json", "./extra/*.json"],
outputDir: "./src/generated",
});- Use specific types, avoid
any - Prefer interfaces over type aliases for objects
- Use
import typefor type-only imports (enforced by oxlint) - Arrays use shorthand syntax:
string[]notArray<string> - Use double quotes for strings (not single quotes)
- ES modules with
.jsextensions for imports - File naming: Use kebab-case for all TypeScript files (e.g.,
root-node-from-anchor.ts,create-codama-from-idls.ts)
Always run these commands to ensure code quality:
bun run build- Check for TypeScript errorsbun run lint:fix- Fix linting and formatting issuesbun run changeset- Record the version bump before opening a PR (see Changesets on Every PR)
Linting is configured in the root .oxlintrc.json and runs as a single
pass over the whole repo via oxlint --disable-nested-config. It enables
the correctness, suspicious, and perf categories plus a curated set
of high-value type-aware rules, with typeAware and typeCheck on (so
oxlint also reports TypeScript compiler errors). Overrides relax generated
code (clients/*/src/generated/**), CLI console output, and tests.
- No floating promises (must be handled)
- No explicit
any - No non-null assertions outside tests
- Type-only imports must use
import type(separated) - No
consoleoutside the CLI packages and build scripts
Formatting is handled by oxfmt; it is scoped to code via
.oxfmtrc.json ignorePatterns.
Tasks are defined in turbo.json:
build: Depends on upstream builds, outputs to./dist/**test: Depends on build, no cachingcodegen: Outputs to./src/generated/**, no caching- Tasks run in topological order respecting dependencies
- Add IDL file: Place in
clients/[program-name]/idls/ - Create config: Add
coda.config.tswith any custom visitors - Add package.json: Include build (
"build": "tsdown") and codegen scripts - Generate client: Run
bun run codegen - Build: Run
bun run build
No per-package tsdown.config.ts is needed — tsdown walks up to the shared root tsdown.config.ts.
Example package.json for a client:
{
"name": "@solana-programs/[program-name]",
"scripts": {
"build": "tsdown",
"codegen": "coda generate",
"clean": "rm -fr dist/"
}
}GitHub Actions workflow runs on push/PR to main:
- Installs dependencies with frozen lockfile
- Builds all packages
- Runs oxlint (lint + type-aware type-checking) and checks formatting with oxfmt
- Runs tests
- Type-checks the coda.config.ts files
- Create changeset:
bun run changeset - Version packages:
bun run ci:version - Publish to npm:
bun run ci:publish - Changesets handle version bumping and changelog generation
Every new PR must include a changeset. Releases are cut from the changesets on master, so a PR without one ships its changes with no version bump and no changelog entry.
- Run
bun run changeset - Select the package(s) the PR affects
- Choose the semver bump:
patchfor fixes and internals,minorfor new backwards-compatible features,majorfor breaking changes - Write a summary aimed at consumers of the package — it becomes the changelog entry
- Commit the generated file in
.changeset/as part of the PR
For changes with no effect on published output — docs, CI workflows, repo
tooling — use bun run changeset --empty to record the deliberate decision,
or say in the PR description why no changeset is needed. This is the
exception; anything that changes what a package publishes needs a real bump.
When creating new packages:
- Build with tsdown. The shared root
tsdown.config.tsis auto-resolved (tsdown walks up the directory tree), so packages need no config of their own. Only add a localtsdown.config.tsto override — e.g. extraentrypoints for a CLI binary, aspackages/codaandpackages/create-codado - Follow the same structure as existing packages
- Scripts should be:
build,clean,test,codegen(if applicable). Linting is a single repo-wideoxlintpass run from the root, so packages do not need their ownlintscript - All packages use ES modules (
"type": "module"in package.json) - Keep package.json scripts simple and consistent
Generated clients provide:
- Instructions: Typed builders for all program instructions
- Accounts: Decoders and fetchers for all account types
- Types: All TypeScript types from the IDL
- PDAs: Helper functions for program-derived addresses
- Errors: Typed error enums and handlers
Example usage:
import { createTransferInstruction } from "./generated";
const instruction = createTransferInstruction({
source: sourceAddress,
destination: destAddress,
authority: authorityAddress,
amount: 1000n,
});When writing documentation for Coda or generated clients:
-
Command Examples:
- Always show direct command usage:
coda generate, notbunx coda generateornpx coda - Assume users have installed the package globally or are using it directly
- Show the simplest path to success
- Always show direct command usage:
-
Code Examples:
- Provide complete, runnable examples
- Show imports clearly at the top
- Use TypeScript for all examples
- Include types where helpful for clarity
-
Structure:
- Start with the most common use case
- Progress from simple to complex
- Link to relevant examples in the repository
- Use clear headings and subheadings
-
Best Practices:
- Explain the "why" not just the "how"
- Include error messages users might see
- Provide solutions to common problems
- Keep examples concise but complete
-
Links and References:
- Link to example repositories (e.g., token-metadata for single IDL, quarry for multiple IDLs)
- Reference the official Codama documentation where appropriate
- Include links to Anchor documentation for IDL-related topics
The apps/docs/ folder contains the documentation site built with Fumadocs and Next.js 15. The structure follows the Fumadocs reference implementation in vendor/fumadocs/apps/docs/.
- Framework: Next.js 15 with App Router
- Documentation: Fumadocs UI
- Styling: Tailwind CSS v4 (no config file needed, uses @source directives)
- MDX: Fumadocs MDX with Shiki syntax highlighting
- Themes: Catppuccin Latte (light) / Catppuccin Mocha (dark) for code blocks
- Linting: oxlint (via the shared root
.oxlintrc.json)
cd apps/docs
bun run dev # Start dev server on localhost:3000
bun run build # Build for production- Create MDX files in
apps/docs/content/docs/ - Update navigation in
meta.jsonfiles if needed - Code blocks automatically get syntax highlighting
- Use frontmatter for page metadata (title, description)
- Important: Do NOT include
# Titleheaders in MDX files - the title is already specified in the YAML frontmatter and will be rendered automatically
src/app/layout.config.tsx- Site branding and navigationsource.config.ts- MDX and syntax highlighting configsrc/app/global.css- Tailwind v4 imports with @source directivescontent/docs/- Documentation content in MDX
- No redundant headers: Since the title is specified in YAML frontmatter, do NOT add
# Titleas the first line of content - Start documentation content directly with the introduction paragraph or first section (
##) - The title from frontmatter will be automatically rendered by Fumadocs
- IDL not found: Ensure Anchor program is built (
anchor build) - Config not loading: Check file is named
coda.config.tswith.tsextension - Import errors: Ensure all imports use
.jsextensions for local files - Type errors: Run
bun run buildto check TypeScript compilation
# Check generated files
ls -la ./src/generated/
# Verify config is valid
node -e "import('./coda.config.ts').then(c => console.log(c.default))"
# Clean and rebuild
bun run clean && bun run codegen && bun run build