Skip to content

feat: macro token account support#2181

Merged
SwenSchaeferjohann merged 4 commits intomainfrom
jorrit/feat-macro-token-account-support
Jan 20, 2026
Merged

feat: macro token account support#2181
SwenSchaeferjohann merged 4 commits intomainfrom
jorrit/feat-macro-token-account-support

Conversation

@ananas-block
Copy link
Contributor

@ananas-block ananas-block commented Jan 19, 2026

Summary by CodeRabbit

  • New Features

    • Support for token accounts and associated token accounts (ATAs) during account init and finalize flows.
    • Automatic generation of token/ATA creation steps when needed.
    • Public-facing helpers to detect token/ATA usage and trigger finalize logic.
  • Refactor

    • Improved handling of context and instruction-argument names for more flexible account/seed parsing and wrapping.
  • Tests

    • Added extensive tests covering token/ATA scenarios, finalization paths, and context-name variants.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

Warning

Rate limit exceeded

@ananas-block has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 133d846 and c7051d1.

⛔ Files ignored due to path filters (4)
  • sdk-tests/csdk-anchor-full-derived-test/src/instructions/d9_seeds/instruction_data.rs is excluded by none and included by none
  • sdk-tests/csdk-anchor-full-derived-test/src/instructions/d9_seeds/mod.rs is excluded by none and included by none
  • sdk-tests/csdk-anchor-full-derived-test/src/lib.rs is excluded by none and included by none
  • sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs is excluded by none and included by none
📒 Files selected for processing (2)
  • sdk-libs/macros/src/light_pdas/account/seed_extraction.rs
  • sdk-libs/macros/src/light_pdas/program/instructions.rs
📝 Walkthrough

Walkthrough

Adds token account and ATA support to the Light Accounts macros: parsing, infra classification, builder hooks, and generated finalize CPI code; updates wrapping logic to conditionally finalize based on delegation and instruction-arg context.

Changes

Cohort / File(s) Summary
Token Field Types & Parsing
sdk-libs/macros/src/light_pdas/accounts/light_account.rs, sdk-libs/macros/src/light_pdas/accounts/parse.rs
Introduces TokenAccountField and AtaField, extends LightAccountType / LightAccountField, adds parsing helpers (parse_token_ata_key_values, build_token_account_field, build_ata_field), and integrates token/ATA fields into parsed struct. Adds infra classification types.
Builder API & Finalize Hooks
sdk-libs/macros/src/light_pdas/accounts/builder.rs, sdk-libs/macros/src/light_pdas/accounts/token.rs
Expands LightAccountsBuilder with has_token_accounts(), has_atas(), needs_token_finalize(), generate_token_finalize_body(); adds TokenAccountsBuilder plus generate_token_account_cpi and generate_ata_cpi to emit CPI finalization for token accounts and ATAs.
Derive Macro Integration & Tests
sdk-libs/macros/src/light_pdas/accounts/derive.rs
Replaces fixed finalize with conditional finalize body that delegates to token finalizer when needed; adds tests covering token account/ATA finalize generation and noop finalization scenarios.
Instruction Arg & Seed Extraction Flow
sdk-libs/macros/src/light_pdas/account/seed_extraction.rs, sdk-libs/macros/src/light_pdas/program/instructions.rs
Adds InstructionArgSet and parse_instruction_arg_names, threads instruction-arg info through seed extraction, updates instruction parsing sites to pass instruction_args into account extraction.
Program Wrapping & Delegation Detection
sdk-libs/macros/src/light_pdas/program/parsing.rs, sdk-libs/macros/src/light_pdas/program/visitors.rs
extract_context_and_params now returns ctx name; wrap_function_with_light branches on delegation-style handlers to skip finalize when delegating; visitors support configurable context identifier names.
Module Integration & Minor Changes
sdk-libs/macros/src/light_pdas/accounts/mod.rs, sdk-libs/macros/src/light_pdas/accounts/mint.rs
Adds mod token; import and derives Debug for LightMintField.

Sequence Diagram(s)

(Skipped — changes are macro/codegen-centric and do not introduce a clear runtime interaction flow across 3+ distinct runtime components suitable for sequence-diagram visualization.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • refactor: sdk macros 2 #2175: Similar modifications to macros' accounts parsing and infrastructure classification; likely overlaps in InfraFieldType and token/seed finalize plumbing.

Suggested reviewers

  • sergeytimoshin
  • SwenSchaeferjohann

Poem

🌱 New token fields sprout in parser's light,
Seeds traced and bound to set accounts right,
Finalizers gather CPIs in line,
Delegation skips where handlers consign,
Macros hum — token ATAs now take flight ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: macro token account support' directly reflects the main objective of this PR: adding comprehensive token account and ATA support to the Light Accounts macro system.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 70.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jorrit/feat-macro-token-account-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (20)
sdk-libs/macros/CLAUDE.md (1)

32-41: Consider adding a language specifier to the fenced code block.

Static analysis flagged this directory structure listing as missing a language identifier. While this is minor, adding ```text or ``` explicitly would satisfy linters.

📝 Suggested fix
-```
+```text
 src/
 ├── lib.rs                 # Macro entry points
 ├── rentfree/              # LightAccounts macro system
sdk-libs/macros/docs/features/comparison.md (2)

224-233: Markdown formatting: Add blank line before code block.

The static analysis tool flagged that the fenced code block should be surrounded by blank lines. There's a missing blank line between the heading and the code block.

📝 Suggested fix
 **After (LightAccounts)**:
+
 ```rust
 #[derive(RentFree, Compressible, HasCompressionInfo)]

173-186: Update RentFree derive to LightAccounts for consistency with line 162 reference.

The example at line 176 uses #[derive(RentFree, ...)] but line 162 in the same table references #[derive(LightAccounts)] as the derive that generates Pack/Unpack traits. Update the example to use #[derive(LightAccounts, Compressible, HasCompressionInfo)] to align with the documented macro name and the PR's rename.

Suggested fix
 ### RentFree Data Struct
 ```rust
-#[derive(RentFree, Compressible, HasCompressionInfo)]
+#[derive(LightAccounts, Compressible, HasCompressionInfo)]
 #[light_account(init)]
 pub struct MyData {
sdk-libs/macros/src/light_pdas/mod.rs (1)

1-7: Documentation terminology inconsistency.

Lines 1, 3-4 still reference "Rent-free" and #[rentfree_program], while line 5 correctly uses LightAccounts. Per the coding guidelines in sdk-libs/macros/docs/accounts/architecture.md, the documentation should consistently use LightAccounts terminology.

📝 Suggested documentation update
-//! Rent-free account compression macros.
+//! Light account compression macros.
 //!
 //! This module organizes all rent-free related macros:
-//! - `program/` - `#[rentfree_program]` attribute macro for program-level auto-discovery
+//! - `program/` - `#[light_program]` attribute macro for program-level auto-discovery
 //! - `accounts/` - `#[derive(LightAccounts)]` derive macro for Accounts structs
-//! - `account/` - Trait derive macros for account data structs (Compressible, Pack, HasCompressionInfo, etc.)
+//! - `account/` - Trait derive macros for account data structs (LightCompressible, Pack, HasCompressionInfo, etc.)
 //! - `shared_utils` - Common utilities (constant detection, identifier extraction)
sdk-libs/macros/docs/light_program/codegen.md (2)

1-3: Documentation title and references not updated to match new naming.

The document title still references #[rentfree_program] (lines 1, 3), but the inline attribute references have been updated to #[light_account(init)] (line 114) and #[light_account(token)] (line 162). Per the coding guidelines in sdk-libs/macros/docs/light_program/codegen.md, the macro surface should be renamed to #[light_program].

Consider updating the title and introductory text:

-# `#[rentfree_program]` Code Generation
+# `#[light_program]` Code Generation
 
-Technical implementation details for the `#[rentfree_program]` attribute macro.
+Technical implementation details for the `#[light_program]` attribute macro.

7-29: Update documentation paths and terminology to reflect light_pdas module structure.

The documentation references sdk-libs/macros/src/rentfree/program/ (lines 7-8, 34) but the actual module is located at sdk-libs/macros/src/light_pdas/program/. Additionally, the file uses outdated terminology:

  • Line 1: #[rentfree_program] → should reference #[light_program] (or appropriate renamed macro)
  • Line 17: RentFreeAccountVariant → should reflect current enum naming
  • Lines 7-41: All rentfree/ path references → should be light_pdas/

Review the entire file and update paths, macro names, and enum references to align with the current codebase structure.

sdk-libs/macros/src/light_pdas/README.md (2)

1-1: Title still references "Rent-Free" terminology.

The README title "# Rent-Free Macros" should be updated to reflect the new LightAccounts / LightPDAs naming convention used throughout the PR.

📝 Suggested fix
-# Rent-Free Macros
+# Light PDAs Macros

7-29: Directory structure documentation is inconsistent with actual location.

The directory structure shows rentfree/ but this file is located in light_pdas/. Additionally, line 13 has a duplicate attribute:

├── parse.rs        # Parsing #[light_account(init)], #[light_account(init)] attributes

This should show both PDA and token attributes:

📝 Suggested fixes
-rentfree/
+light_pdas/
 ├── mod.rs              # Module declaration
 ├── README.md           # This file
 ├── accounts/           # #[derive(LightAccounts)] implementation
 │   ├── mod.rs          # Entry point: derive_rentfree()
-│   ├── parse.rs        # Parsing #[light_account(init)], #[light_account(init)] attributes
+│   ├── parse.rs        # Parsing #[light_account(init)], #[light_account(token)] attributes
sdk-libs/macros/docs/light_program/architecture.md (1)

7-7: Update source path to reflect module reorganization.

Line 7 references sdk-libs/macros/src/rentfree/program/, but the actual implementation now resides at sdk-libs/macros/src/light_pdas/program/. Update the path to match the current directory structure.

sdk-libs/macros/docs/account/light_compressible.md (1)

315-315: Update broken documentation link to RentFree macro.

The link on line 315 references a non-existent file ../rentfree.md. The RentFree derive macro documentation is located in accounts/architecture.md (titled "RentFree Derive Macro and Trait Derives"). Update the table entry to:

| [`RentFree`](../accounts/architecture.md) | Uses traits from `LightCompressible` |
sdk-libs/macros/docs/features/light-features.md (2)

9-15: Update the example to derive LightAccounts.

The snippet still imports/derives RentFree while showing #[light_account(init)]. Please switch to LightAccounts (or label as legacy) to avoid mismatched guidance.

As per coding guidelines, keep LightAccounts naming in public docs unless explicitly documenting legacy terms.


452-457: Complete example should also derive LightAccounts.

UserProfile still derives RentFree while using #[light_account(init)]. Update the derive/import to the LightAccounts surface (or explicitly mark legacy).

As per coding guidelines, keep LightAccounts naming in public docs unless explicitly documenting legacy terms.

sdk-libs/macros/docs/accounts/light_mint.md (2)

5-41: Fix example imports and infra field names to match the LightAccounts surface.

The example still imports RentFree and uses ctoken_rent_sponsor, but the rest of the doc (and code) is using LightAccounts and light_token_* naming. Also, the source path points to the old rentfree module.

📚 Suggested doc corrections
-**Source**: `sdk-libs/macros/src/rentfree/accounts/light_mint.rs`
+**Source**: `sdk-libs/macros/src/light_pdas/accounts/light_mint.rs`

-use light_sdk_macros::RentFree;
+use light_sdk_macros::LightAccounts;

@@
-    pub ctoken_rent_sponsor: Account<'info, CtokenRentSponsor>,
+    pub light_token_rent_sponsor: Account<'info, CtokenRentSponsor>,

As per coding guidelines, docs should mirror the current LightAccounts API and naming.


292-296: Clarify the mixed mint/PDA case (duplicated attribute text).

This line references #[light_account(init)] twice; it should describe the mint + PDA combination (e.g., #[light_account(init, mint)] + #[light_account(init)]).

As per coding guidelines, docs must accurately reflect current attribute semantics.

sdk-libs/macros/src/light_pdas/account/seed_extraction.rs (1)

95-151: Token fields with init are misclassified as PDAs.

check_light_account_type only looks for init/mint, so #[light_account(init, token, ...)] will be treated as a PDA and bypass the token path. That drops token fields from token_fields and will break token seed/variant generation.

🔧 One possible fix (short-circuit token fields before PDA classification)
-        let (has_light_account_pda, has_light_account_mint) =
-            check_light_account_type(&field.attrs);
-
-        if has_light_account_mint {
-            has_light_mint_fields = true;
-        }
-
-        // Check for #[light_account(token, ...)] attribute
-        let token_attr = extract_light_token_attr(&field.attrs);
+        // Check for #[light_account(token, ...)] attribute first
+        let token_attr = extract_light_token_attr(&field.attrs)?;
+
+        let (has_light_account_pda, has_light_account_mint) =
+            check_light_account_type(&field.attrs);

@@
-        if has_light_account_pda {
+        if let Some(token_attr) = token_attr {
+            // Token field - derive variant name from field name if not provided
+            let seeds = extract_anchor_seeds(&field.attrs)?;
+            let variant_name = token_attr.variant_name.unwrap_or_else(|| {
+                let camel = snake_to_camel_case(&field_ident.to_string());
+                Ident::new(&camel, field_ident.span())
+            });
+
+            token_fields.push(ExtractedTokenSpec {
+                field_name: field_ident,
+                variant_name,
+                seeds,
+                authority_field: None,
+                authority_seeds: token_attr.authority_seeds,
+            });
+        } else if has_light_account_pda {
             // Extract inner type from Account<'info, T> or Box<Account<'info, T>>
             // Note: is_boxed is not needed for ExtractedSeedSpec, only inner_type
             let (_, inner_type) = match extract_account_inner_type(&field.ty) {
                 Some(result) => result,
                 None => {
                     return Err(syn::Error::new_spanned(
                         &field.ty,
                         "#[light_account(init)] requires Account<'info, T> or Box<Account<'info, T>>",
                     ));
                 }
             };
@@
-        } else if let Some(token_attr) = token_attr {
-            // Token field - derive variant name from field name if not provided
-            let seeds = extract_anchor_seeds(&field.attrs)?;
-
-            // Derive variant name: snake_case field -> CamelCase variant
-            let variant_name = token_attr.variant_name.unwrap_or_else(|| {
-                let camel = snake_to_camel_case(&field_ident.to_string());
-                Ident::new(&camel, field_ident.span())
-            });
-
-            token_fields.push(ExtractedTokenSpec {
-                field_name: field_ident,
-                variant_name,
-                seeds,
-                authority_field: None,
-                // Use authority from attribute if provided
-                authority_seeds: token_attr.authority_seeds,
-            });
         }
sdk-libs/macros/docs/accounts/architecture.md (3)

14-26: Module structure still points to sdk-libs/macros/src/rentfree/.

The root path and submodule references should reflect the light_pdas rename to avoid misleading readers and breaking navigation.

As per coding guidelines, documentation paths and module names should match the current LightAccounts/LightPDAs structure.


101-138: Replace duplicated #[light_account(init)] mentions with the mint variant where intended.

Several sections describe mint handling but still refer to #[light_account(init)] twice. These should consistently use #[light_account(init, mint)] for mint fields.

As per coding guidelines, docs must accurately describe the attribute surface.

Also applies to: 571-581


126-135: markdownlint: add languages and spacing around fenced blocks.

Static analysis flagged missing language identifiers and blank lines around code fences. Please add languages (e.g., rust / text) and surround fences with blank lines for MD031/MD040 compliance.

sdk-libs/macros/src/light_pdas/program/instructions.rs (2)

26-30: Stale comment references "rentfree module".

Line 30 still mentions "rentfree module" but this is now the light_pdas module. Small housekeeping item to keep documentation consistent with the rename.

📝 Suggested doc fix
-/// Orchestrates all code generation for the rentfree module.
+/// Orchestrates all code generation for the light_pdas module.

498-540: Stale "rentfree" references in comments and variable names.

Several internal identifiers and comments still use the old naming:

  • Line 500: comment says "extract rentfree field info"
  • Line 503: variable rentfree_struct_names
  • Line 527: comment says "rentfree Accounts structs"
  • Line 533: comment says "rentfree Accounts struct"

While these don't affect functionality, they reduce clarity for future maintainers trying to understand the codebase.

📝 Suggested naming consistency fix
-    // Find all structs with #[derive(Accounts)] and extract rentfree field info
+    // Find all structs with #[derive(Accounts)] and extract light account field info
     let mut pda_specs: Vec<ExtractedSeedSpec> = Vec::new();
     let mut token_specs: Vec<ExtractedTokenSpec> = Vec::new();
-    let mut rentfree_struct_names = std::collections::HashSet::new();
+    let mut light_account_struct_names = std::collections::HashSet::new();

     for item_struct in crate_ctx.structs_with_derive("Accounts") {
         if let Some(info) = extract_from_accounts_struct(item_struct)? {
             if !info.pda_fields.is_empty()
                 || !info.token_fields.is_empty()
                 || info.has_light_mint_fields
             {
-                rentfree_struct_names.insert(info.struct_name.to_string());
+                light_account_struct_names.insert(info.struct_name.to_string());
                 pda_specs.extend(info.pda_fields);
                 token_specs.extend(info.token_fields);
             }
         }
     }
     ...
-    // Auto-wrap instruction handlers that use rentfree Accounts structs
+    // Auto-wrap instruction handlers that use Light Accounts structs
     if let Some((_, ref mut items)) = module.content {
         for item in items.iter_mut() {
             if let Item::Fn(fn_item) = item {
-                // Check if this function uses a rentfree Accounts struct
+                // Check if this function uses a Light Accounts struct
                 if let Some((context_type, params_ident)) = extract_context_and_params(fn_item) {
-                    if rentfree_struct_names.contains(&context_type) {
+                    if light_account_struct_names.contains(&context_type) {
🤖 Fix all issues with AI agents
In `@sdk-libs/compressible-client/src/lib.rs`:
- Around line 253-255: The doc comment for the function accounts is
inconsistent: it uses light_token_cpi_authority but still references
ctoken_rent_sponsor and ctoken_config; update those names to match the Light
Token naming (e.g., rename ctoken_rent_sponsor -> light_token_rent_sponsor and
ctoken_config -> light_token_config) so the comment lists:
light_token_rent_sponsor, light_token_program, light_token_cpi_authority,
light_token_config.

In `@sdk-libs/macros/docs/account/light_compressible.md`:
- Around line 273-284: Update the outdated comment above the Create accounts
struct: replace the phrase "apply RentFree" with terminology matching the derive
attributes (e.g., mention "LightAccounts" or "light accounts") so the comment
aligns with #[derive(Accounts, LightAccounts)] on the Create<'info> struct and
the #[light_account(init)] usage for user_record.

In `@sdk-libs/macros/docs/accounts/light_mint.md`:
- Around line 301-308: The account-name table in light_mint.md is out of sync
with the parser: reconcile it with InfraFieldClassifier by either (A) updating
the table entries for "CToken Config", "CToken Rent Sponsor", "CToken Program",
"CToken CPI Authority" and "Fee Payer" to only list the exact names
InfraFieldClassifier accepts, or (B) extend InfraFieldClassifier to accept the
legacy aliases (`ctoken_*`, `light_token_program_cpi_authority`, etc.) so the
docs remain valid; locate the table in light_mint.md and then (if changing code)
add the legacy aliases to the classifier’s mapping (InfraFieldClassifier) and
update any related tests to cover these names, or (if changing docs) remove
legacy names and run docs build/validation.

In `@sdk-libs/macros/docs/CLAUDE.md`:
- Around line 51-60: Update the documentation hierarchy diagram to rename the
generated enum from RentFreeAccountVariant to LightAccountVariant so it matches
the #[derive(LightAccounts)] naming; search for occurrences of
RentFreeAccountVariant in the CLAUDE.md diagram and any surrounding explanatory
text and replace them with LightAccountVariant, ensuring the public docs
consistently use the LightAccounts naming rather than legacy terms.

In `@sdk-libs/macros/src/light_pdas/account/seed_extraction.rs`:
- Around line 242-267: The function extract_light_token_attr currently ignores
errors from parse_light_token_list and returns a default LightTokenAttr when
parsing the authority list fails; change extract_light_token_attr to propagate
parse errors instead of swallowing them by making it return
Result<Option<LightTokenAttr>, syn::Error> (or another appropriate error type),
replace the branch that does `if let Ok(parsed) =
parse_light_token_list(&tokens) { return Some(parsed); } return Some(...)` with
logic that returns Ok(Some(parsed)) on success and returns Err(parse_error) when
parse_light_token_list fails (or map the error into syn::Error), and update all
callers to handle the Result (e.g., bubble the error up) so malformed `authority
= [...]` syntax surfaces rather than producing a silent default.

In `@sdk-libs/macros/src/light_pdas/accounts/builder.rs`:
- Around line 75-113: In validate_infra_fields() add the token program to the
infra validation: when needs_token_infra (i.e., for mints, token_accounts or
atas) also check self.parsed.infra_fields.light_token_program and push
InfraFieldType::LightTokenProgram to missing if it's None, alongside the
existing LightTokenConfig/LightTokenRentSponsor/LightTokenCpiAuthority checks so
missing token program is reported at macro-compile time.

In `@sdk-libs/macros/src/light_pdas/accounts/parse.rs`:
- Around line 34-75: Documentation lists alias names that the code doesn't
recognize, causing silent ignores; either update the docs to match the actual
accepted names or add alias support in the classifier. To fix: (preferred) edit
the architecture.md table (section 2.3) to remove the unimplemented aliases and
list only the names returned by InfraFieldType::accepted_names() and described
by InfraFieldType::description(); or (alternative) extend
InfraFieldClassifier::classify to include the alias strings (mapping them to
their InfraFieldType variants) and update InfraFieldType::accepted_names() to
include the aliases so implementation and docs stay consistent. Ensure any
change references the same symbols (InfraFieldClassifier::classify,
InfraFieldType::accepted_names, InfraFieldType::description) so the table and
code remain in sync.

In `@sdk-libs/macros/src/light_pdas/program/parsing.rs`:
- Around line 401-486: The current delegation heuristic (is_delegation_body) can
accidentally skip finalize; require an explicit #[light_delegate] attribute to
opt-in. Update wrap_function_with_light to only treat a handler as delegation
when both is_delegation_body(&fn_block) is true AND the function has an
attribute named "light_delegate" (inspect fn_attrs for syn::Attribute whose
path.is_ident("light_delegate")); you can add a small helper like
has_light_delegate_attr(fn_attrs: &Vec<Attribute>) or inline the check, and only
then use the delegation branch — otherwise use the full pre_init+finalize flow.

In `@sdk-libs/macros/src/light_pdas/shared_utils.rs`:
- Around line 86-89: The doc example for attribute parsing is missing a space
after a comma in the attribute string `#[light_account(init, mint,mint_signer =
self.authority)]`; update that example to `#[light_account(init, mint,
mint_signer = self.authority)]` (the surrounding comment near the
`#[derive(Clone)]` block / the shared_utils documentation) to tighten formatting
and improve readability.

In `@sdk-libs/token-sdk/src/token/decompress_mint.rs`:
- Around line 396-400: The inline comment incorrectly references "ctoken's CPI
authority" while the argument uses light_token_cpi_authority; update the comment
near the call site (the argument list containing
self.light_token_cpi_authority.clone()) to say "Use Light Token's CPI authority
for the CPI, not the calling program's authority" (or similar Light Token
terminology) so the comment matches the symbol name light_token_cpi_authority.

@ananas-block ananas-block force-pushed the jorrit/feat-macro-token-account-support branch from 14fef0f to a7708d4 Compare January 20, 2026 00:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@sdk-libs/macros/src/light_pdas/accounts/token.rs`:
- Around line 232-235: finalize currently unconditionally uses
self.system_program (let __system_program =
self.system_program.to_account_info()), which yields confusing compile errors if
a user omits that field; update the macro to either (A) validate at builder
construction time that the generated builder includes a required system_program
field (e.g., mark it required in the builder or emit a clear compile_error if
missing), or (B) derive/fill system_program automatically from the remaining
accounts (or the known system program id) inside finalize before calling
to_account_info, and if absent emit an explicit error message mentioning
finalize and system_program so users know to provide that field; locate and
modify the finalize macro and the builder generation code that references
self.system_program to implement the validation/derivation and replace the
opaque compiler error with a clear, actionable message.

In `@sdk-libs/macros/src/light_pdas/program/parsing.rs`:
- Around line 466-469: The delegation branch imports both LightPreInit and
LightFinalize but only uses LightPreInit; remove the unused LightFinalize import
to avoid unused_imports warnings. Locate the import line inside the generated
function block (the line that reads use light_sdk::interface::{LightPreInit,
LightFinalize};) and change it to only import LightPreInit so the call to
ctx.accounts.light_pre_init(ctx.remaining_accounts, &#params_ident) remains
unchanged.
♻️ Duplicate comments (2)
sdk-libs/macros/src/light_pdas/program/parsing.rs (1)

458-476: Avoid skipping light_finalize based solely on the delegation heuristic.
The current delegation branch skips finalize whenever a single-call body passes ctx. If that target isn’t Light-wrapped, token/ATA creation will be silently skipped. Consider requiring an explicit opt-in attribute (e.g., #[light_delegate]) to guard this path.

Suggested tightening (opt‑in delegation)
-    let is_delegation = is_delegation_body(fn_block);
+    let is_delegate_attr = fn_attrs.iter().any(|a| a.path().is_ident("light_delegate"));
+    let is_delegation = is_delegate_attr && is_delegation_body(fn_block);
sdk-libs/macros/src/light_pdas/accounts/builder.rs (1)

99-113: Include light_token_program in required infra validation.
Token/mint/ATA flows still require the token program account; without validation this fails at CPI time instead of macro time.

Suggested fix
         if needs_token_infra {
             if self.parsed.infra_fields.light_token_config.is_none() {
                 missing.push(InfraFieldType::LightTokenConfig);
             }
             if self.parsed.infra_fields.light_token_rent_sponsor.is_none() {
                 missing.push(InfraFieldType::LightTokenRentSponsor);
             }
+            if self.parsed.infra_fields.light_token_program.is_none() {
+                missing.push(InfraFieldType::LightTokenProgram);
+            }
             // CPI authority is required for mints and token accounts (PDA-based signing)
             if (has_mints || has_token_accounts)
                 && self.parsed.infra_fields.light_token_cpi_authority.is_none()
             {
                 missing.push(InfraFieldType::LightTokenCpiAuthority);
             }
         }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdk-libs/macros/src/light_pdas/program/visitors.rs (1)

221-291: Client seed mapping still assumes ctx, despite broader classification.

classify_field_expr and classify_method_call now accept any <identifier>.accounts.*, but map_call_arg still only recognizes ctx/ctx.accounts. If a seed uses context.accounts.* inside a function call, the generated client code won’t parameterize it, leading to unresolved identifiers. Align map_call_arg with the new “any ctx name” behavior.

🔧 Suggested alignment in map_call_arg
-                if FieldExtractor::is_ctx_accounts(&field_expr.base) {
+                if FieldExtractor::is_any_ctx_accounts(&field_expr.base).is_some() {
                     if seen_params.insert(field_name.to_string()) {
                         parameters.push(quote! { `#field_name`: &solana_pubkey::Pubkey });
                     }
                     return Ok(quote! { `#field_name` });
                 }
                 // Check for ctx.field or data.field
                 if let syn::Expr::Path(path) = &*field_expr.base {
                     if let Some(segment) = path.path.segments.first() {
                         if segment.ident == "data" {
                             ...
-                        } else if segment.ident == "ctx" {
+                        } else {
                             if seen_params.insert(field_name.to_string()) {
                                 parameters.push(quote! { `#field_name`: &solana_pubkey::Pubkey });
                             }
                             return Ok(quote! { `#field_name` });
                         }
                     }
                 }
🤖 Fix all issues with AI agents
In `@sdk-libs/macros/src/light_pdas/program/instructions.rs`:
- Around line 536-542: The wrapper currently only passes a single params
identifier into wrap_function_with_light (and thus into light_pre_init/finalize)
which drops extra instruction args; update the call site around
extract_context_and_params so you detect multi‑arg "format‑2" cases and fail
fast: have extract_context_and_params return either a single Ident or a
Vec/count, then if you observe more than one params ident emit a compile error
(via syn::Error::new_spanned or compile_error!) with a clear message asking the
user to supply a single params struct (or enable automatic synthesis), rather
than silently proceeding; alternatively, if you prefer synthesis, extend
wrap_function_with_light to accept the Vec of idents and synthesize a params
struct before generating light_pre_init/finalize calls, but the minimal fix is
to add the guard and error on multiple params to prevent silent mismatches
(references: extract_context_and_params, wrap_function_with_light,
light_pre_init/finalize, params_ident, ctx_name, rentfree_struct_names).
♻️ Duplicate comments (2)
sdk-libs/macros/src/light_pdas/account/seed_extraction.rs (1)

315-343: Surface parse errors for #[light_account(token, authority = ...)].

parse_light_token_list errors are swallowed, which can silently drop authority seeds and generate invalid CPI signing behavior. Propagate the parse error to fail fast on malformed attributes.

🔧 Error propagation sketch
-fn extract_light_token_attr(
-    attrs: &[syn::Attribute],
-    instruction_args: &InstructionArgSet,
-) -> Option<LightTokenAttr> {
+fn extract_light_token_attr(
+    attrs: &[syn::Attribute],
+    instruction_args: &InstructionArgSet,
+) -> syn::Result<Option<LightTokenAttr>> {
     for attr in attrs {
         if attr.path().is_ident("light_account") {
             ...
             if has_token {
-                if let Ok(parsed) = parse_light_token_list(&tokens, instruction_args) {
-                    return Some(parsed);
-                }
-                return Some(LightTokenAttr { variant_name: None, authority_seeds: None });
+                let parsed = parse_light_token_list(&tokens, instruction_args)?;
+                return Ok(Some(parsed));
             }
         }
     }
-    None
+    Ok(None)
 }

Then at the call site:

-        let token_attr = extract_light_token_attr(&field.attrs, instruction_args);
+        let token_attr = extract_light_token_attr(&field.attrs, instruction_args)?;
sdk-libs/macros/src/light_pdas/program/parsing.rs (1)

474-493: Require explicit opt‑in before skipping finalize.

The current heuristic skips finalize whenever the body is a single call that takes ctx. If the target isn’t macro‑wrapped, finalize never runs. Gate this with an explicit #[light_delegate] attribute so skipping is intentional.

🔧 Suggested guard for delegation
-    let is_delegation = is_delegation_body(fn_block, &ctx_name_str);
+    let is_delegate_attr = fn_attrs.iter().any(|a| a.path().is_ident("light_delegate"));
+    let is_delegation = is_delegate_attr && is_delegation_body(fn_block, &ctx_name_str);

@ananas-block ananas-block force-pushed the jorrit/feat-macro-token-account-support branch from 133d846 to c7051d1 Compare January 20, 2026 02:25
@SwenSchaeferjohann SwenSchaeferjohann merged commit ef3eb68 into main Jan 20, 2026
19 checks passed
@ananas-block ananas-block deleted the jorrit/feat-macro-token-account-support branch January 20, 2026 04:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants