Skip to content

feat: support additionalPrograms via #[codama(program(...))] directive#82

Merged
lorisleiva merged 7 commits intocodama-idl:mainfrom
amilz:feat/additional-programs
Mar 12, 2026
Merged

feat: support additionalPrograms via #[codama(program(...))] directive#82
lorisleiva merged 7 commits intocodama-idl:mainfrom
amilz:feat/additional-programs

Conversation

@amilz
Copy link
Copy Markdown
Contributor

@amilz amilz commented Mar 3, 2026

Summary

  • Adds #[codama(program(name = "...", address = "..."))] directive for declaring external program ownership on first-class nodes.
  • Supported on CodamaPda, CodamaInstruction, CodamaInstructions, CodamaAccount, CodamaAccounts, and CodamaErrors.
  • When present, the node is wrapped in (or applied to) a ProgramNode with the given name/address. CombineModulesVisitor routes it to additionalPrograms automatically.

Usage

#[derive(CodamaPda)]
#[codama(program(name = "associatedToken", address = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"))]
#[codama(seed(name = "owner", type = public_key))]
#[codama(seed(name = "tokenProgram", type = public_key))]
#[codama(seed(name = "mint", type = public_key))]
pub struct AssociatedToken;

Then reference in instructions via pda("associatedToken", program = "associatedToken", [...]). The IDL gets an additionalPrograms entry with the program's address and PDA seeds, and account defaults use pdaLinkNode with a programLinkNode reference.

Verified against an escrow program with ATA vault accounts — IDL output is correct.

How it works

  • ProgramDirective parses name and address via SetOnce (same pattern as ErrorDirective)
  • SetPdasVisitor: wraps PdaNode in a ProgramNode when directive present
  • SetInstructionsVisitor: wraps InstructionNode (struct) or sets name/address on ProgramNode (enum)
  • SetAccountsVisitor: applies to existing AccountNode or ProgramNode via apply_program_directive helper
  • SetErrorsVisitor: sets name/address on ProgramNode (enum)
  • CombineModulesVisitor sees non-matching public_key → routes to additional_programs

Test plan

  • cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test
  • 10 new tests (3 unit + 2 PDA integration + 2 instruction + 2 account + 1 error)
  • Verified IDL output against escrow program with ATA vault accounts

Closes #68

@amilz amilz marked this pull request as ready for review March 3, 2026 23:26
@amilz
Copy link
Copy Markdown
Contributor Author

amilz commented Mar 4, 2026

Pushed minor refactor based on feeback in 81 and 82

Copy link
Copy Markdown
Member

@lorisleiva lorisleiva left a comment

Choose a reason for hiding this comment

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

Nice, this is looking really good! I've made some comments mainly on adding higher-level tests with modules and trying to extract some common logic on the directive itself to clean up the visitors slightly.

amilz added 6 commits March 5, 2026 09:55
Add a program directive that allows CodamaPda structs to declare they
belong to an external program. The PDA is wrapped in a ProgramNode with
the given name and address, which CombineModulesVisitor routes to
additionalPrograms in the IDL.

Usage:
  #[derive(CodamaPda)]
  #[codama(program(name = "associatedToken", address = "AToken..."))]
  #[codama(seed(name = "owner", type = public_key))]
  struct AssociatedToken;
…shared helper

- Change ProgramDirective.name from String to CamelCaseString
- Add update_or_wrap_program_node() method on ProgramDirective to consolidate
  wrapping/updating logic across all visitors
- Refactor set_accounts, set_instructions, set_errors, set_pdas visitors to
  use the shared helper instead of duplicating inline logic
- Support #[codama(program(...))] on mod blocks via CombineModulesVisitor
- Add combine_modules_visitor tests for program directive scenarios

refactor: extract ProgramDirective::apply to reduce repeated match pattern

Consolidates the 7 repeated `match get_last(filter) { Some => helper, None => node }`
blocks across visitors into a single ProgramDirective::apply() static method.
@amilz amilz force-pushed the feat/additional-programs branch from dc72a70 to 0a1a2f7 Compare March 5, 2026 17:56
@amilz
Copy link
Copy Markdown
Contributor Author

amilz commented Mar 5, 2026

Thanks for the feedback @lorisleiva. I think we're good but lmk if the tests/refactor don't quite get at what you're thinking.

  • Rebased on latest main (resolved conflicts with #[codama(skip)] directive from feat: add #[codama(skip)] directive for all enum variants #80)
  • Changed ProgramDirective.name to CamelCaseString and added update_or_wrap_program_node() helper to consolidate wrapping logic across all visitors
  • Extracted ProgramDirective::apply() so visitors don't repeat the attribute lookup + match pattern
  • Added combine_modules_visitor tests using declare_id! + mod blocks to validate module resolution with program directives, including #[codama(program(...))] on mod blocks directly. One note: When no main program is established via SetProgramMetadataVisitor, the first ProgramNode from a program directive becomes the main program and scraps end up as additional_programs. Seems okay to me, but figured I'd flag this for your thoughts/awareness.
  • Added program directive support in CombineModulesVisitor::visit_module to enable the mod-block scenario

Copy link
Copy Markdown
Member

@lorisleiva lorisleiva left a comment

Choose a reason for hiding this comment

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

Perfection! Sorry for the late reply. Just a small test request but good to merge otherwise! 🙌

Ensures items with program directives are correctly separated from the
main program regardless of declaration order within a module.
@amilz
Copy link
Copy Markdown
Contributor Author

amilz commented Mar 12, 2026

Awesome. Thanks @lorisleiva. Added that additional test. Should be gtg.

@lorisleiva lorisleiva merged commit 599bd34 into codama-idl:main Mar 12, 2026
2 checks passed
@lorisleiva
Copy link
Copy Markdown
Member

Thank you! will publish a new set of crates today.

@amilz amilz deleted the feat/additional-programs branch March 12, 2026 16:00
@lorisleiva
Copy link
Copy Markdown
Member

@amilz New crates published! 🚀

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.

Add ability to specify the program owning a PDA

2 participants