8000 fix: SystemAccount compilation error in #[derive(Accounts)] macro (#3696) by hasip-timurtas · Pull Request #3752 · solana-foundation/anchor · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix: SystemAccount compilation error in #[derive(Accounts)] macro (#3696) #3752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

hasip-timurtas
Copy link
Contributor

Problem

Using SystemAccount<'info> as a field in a struct with #[derive(Accounts)] was causing compilation errors:

error[E0425]: cannot find crate `try_from_unchecked` in the list of imported crates
error[E0425]: cannot find crate `try_from` in the list of imported crates

Reproduction case:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 0)]
    pub system_account: SystemAccount<'info>,  // ❌ This failed to compile
    #[account(mut, signer)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Root Cause

The issue occurred in lang/syn/src/lib.rs in the from_account_info method:

  1. SystemAccount and Signer have try_from methods that take only one parameter (AccountInfo)
  2. However, they were falling through to the default _ case which generates code for two parameters
  3. The default case generates: #container_ty::try_from(#owner_addr, &#field)
  4. For SystemAccount, container_ty() returns quote! {} (empty), making this: ::try_from(owner_addr, &field)
  5. But SystemAccount::try_from only accepts: try_from(info: &AccountInfo)

Solution

Added specific cases for SystemAccount and Signer in the from_account_info method to generate the correct single-parameter try_from calls:

Ty::SystemAccount => {
    quote! {
        match SystemAccount::try_from(&#field) {
            Ok(val) => val,
            Err(e) => return Err(e.with_account_name(#field_str))
        }
    }
}
Ty::Signer => {
    quote! {
        match Signer::try_from(&#field) {
            Ok(val) => val,
            Err(e) => return Err(e.with_account_name(#field_str))
        }
    }
}

Testing

Compilation Tests:

  • cargo build --workspace --exclude anchor-cli - SUCCESS
  • tests/system-accounts program builds (has existing SystemAccount usage)
  • tests/misc program builds (added test case)

Validation:

  • The existing tests/system-accounts/programs/system-accounts/src/lib.rs already contains the exact failing pattern and now compiles successfully
  • No regressions: all other account types (Account, AccountLoader, UncheckedAccount, etc.) continue to work as before

CI Integration:

  • SystemAccount tests are already part of the CI pipeline via tests/system-accounts

Files Changed

  • lang/syn/src/lib.rs - Core fix: Added specific cases for SystemAccount and Signer
  • tests/misc/programs/misc/src/context.rs - Added test case for SystemAccount
  • tests/misc/programs/misc/src/lib.rs - Added test handler

Backward Compatibility

Fully backward compatible - no existing code needs to change. This only fixes previously broken SystemAccount usage.

Closes #3696

This commit introduces handling for `Signer` and `SystemAccount` types in the `Field` implementation, allowing for proper conversion and error handling. Additionally, a new `TestSystemAccount` struct is added to the test suite to facilitate testing of the `SystemAccount` functionality.
Copy link
vercel bot commented Jun 20, 2025

@hasip-timurtas is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@Aursen
Copy link
Collaborator
Aursen commented Jul 1, 2025

Actually this is not a good thing to allow that:

    #[account(init, payer = user, space = 0)]
    pub system_account: SystemAccount<'info>,

Normally, you initialize the account to assign it to the current program. So, what’s the purpose of assigning it to a System Account in the context after the init?

@hasip-timurtas
Copy link
Contributor Author

@Aursen Yes, you're right that this looks weird. I was just fixing the compilation error where SystemAccount and Signer types weren't handled in the field processing code.

The #[account(init, payer = user, space = 0)] with SystemAccount pattern is definitely edge case stuff. Most people should just use:

#[account(init, payer = user, space = 8 + MyData::INIT_SPACE)]
pub my_data: Account<'info, MyData>,

for their program data, or plain:

pub system_account: SystemAccount<'info>,

for existing system accounts.

But sometimes you actually need to create a system account at a deterministic PDA address, like when you're building something that other programs need to find later. It's rare but when you need it, the framework shouldn't just break.

I'm not trying to encourage this pattern or anything, just making sure the framework doesn't crash when people use it. If you think we should add a warning or better docs about when this makes sense vs the normal patterns, I'm down for that.

What do you think? Should we add some guardrails or is fixing the compilation issue enough for now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang Patch Change that can be done in a patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

#[derive(Accounts)] breaks on a struct with SystemAccount as a field
2 participants
0