diff --git a/CHANGELOG.md b/CHANGELOG.md index f45a33d948..207051d38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ incremented for features. * cli: `target/types` directory now created on build to store a TypeScript types file for each program's IDL ([#795](https://github.com/project-serum/anchor/pull/795)). * ts: `Program` can now be typed with an IDL type ([#795](https://github.com/project-serum/anchor/pull/795)). +* lang: Add `mint::freeze_authority` keyword for mint initialization within `#[derive(Accounts)]` ([#835](https://github.com/project-serum/anchor/pull/835)). ## [0.17.0] - 2021-10-03 diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 91568fb08c..801bb8edb2 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -433,13 +433,21 @@ pub fn generate_init( }; } } - InitKind::Mint { owner, decimals } => { + InitKind::Mint { + owner, + decimals, + freeze_authority, + } => { let create_account = generate_create_account( field, quote! {anchor_spl::token::Mint::LEN}, quote! {token_program.to_account_info().key}, seeds_with_nonce, ); + let freeze_authority = match freeze_authority { + Some(fa) => quote! { Some(&#fa.key()) }, + None => quote! { None }, + }; quote! { let #field: #ty_decl = { // Define payer variable. @@ -455,7 +463,7 @@ pub fn generate_init( rent: rent.to_account_info(), }; let cpi_ctx = CpiContext::new(cpi_program, accounts); - anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.to_account_info().key, None)?; + anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.to_account_info().key, #freeze_authority)?; let pa: #ty_decl = #from_account_info; pa }; diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 2b7f3f1da0..d4a57c84d3 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -564,6 +564,7 @@ pub enum ConstraintToken { AssociatedTokenMint(Context), AssociatedTokenAuthority(Context), MintAuthority(Context), + MintFreezeAuthority(Context), MintDecimals(Context), Bump(Context), } @@ -658,12 +659,24 @@ pub struct ConstraintSpace { #[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] pub enum InitKind { - Program { owner: Option }, + Program { + owner: Option, + }, // Owner for token and mint represents the authority. Not to be confused // with the owner of the AccountInfo. - Token { owner: Expr, mint: Expr }, - AssociatedToken { owner: Expr, mint: Expr }, - Mint { owner: Expr, decimals: Expr }, + Token { + owner: Expr, + mint: Expr, + }, + AssociatedToken { + owner: Expr, + mint: Expr, + }, + Mint { + owner: Expr, + freeze_authority: Option, + decimals: Expr, + }, } #[derive(Debug, Clone)] @@ -686,6 +699,11 @@ pub struct ConstraintMintAuthority { mint_auth: Expr, } +#[derive(Debug, Clone)] +pub struct ConstraintMintFreezeAuthority { + mint_freeze_auth: Expr, +} + #[derive(Debug, Clone)] pub struct ConstraintMintDecimals { decimals: Expr, diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index 31488f6870..cfb37e79ad 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -85,6 +85,12 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { mint_auth: stream.parse()?, }, )), + "freeze_authority" => ConstraintToken::MintFreezeAuthority(Context::new( + span, + ConstraintMintFreezeAuthority { + mint_freeze_auth: stream.parse()?, + }, + )), "decimals" => ConstraintToken::MintDecimals(Context::new( span, ConstraintMintDecimals { @@ -276,6 +282,7 @@ pub struct ConstraintGroupBuilder<'ty> { pub associated_token_mint: Option>, pub associated_token_authority: Option>, pub mint_authority: Option>, + pub mint_freeze_authority: Option>, pub mint_decimals: Option>, pub bump: Option>, } @@ -305,6 +312,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { associated_token_mint: None, associated_token_authority: None, mint_authority: None, + mint_freeze_authority: None, mint_decimals: None, bump: None, } @@ -460,6 +468,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { associated_token_mint, associated_token_authority, mint_authority, + mint_freeze_authority, mint_decimals, bump, } = self; @@ -523,7 +532,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> { d.span(), "authority must be provided to initialize a mint program derived address" )) - } + }, + freeze_authority: mint_freeze_authority.map(|fa| fa.into_inner().mint_freeze_auth) } } else { InitKind::Program { @@ -570,6 +580,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> { ConstraintToken::AssociatedTokenAuthority(c) => self.add_associated_token_authority(c), ConstraintToken::AssociatedTokenMint(c) => self.add_associated_token_mint(c), ConstraintToken::MintAuthority(c) => self.add_mint_authority(c), + ConstraintToken::MintFreezeAuthority(c) => self.add_mint_freeze_authority(c), ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), ConstraintToken::Bump(c) => self.add_bump(c), } @@ -739,6 +750,26 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } + fn add_mint_freeze_authority( + &mut self, + c: Context, + ) -> ParseResult<()> { + if self.mint_freeze_authority.is_some() { + return Err(ParseError::new( + c.span(), + "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(()) + } + fn add_mint_decimals(&mut self, c: Context) -> ParseResult<()> { if self.mint_decimals.is_some() { return Err(ParseError::new(c.span(), "mint decimals already provided")); diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 62ed0ecb02..a095fdfa22 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -183,7 +183,7 @@ pub struct TestInitZeroCopy<'info> { #[derive(Accounts)] pub struct TestInitMint<'info> { - #[account(init, mint::decimals = 6, mint::authority = payer, payer = payer)] + #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer)] pub mint: Account<'info, Mint>, #[account(signer)] pub payer: AccountInfo<'info>, @@ -218,4 +218,4 @@ pub struct TestFetchAll<'info> { pub data: Account<'info, DataWithFilter>, pub authority: Signer<'info>, pub system_program: Program<'info, System>, -} \ No newline at end of file +} diff --git a/tests/misc/tests/misc.js b/tests/misc/tests/misc.js index 5b2632bd1e..49ca1f7acf 100644 --- a/tests/misc/tests/misc.js +++ b/tests/misc/tests/misc.js @@ -455,6 +455,9 @@ describe("misc", () => { assert.ok( mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) ); + assert.ok( + mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) + ); }); it("Can create a random mint account prefunded", async () => {