Skip to content

Fix byte-array PDA seeds deriving with a Borsh length prefix - #1101

Closed
latent-9 wants to merge 1 commit into
codama-idl:mainfrom
latent-9:fix/bytes-pda-seed
Closed

Fix byte-array PDA seeds deriving with a Borsh length prefix#1101
latent-9 wants to merge 1 commit into
codama-idl:mainfrom
latent-9:fix/bytes-pda-seed

Conversation

@latent-9

Copy link
Copy Markdown
Contributor

Problem

A Vec<u8> instruction argument used as a PDA seed keeps its Borsh sizePrefixTypeNode(u32, bytesTypeNode()) type when converted from an Anchor IDL. Anchor derives byte-array seeds from the raw bytes without the length prefix, so the generated client computes a different PDA than the on-chain program.

Minimal reproduction of the derivation difference, for seed data [1, 2, 3, 4, 5]:

  • Anchor on-chain (create_program_address(&[seed], program)): CREFgTpumervLBfVvvuLx6w2yvgYCGGiXwcoozu22VHK
  • Generated client (length-prefixed): 3mS84rEyJwNSpaYHeJN9umbi4FPZ94pkUg4Wv5hPBJ1s

String seeds already strip the prefix (see the isBorshString check); byte-array seeds were the missing case.

Change

Extend the argument-seed conversion in pdaSeedNodeFromAnchorV01 to also recognize a Borsh size-prefixed bytes argument and emit a raw bytesTypeNode seed, matching the on-chain derivation.

Tests

Adds it removes the bytes prefix from arg Anchor seeds, mirroring the existing string-prefix test. The full @codama/nodes-from-anchor suite passes.

Note

This is complementary to #990, which resolves nested seed paths in the same function. That PR does not change the byte-array seed behavior addressed here.

A Vec<u8> instruction argument used as a PDA seed keeps its Borsh
sizePrefixTypeNode(u32, bytes) type when converted from an Anchor IDL.
Anchor derives byte-array seeds from the raw bytes without the length
prefix, so the generated client computes a different PDA than the
on-chain program. String seeds already strip the prefix; byte-array
seeds were the missing case.

Extend the argument-seed conversion to also recognize a size-prefixed
bytes argument and emit a raw bytesTypeNode seed.
@changeset-bot

changeset-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 943692c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@codama/nodes-from-anchor Patch

Not sure what this means? Click here to learn what changesets are.

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

@lorisleiva
lorisleiva deleted the branch codama-idl:main August 27, 2026 13:20
@lorisleiva lorisleiva closed this Aug 27, 2026
@latent-9
latent-9 deleted the fix/bytes-pda-seed branch August 27, 2026 15:21
@lorisleiva

Copy link
Copy Markdown
Member

OMG I'm so sorry I was cleaning up some stale branches yesterday and I think I've accidentally deleted your branch. How rude of me. Let me just triage this with the AI reviewer first before I trouble you again. 🙏

@lorisleiva

Copy link
Copy Markdown
Member

@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

Fixes PDA derivation for Vec<u8> instruction arguments used as seeds in Anchor v01 IDLs. Anchor derives byte-array seeds from the raw bytes, but the converted argument type kept its Borsh sizePrefixTypeNode(u32, bytesTypeNode()) encoding, so generated clients computed a different PDA than the on-chain program. The fix mirrors the existing isBorshString special case with an isBorshBytes counterpart that emits a raw bytesTypeNode() seed.

This is correct: bytes in an Anchor IDL is the Borsh encoding of Vec<u8> (u32 length prefix + data), while create_program_address receives the unprefixed slice via AsRef<[u8]>. Fixed-size arrays ([u8; N]) are unaffected since they carry no prefix, so this closes out the remaining variable-length case alongside strings.

Notes

  • The new test mirrors the existing string-prefix test one-to-one, and the changeset is a patch bump on the independently-versioned @codama/nodes-from-anchor, starting with a verb per CONTRIBUTING — all consistent with repo conventions.
  • Behavior note for anyone tracking downstream impact: any client currently generated from an IDL with a Vec<u8> seed will start deriving a different (now correct) PDA after this fix. That's the point of the fix, but worth remembering if a consumer somehow depended on the old, wrong derivation.
  • This touches the same arg branch as #990 (nested seed path resolution), so whichever lands second will need a trivial rebase. The two changes are logically independent.
  • Optional refactor, not blocking: isBorshString and isBorshBytes share the sizePrefixTypeNode + u32 prefix checks; a small helper like isBorshSizePrefixed(type, innerKind) could collapse the duplication if this pattern grows a third case.

@lorisleiva

Copy link
Copy Markdown
Member

Let me know if you're happy to resubmit (again so sorry) otherwise I'm happy to re-open a similar PR a bit later if you'd rather not.

@latent-9

Copy link
Copy Markdown
Contributor Author

Please re-open, happy to let you handle it.
Thank you @lorisleiva @trevor-cortex 🙏

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.

3 participants