From 04d81bef36096b6cde5b08510483e95d6e781d05 Mon Sep 17 00:00:00 2001 From: blockchainlover2019 Date: Tue, 22 Feb 2022 17:56:44 +0100 Subject: [PATCH 1/9] Add TokenConstraint feature and Constraint unit test --- lang/syn/src/codegen/accounts/constraints.rs | 70 +++ lang/syn/src/lib.rs | 17 + lang/syn/src/parser/accounts/constraints.rs | 80 ++- tests/misc/programs/misc/src/context.rs | 151 +++++ tests/misc/programs/misc/src/lib.rs | 50 ++ tests/misc/tests/misc.ts | 572 +++++++++++++++++++ 6 files changed, 895 insertions(+), 45 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 88102877ee..726683183f 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -57,6 +57,8 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { close, address, associated_token, + spl_account, + spl_mint, } = c_group.clone(); let mut constraints = Vec::new(); @@ -100,6 +102,12 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { if let Some(c) = address { constraints.push(Constraint::Address(c)); } + if let Some(c) = spl_account { + constraints.push(Constraint::SplAccount(c)); + } + if let Some(c) = spl_mint { + constraints.push(Constraint::SplMint(c)); + } constraints } @@ -120,6 +128,8 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { Constraint::Close(c) => generate_constraint_close(f, c), Constraint::Address(c) => generate_constraint_address(f, c), Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c), + Constraint::SplAccount(c) => generate_constraint_spl_account(f, c), + Constraint::SplMint(c) => generate_constraint_spl_mint(f, c), } } @@ -682,6 +692,66 @@ fn generate_constraint_associated_token( } } +fn generate_constraint_spl_account( + f: &Field, + c: &ConstraintSplTokenAccount, +) -> proc_macro2::TokenStream { + let name = &f.ident; + let account_owner = match &c.auth { + Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, + None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + }; + + let spl_token_mint = match &c.mint { + Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, + None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + }; + + quote! { + if #account_owner.is_some() && #name.owner != *#account_owner.unwrap() { + return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); + } + if #spl_token_mint.is_some() && #name.mint != *#spl_token_mint.unwrap() { + return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); + } + } +} + +fn generate_constraint_spl_mint(f: &Field, c: &ConstraintSplTokenMint) -> proc_macro2::TokenStream { + let name = &f.ident; + + let decimals = match &c.decimals { + Some(fa) => quote! { Option::::Some(#fa) }, + None => quote! { Option::::None }, + }; + let mint_authority = match &c.mint_auth { + Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, + None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + }; + let freeze_authority = match &c.mint_freeze_auth { + Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, + None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + }; + + quote! { + if #decimals.is_some() && #name.decimals != #decimals.unwrap() { + return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into()); + } + if #mint_authority + .as_ref() + .map(|fa| #name.mint_authority.as_ref().map(|expected_fa| *fa != expected_fa).unwrap_or(true)) + .unwrap_or(false) { + return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); + } + if #freeze_authority + .as_ref() + .map(|fa| #name.freeze_authority.as_ref().map(|expected_fa| *fa != expected_fa).unwrap_or(true)) + .unwrap_or(false) { + return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); + } + } +} + // Generated code to create an account with with system program with the // given `space` amount of data, owned by `owner`. // diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 63ad84d8f1..acc5babbc6 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -587,6 +587,8 @@ pub struct ConstraintGroup { close: Option, address: Option, associated_token: Option, + spl_account: Option, + spl_mint: Option, } impl ConstraintGroup { @@ -628,6 +630,8 @@ pub enum Constraint { State(ConstraintState), Close(ConstraintClose), Address(ConstraintAddress), + SplAccount(ConstraintSplTokenAccount), + SplMint(ConstraintSplTokenMint), } // Constraint token is a single keyword in a `#[account()]` attribute. @@ -832,6 +836,19 @@ pub struct ConstraintAssociatedToken { pub mint: Expr, } +#[derive(Debug, Clone)] +pub struct ConstraintSplTokenAccount { + pub mint: Option, + pub auth: Option, +} + +#[derive(Debug, Clone)] +pub struct ConstraintSplTokenMint { + pub decimals: Option, + pub mint_auth: Option, + pub mint_freeze_auth: Option, +} + // Syntaxt context object for preserving metadata about the inner item. #[derive(Debug, Clone)] pub struct Context { diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index 42454fec4f..a767acf96d 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -464,43 +464,36 @@ impl<'ty> ConstraintGroupBuilder<'ty> { // Token. if let Some(token_mint) = &self.token_mint { - if self.token_authority.is_none() { + if self.init.is_some() && self.token_authority.is_none() { return Err(ParseError::new( token_mint.span(), - "token authority must be provided if token mint is", - )); - } - - if self.init.is_none() { - return Err(ParseError::new( - token_mint.span(), - "init is required for a pda token", + "token authority must be provided if token mint is when initialize", )); } } if let Some(token_authority) = &self.token_authority { - if self.token_mint.is_none() { + if self.init.is_some() && self.token_mint.is_none() { return Err(ParseError::new( token_authority.span(), - "token mint must be provided if token authority is", + "token mint must be provided if token authority is when initialize", )); } } // Mint. if let Some(mint_decimals) = &self.mint_decimals { - if self.mint_authority.is_none() { + if self.init.is_some() && self.mint_authority.is_none() { return Err(ParseError::new( mint_decimals.span(), - "mint authority must be provided if mint decimals is", + "mint authority must be provided if mint decimals is when initialize", )); } } if let Some(mint_authority) = &self.mint_authority { - if self.mint_decimals.is_none() { + if self.init.is_some() && self.mint_decimals.is_none() { return Err(ParseError::new( mint_authority.span(), - "mint decimals must be provided if mint authority is", + "mint decimals must be provided if mint authority is when initialize", )); } } @@ -600,6 +593,31 @@ impl<'ty> ConstraintGroupBuilder<'ty> { } _ => None, }; + let spl_account = match (&token_mint, &token_authority) { + (None, None) => None, + _ => Some(ConstraintSplTokenAccount { + mint: token_mint.as_ref().map(|a| a.clone().into_inner().mint), + auth: token_authority + .as_ref() + .map(|a| a.clone().into_inner().auth), + }), + }; + + let spl_mint = match (&mint_decimals, &mint_authority, &mint_freeze_authority) { + (None, None, None) => None, + _ => Some(ConstraintSplTokenMint { + decimals: mint_decimals + .as_ref() + .map(|a| a.clone().into_inner().decimals), + mint_auth: mint_authority + .as_ref() + .map(|a| a.clone().into_inner().mint_auth), + mint_freeze_auth: mint_freeze_authority + .as_ref() + .map(|a| a.clone().into_inner().mint_freeze_auth), + }), + }; + Ok(ConstraintGroup { init: init.as_ref().map(|i| Ok(ConstraintInitGroup { if_needed: i.if_needed, @@ -654,6 +672,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> { address: into_inner!(address), associated_token: if !is_init { associated_token } else { None }, seeds, + spl_account: if !is_init {spl_account} else {None}, + spl_mint: if !is_init {spl_mint} else {None}, }) } @@ -751,12 +771,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { "associated token mint already provided", )); } - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before token", - )); - } self.token_mint.replace(c); Ok(()) } @@ -823,12 +837,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { "token authority already provided", )); } - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before token authority", - )); - } self.token_authority.replace(c); Ok(()) } @@ -857,12 +865,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { if self.mint_authority.is_some() { return Err(ParseError::new(c.span(), "mint authority already provided")); } - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint authority", - )); - } self.mint_authority.replace(c); Ok(()) } @@ -877,12 +879,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { "mint freeze_authority already provided", )); } - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint freeze_authority", - )); - } self.mint_freeze_authority.replace(c); Ok(()) } @@ -891,12 +887,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { if self.mint_decimals.is_some() { return Err(ParseError::new(c.span(), "mint decimals already provided")); } - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint decimals", - )); - } self.mint_decimals.replace(c); Ok(()) } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index a51c11dc1b..751d7e638f 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -476,3 +476,154 @@ pub struct TestUnsafeFieldSafetyErrors<'info> { pub signer: Signer<'info>, pub system_program: Program<'info, System>, } + +#[derive(Accounts)] +pub struct TestConstraintToken<'info> { + #[account( + token::mint = mint, + token::authority = payer + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestAuthorityConstraint<'info> { + #[account( + token::mint = mint, + token::authority = fake_authority + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub fake_authority: AccountInfo<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, +} +#[derive(Accounts)] +pub struct TestOnlyAuthorityConstraint<'info> { + #[account( + token::authority = payer + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, +} +#[derive(Accounts)] +pub struct TestOnlyMintConstraint<'info> { + #[account( + token::mint = mint, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(_decimals: u8)] +pub struct TestMintConstraint<'info> { + #[account( + mint::decimals = _decimals, + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, + pub mint_authority: AccountInfo<'info>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(_decimals: u8)] +pub struct TestMintOnlyDecimalsConstraint<'info> { + #[account( + mint::decimals = _decimals, + )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestMintAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, + pub mint_authority: AccountInfo<'info>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestMintOneAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, + pub mint_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(_decimals: u8)] +pub struct TestMintMissMintAuthConstraint<'info> { + #[account( + mint::decimals = _decimals, + mint::freeze_authority = freeze_authority, + )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: AccountInfo<'info>, + pub token_program: AccountInfo<'info>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestAssociatedToken<'info> { + #[account( + associated_token::mint = mint, + associated_token::authority = authority, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + pub authority: AccountInfo<'info>, +} diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index 149ce654b2..821f5eb60a 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -303,4 +303,54 @@ pub mod misc { ) -> Result<()> { Ok(()) } + + pub fn test_token_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_token_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_mint_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_mint_constraint(_ctx: Context, _decimals: u8) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_decimals_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_one_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_miss_mint_auth_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_associated_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } } diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index f02d7f1a53..36f1fe35c1 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -1605,4 +1605,576 @@ describe("misc", () => { }); }); }); + describe("Token Constraint Test", () => { + it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.ok(account.owner.equals(program.provider.wallet.publicKey)); + assert.ok(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.ok(account.owner.equals(program.provider.wallet.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyMintConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.ok(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + try { + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2014); + } + }); + + it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + fakeAuthority: fakeAuthority.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + console.log("err", err); + assert.equal(err.code, 2015); + } + }); + + it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + payer: program.provider.wallet.publicKey, + fakeAuthority: fakeAuthority.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2015); + } + }); + + it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + // right + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: program.provider.wallet.publicKey, + freezeAuthority: program.provider.wallet.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.ok(mintAccount.decimals === 6); + assert.ok( + mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) + ); + assert.ok( + mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const fakeDecimal = 5; + try { + await program.rpc.testMintConstraint(fakeDecimal, { + accounts: { + mint: mint.publicKey, + mintAuthority: program.provider.wallet.publicKey, + freezeAuthority: program.provider.wallet.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2018); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + // mintAuthority mismatch + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: fakeAuthority.publicKey, + freezeAuthority: program.provider.wallet.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2016); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + // freezeAuthority mismatch + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: program.provider.wallet.publicKey, + freezeAuthority: fakeAuthority.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + assert.ok(false); + } catch (err) { + assert.equal(err.code, 2017); + } + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + // right + await program.rpc.testMintOnlyDecimalsConstraint(6, { + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.ok(mintAccount.decimals === 6); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + // right + await program.rpc.testMintOnlyAuthConstraint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: program.provider.wallet.publicKey, + freezeAuthority: program.provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.ok( + mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) + ); + assert.ok( + mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + // right + await program.rpc.testMintOnlyOneAuthConstraint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: program.provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.ok( + mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + // right + await program.rpc.testMintMissMintAuthConstraint(6, { + accounts: { + mint: mint.publicKey, + payer: program.provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + freezeAuthority: program.provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + program.provider.wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.ok(mintAccount.decimals === 6); + assert.ok( + mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) + ); + }); + }); }); From 0f1fe0211067a62f9052890ebf2a0b07e9647db2 Mon Sep 17 00:00:00 2001 From: blockchainlover2019 Date: Tue, 29 Mar 2022 02:55:06 +0200 Subject: [PATCH 2/9] Modify some spellings and fix logic --- lang/syn/src/codegen/accounts/constraints.rs | 28 +++--- lang/syn/src/lib.rs | 18 ++-- lang/syn/src/parser/accounts/constraints.rs | 90 ++++++++++---------- tests/misc/programs/misc/src/context.rs | 70 ++++++++------- tests/misc/tests/misc.ts | 44 +++++----- 5 files changed, 131 insertions(+), 119 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 726683183f..0104afccc5 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -57,8 +57,8 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { close, address, associated_token, - spl_account, - spl_mint, + token_account, + mint, } = c_group.clone(); let mut constraints = Vec::new(); @@ -102,11 +102,11 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { if let Some(c) = address { constraints.push(Constraint::Address(c)); } - if let Some(c) = spl_account { - constraints.push(Constraint::SplAccount(c)); + if let Some(c) = token_account { + constraints.push(Constraint::TokenAccount(c)); } - if let Some(c) = spl_mint { - constraints.push(Constraint::SplMint(c)); + if let Some(c) = mint { + constraints.push(Constraint::Mint(c)); } constraints } @@ -128,8 +128,8 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { Constraint::Close(c) => generate_constraint_close(f, c), Constraint::Address(c) => generate_constraint_address(f, c), Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c), - Constraint::SplAccount(c) => generate_constraint_spl_account(f, c), - Constraint::SplMint(c) => generate_constraint_spl_mint(f, c), + Constraint::TokenAccount(c) => generate_constraint_token_account(f, c), + Constraint::Mint(c) => generate_constraint_mint(f, c), } } @@ -692,12 +692,12 @@ fn generate_constraint_associated_token( } } -fn generate_constraint_spl_account( +fn generate_constraint_token_account( f: &Field, - c: &ConstraintSplTokenAccount, + c: &ConstraintTokenAccountGroup, ) -> proc_macro2::TokenStream { let name = &f.ident; - let account_owner = match &c.auth { + let account_owner = match &c.authority { Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, }; @@ -717,18 +717,18 @@ fn generate_constraint_spl_account( } } -fn generate_constraint_spl_mint(f: &Field, c: &ConstraintSplTokenMint) -> proc_macro2::TokenStream { +fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream { let name = &f.ident; let decimals = match &c.decimals { Some(fa) => quote! { Option::::Some(#fa) }, None => quote! { Option::::None }, }; - let mint_authority = match &c.mint_auth { + let mint_authority = match &c.mint_authority { Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, }; - let freeze_authority = match &c.mint_freeze_auth { + let freeze_authority = match &c.mint_freeze_authority { Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, }; diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index acc5babbc6..48ba22981f 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -587,8 +587,8 @@ pub struct ConstraintGroup { close: Option, address: Option, associated_token: Option, - spl_account: Option, - spl_mint: Option, + token_account: Option, + mint: Option, } impl ConstraintGroup { @@ -630,8 +630,8 @@ pub enum Constraint { State(ConstraintState), Close(ConstraintClose), Address(ConstraintAddress), - SplAccount(ConstraintSplTokenAccount), - SplMint(ConstraintSplTokenMint), + TokenAccount(ConstraintTokenAccountGroup), + Mint(ConstraintTokenMintGroup), } // Constraint token is a single keyword in a `#[account()]` attribute. @@ -837,16 +837,16 @@ pub struct ConstraintAssociatedToken { } #[derive(Debug, Clone)] -pub struct ConstraintSplTokenAccount { +pub struct ConstraintTokenAccountGroup { pub mint: Option, - pub auth: Option, + pub authority: Option, } #[derive(Debug, Clone)] -pub struct ConstraintSplTokenMint { +pub struct ConstraintTokenMintGroup { pub decimals: Option, - pub mint_auth: Option, - pub mint_freeze_auth: Option, + pub mint_authority: Option, + pub mint_freeze_authority: Option, } // Syntaxt context object for preserving metadata about the inner item. diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index a767acf96d..468db4365d 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -424,6 +424,42 @@ impl<'ty> ConstraintGroupBuilder<'ty> { )); } } + + // TokenAccount. + if let Some(token_mint) = &self.token_mint { + if self.token_authority.is_none() { + return Err(ParseError::new( + token_mint.span(), + "when initializing, token authority must be provided if token mint is", + )); + } + } + if let Some(token_authority) = &self.token_authority { + if self.token_mint.is_none() { + return Err(ParseError::new( + token_authority.span(), + "when initializing, token mint must be provided if token authority is", + )); + } + } + + // Mint. + if let Some(mint_decimals) = &self.mint_decimals { + if self.mint_authority.is_none() { + return Err(ParseError::new( + mint_decimals.span(), + "when initializing, mint authority must be provided if mint decimals is", + )); + } + } + if let Some(mint_authority) = &self.mint_authority { + if self.mint_decimals.is_none() { + return Err(ParseError::new( + mint_authority.span(), + "when initializing, mint decimals must be provided if mint authority is", + )); + } + } } // Zero. @@ -462,42 +498,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { } } - // Token. - if let Some(token_mint) = &self.token_mint { - if self.init.is_some() && self.token_authority.is_none() { - return Err(ParseError::new( - token_mint.span(), - "token authority must be provided if token mint is when initialize", - )); - } - } - if let Some(token_authority) = &self.token_authority { - if self.init.is_some() && self.token_mint.is_none() { - return Err(ParseError::new( - token_authority.span(), - "token mint must be provided if token authority is when initialize", - )); - } - } - - // Mint. - if let Some(mint_decimals) = &self.mint_decimals { - if self.init.is_some() && self.mint_authority.is_none() { - return Err(ParseError::new( - mint_decimals.span(), - "mint authority must be provided if mint decimals is when initialize", - )); - } - } - if let Some(mint_authority) = &self.mint_authority { - if self.init.is_some() && self.mint_decimals.is_none() { - return Err(ParseError::new( - mint_authority.span(), - "mint decimals must be provided if mint authority is when initialize", - )); - } - } - // Space. if let Some(i) = &self.init { let initializing_token_program_acc = self.token_mint.is_some() @@ -593,26 +593,26 @@ impl<'ty> ConstraintGroupBuilder<'ty> { } _ => None, }; - let spl_account = match (&token_mint, &token_authority) { + let token_account = match (&token_mint, &token_authority) { (None, None) => None, - _ => Some(ConstraintSplTokenAccount { + _ => Some(ConstraintTokenAccountGroup { mint: token_mint.as_ref().map(|a| a.clone().into_inner().mint), - auth: token_authority + authority: token_authority .as_ref() .map(|a| a.clone().into_inner().auth), }), }; - let spl_mint = match (&mint_decimals, &mint_authority, &mint_freeze_authority) { + let mint = match (&mint_decimals, &mint_authority, &mint_freeze_authority) { (None, None, None) => None, - _ => Some(ConstraintSplTokenMint { + _ => Some(ConstraintTokenMintGroup { decimals: mint_decimals .as_ref() .map(|a| a.clone().into_inner().decimals), - mint_auth: mint_authority + mint_authority: mint_authority .as_ref() .map(|a| a.clone().into_inner().mint_auth), - mint_freeze_auth: mint_freeze_authority + mint_freeze_authority: mint_freeze_authority .as_ref() .map(|a| a.clone().into_inner().mint_freeze_auth), }), @@ -672,8 +672,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> { address: into_inner!(address), associated_token: if !is_init { associated_token } else { None }, seeds, - spl_account: if !is_init {spl_account} else {None}, - spl_mint: if !is_init {spl_mint} else {None}, + token_account: if !is_init {token_account} else {None}, + mint: if !is_init {mint} else {None}, }) } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 751d7e638f..4ca0332453 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -487,9 +487,9 @@ pub struct TestConstraintToken<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, } #[derive(Accounts)] @@ -502,10 +502,11 @@ pub struct TestAuthorityConstraint<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, + /// CHECK: pub fake_authority: AccountInfo<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestOnlyAuthorityConstraint<'info> { @@ -516,9 +517,9 @@ pub struct TestOnlyAuthorityConstraint<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestOnlyMintConstraint<'info> { @@ -529,41 +530,43 @@ pub struct TestOnlyMintConstraint<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, } #[derive(Accounts)] -#[instruction(_decimals: u8)] +#[instruction(decimals: u8)] pub struct TestMintConstraint<'info> { #[account( - mint::decimals = _decimals, + mint::decimals = decimals, mint::authority = mint_authority, mint::freeze_authority = freeze_authority )] pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, + /// CHECK: pub mint_authority: AccountInfo<'info>, + /// CHECK: pub freeze_authority: AccountInfo<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } #[derive(Accounts)] -#[instruction(_decimals: u8)] +#[instruction(decimals: u8)] pub struct TestMintOnlyDecimalsConstraint<'info> { #[account( - mint::decimals = _decimals, + mint::decimals = decimals, )] pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, } #[derive(Accounts)] @@ -575,11 +578,13 @@ pub struct TestMintAuthorityConstraint<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, + /// CHECK: pub mint_authority: AccountInfo<'info>, + /// CHECK: pub freeze_authority: AccountInfo<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } #[derive(Accounts)] @@ -590,26 +595,28 @@ pub struct TestMintOneAuthorityConstraint<'info> { pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, + /// CHECK: pub mint_authority: AccountInfo<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } #[derive(Accounts)] -#[instruction(_decimals: u8)] +#[instruction(decimals: u8)] pub struct TestMintMissMintAuthConstraint<'info> { #[account( - mint::decimals = _decimals, + mint::decimals = decimals, mint::freeze_authority = freeze_authority, )] pub mint: Account<'info, Mint>, #[account(mut)] pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, + /// CHECK: pub freeze_authority: AccountInfo<'info>, + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } #[derive(Accounts)] @@ -621,9 +628,10 @@ pub struct TestAssociatedToken<'info> { pub token: Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, + /// CHECK: + pub authority: AccountInfo<'info>, pub token_program: Program<'info, Token>, pub associated_token_program: Program<'info, AssociatedToken>, - pub authority: AccountInfo<'info>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index 36f1fe35c1..912e221c45 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -1791,8 +1791,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - assert.equal(err.code, 2014); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); } }); @@ -1834,9 +1836,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - console.log("err", err); - assert.equal(err.code, 2015); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); } }); @@ -1889,8 +1892,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - assert.equal(err.code, 2015); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); } }); @@ -1906,7 +1911,6 @@ describe("misc", () => { }, signers: [mint], }); - // right await program.rpc.testMintConstraint(6, { accounts: { mint: mint.publicKey, @@ -1960,8 +1964,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - assert.equal(err.code, 2018); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); } }); @@ -1978,7 +1984,6 @@ describe("misc", () => { signers: [mint], }); - // mintAuthority mismatch const fakeAuthority = Keypair.generate(); try { await program.rpc.testMintConstraint(6, { @@ -1993,8 +1998,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - assert.equal(err.code, 2016); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); } }); @@ -2012,7 +2019,6 @@ describe("misc", () => { }); const fakeAuthority = Keypair.generate(); - // freezeAuthority mismatch try { await program.rpc.testMintConstraint(6, { accounts: { @@ -2026,8 +2032,10 @@ describe("misc", () => { }, }); assert.ok(false); - } catch (err) { - assert.equal(err.code, 2017); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); } }); @@ -2044,7 +2052,6 @@ describe("misc", () => { signers: [mint], }); - // right await program.rpc.testMintOnlyDecimalsConstraint(6, { accounts: { mint: mint.publicKey, @@ -2077,7 +2084,6 @@ describe("misc", () => { signers: [mint], }); - // right await program.rpc.testMintOnlyAuthConstraint({ accounts: { mint: mint.publicKey, @@ -2117,7 +2123,6 @@ describe("misc", () => { signers: [mint], }); - // right await program.rpc.testMintOnlyOneAuthConstraint({ accounts: { mint: mint.publicKey, @@ -2153,7 +2158,6 @@ describe("misc", () => { signers: [mint], }); - // right await program.rpc.testMintMissMintAuthConstraint(6, { accounts: { mint: mint.publicKey, From b2916bb797487227f82087d99af06403521f3d5b Mon Sep 17 00:00:00 2001 From: blockchainlover2019 Date: Fri, 1 Apr 2022 06:35:04 +0200 Subject: [PATCH 3/9] Make improvments --- lang/syn/src/codegen/accounts/constraints.rs | 75 ++++++++++---------- lang/syn/src/lib.rs | 2 +- lang/syn/src/parser/accounts/constraints.rs | 44 +++++++++++- tests/misc/programs/misc/src/context.rs | 5 +- tests/misc/tests/misc.ts | 38 +++++----- 5 files changed, 100 insertions(+), 64 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 0104afccc5..bceb97421d 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -697,58 +697,55 @@ fn generate_constraint_token_account( c: &ConstraintTokenAccountGroup, ) -> proc_macro2::TokenStream { let name = &f.ident; - let account_owner = match &c.authority { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, - None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + let authority_check = match &c.authority { + Some(authority) => { + quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } } + } + None => quote! {}, }; - - let spl_token_mint = match &c.mint { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, - None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + let mint_check = match &c.mint { + Some(mint) => { + quote! { if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } } + } + None => quote! {}, }; - quote! { - if #account_owner.is_some() && #name.owner != *#account_owner.unwrap() { - return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); - } - if #spl_token_mint.is_some() && #name.mint != *#spl_token_mint.unwrap() { - return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); - } + #authority_check + #mint_check } } fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream { let name = &f.ident; - let decimals = match &c.decimals { - Some(fa) => quote! { Option::::Some(#fa) }, - None => quote! { Option::::None }, + let decimal_check = match &c.decimals { + Some(decimals) => quote! { + if #name.decimals != #decimals { + return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into()); + } + }, + None => quote! {}, }; - let mint_authority = match &c.mint_authority { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, - None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + let mint_authority_check = match &c.mint_authority { + Some(mint_authority) => quote! { + if #name.mint_authority.is_none() || #name.mint_authority.unwrap() != #mint_authority.key() { + return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); + } + }, + None => quote! {}, }; - let freeze_authority = match &c.mint_freeze_authority { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, - None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, + let freeze_authority_check = match &c.freeze_authority { + Some(freeze_authority) => quote! { + if #name.freeze_authority.is_none() || #name.freeze_authority.unwrap() != #freeze_authority.key() { + return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); + } + }, + None => quote! {}, }; - quote! { - if #decimals.is_some() && #name.decimals != #decimals.unwrap() { - return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into()); - } - if #mint_authority - .as_ref() - .map(|fa| #name.mint_authority.as_ref().map(|expected_fa| *fa != expected_fa).unwrap_or(true)) - .unwrap_or(false) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); - } - if #freeze_authority - .as_ref() - .map(|fa| #name.freeze_authority.as_ref().map(|expected_fa| *fa != expected_fa).unwrap_or(true)) - .unwrap_or(false) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); - } + #decimal_check + #mint_authority_check + #freeze_authority_check } } diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 48ba22981f..ac291b6bb9 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -846,7 +846,7 @@ pub struct ConstraintTokenAccountGroup { pub struct ConstraintTokenMintGroup { pub decimals: Option, pub mint_authority: Option, - pub mint_freeze_authority: Option, + pub freeze_authority: Option, } // Syntaxt context object for preserving metadata about the inner item. diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index 468db4365d..98e11331d0 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -612,7 +612,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { mint_authority: mint_authority .as_ref() .map(|a| a.clone().into_inner().mint_auth), - mint_freeze_authority: mint_freeze_authority + freeze_authority: mint_freeze_authority .as_ref() .map(|a| a.clone().into_inner().mint_freeze_auth), }), @@ -714,6 +714,48 @@ impl<'ty> ConstraintGroupBuilder<'ty> { if self.zeroed.is_some() { return Err(ParseError::new(c.span(), "zeroed already provided")); } + if self.token_mint.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before token mint", + )); + } + if self.associated_token_mint.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before associated token mint", + )); + } + if self.token_authority.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before token authority", + )); + } + if self.associated_token_authority.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before associated token authority", + )); + } + if self.mint_authority.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before mint authority", + )); + } + if self.mint_freeze_authority.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before mint freeze authority", + )); + } + if self.mint_decimals.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before mint decimals", + )); + } self.init.replace(c); Ok(()) } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 4ca0332453..878ebe4d5d 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -16,7 +16,6 @@ pub struct TestTokenSeedsInit<'info> { payer = authority, mint::decimals = 6, mint::authority = authority, - )] pub mint: Account<'info, Mint>, #[account( @@ -26,7 +25,6 @@ pub struct TestTokenSeedsInit<'info> { payer = authority, token::mint = mint, token::authority = authority, - )] pub my_pda: Account<'info, TokenAccount>, #[account(mut)] @@ -41,10 +39,9 @@ pub struct TestTokenSeedsInit<'info> { pub struct TestInitAssociatedToken<'info> { #[account( init, - payer = payer, associated_token::mint = mint, + payer = payer, associated_token::authority = payer, - )] pub token: Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index 912e221c45..311e5d2560 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -1648,8 +1648,8 @@ describe("misc", () => { program.provider.wallet.payer ); const account = await mintAccount.getAccountInfo(token.publicKey); - assert.ok(account.owner.equals(program.provider.wallet.publicKey)); - assert.ok(account.mint.equals(mint.publicKey)); + assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); }); it("Token Constraint Test(no init) - Can make only token::authority", async () => { @@ -1694,7 +1694,7 @@ describe("misc", () => { program.provider.wallet.payer ); const account = await mintAccount.getAccountInfo(token.publicKey); - assert.ok(account.owner.equals(program.provider.wallet.publicKey)); + assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); }); it("Token Constraint Test(no init) - Can make only token::mint", async () => { @@ -1739,7 +1739,7 @@ describe("misc", () => { program.provider.wallet.payer ); const account = await mintAccount.getAccountInfo(token.publicKey); - assert.ok(account.mint.equals(mint.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); }); it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { @@ -1790,7 +1790,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1835,7 +1835,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1891,7 +1891,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1929,11 +1929,11 @@ describe("misc", () => { program.provider.wallet.payer ); const mintAccount = await client.getMintInfo(); - assert.ok(mintAccount.decimals === 6); - assert.ok( + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) ); - assert.ok( + assert.isTrue( mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) ); }); @@ -1963,7 +1963,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1997,7 +1997,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -2031,7 +2031,7 @@ describe("misc", () => { rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - assert.ok(false); + assert.isTrue(false); } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -2068,7 +2068,7 @@ describe("misc", () => { program.provider.wallet.payer ); const mintAccount = await client.getMintInfo(); - assert.ok(mintAccount.decimals === 6); + assert.strictEqual(mintAccount.decimals, 6); }); it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { @@ -2102,10 +2102,10 @@ describe("misc", () => { program.provider.wallet.payer ); const mintAccount = await client.getMintInfo(); - assert.ok( + assert.isTrue( mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) ); - assert.ok( + assert.isTrue( mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) ); }); @@ -2140,7 +2140,7 @@ describe("misc", () => { program.provider.wallet.payer ); const mintAccount = await client.getMintInfo(); - assert.ok( + assert.isTrue( mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) ); }); @@ -2175,8 +2175,8 @@ describe("misc", () => { program.provider.wallet.payer ); const mintAccount = await client.getMintInfo(); - assert.ok(mintAccount.decimals === 6); - assert.ok( + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) ); }); From 26be25de5e31e6d57ca9ca6503d62fceada3b088 Mon Sep 17 00:00:00 2001 From: blockchainlover2019 Date: Thu, 7 Apr 2022 18:10:31 +0200 Subject: [PATCH 4/9] Add error code checks --- tests/misc/tests/misc.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index 311e5d2560..3246825e82 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -1795,6 +1795,7 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2014); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); } }); @@ -1840,6 +1841,7 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); } }); @@ -1896,6 +1898,7 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); } }); @@ -1968,6 +1971,7 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2018); + assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); } }); @@ -2002,6 +2006,10 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2016); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintMintAuthority" + ); } }); @@ -2036,6 +2044,10 @@ describe("misc", () => { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2017); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintFreezeAuthority" + ); } }); From 63ad92b31b44c5e582a43b154cf9e23e70ccde15 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Mon, 11 Apr 2022 13:55:42 -0400 Subject: [PATCH 5/9] lang/tests: review comments --- lang/syn/src/codegen/accounts/constraints.rs | 4 +-- lang/syn/src/parser/accounts/constraints.rs | 24 ++++++------- tests/misc/tests/misc.ts | 36 ++++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index bceb97421d..387c713dd3 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -728,7 +728,7 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac }; let mint_authority_check = match &c.mint_authority { Some(mint_authority) => quote! { - if #name.mint_authority.is_none() || #name.mint_authority.unwrap() != #mint_authority.key() { + if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) { return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); } }, @@ -736,7 +736,7 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac }; let freeze_authority_check = match &c.freeze_authority { Some(freeze_authority) => quote! { - if #name.freeze_authority.is_none() || #name.freeze_authority.unwrap() != #freeze_authority.key() { + if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) { return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); } }, diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index 98e11331d0..d6ce11b109 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -720,24 +720,12 @@ impl<'ty> ConstraintGroupBuilder<'ty> { "init must be provided before token mint", )); } - if self.associated_token_mint.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before associated token mint", - )); - } if self.token_authority.is_some() { return Err(ParseError::new( c.span(), "init must be provided before token authority", )); } - if self.associated_token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before associated token authority", - )); - } if self.mint_authority.is_some() { return Err(ParseError::new( c.span(), @@ -756,6 +744,18 @@ impl<'ty> ConstraintGroupBuilder<'ty> { "init must be provided before mint decimals", )); } + if self.associated_token_mint.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before associated token mint", + )); + } + if self.associated_token_authority.is_some() { + return Err(ParseError::new( + c.span(), + "init must be provided before associated token authority", + )); + } self.init.replace(c); Ok(()) } diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index 3246825e82..f4372e7c9a 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -14,7 +14,7 @@ import { import { Misc } from "../target/types/misc"; import { Misc2 } from "../target/types/misc2"; const utf8 = anchor.utils.bytes.utf8; -const { assert } = require("chai"); +const { assert, expect } = require("chai"); const nativeAssert = require("assert"); const miscIdl = require("../target/idl/misc.json"); @@ -232,7 +232,7 @@ describe("misc", () => { solDest: data.publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (err) { const errMsg = "A close constraint was violated"; assert.strictEqual(err.error.errorMessage, errMsg); @@ -266,7 +266,7 @@ describe("misc", () => { ).lamports; // Retrieved rent exemption sol. - assert.ok(afterBalance > beforeBalance); + expect(afterBalance > beforeBalance).to.be.true; const closedAccount = await program.provider.connection.getAccountInfo( data.publicKey @@ -989,7 +989,7 @@ describe("misc", () => { owner: anchor.web3.Keypair.generate().publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1028,7 +1028,7 @@ describe("misc", () => { owner: anchor.web3.Keypair.generate().publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1057,7 +1057,7 @@ describe("misc", () => { }, signers: [newAcc], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1091,7 +1091,7 @@ describe("misc", () => { }, signers: [mint], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1125,7 +1125,7 @@ describe("misc", () => { }, signers: [mint], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1159,7 +1159,7 @@ describe("misc", () => { }, signers: [mint], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1206,7 +1206,7 @@ describe("misc", () => { }, signers: [token], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1265,7 +1265,7 @@ describe("misc", () => { }, signers: [token], }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1318,7 +1318,7 @@ describe("misc", () => { authority: anchor.web3.Keypair.generate().publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1383,7 +1383,7 @@ describe("misc", () => { authority: program.provider.wallet.publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1449,7 +1449,7 @@ describe("misc", () => { authority: program.provider.wallet.publicKey, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1520,7 +1520,7 @@ describe("misc", () => { second: secondPDA, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1535,7 +1535,7 @@ describe("misc", () => { second: secondPDA, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1574,7 +1574,7 @@ describe("misc", () => { second: secondPDA, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; @@ -1589,7 +1589,7 @@ describe("misc", () => { second: secondPDA, }, }); - assert.ok(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; From 20651d4cff69238dd5eeade0ec1ae133b52d3582 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Mon, 11 Apr 2022 14:35:33 -0400 Subject: [PATCH 6/9] tests: adjust compute units --- tests/misc/tests/misc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts index f4372e7c9a..f23d621f4d 100644 --- a/tests/misc/tests/misc.ts +++ b/tests/misc/tests/misc.ts @@ -160,7 +160,7 @@ describe("misc", () => { "Program data: jvbowsvlmkcJAAAA", "Program data: zxM5neEnS1kBAgMEBQYHCAkK", "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", - "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh consumed 5395 of 1400000 compute units", + "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh consumed 4023 of 1400000 compute units", "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh success", ]; From 6d3887f5f6625455a17a752d2a1988bee5d89d61 Mon Sep 17 00:00:00 2001 From: Paul Schaaf Date: Mon, 11 Apr 2022 16:34:44 -0400 Subject: [PATCH 7/9] docs --- lang/derive/accounts/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs index 7cf5ee4f55..85f5505f66 100644 --- a/lang/derive/accounts/src/lib.rs +++ b/lang/derive/accounts/src/lib.rs @@ -436,8 +436,9 @@ use syn::parse_macro_input; /// #[account(token::mint = <target_account>, token::authority = <target_account>)] /// /// -/// Can currently only be used with init to create a token -/// account with the given mint address and authority. +/// Can be used as a check or with init to create a token +/// account with the given mint address and authority.
+/// When used as a check, it's possible to only specify a subset of the constraints. ///

/// Example: ///
@@ -466,9 +467,10 @@ use syn::parse_macro_input;
 ///                 #[account(mint::authority = <target_account>, mint::decimals = <expr>, mint::freeze_authority = <target_account>)]
 ///             
 ///             
-///                 Can currently only be used with init to create a mint
+///                 Can be used as a check or with init to create a mint
 ///                 account with the given mint decimals and mint authority.
-/// The freeze authority is optional. +/// The freeze authority is optional when used with init.
+/// When used as a check, it's possible to only specify a subset of the constraints. ///

/// Example: ///

From a0abcd7b1f880552a84238fb6a29afe8e0f996c1 Mon Sep 17 00:00:00 2001
From: Paul Schaaf 
Date: Mon, 11 Apr 2022 16:34:53 -0400
Subject: [PATCH 8/9] tests: remove unnecessary accounts

---
 tests/misc/programs/misc/src/context.rs | 56 -------------------------
 tests/misc/tests/misc.ts                | 55 ------------------------
 2 files changed, 111 deletions(-)

diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs
index 878ebe4d5d..691109393d 100644
--- a/tests/misc/programs/misc/src/context.rs
+++ b/tests/misc/programs/misc/src/context.rs
@@ -482,11 +482,7 @@ pub struct TestConstraintToken<'info> {
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
     pub payer: Signer<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -497,13 +493,7 @@ pub struct TestAuthorityConstraint<'info> {
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub fake_authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 #[derive(Accounts)]
 pub struct TestOnlyAuthorityConstraint<'info> {
@@ -512,11 +502,7 @@ pub struct TestOnlyAuthorityConstraint<'info> {
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
     pub payer: Signer<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 #[derive(Accounts)]
 pub struct TestOnlyMintConstraint<'info> {
@@ -525,11 +511,6 @@ pub struct TestOnlyMintConstraint<'info> {
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -541,15 +522,8 @@ pub struct TestMintConstraint<'info> {
         mint::freeze_authority = freeze_authority
     )]
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub mint_authority: AccountInfo<'info>,
-    /// CHECK:
     pub freeze_authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -559,11 +533,6 @@ pub struct TestMintOnlyDecimalsConstraint<'info> {
         mint::decimals = decimals,
     )]
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -573,15 +542,8 @@ pub struct TestMintAuthorityConstraint<'info> {
         mint::freeze_authority = freeze_authority
     )]
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub mint_authority: AccountInfo<'info>,
-    /// CHECK:
     pub freeze_authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -590,13 +552,7 @@ pub struct TestMintOneAuthorityConstraint<'info> {
         mint::authority = mint_authority,
     )]
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub mint_authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -607,13 +563,7 @@ pub struct TestMintMissMintAuthConstraint<'info> {
         mint::freeze_authority = freeze_authority,
     )]
     pub mint: Account<'info, Mint>,
-    #[account(mut)]
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub freeze_authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -624,11 +574,5 @@ pub struct TestAssociatedToken<'info> {
     )]
     pub token: Account<'info, TokenAccount>,
     pub mint: Account<'info, Mint>,
-    pub payer: Signer<'info>,
-    /// CHECK:
     pub authority: AccountInfo<'info>,
-    pub token_program: Program<'info, Token>,
-    pub associated_token_program: Program<'info, AssociatedToken>,
-    pub system_program: Program<'info, System>,
-    pub rent: Sysvar<'info, Rent>,
 }
diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts
index f23d621f4d..aab1c4fb80 100644
--- a/tests/misc/tests/misc.ts
+++ b/tests/misc/tests/misc.ts
@@ -1463,7 +1463,6 @@ describe("misc", () => {
     await program.rpc.testMultidimensionalArray(array2d, {
       accounts: {
         data: data.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
       },
       signers: [data],
       instructions: [
@@ -1482,7 +1481,6 @@ describe("misc", () => {
     await program.rpc.testMultidimensionalArrayConstSizes(array2d, {
       accounts: {
         data: data.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
       },
       signers: [data],
       instructions: [
@@ -1636,9 +1634,6 @@ describe("misc", () => {
           token: token.publicKey,
           mint: mint.publicKey,
           payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         },
       });
       const mintAccount = new Token(
@@ -1682,9 +1677,6 @@ describe("misc", () => {
           token: token.publicKey,
           mint: mint.publicKey,
           payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         },
       });
       const mintAccount = new Token(
@@ -1726,10 +1718,6 @@ describe("misc", () => {
         accounts: {
           token: token.publicKey,
           mint: mint.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         },
       });
       const mintAccount = new Token(
@@ -1785,9 +1773,6 @@ describe("misc", () => {
             token: token.publicKey,
             mint: mint1.publicKey,
             payer: program.provider.wallet.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -1829,11 +1814,7 @@ describe("misc", () => {
           accounts: {
             token: token.publicKey,
             mint: mint.publicKey,
-            payer: program.provider.wallet.publicKey,
             fakeAuthority: fakeAuthority.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -1886,11 +1867,7 @@ describe("misc", () => {
           accounts: {
             token: token.publicKey,
             mint: mint1.publicKey,
-            payer: program.provider.wallet.publicKey,
             fakeAuthority: fakeAuthority.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -1919,10 +1896,6 @@ describe("misc", () => {
           mint: mint.publicKey,
           mintAuthority: program.provider.wallet.publicKey,
           freezeAuthority: program.provider.wallet.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         },
       });
       const client = new Token(
@@ -1960,10 +1933,6 @@ describe("misc", () => {
             mint: mint.publicKey,
             mintAuthority: program.provider.wallet.publicKey,
             freezeAuthority: program.provider.wallet.publicKey,
-            payer: program.provider.wallet.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -1995,10 +1964,6 @@ describe("misc", () => {
             mint: mint.publicKey,
             mintAuthority: fakeAuthority.publicKey,
             freezeAuthority: program.provider.wallet.publicKey,
-            payer: program.provider.wallet.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -2033,10 +1998,6 @@ describe("misc", () => {
             mint: mint.publicKey,
             mintAuthority: program.provider.wallet.publicKey,
             freezeAuthority: fakeAuthority.publicKey,
-            payer: program.provider.wallet.publicKey,
-            systemProgram: anchor.web3.SystemProgram.programId,
-            tokenProgram: TOKEN_PROGRAM_ID,
-            rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           },
         });
         assert.isTrue(false);
@@ -2067,10 +2028,6 @@ describe("misc", () => {
       await program.rpc.testMintOnlyDecimalsConstraint(6, {
         accounts: {
           mint: mint.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         },
       });
       const client = new Token(
@@ -2099,10 +2056,6 @@ describe("misc", () => {
       await program.rpc.testMintOnlyAuthConstraint({
         accounts: {
           mint: mint.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           mintAuthority: program.provider.wallet.publicKey,
           freezeAuthority: program.provider.wallet.publicKey,
         },
@@ -2138,10 +2091,6 @@ describe("misc", () => {
       await program.rpc.testMintOnlyOneAuthConstraint({
         accounts: {
           mint: mint.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           mintAuthority: program.provider.wallet.publicKey,
         },
       });
@@ -2173,10 +2122,6 @@ describe("misc", () => {
       await program.rpc.testMintMissMintAuthConstraint(6, {
         accounts: {
           mint: mint.publicKey,
-          payer: program.provider.wallet.publicKey,
-          systemProgram: anchor.web3.SystemProgram.programId,
-          tokenProgram: TOKEN_PROGRAM_ID,
-          rent: anchor.web3.SYSVAR_RENT_PUBKEY,
           freezeAuthority: program.provider.wallet.publicKey,
         },
       });

From d0be41b59d7dd6b7dd16d61865a6b6f91b707da3 Mon Sep 17 00:00:00 2001
From: Paul Schaaf 
Date: Mon, 11 Apr 2022 16:50:45 -0400
Subject: [PATCH 9/9] tests: fix misc

---
 tests/misc/tests/misc.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/misc/tests/misc.ts b/tests/misc/tests/misc.ts
index c766e7a4a3..cc28474acc 100644
--- a/tests/misc/tests/misc.ts
+++ b/tests/misc/tests/misc.ts
@@ -160,7 +160,7 @@ describe("misc", () => {
       "Program data: jvbowsvlmkcJAAAA",
       "Program data: zxM5neEnS1kBAgMEBQYHCAkK",
       "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==",
-      "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh consumed 4023 of 1400000 compute units",
+      "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh consumed 3983 of 1400000 compute units",
       "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh success",
     ];