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-instructions

v0.3.0

Published

Runtime instruction creation for Codama IDLs

Readme

Codama ➤ Dynamic Instructions

npm npm-downloads

This package provides a runtime Solana instruction builder that dynamically constructs Instruction (@solana/instructions). It provides instruction arguments encoding and validation, accounts resolution. Powers @codama/dynamic-client with InstructionsBuilder.

It also provides a clear-signing display layer that turns a concrete instruction into human-readable text — see Instruction display.

Installation

pnpm install @codama/dynamic-instructions

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

Types generation

This package can emit TypeScript types per-instruction - ${Name}Args, ${Name}Accounts, ${Name}Resolvers, and ${Name}Signers aliases, plus an aggregate ${Program}InstructionBuilders map.

The ${Name}Args / ${Name}Accounts / ${Name}Resolvers type contracts that resolvers operate on are emitted by @codama/dynamic-address-resolution/codegen and re-used here. The builder depends on resolution because the input shape it accepts (e.g. optional auto-resolvable accounts) is a direct consequence of resolution rules.

CLI

npx @codama/dynamic-instructions generate-types <path/to/idl.json> <output-dir>

Writes <idl-name>-instruction-types.ts to the output directory.

Programmatic

import { generateTypes } from '@codama/dynamic-instructions/codegen';

const source = generateTypes(idl);

Functions

createInstructionsBuilder(root, ixNode)

Creates an async instruction builder function for a given InstructionNode. The returned function validates inputs, resolves defaults, encodes arguments, and assembles the final Instruction.

Untyped:

const build = createInstructionsBuilder(root, ixNode);
const instruction = await build(args, accounts, signers, resolvers);

Typed:

Types are generated via generate-types.

import type { CreateItemAccounts, CreateItemArgs, CreateItemResolvers } from './generated/<idl-name>-instruction-types';

const build = createInstructionsBuilder<CreateItemArgs, CreateItemAccounts, [], CreateItemResolvers>(root, ixNode);
const instruction = await build({ name: 'item' }, { authority }, [], {
    resolveOwner: async (args, accounts) => accounts.authority,
});

createAccountMeta(root, ixNode, argumentsInput?, accountsInput?, signers?, resolversInput?)

Resolves and builds AccountMeta[] for an instruction. Handles PDA derivation, default value resolution, optional accounts, and signer disambiguation.

Untyped:

const accountMetas = await createAccountMeta(root, ixNode, args, accounts, ['owner'], resolvers);

Typed:

Types are generated via generate-types.

import type { CreateItemAccounts, CreateItemArgs, CreateItemResolvers } from './generated/<idl-name>-instruction-types';

const accountMetas = await createAccountMeta<CreateItemAccounts, CreateItemArgs, CreateItemResolvers>(
    root,
    ixNode,
    { name: 'item' },
    { authority },
    ['owner'],
    { resolveOwner: async (args, accounts) => accounts.authority },
);

encodeInstructionArguments(root, ixNode, argumentsInput?)

Encodes instruction arguments into a ReadonlyUint8Array buffer according to the Codama schema. Auto-encodes arguments with defaultValueStrategy: 'omitted' (e.g. discriminators).

Untyped:

const data = encodeInstructionArguments(root, ixNode, { amount: 1_000_000_000 });

Typed:

Types are generated via generate-types.

import type { TransferArgs } from './generated/<idl-name>-instruction-types';

const data = encodeInstructionArguments<TransferArgs>(root, ixNode, { amount: 1_000_000_000n });

Instruction display (clear signing)

Given an IDL enriched with display metadata (per sRFC 39), this package resolves a concrete instruction into human-readable text for user verification. The result carries both presentation modes and lets the renderer choose:

type InstructionDisplay = {
    // A short imperative label, e.g. "Transfer" (derived from the instruction name when absent).
    intent: string;
    // The interpolated sentence, e.g. "Transfer 1.5 USDC to toly.sol", or `null` when a
    // placeholder cannot be resolved (the renderer then falls back to `fields`).
    interpolatedIntent: string | null;
    // The structured fallback list of labelled fields, e.g. [{ label: 'Amount', value: '1.5 USDC' }].
    fields: { label: string; value: string }[];
};

getInstructionDisplay(root, instruction, options?)

Parses a raw Instruction (@solana/instructions) against the root and resolves its display. Returns null when the instruction cannot be identified or decoded (e.g. an instruction from an unknown program).

import { getInstructionDisplay } from '@codama/dynamic-instructions';

const display = await getInstructionDisplay(root, instruction);
// => { intent: 'Transfer', interpolatedIntent: 'Transfer 1500000 to 3Wnd5…5PxJX', fields: [...] } | null

getInstructionDisplayFromParsedInstruction(root, parsedInstruction, options?)

The same, starting from an already-parsed instruction (ParsedInstruction from @codama/dynamic-parsers). Useful when you have already called parseInstruction.

import { parseInstruction } from '@codama/dynamic-parsers';
import { getInstructionDisplayFromParsedInstruction } from '@codama/dynamic-instructions';

const parsed = parseInstruction(root, instruction);
if (parsed) {
    const display = await getInstructionDisplayFromParsedInstruction(root, parsed);
}

Options

Some display values live in on-chain account state (e.g. a token's decimals/symbol injected into an amount, or interpolation paths that read an account field). Supply fetchAccount to resolve them; without it, such values degrade gracefully (amounts stay raw, whenInjected fields remain visible).

fetchAccount returns Kit's MaybeEncodedAccount — an exists flag plus, when the account exists, its raw bytes. No decoding is required on your side: the display layer decodes the bytes itself using the referenced account's accountLink from the IDL, which already describes the layout. This makes fetchEncodedAccount a drop-in.

import type { Address } from '@solana/addresses';
import { fetchEncodedAccount } from '@solana/accounts';

const display = await getInstructionDisplay(root, instruction, {
    // Forward Kit's MaybeEncodedAccount for an address.
    fetchAccount: (address: Address) => fetchEncodedAccount(rpc, address),
});

Address presentation (.sol names, address-book aliases, truncation) is intentionally left to the renderer: fields and interpolatedIntent contain raw base58 addresses that the consuming wallet/UI formats as it sees fit.