npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

codama-renderers-dart

v0.5.5

Published

Codama renderer for generating Dart code targeting the solana_kit SDK

Readme

codama-renderers-dart

pub package CI Coverage

A Codama renderer that generates Dart code targeting the solana_kit SDK.

Given a Codama IDL (Interface Description Language) describing a Solana program, this renderer produces a complete Dart package with typed account classes, instruction builders, codec functions, error definitions, PDA helpers, and barrel exports.

Numeric prefix codecs retain the declared byte order, and pre/post offset types retain their cursor or padding strategy. Constant PDA integer seeds use the matching Dart integer type; malformed hexadecimal seeds and unsupported byte encodings are rejected.

Fixed-size Codama types generate non-truncating encoders. Values within the declared byte capacity are zero-padded, while over-capacity values throw before any malformed bytes can be emitted. For UTF-8 strings, capacity is measured in encoded bytes rather than Dart code units.

Installation

pnpm add codama-renderers-dart
# or
npm install codama-renderers-dart

Quick start

Programmatic API

import { renderVisitor } from "codama-renderers-dart";
import { visit } from "@codama/visitors-core";
import { rootNode, programNode /* ... */ } from "@codama/nodes";

// Build or load a Codama IDL root node
const root = rootNode(programNode({ /* ... */ }));

// Generate Dart files into the output directory
visit(root, renderVisitor("lib/src/generated", {
  formatCode: true,             // Run `dart format` on output (default: false)
  deleteFolderBeforeRendering: true, // Clean output dir first (default: true)
}));

Serialized Pina IDLs

renderVisitor accepts parsed Codama JSON as well as roots made with Codama constructors. Pina omits empty collections from its serialized IDLs; the renderer restores those structural defaults on an internal copy before traversal, leaving the parsed object unchanged.

IDL names may contain letters, digits, underscores, hyphens, and spaces; names containing path separators, quotes, or other source syntax are rejected. Documentation and string values are escaped before they enter generated Dart. Rendering resolves all output before clearing the previous generated directory, so invalid IDLs do not delete an existing client. Formatter paths are passed as literal process arguments. Numeric defaults, counts, sizes, and error codes are validated before they are written as source. Duplicate generated paths are rejected, including names reserved for category barrel files, so nodes cannot silently replace one another.

import { readFile } from "node:fs/promises";
import { visit } from "@codama/visitors-core";
import { renderVisitor } from "codama-renderers-dart";

const idl = JSON.parse(await readFile("target/codama/my_program.json", "utf8"));
visit(idl, renderVisitor("lib/src/generated"));

Use normalizeRootNode(idl) when calling lower-level visitors such as getRenderMapVisitor directly.

Codama CLI

Create a codama.json configuration file:

{
  "idl": "idl.json",
  "scripts": {
    "dart": {
      "from": "codama-renderers-dart",
      "args": ["lib/src/generated"]
    }
  }
}

Then run:

codama run dart

Generated output structure

For a program called myProgram, the renderer generates:

lib/src/generated/
  my_program.dart                    # Root barrel export
  accounts/
    accounts.dart                    # Category barrel
    my_account.dart                  # Account type + codecs + decode helper
  instructions/
    instructions.dart                # Category barrel
    my_instruction.dart              # Instruction data + builder + parser
  types/
    types.dart                       # Category barrel
    my_struct.dart                   # Struct type + codecs
    my_enum.dart                     # Enum/sealed class + codecs
  errors/
    errors.dart                      # Category barrel
    my_program.dart                  # Error constants + message helpers
  programs/
    programs.dart                    # Category barrel
    my_program.dart                  # Program address + identifier enums
  pdas/
    pdas.dart                        # Category barrel
    my_pda.dart                      # PDA seeds class + finder function

Generated code patterns

Accounts

Each account generates an @immutable Dart class with typed fields, const constructor, proper ==/hashCode/toString implementations, and codec functions:

@immutable
class MyAccount {
  const MyAccount({
    required this.authority,
    required this.count,
  });

  final Address authority;
  final BigInt count;

  // ... equality, hashCode, toString
}

Encoder<MyAccount> getMyAccountEncoder() { ... }
Decoder<MyAccount> getMyAccountDecoder() { ... }
Codec<MyAccount, MyAccount> getMyAccountCodec() { ... }
Account<MyAccount> decodeMyAccount(EncodedAccount encodedAccount) { ... }

Instructions

Each instruction generates a data class, codec functions, a builder function, and a parse function:

@immutable
class TransferInstructionData {
  const TransferInstructionData({
    this.discriminator = 3,
    required this.amount,
  });

  final int discriminator;
  final BigInt amount;
}

Instruction getTransferInstruction({
  required Address programAddress,
  required Address source,
  required Address destination,
  required BigInt amount,
}) { ... }

TransferInstructionData parseTransferInstruction(Instruction instruction) { ... }

Discriminators and omitted defaults

Generated codecs treat Codama discriminators as wire invariants rather than caller input. A struct field or instruction argument with a default value strategy of omitted is not accepted by the generated constructor or instruction builder. Its declared default is always written by the encoder.

Account and instruction decoders validate every declared constant, field, and size discriminator before returning typed data. Invalid discriminator bytes and unexpected discriminator sizes throw a SolanaError instead of decoding as the wrong account or instruction type. Generation fails when a field discriminator has no deterministic default or uses a value form the renderer cannot encode safely.

Top-level instruction decoders require exact input consumption. Account decoders always reject truncated data, but accept unread trailing capacity unless the account declares a sizeDiscriminatorNode; size-discriminated accounts reject both truncation and suffix bytes.

Accounts declared with isSigner: "either" expose an additional boolean such as authorityIsSigner, defaulting to true. Set it to false when the authority does not sign, including multisig authorities.

Optional instruction accounts use Codama's programId strategy by default. When an optional account is absent, the builder emits a readonly program-address placeholder so every later account keeps its declared index. The account slot is removed only when the instruction explicitly selects the legacy omitted strategy.

Scalar enums

Scalar enums (all-empty variants) generate a Dart enum with index-based encoder/decoder:

enum AccountStatus {
  active,
  frozen,
  closed,
}

Encoder<AccountStatus> getAccountStatusEncoder() { ... }
Decoder<AccountStatus> getAccountStatusDecoder() { ... }

The generated enum API is the same for u8, u16, u32, and u64 discriminators. The renderer converts u64 indices to and from BigInt at the codec boundary and rejects out-of-range discriminators before converting them to Dart enum indices.

Data enums (discriminated unions)

Data enums generate Dart 3 sealed class hierarchies:

sealed class TokenInstruction {
  const TokenInstruction();
}

final class Transfer extends TokenInstruction {
  const Transfer({required this.amount});
  final BigInt amount;
}

final class Approve extends TokenInstruction {
  const Approve({required this.amount});
  final BigInt amount;
}

Errors

Program errors generate constants, a message map, and helper functions:

const int myProgramErrorInvalidAuthority = 0x1770; // 6000

String? getMyProgramErrorMessage(int code) { ... }
bool isMyProgramError(int code) { ... }

PDAs

PDAs generate a seeds class and finder function:

@immutable
class MyPdaSeeds {
  const MyPdaSeeds({required this.authority});
  final Address authority;
}

Future<(Address, int)> findMyPdaPda({
  required MyPdaSeeds seeds,
  required Address programAddress,
}) async { ... }

Type mapping

| Codama Type | Dart Type | Codec | | ----------------------------------- | --------------- | -------------------------------- | | numberTypeNode(u8/u16/u32) | int | getU8Encoder() etc. | | numberTypeNode(u64/u128/i64/i128) | BigInt | getU64Encoder() etc. | | numberTypeNode(f32/f64) | double | getF32Encoder() etc. | | booleanTypeNode | bool | getBooleanEncoder() | | stringTypeNode | String | getUtf8Encoder() etc. | | publicKeyTypeNode | Address | getAddressEncoder() | | bytesTypeNode | Uint8List | getBytesEncoder() | | arrayTypeNode | List<T> | getArrayEncoder() | | mapTypeNode | Map<K, V> | getMapEncoder() | | setTypeNode | Set<T> | getSetEncoder() | | tupleTypeNode | (T1, T2, ...) | getTupleEncoder() | | optionTypeNode | T? | getNullableEncoder() | | structTypeNode | Named class | getStructEncoder() | | enumTypeNode (scalar) | enum | transformEncoder() | | enumTypeNode (data) | sealed class | getDiscriminatedUnionEncoder() |

Options

RenderOptions

| Option | Type | Default | Description | | ----------------------------- | ------------------------ | ------- | --------------------------------------------------- | | deleteFolderBeforeRendering | boolean | true | Delete output directory before generating | | formatCode | boolean | false | Run dart format on generated files | | nameApi | Partial<DartNameApi> | — | Override naming conventions | | dependencyMap | Record<string, string> | — | Override logical module to Dart package URI mapping |

DartNameApi

All naming conventions are customizable:

import { renderVisitor, createDartNameApi } from "codama-renderers-dart";

const nameApi = {
  ...createDartNameApi(),
  dataType: (name) => `My${pascalCase(name)}`,
};

visit(root, renderVisitor("output", { nameApi }));

Target packages

Generated Dart code depends on these solana_kit packages:

  • solana_kit_addressesAddress type and encoder/decoder
  • solana_kit_codecs_coreEncoder, Decoder, Codec base types
  • solana_kit_codecs_data_structures — Struct, array, boolean, nullable codecs
  • solana_kit_codecs_numbers — Number codecs (u8–u128, i8–i128, f32, f64)
  • solana_kit_codecs_strings — String codecs (utf8, base58, base64, base16)
  • solana_kit_accountsAccount, EncodedAccount, decodeAccount
  • solana_kit_instructionsInstruction, AccountMeta, AccountRole
  • solana_kit_errorsSolanaError error types
  • meta@immutable annotation

Architecture

This renderer follows the same architecture as the official Codama renderers:

  1. Visitor pattern — Uses @codama/visitors-core to traverse the IDL node tree
  2. Fragment system — Composable tagged template literals that track imports
  3. RenderMap — Maps file paths to code fragments
  4. Type manifest — Maps Codama type nodes to Dart types and codec expressions

Key source files:

  • src/visitors/renderVisitor.ts — Top-level entry point (file I/O)
  • src/visitors/getRenderMapVisitor.ts — Maps IDL nodes to output files
  • src/visitors/getTypeManifestVisitor.ts — Maps type nodes to Dart types/codecs
  • src/fragments/*.ts — Code generation for each entity type
  • src/utils/ — Import maps, naming, fragment helpers

Development

pnpm install
npx tsc --noEmit
pnpm test
pnpm test:watch
pnpm build

License

MIT