Skip to content

[2.x] Adapt instruction visitors to Codama v2 - #1179

Merged
lorisleiva merged 1 commit into
mainfrom
09-25-adapt_instruction_visitors_to_codama_v2
Sep 30, 2026
Merged

lorisleiva merged 1 commit into
mainfrom
09-25-adapt_instruction_visitors_to_codama_v2

Conversation

@lorisleiva

@lorisleiva lorisleiva commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

This PR adapts the remaining instruction visitors of @codama/visitors to the Codama v2 node model: setInstructionAccountDefaultValuesVisitor, setInstructionDiscriminatorsVisitor and setNumberWrappersVisitor. With it, the whole package builds against v2.

setInstructionAccountDefaultValuesVisitor

  • String account and instruction rules are matched exactly rather than camelCased.
  • getCommonInstructionAccountDefaultRules matches both camelCase and snake_case identifiers (e.g. systemProgram and system_program), since identifiers keep the casing of their program.
  • Accounts of sub-instructions are now updated too.
  • Only a failure to fill the seeds of a PDA default value skips a rule; any other error is rethrown instead of being silently swallowed.
  • The given rules are no longer mutated when sorting instruction-specific rules first.

setInstructionDiscriminatorsVisitor

setInstructionDiscriminatorsVisitor({ transfer: { value: integerValueNode('3') } });
  • When the instruction data is an inline struct without transforms (or absent), the discriminator is prepended as a data field with a fieldDiscriminatorNode, as before.
  • Otherwise (e.g. linked data or a struct with a size prefix), it is added as a hiddenPrefixTransformNode on the data with a constantDiscriminatorNode, leaving any shared defined type untouched.
  • Existing field and constant discriminator offsets, as well as size discriminators, are shifted by the size of the new discriminator.
  • The option name becomes identifier, type defaults to integerTypeNode('u8') and unknown keys throw UNRECOGNIZED_UPDATE_KEYS.
  • A new CODAMA_ERROR__VISITORS__CANNOT_SET_INSTRUCTION_DISCRIMINATOR error (1200019) is thrown when the field identifier already exists, when the optional strategy is used with a hidden prefix, or when the discriminator type does not have a fixed size.

setNumberWrappersVisitor

setNumberWrappersVisitor({
    'mint.supply': { kind: 'FixedPoint', scale: 6, unit: 'USDC' },
    lamports: { kind: 'SolAmount' },
    createdAt: { kind: 'DateTime' },
    'transfer.amount': { decimals: injectedValueNode({ key: 'decimals' }), kind: 'AmountDisplay' },
});
Wrapper Applies to Result
FixedPoint { scale, base?, unit? } integers fixedPointTypeNode
SolAmount integers fixedPointTypeNode of scale 9 in SOL
DateTime / Duration { ticksPerSecond? } integers dateTimeTypeNode / durationTypeNode
Unit { unit } integers and floats unit attribute
AmountDisplay { decimals, unit? } integers amountNumberDisplayNode display
UnitDisplay { unit } integers and floats unitNumberDisplayNode display
  • The v1 Amount wrapper is replaced by FixedPoint, Unit and AmountDisplay.
  • Wrappers carry the transforms of the number they wrap, and integers used as sizes or prefixes (enum sizes, size prefixes, option prefixes, etc.), already wrapped or within the type of a constant are left untouched.
  • CODAMA_ERROR__VISITORS__INVALID_NUMBER_WRAPPER now reports the wrapper kind and a reason. It is thrown for unknown kinds, zero-scale or shortU16 fixed points, and integers that already carry a unit or display.

Tests

New test files cover the three visitors, including every error case, rule precedence, PDA seed filling and the skipped integer positions. The README is updated accordingly.

@lorisleiva lorisleiva changed the title Adapt instruction visitors to Codama v2 [2.x] Adapt instruction visitors to Codama v2 Sep 25, 2026
@changeset-bot

changeset-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 45f5d3f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@lorisleiva

Copy link
Copy Markdown
Member Author

@trevor-cortex

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Ports the last three instruction visitors of @codama/visitors to the v2 node model:

  • setInstructionAccountDefaultValuesVisitor moves from a hand-rolled extendVisitor + NodeStack to a bottomUpTransformerVisitor on [instructionNode] (so sub-instructions are covered for free), matches string rules exactly, generates camelCase + snake_case alternatives for the common rules, stops mutating the caller's rules array, and only swallows INVALID_PDA_SEED_VALUES instead of every error.
  • setInstructionDiscriminatorsVisitor now branches on the data shape: inline struct/absent → prepend a structFieldTypeNode + fieldDiscriminatorNode; anything else → append a hiddenPrefixTransformNode + constantDiscriminatorNode. Existing discriminators are shifted by the byte size of the new one (computed with getByteSizeVisitor), and a new CANNOT_SET_INSTRUCTION_DISCRIMINATOR (1200019) error covers the three failure modes.
  • setNumberWrappersVisitor replaces the v1 Amount/DateTime/SolAmount wrappers with the seven v2 variants, targets integerTypeNode/floatTypeNode via a | selector plus a path predicate that skips size/prefix/already-wrapped integers, and carries transforms onto the wrapper node.

I verified the things that looked risky and they're all fine:

  • The { ...node, ... } spreads passed as factory options (sizeDiscriminatorNode(size + n, { ...discriminator }), integerTypeNode(format, { ...number, transforms: undefined }), structTypeNode(fields, { ...node.data })) are safe — the generated factories pick named options explicitly and drop undefined, so the positional arg always wins and no stray keys leak in.
  • NodeStack.getPath('instructionNode') in v2 asserts on the last node rather than truncating, so instructionPath correctly ends at the sub-instruction when transforming one.
  • fillDefaultPdaSeedValuesVisitor in strict mode only throws INVALID_PDA_SEED_VALUES, so narrowing the catch to that code doesn't regress the "best-effort" bulk behaviour.
  • bottomUpTransformerVisitor hands selector functions the stack of original nodes, so the parent.prefix === number identity check in isValueNumber is reliable.
  • addTypeNodeTransforms appends outermost, so the hidden-prefix path really does put the discriminator at byte 0 and the uniform offset shift is correct there.

Things to watch

  1. Inline struct data that already carries transforms (see inline comment on setInstructionDiscriminatorsVisitor.ts). Prepending a field inside a struct that has a hiddenPrefixTransformNode / sizePrefixTransformNode / preOffsetTransformNode doesn't put the discriminator at byte 0, so the new fieldDiscriminatorNode (offset 0) and the uniform shift of existing discriminators are wrong in that case. Falling back to the hidden-prefix path when node.data.transforms is non-empty would fix it cheaply since that path already exists.
  2. constantValueNode types are treated as values by isValueNumber (see inline comment on setNumberWrappersVisitor.ts). With a broad selector like [structTypeNode] or [instructionNode], the integerTypeNode inside a hidden prefix/sentinel/zeroValue constant would get wrapped, and a FixedPoint/DateTime wrapper would leave the constant's integerValueNode mismatched with its new type.
  3. Changeset: none in the diff. CONTRIBUTING.md asks for one on user-facing changes to @codama/errors / @codama/visitors — ignore if the 2.x branch is riding the seeded major changeset.

Notes for other reviewers

  • The behavioural changes to setInstructionAccountDefaultValuesVisitor (exact string matching, no more camelCasing, rethrowing non-seed errors) are intentional per the PR description and documented in the README + docblocks — don't flag them as regressions.
  • The README example for setInstructionDiscriminatorsVisitor switching from name to identifier is enforced at runtime by assertValidUpdateKeys, with a test covering the v1 key.
  • Test coverage is solid for the happy paths and every error branch. The one gap is the hidden-prefix path combined with pre-existing discriminators, which ties into point 1 above.

Comment thread packages/visitors/src/setInstructionDiscriminatorsVisitor.ts Outdated
Comment thread packages/visitors/src/setNumberWrappersVisitor.ts
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from 9ce770c to 1e898a4 Compare September 25, 2026 11:55

@trevor-cortex trevor-cortex left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review

Both points from my previous pass are addressed:

  1. Inline struct with transforms — isPlainStruct now requires an empty transforms array before taking the field path, so a struct with a hiddenPrefixTransformNode / sizePrefixTransformNode / etc. falls through to the hidden-prefix path. The new test ("it adds a hidden prefix to struct data that carries transforms") checks the exact case I was worried about: the pre-existing constantDiscriminatorNode at offset 0 is shifted to offset 1 and the new constant sits at 0, which is correct since addTypeNodeTransforms appends outermost.
  2. Constants' types — isValueNumber now short-circuits when any ancestor in the path is a constantValueNode or constantPdaSeedNode, so integers nested arbitrarily deep inside a constant's type are skipped. The new test covers both a direct integer constant (hidden prefix) and a struct-typed constant (hidden suffix), with [structTypeNode] as the broad selector.

Docblocks, README and PR description were updated to match. No other files changed since the last pass.

Remaining nit

  • packages/visitors/test/setNumberWrappersVisitor.test.ts L3–4: constantValueNode is imported before assertIsNode, which the repo's simple-import-sort rule will flag. Lint will catch it; mentioning only so it's not a surprise in CI.

For other reviewers

Nothing new to call out beyond the previous review's notes. The changeset question from last time still stands as a soft check if the 2.x branch isn't riding a seeded major changeset.

Comment thread packages/visitors/test/setNumberWrappersVisitor.test.ts Outdated
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch from 46aec0a to 9bb6ed0 Compare September 30, 2026 13:54
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from fcc8af3 to 566f28f Compare September 30, 2026 13:54
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch from 9bb6ed0 to f74457a Compare September 30, 2026 14:07
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch 2 times, most recently from 621d4b9 to 90e3656 Compare September 30, 2026 14:08
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch from f74457a to 0e134a7 Compare September 30, 2026 14:08
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from 90e3656 to dcf7a0d Compare September 30, 2026 14:09
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch 2 times, most recently from 0159613 to 0d6f7f5 Compare September 30, 2026 14:11
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from dcf7a0d to 015a2ca Compare September 30, 2026 14:11
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch from 0d6f7f5 to f0cba6f Compare September 30, 2026 14:12
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch 2 times, most recently from 6ef73f4 to b8ab0fe Compare September 30, 2026 14:13
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch 2 times, most recently from f0cba6f to 4c2d13f Compare September 30, 2026 14:13
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from 6ef73f4 to b8ab0fe Compare September 30, 2026 14:13
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_update_visitors_to_codama_v2 branch from 4c2d13f to 8d4e2b1 Compare September 30, 2026 14:14
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from b8ab0fe to 45f5d3f Compare September 30, 2026 14:14
Base automatically changed from 09-25-adapt_update_visitors_to_codama_v2 to main September 30, 2026 14:15
@lorisleiva
lorisleiva force-pushed the 09-25-adapt_instruction_visitors_to_codama_v2 branch from 45f5d3f to f8d92f5 Compare September 30, 2026 14:15
@lorisleiva
lorisleiva marked this pull request as ready for review September 30, 2026 14:16
@lorisleiva
lorisleiva merged commit 48d650a into main Sep 30, 2026
2 of 4 checks passed
@lorisleiva
lorisleiva deleted the 09-25-adapt_instruction_visitors_to_codama_v2 branch September 30, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants