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/dynamic-client

v0.1.0

Published

Tool to dynamically build instructions based on Codama IDLs

Downloads

1,091

Readme

Codama ➤ Dynamic Client

npm npm-downloads

This package provides a runtime Solana instruction builder that dynamically constructs Instruction (@solana/instructions) from Codama IDL and provides type generation for full TypeScript type safety.

Installation

pnpm install @codama/dynamic-client

[!NOTE] This package is not included in the main codama package.

Quick Start

Untyped

import { createProgramClient } from '@codama/dynamic-client';
import idl from './my-program-idl.json';

const client = createProgramClient(idl);

const instruction = await client.methods
    .transferSol({ amount: 1_000_000_000 })
    .accounts({ source: senderAddress, destination: receiverAddress })
    .instruction();

Typed with generated types

import { createProgramClient } from '@codama/dynamic-client';
import type { MyProgramClient } from './generated/my-program-types';
import idl from './my-program-idl.json';

const client = createProgramClient<MyProgramClient>(idl);
// client.methods, .accounts(), args are now fully typed

API Reference

createProgramClient<T>(idl, options?)

Creates a program client from a Codama IDL.

| Parameter | Type | Description | | ------------------- | ------------------ | ----------------------------------------- | | idl | object \| string | Codama IDL object or JSON string | | options.programId | AddressInput | Override the program address from the IDL |

Returns a ProgramClient (or T when a type parameter is provided).

ProgramClient

type InstructionName = CamelCaseString;
type AccountName = CamelCaseString;

type ProgramClient = {
    methods: Record<InstructionName, (args?) => ProgramMethodBuilder>;
    pdas?: Record<AccountName, (seeds?) => Promise<ProgramDerivedAddress>>;
    programAddress: Address;
    instructions: Map<InstructionName, InstructionNode>;
    root: RootNode;
};

ProgramMethodBuilder (fluent API)

client.methods
    .myInstruction(args) // provide instruction arguments
    .accounts(accounts) // provide account addresses
    .signers(['accountName']) // optionally mark ambiguous accounts as signers
    .resolvers({ customResolver: async (argumentsInput, accountsInput) => {} }) // optionally provide custom resolver according to resolverValueNode in IDL
    .instruction(); // Promise<Instruction>

AddressInput

Accepts any of:

  • Address (from @solana/addresses)
  • Legacy PublicKey (any object with .toBase58())
  • Base58 string

Accounts

Automatic resolution rules

Accounts (pda, program ids) with defaultValue are resolved automatically, hence can be omitted.

| Account scenario | Type in .accounts() | Auto resolution | | --------------------------------------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------- | | Required account without defaultValue | { system: Address } | No | | Required account with defaultValue(PDA, programId, etc.) | { system?: Address } | Auto-resolved to defaultValue if omitted | | Optional account (isOptional: true)without defaultValue | { system: Address \| null } | Resolved via optionalAccountStrategy,if provided as null | | Optional account (isOptional: true)with defaultValue | { system?: Address \| null } | - null resolves via optionalAccountStrategy- undefined resolves via defaultValue |

Auto-resolved account addresses

Accounts with defaultValue in the IDL are automatically resolved when omitted from .accounts(). This includes:

  • PDA accounts — derived from seeds defined in the IDL
  • Program IDs — resolved to known program addresses (e.g., System Program, Token Program)
  • Constants — resolved from constant value nodes

You can always override auto-derived accounts by providing an explicit address.

Optional accounts

Pass null for optional accounts to be resolved according to optionalAccountStrategy (either will be omitted or replaced on programId):

.accounts({
    authority,
    program: programAddress,
    programData: null,  // optional - resolved via optionalAccountStrategy
})

Ambiguous signers

When an account has isSigner: 'either' in the IDL, use .signers() to explicitly mark it:

.accounts({ owner: ownerAddress })
.signers(['owner'])

Custom resolvers

When an account or argument is resolverValueNode in the IDL, provide a custom resolver function .resolvers({ [resolverName]: async fn }) to help with account/arguments resolution:

client.methods
    .create({ tokenStandard: 'NonFungible' })
    .accounts({ owner: ownerAddress })
    .resolvers({
        resolveIsNonFungible: async (argumentsInput, accountsInput) => {
            return argumentsInput.tokenStandard === 'NonFungible';
        },
    });

PDA Derivation

Standalone

const [address, bump] = await client.pdas.canonical({
    program: programAddress,
    seed: 'idl',
});

Auto-derived in instructions

Accounts with pdaValueNode defaults are resolved automatically. Seeds are pulled from other accounts and arguments in the instruction:

// metadata PDA is auto-derived from program + seed
const ix = await client.methods
    .initialize({ seed: 'idl', data: myData /* ... */ })
    .accounts({ authority, program: programAddress, programData })
    .instruction();

Nested/dependent PDAs (where one PDA seed references another PDA) are resolved recursively.

Arguments

Arguments with defaultValueStrategy: 'omitted' (e.g., discriminators) are auto-encoded and should not be provided.

Error Handling

All errors are instances of CodamaError from @codama/errors:

import { CodamaError, isCodamaError, CODAMA_ERROR__DYNAMIC_CLIENT__ACCOUNT_MISSING } from '@codama/dynamic-client';

try {
    const ix = await client.methods.transferSol({ amount: 100 }).accounts({}).instruction();
} catch (err) {
    if (isCodamaError(err, CODAMA_ERROR__DYNAMIC_CLIENT__ACCOUNT_MISSING)) {
        console.error(`Missing account: ${err.context.accountName}`);
    }
}

CLI

The package includes a CLI for generating TypeScript types from Codama IDL files.

npx @codama/dynamic-client generate-client-types <codama-idl.json> <output-dir>

Example:

npx @codama/dynamic-client generate-client-types ./idl/codama.json ./generated

This reads the IDL file and writes a *-types.ts file to the output directory containing strongly-typed interfaces for all instructions, accounts, arguments, PDAs, and the program client.

generateClientTypes(idl)

The same is available as a TypeScript function:

import { generateClientTypes } from '@codama/dynamic-client';
import type { RootNode } from 'codama';
import { readFileSync, writeFileSync } from 'node:fs';

const idl: RootNode = JSON.parse(readFileSync('./my-program-idl.json', 'utf-8'));
const typesSource = generateClientTypes(idl);
writeFileSync('./generated/my-program-idl-types.ts', typesSource);

Utilities

import { toAddress, isPublicKeyLike } from '@codama/dynamic-client';

// Convert any AddressInput to Address
const addr = toAddress('11111111111111111111111111111111');
const addr2 = toAddress(new PublicKey('...'));

// Type guard for legacy PublicKey objects
if (isPublicKeyLike(value)) {
    const addr = toAddress(value);
}