diff --git a/CHANGELOG.md b/CHANGELOG.md index ebae6f8d8f..f0b1b81998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ incremented for features. ### Fixes +* lang: `init_if_needed` now checks rent exemption when init is not needed ([#1250](https://github.com/project-serum/anchor/pull/1250)). * lang: Add missing owner check when `associated_token::authority` is used ([#1240](https://github.com/project-serum/anchor/pull/1240)). * ts: Add type declarations for conditional `workspace` and `Wallet` exports ([#1137](https://github.com/project-serum/anchor/pull/1137)). * ts: Change commitment message `recent` to `processed` and `max` to `finalized` ([#1128](https://github.com/project-serum/anchor/pull/1128)) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index a0e5661ef1..dcb203d500 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -621,6 +621,13 @@ pub fn generate_init( return Err(anchor_lang::__private::ErrorCode::ConstraintOwner.into()); } + { + let required_lamports = __anchor_rent.minimum_balance(space); + if pa.to_account_info().lamports() < required_lamports { + return Err(anchor_lang::__private::ErrorCode::ConstraintRentExempt.into()); + } + } + #pda_check } pa diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 248e4fe294..d40479e86e 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -360,3 +360,22 @@ pub struct EnforceRentExempt<'info> { #[account(rent_exempt = enforce)] pub data: AccountInfo<'info>, } + +#[derive(Accounts)] +pub struct InitDecreaseLamports<'info> { + #[account(init, payer = user, space = 1000)] + pub data: AccountInfo<'info>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System> +} + +#[derive(Accounts)] +pub struct InitIfNeededChecksRentExemption<'info> { + #[account(init_if_needed, payer = user, space = 1000)] + pub data: AccountInfo<'info>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System> +} + diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index 0f89fcf84d..7cc271a1cf 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -258,4 +258,14 @@ pub mod misc { pub fn test_enforce_rent_exempt(ctx: Context) -> ProgramResult { Ok(()) } + + pub fn init_decrease_lamports(ctx: Context) -> ProgramResult { + **ctx.accounts.data.try_borrow_mut_lamports()? -= 1; + **ctx.accounts.user.try_borrow_mut_lamports()? += 1; + Ok(()) + } + + pub fn init_if_needed_checks_rent_exemption(_ctx: Context) -> ProgramResult { + Ok(()) + } } diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index 19535a186b..cc8d457781 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -1398,6 +1398,32 @@ describe("misc", () => { } }); + it("init_if_needed checks rent_exemption if init is not needed", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.initDecreaseLamports({ + accounts: { + data: data.publicKey, + user: anchor.getProvider().wallet.publicKey, + systemProgram: SystemProgram.programId, + }, + signers: [data], + }); + + try { + await program.rpc.initIfNeededChecksRentExemption({ + accounts: { + data: data.publicKey, + user: anchor.getProvider().wallet.publicKey, + systemProgram: SystemProgram.programId, + }, + signers: [data], + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2005); + } + }); + it("Can use multidimensional array", async () => { const array2d = new Array(10).fill(new Array(10).fill(99)); const data = anchor.web3.Keypair.generate();