diff --git a/CHANGELOG.md b/CHANGELOG.md index cf5ae7807..b4c5ac197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The minor version will be incremented upon a breaking change and the patch versi - idl: Make safety comment checks fail silently when program path env is not set ([#3045](https://github.com/coral-xyz/anchor/pull/3045)). - idl: Avoid interference from rust tests during IDL generation ([#3058](https://github.com/coral-xyz/anchor/pull/3058)). - lang: Fix `align` repr support in `declare-program!` ([#3056](https://github.com/coral-xyz/anchor/pull/3056)). +- lang: Make stack frames slimmer on ATA creation ([#3065](https://github.com/coral-xyz/anchor/pull/3065)). ### Breaking diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 8c0ecadb7..30bbbe85e 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -624,17 +624,19 @@ fn generate_constraint_init_group( if !#if_needed || owner_program == &anchor_lang::solana_program::system_program::ID { #payer_optional_check - let cpi_program = associated_token_program.to_account_info(); - let cpi_accounts = ::anchor_spl::associated_token::Create { - payer: #payer.to_account_info(), - associated_token: #field.to_account_info(), - authority: #owner.to_account_info(), - mint: #mint.to_account_info(), - system_program: system_program.to_account_info(), - token_program: #token_program.to_account_info(), - }; - let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts); - ::anchor_spl::associated_token::create(cpi_ctx)?; + ::anchor_spl::associated_token::create( + anchor_lang::context::CpiContext::new( + associated_token_program.to_account_info(), + ::anchor_spl::associated_token::Create { + payer: #payer.to_account_info(), + associated_token: #field.to_account_info(), + authority: #owner.to_account_info(), + mint: #mint.to_account_info(), + system_program: system_program.to_account_info(), + token_program: #token_program.to_account_info(), + } + ) + )?; } let pa: #ty_decl = #from_account_info_unchecked; if #if_needed { diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs index 90a82e80e..1b988892c 100644 --- a/tests/misc/programs/misc-optional/src/context.rs +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -693,3 +693,54 @@ pub struct TestAssociatedTokenWithTokenProgramConstraint<'info> { /// CHECK: ignore pub associated_token_token_program: Option>, } + +#[derive(Accounts)] +pub struct InitManyAssociatedTokenAccounts<'info> { + #[account( + init, + payer = user, + mint::authority = user, + mint::decimals = 9, + )] + pub mint: Account<'info, Mint>, + #[account( + init, + payer = user, + associated_token::authority = user, + associated_token::mint = mint, + )] + pub ata1: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = system_program, + associated_token::mint = mint, + )] + pub ata2: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = token_program, + associated_token::mint = mint, + )] + pub ata3: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = associated_token_program, + associated_token::mint = mint, + )] + pub ata4: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = mint, + associated_token::mint = mint, + )] + pub ata5: Account<'info, TokenAccount>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, +} diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs index 054f44ced..b209a4d2e 100644 --- a/tests/misc/programs/misc-optional/src/lib.rs +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -395,4 +395,11 @@ pub mod misc_optional { ) -> Result<()> { Ok(()) } + + #[allow(unused_variables)] + pub fn test_init_many_associated_token_accounts( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index d844aaae8..86748768d 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -601,6 +601,7 @@ pub struct TestOnlyTokenProgramConstraint<'info> { token::token_program = token_token_program )] pub token: Account<'info, TokenAccount>, + /// CHECK: ignore pub token_token_program: AccountInfo<'info>, } @@ -751,3 +752,54 @@ pub struct TestUsedIdentifiers<'info> { /// CHECK: ignore pub test4: AccountInfo<'info>, } + +#[derive(Accounts)] +pub struct InitManyAssociatedTokenAccounts<'info> { + #[account( + init, + payer = user, + mint::authority = user, + mint::decimals = 9, + )] + pub mint: Account<'info, Mint>, + #[account( + init, + payer = user, + associated_token::authority = user, + associated_token::mint = mint, + )] + pub ata1: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = system_program, + associated_token::mint = mint, + )] + pub ata2: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = token_program, + associated_token::mint = mint, + )] + pub ata3: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = associated_token_program, + associated_token::mint = mint, + )] + pub ata4: Account<'info, TokenAccount>, + #[account( + init, + payer = user, + associated_token::authority = mint, + associated_token::mint = mint, + )] + pub ata5: Account<'info, TokenAccount>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, +} diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index d0056018f..bdb528b37 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -385,4 +385,11 @@ pub mod misc { ) -> Result<()> { Ok(()) } + + #[allow(unused_variables)] + pub fn test_init_many_associated_token_accounts( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } } diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts index 7947e8272..60bfb7488 100644 --- a/tests/misc/tests/misc/misc.ts +++ b/tests/misc/tests/misc/misc.ts @@ -2451,6 +2451,15 @@ const miscTest = ( assert.deepStrictEqual(dataAccount.data, array2d); }); + it("Can initialize 5 associated token accounts in one instruction", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.methods + .testInitManyAssociatedTokenAccounts() + .accounts({ mint: mint.publicKey, user: provider.wallet.publicKey }) + .signers([mint]) + .rpc(); + }); + describe("Can validate PDAs derived from other program ids", () => { it("With bumps using create_program_address", async () => { const [firstPDA, firstBump] =