Major Changes
-
#1262
e1cce0dThanks @lorisleiva! - Remove the deprecated node kind aliases, theProgramVersiontype and the duplicate node path formatters. Each has a canonical replacement that was already exported.BREAKING CHANGES
Node kind aliases removed. The legacy
*_NODESaliases of@codama/nodesare removed in favour of the*_NODE_KINDSconstants generated from the spec.- import { CONTEXTUAL_VALUE_NODES, COUNT_NODES, DISCRIMINATOR_NODES, INSTRUCTION_INPUT_VALUE_NODES } from '@codama/nodes'; - import { LINK_NODES, PDA_SEED_NODES, TYPE_NODES, VALUE_NODES } from '@codama/nodes'; + import { CONTEXTUAL_VALUE_NODE_KINDS, COUNT_NODE_KINDS, DISCRIMINATOR_NODE_KINDS, INSTRUCTION_INPUT_VALUE_NODE_KINDS } from '@codama/nodes'; + import { LINK_NODE_KINDS, PDA_SEED_NODE_KINDS, TYPE_NODE_KINDS, VALUE_NODE_KINDS } from '@codama/nodes';
ProgramVersionremoved. Use theVersiontype instead.- import type { ProgramVersion } from '@codama/node-types'; + import type { Version } from '@codama/node-types';
Node path formatters removed.
nodePathToStringandnodePathToStringArrayare removed from@codama/visitors-core. UsegetNodePathStringfrom@codama/errors, which renders paths the way error messages do, orNodeStack#toStringfor the current path of a stack.- import { nodePathToString, nodePathToStringArray } from '@codama/visitors-core'; + import { getNodePathString } from '@codama/errors'; - nodePathToString(path); + getNodePathString(path); - nodePathToStringArray(path); + path.map(node => getNodePathString([node]));
-
#1157
64d52ebThanks @codama-releases! - Release Codama v2, supporting IDLs of the v2 standard. Upgrade v1 IDLs with thecodama upgradecommand or the@codama/upgradepackage. -
#1248
7f8bedaThanks @lorisleiva! - Rework theupdate*Visitorfunctions for Codama v2: they match and rename nodes by exactidentifier, reject unknown or missing targets, and repoint every reference to the nodes and members they rename.BREAKING CHANGES
name→identifier. Every update visitor (updateAccountsVisitor,updateDefinedTypesVisitor,updateErrorsVisitor,updateInstructionsVisitorandupdateProgramsVisitor) renames withidentifier, including instruction accounts and data fields. Selector keys and new identifiers are used as given rather than camelCased.updateInstructionsVisitor({ send: { - name: 'transfer', - accounts: { owner: { name: 'authority' } }, + identifier: 'transfer', + accounts: { owner: { identifier: 'authority' } }, }, });Unknown keys throw. An update containing an unrecognised key, such as v1's
name, throwsCODAMA_ERROR__VISITORS__UNRECOGNIZED_UPDATE_KEYSinstead of being silently ignored.arguments→ path-keyeddata.updateInstructionsVisitorupdates the fields of the instruction's inlinedatastruct, keyed by path, andInstructionArgumentUpdatesis replaced byInstructionDataUpdates. Fields behind adefinedTypeLinkNodecannot be updated since the defined type may be shared: unwrap it first. Default values must beValueNodes, so contextual defaults are expressed with aninjectedValueNodeand a matchingprovidesentry.updateInstructionsVisitor({ transfer: { - arguments: { - amount: { defaultValue: numberValueNode(1) }, - bump: { defaultValue: accountBumpValueNode('vault') }, - }, + data: { + amount: { defaultValue: integerValueNode('1') }, + 'config.decimals': { identifier: 'mintDecimals' }, + bump: { defaultValue: injectedValueNode({ key: 'bump' }) }, + }, + provides: { bump: accountBumpValueNode('vault') }, }, });providesmerged by key.providesis a record merged by identifier with the instruction'sProvidedNodes, where anullvalue removes an entry.Missing targets throw. Updating an instruction account or a data field that does not exist throws
CODAMA_ERROR__VISITORS__INSTRUCTION_ACCOUNT_NOT_FOUNDorCODAMA_ERROR__VISITORS__INSTRUCTION_DATA_FIELD_NOT_FOUND. In v1, unknown arguments were silently added as extra arguments. Renaming a missing account field or defined type member throwsCODAMA_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUNDorCODAMA_ERROR__VISITORS__DEFINED_TYPE_MEMBER_NOT_FOUND.Updates are merged per node. All entries matching a node, such as
transferandmyProgram.transfer, are merged and applied at once against the original node, and adeleteentry wins over the others. Nodes supplied by the updates, such as default values or provided nodes, must use the new identifiers.Renames repoint every reference. References are resolved through the linked nodes rather than matched by name, so links from other programs are handled and their
programand other attributes are kept.- Renaming a program, account, PDA, defined type or instruction renames every link pointing to it. Renaming an account also renames the PDA of the same program sharing its identifier.
- Renaming an instruction account repoints the
accountValueNode,accountBumpValueNode,accountDataValueNode,instructionAccountLinkNodeand${accounts.…}placeholders pointing to it. - Renaming instruction data fields, account data fields or struct fields of a defined type repoints every path going through them, such as
dataValueNodes,accountDataValueNodes,fieldDiscriminatorNodes and${data.…}placeholders, following defined type links and crossing account links through.datasegments (e.g.mint.data.decimals). - Renaming the variants of an enum defined type repoints the
enumValueNodes of that type.
-
#1248
7f8bedaThanks @lorisleiva! - Rename the visitors built around instruction arguments now that Codama v2 stores them as a struct ininstructionNode.data, and remove the helpers that no longer apply.BREAKING CHANGES
Instruction argument visitors renamed. Each visitor keeps its purpose but now operates on the instruction's
data.- flattenInstructionDataArgumentsVisitor() + flattenInstructionDataVisitor() - unwrapInstructionArgsDefinedTypesVisitor() + unwrapInstructionDataDefinedTypesVisitor() - createSubInstructionsFromEnumArgsVisitor({ mint: 'mintArgs' }) + createSubInstructionsFromEnumDataVisitor({ mint: 'mintArgs' })
flattenInstructionArgumentsremoved. The helper and itsFlattenInstructionArgumentsConfigtype are gone. UseflattenStructon the instruction'sdatastruct instead.flattenInstructionDataVisitoronly flattens inline struct data: linked data is left untouched since the defined type may be shared, so rununwrapInstructionDataDefinedTypesVisitorfirst to flatten it.Sub-instructions follow the enum.
createSubInstructionsFromEnumDataVisitornames each sub-instruction${instruction}_${variant}without any casing transformation, instead of camelCasing it. Its discriminator field is named${instruction}_${variant}_discriminator, uses the enum'ssizeinstead of alwaysu8, and defaults to the variant'sdiscriminator(falling back to its index). Struct payloads are inlined, and enums carrying transforms keep the discriminator and payload grouped in a struct carrying them, so the wire format is preserved. Both the instruction data and the enum field may be linked. It throwsCODAMA_ERROR__VISITORS__INSTRUCTION_ENUM_DATA_FIELD_NOT_FOUNDwhen the instruction has no data, no such field, or a field that is not an enum.Histogram keys renamed.
DefinedTypeHistogramis keyed byprogram.typeidentifiers, or by the type identifier alone outside of a program, rather than by camelCase names, and its counters follow the new data model.{ - directlyAsInstructionArgs: number; + directlyAsInstructionData: number; inAccounts: number; inDefinedTypes: number; inEvents: number; - inInstructionArgs: number; + inInstructionData: number; + inInstructionReturnData: number; total: number; }directlyAsInstructionDatacounts uses as an instruction'sdataitself or as the type of one of its top-level fields.totalnow counts everydefinedTypeLinkNode, including those in default values, PDA seeds and constants, and links with an explicitprogramare keyed by that program rather than the program they appear in. -
#1264
909960fThanks @lorisleiva! - Trim public exports that were only meant for internal use, so the public API only exposes what consumers need.BREAKING CHANGES
@codama/errorsno longer exportssafeCaptureStackTrace. It was unused, even byCodamaError, and is also gone fromcodama, which re-exports it. CallError.captureStackTracedirectly if you need it.@codama/fragments/javascriptno longer exportspreferIncoming. UsemergeImportMapsoraddToImportMap, which apply the same conflict-resolution rule.@codama/dynamic-address-resolutionno longer exportsOPTIONAL_NODE_KINDS. Check the type node kind directly, e.g.isNode(type, ['optionTypeNode', 'remainderOptionTypeNode', 'zeroableOptionTypeNode']).@codama/nodes-from-anchoronly exports its root and program converters. The per-node converters, their options types and the generics helpers are now internal. Convert whole IDLs withrootNodeFromAnchor(orrootNodeFromAnchorV00,rootNodeFromAnchorV01,programNodeFromAnchorV00andprogramNodeFromAnchorV01) and pick the nodes you need from the result. The removed exports are:accountNodeFromAnchorV00,arrayTypeNodeFromAnchorV00,constantNodeFromAnchorV00,definedTypeNodeFromAnchorV00,enumTypeNodeFromAnchorV00,enumVariantTypeNodeFromAnchorV00,errorNodeFromAnchorV00,eventNodeFromAnchorV00,instructionAccountNodeFromAnchorV00,instructionAccountNodesFromAnchorV00,instructionNodeFromAnchorV00,mapTypeNodeFromAnchorV00,optionTypeNodeFromAnchorV00,pdaNodeFromAnchorV00,pdaSeedTypeNodeFromAnchorV00,setTypeNodeFromAnchorV00,structFieldTypeNodeFromAnchorV00,structTypeNodeFromAnchorV00,tupleTypeNodeFromAnchorV00,typeNodeFromAnchorV00andInstructionAccountNodeFromAnchorV00Options.accountNodeFromAnchorV01,arrayTypeNodeFromAnchorV01,constantNodeFromAnchorV01,definedTypeNodeFromAnchorV01,enumTypeNodeFromAnchorV01,enumVariantTypeNodeFromAnchorV01,errorNodeFromAnchorV01,eventNodeFromAnchorV01,instructionAccountNodeFromAnchorV01,instructionAccountNodesFromAnchorV01,instructionNodeFromAnchorV01,optionTypeNodeFromAnchorV01,pdaSeedNodeFromAnchorV01,structFieldTypeNodeFromAnchorV01,structTypeNodeFromAnchorV01,tupleTypeNodeFromAnchorV01,typeNodeFromAnchorV01,unwrapGenericTypeFromAnchorV01,extractGenerics,GenericsV01,InstructionAccountNodeFromAnchorV01OptionsandPdaSeedNodeFromAnchorV01Options.extractPdasFromProgram: useextractPdasVisitorinstead.
- import { accountNodeFromAnchorV01 } from '@codama/nodes-from-anchor'; - const account = accountNodeFromAnchorV01(idl.accounts[0], idl.types, generics); + import { rootNodeFromAnchor } from '@codama/nodes-from-anchor'; + const account = rootNodeFromAnchor(idl).program.accounts?.[0];
@codama/visitors-coreno longer exportsLINKABLE_NODES,getDependenciesandassertNodePathHasProgram. These are also gone from@codama/visitorsandcodama, which re-export it. UsefindProgramNodeFromPathto check that a path has a program, andgetResolvedInstructionInputsVisitorto resolve instruction input dependencies. -
#1261
bb09077Thanks @lorisleiva! - Carry node paths in the remaining error contexts. Contexts that still held nodes or account names now hold node paths instead, e.g. thecycleof a circular dependency lists the path of each account and data field it goes through. Production-encoded contexts encode lists of node paths as lists of node selectors.getInstructionDataFieldsnow returns thefieldPathof each field, andmapVisitorgives the visited node to its map function.BREAKING CHANGES
Nodes and account names replaced by node paths in error contexts. The following contexts changed, completing the move to node paths.
if (isCodamaError(e, CODAMA_ERROR__DYNAMIC_CLIENT__CIRCULAR_ACCOUNT_DEPENDENCY)) { - const { chain } = e.context; // "a -> b -> a" + const { cycle, formattedCycle, path } = e.context; // formattedCycle: "a -> b -> a" + const account = cycle[0].at(-1); // instructionAccountNode a } if (isCodamaError(e, CODAMA_ERROR__DYNAMIC_CLIENT__UNEXPECTED_ADDRESS_TYPE)) { - const { accountName } = e.context; + const account = e.context.path.at(-1); // e.g. instructionAccountNode }Code Removed Added VISITORS__CYCLIC_DEPENDENCY_DETECTED_WHEN_RESOLVING_INSTRUCTION_DEFAULT_VALUES(1200002)cycleholds the path of each account and data field instead of the nodes themselvesVISITORS__INVALID_INSTRUCTION_DEFAULT_VALUE_DEPENDENCY(1200004)dependencyuse dependencyKindanddependencyNameVISITORS__INVALID_NUMBER_WRAPPER(1200006)pathof the number to wrap (empty when the wrapper is invalid on its own)VISITORS__FAILED_TO_VALIDATE_NODE(1200008)pathof the validated nodeDYNAMIC_CLIENT__INSTRUCTION_NOT_FOUND(2500000)pathof the programDYNAMIC_CLIENT__PDA_NOT_FOUND(2500001)pathof the programDYNAMIC_CLIENT__UNEXPECTED_ADDRESS_TYPE(2500005)accountNamepathof the account, remaining accounts, PDA or data field, and theindexof the remaining account at faultDYNAMIC_CLIENT__CIRCULAR_ACCOUNT_DEPENDENCY(2500008)chainpathof the instruction,cycle,formattedCycletoAddressOrThrowtakes a node path. The second argument oftoAddressOrThrowfrom@codama/dynamic-address-resolutionis now the path of the node the value should be the address of, rather than an account name.- toAddressOrThrow(value, 'mint'); + toAddressOrThrow(value, [root, program, instruction, mint]);
It also takes an optional
indexfor values given in a list, e.g.toAddressOrThrow(value, [root, program, instruction, signers], 1), which the error carries in its context (it is not rendered in the message).Messages changed accordingly. The messages of these errors now render their
path, e.g.Circular dependency detected: [a -> b -> a].becomesCircular dependency detected when resolving the accounts and data of instruction [programNode]token.[instructionNode]transfer. Got the following dependency cycle [a -> b -> a].. -
#1260
3ad402aThanks @lorisleiva! - Require Node.js 22.12.0 or later.BREAKING CHANGES
Node.js 20 is no longer supported. Every package now declares
"engines": { "node": ">=22.12.0" }, the first Node.js 22 release that supportsrequire(esm)by default. Node.js 20 reached its end of life in April 2026, and some dependencies (e.g.commander) already require Node.js 22.12.0. -
#1248
7f8bedaThanks @lorisleiva! - AdaptsetNumberWrappersVisitor,setInstructionDiscriminatorsVisitor,setInstructionAccountDefaultValuesVisitorandfillDefaultPdaSeedValuesVisitorto the Codama v2 numeric system and instruction data.BREAKING CHANGES
New
NumberWrapperkinds.setNumberWrappersVisitortargetsintegerTypeNodes (andfloatTypeNodes forUnitandUnitDisplay). TheAmountwrapper is removed in favour ofFixedPoint, and new kinds cover durations, units and displays.setNumberWrappersVisitor({ lamports: { kind: 'SolAmount' }, - timestamp: { kind: 'DateTime' }, - supply: { kind: 'Amount', decimals: 6, unit: 'USDC' }, + timestamp: { kind: 'DateTime', ticksPerSecond: 1 }, + supply: { kind: 'FixedPoint', scale: 6, unit: 'USDC' }, + lockup: { kind: 'Duration' }, + percent: { kind: 'Unit', unit: '%' }, + amount: { kind: 'AmountDisplay', decimals: injectedValueNode({ key: 'decimals' }) }, });SolAmountbecomes afixedPointTypeNodeof scale 9 inSOL, andAmountDisplayandUnitDisplayset the number'sdisplay. Wrappers carry thetransformsof the number they wrap, and integers used as sizes or prefixes, as well as numbers within the type of a constant, are left untouched. It throwsCODAMA_ERROR__VISITORS__INVALID_NUMBER_WRAPPERfor an unknown kind, a zero fixed-point scale, a fixed point wrapping ashortU16, or a wrapped integer that already carries aunitordisplay.setInstructionDiscriminatorsVisitoroptions. The exportedInstructionDiscriminatoroptions useidentifierinstead ofname, and unknown keys throwCODAMA_ERROR__VISITORS__UNRECOGNIZED_UPDATE_KEYS. Thetypedefaults tointegerTypeNode('u8')and must have a fixed size.setInstructionDiscriminatorsVisitor({ - transfer: { name: 'kind', type: numberTypeNode('u32'), value: numberValueNode(1) }, + transfer: { identifier: 'kind', type: integerTypeNode('u32'), value: integerValueNode('1') }, });The discriminator is added as the first field of inline struct data without transforms (or absent data), with a
fieldDiscriminatorNode. Otherwise, such as for linked data or data with a size prefix, it is added as ahiddenPrefixTransformNodeon the data with aconstantDiscriminatorNode, leaving any shared defined type untouched. The offsets of existing field and constant discriminators and the size of existing size discriminators are shifted by the discriminator's size. It throwsCODAMA_ERROR__VISITORS__CANNOT_SET_INSTRUCTION_DISCRIMINATORif the data already has a field with that identifier, if theoptionalstrategy is used with a hidden prefix, or if the type has no fixed size.Exact account matching in
setInstructionAccountDefaultValuesVisitor. Stringaccountandinstructionrules match identifiers exactly instead of after camelCasing. The rules fromgetCommonInstructionAccountDefaultRulesmatch both the camelCase and snake_case identifiers of common accounts (e.g.systemProgramandsystem_program). Sub-instructions now get default values too, and the given rules array is no longer sorted in place. Only rules whose PDA seeds cannot all be filled are skipped: any other error is now thrown rather than swallowed.fillDefaultPdaSeedValuesVisitorfillsdataValueNodes. Variable seeds that do not match an account are filled with adataValueNodewhen the instruction's data has a top-level field with the seed's identifier, following defined type links, instead of anargumentValueNode. Seeds match accounts and fields by exact identifier, and other attributes of thepdaValueNodeare kept. -
#1248
7f8bedaThanks @lorisleiva! - Adapt the remaining structural visitors to Codama v2 identifiers, flat transforms and plugins, matching identifiers exactly and refusing transformations that would silently lose information.BREAKING CHANGES
Exact identifier matching. Identifiers given to
addPdasVisitor,flattenStructVisitor,setStructDefaultValuesVisitor,setAccountDiscriminatorFromFieldVisitor,transformDefinedTypesIntoAccountsVisitorandunwrapDefinedTypesVisitorare matched exactly instead of after camelCasing, since v2 identifiers keep their casing.camelCase collisions throw.
addPdasVisitorthrowsCODAMA_ERROR__VISITORS__CANNOT_ADD_DUPLICATED_PDA_NAMESwhen a new PDA shares a camelCase form with an existing or another new PDA of the same program.flattenStructandflattenStructVisitorthrowCODAMA_ERROR__VISITORS__CANNOT_FLATTEN_STRUCT_WITH_CONFLICTING_ATTRIBUTESwhen two resulting fields share a camelCase form, following the spec's casing-collision rule.Flattening preserves the wire format.
flattenStructkeeps struct fields whose struct carriestransformsas they are, and throwsCODAMA_ERROR__VISITORS__CANNOT_FLATTEN_STRUCT_WITH_PLUGINSwhen a struct to inline carriesplugins. The flattened struct keeps its own attributes.Inlining applies link transforms.
unwrapDefinedTypesVisitor,unwrapTypeDefinedLinksVisitorandunwrapTupleEnumWithSingleStructVisitorlayer thetransformsof the replaced link or tuple on top of the inlined type, and qualify the links inside a type inlined into another program with its original program.Cyclic defined types are not inlined.
unwrapDefinedTypesVisitorkeeps defined types in a cycle made only of types to inline, such asnode = struct { next: option<link(node)> }, along with the links to them, since inlining them would never end.Inline struct data required.
setAccountDiscriminatorFromFieldVisitorrequires the account data to be an inlinestructTypeNode, since changing a linked defined type would affect every node using it.setStructDefaultValuesVisitorno longer has dedicated handling for instruction arguments: it updates the inlinedatastruct of selected instructions like any other struct, and contextual defaults are expressed with aninjectedValueNodeand a matchingprovidesentry.Other behaviour changes.
addPdasVisitoraccepts fullPdaNodeInputs, keeping every attribute such asplugins.deduplicateIdenticalDefinedTypesVisitorrepoints links to the removed copies at the kept one, and only treats types as identical when every defined type they link to without an explicit program is itself deduplicated across the same programs.transformDefinedTypesIntoAccountsVisitoraccepts defined types of any type, not only structs, and keeps theirdocsandplugins.transformU8ArraysToBytesVisitorproduces abytesTypeNodewith afixedSizeTransformNode, only converts plainu8items (withoutunit,display,transformsorplugins) and keeps the array's owntransformsandplugins.unwrapTupleEnumWithSingleStructVisitorunwrapsenumVariantTypeNodes whose data is a tuple with a single struct item or link to one, and removes the linked defined types that are no longer used.
Minor Changes
- #1221
b869cefThanks @tsiklic1! - AddaddProgramsVisitorto append programs to a root node's additional programs, rejecting identifiers that collide with existing or other new programs after conversion to camelCase.
Patch Changes
-
#1250
cedeb1eThanks @lorisleiva! - Keep the links of enum values inunwrapDefinedTypesVisitorandunwrapTypeDefinedLinksVisitor, along with the defined types they target, instead of throwing on any enum default value. -
#1250
cedeb1eThanks @lorisleiva! - Repoint the data paths going through inlined fields inflattenInstructionDataVisitor, e.g. aparams.seedPDA seed becomesseed. It no longer throws on conflicts: a struct field is kept nested, while the others are still inlined, when inlining it would produce conflicting fields or when a data path refers to it as a whole, e.g. aparamsPDA seed. -
Updated dependencies [
7f8beda,1f0fb88,3f76a7f,e1cce0d,64d52eb,e92df74,7f8beda,909960f,3f76a7f,bb09077,bb09077,277eae1,277eae1,3ad402a,7f8beda,7f8beda,277eae1,7f8beda,7f8beda,b869cef,7f8beda,7f8beda,08d72e7]:- @codama/visitors-core@2.0.0-rc.0
- @codama/errors@2.0.0-rc.0
- @codama/fragments@2.0.0-rc.0
- @codama/nodes@2.0.0-rc.0