Skip to content

Commit f5dffe6

Browse files
lang: allow token::... and mint::... to be used as checks without init (#1505)
Co-authored-by: Paul Schaaf <[email protected]>
1 parent 537d470 commit f5dffe6

File tree

7 files changed

+903
-102
lines changed

7 files changed

+903
-102
lines changed

lang/derive/accounts/src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,9 @@ use syn::parse_macro_input;
436436
/// <code>#[account(token::mint = &lt;target_account&gt;, token::authority = &lt;target_account&gt;)]</code>
437437
/// </td>
438438
/// <td>
439-
/// Can currently only be used with <code>init</code> to create a token
440-
/// account with the given mint address and authority.
439+
/// Can be used as a check or with <code>init</code> to create a token
440+
/// account with the given mint address and authority.<br>
441+
/// When used as a check, it's possible to only specify a subset of the constraints.
441442
/// <br><br>
442443
/// Example:
443444
/// <pre>
@@ -466,9 +467,10 @@ use syn::parse_macro_input;
466467
/// <code>#[account(mint::authority = &lt;target_account&gt;, mint::decimals = &lt;expr&gt;, mint::freeze_authority = &lt;target_account&gt;)]</code>
467468
/// </td>
468469
/// <td>
469-
/// Can currently only be used with <code>init</code> to create a mint
470+
/// Can be used as a check or with <code>init</code> to create a mint
470471
/// account with the given mint decimals and mint authority.<br>
471-
/// The freeze authority is optional.
472+
/// The freeze authority is optional when used with <code>init</code>.<br>
473+
/// When used as a check, it's possible to only specify a subset of the constraints.
472474
/// <br><br>
473475
/// Example:
474476
/// <pre>

lang/syn/src/codegen/accounts/constraints.rs

+67
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
5757
close,
5858
address,
5959
associated_token,
60+
token_account,
61+
mint,
6062
} = c_group.clone();
6163

6264
let mut constraints = Vec::new();
@@ -100,6 +102,12 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
100102
if let Some(c) = address {
101103
constraints.push(Constraint::Address(c));
102104
}
105+
if let Some(c) = token_account {
106+
constraints.push(Constraint::TokenAccount(c));
107+
}
108+
if let Some(c) = mint {
109+
constraints.push(Constraint::Mint(c));
110+
}
103111
constraints
104112
}
105113

@@ -120,6 +128,8 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
120128
Constraint::Close(c) => generate_constraint_close(f, c),
121129
Constraint::Address(c) => generate_constraint_address(f, c),
122130
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
131+
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c),
132+
Constraint::Mint(c) => generate_constraint_mint(f, c),
123133
}
124134
}
125135

@@ -682,6 +692,63 @@ fn generate_constraint_associated_token(
682692
}
683693
}
684694

695+
fn generate_constraint_token_account(
696+
f: &Field,
697+
c: &ConstraintTokenAccountGroup,
698+
) -> proc_macro2::TokenStream {
699+
let name = &f.ident;
700+
let authority_check = match &c.authority {
701+
Some(authority) => {
702+
quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } }
703+
}
704+
None => quote! {},
705+
};
706+
let mint_check = match &c.mint {
707+
Some(mint) => {
708+
quote! { if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } }
709+
}
710+
None => quote! {},
711+
};
712+
quote! {
713+
#authority_check
714+
#mint_check
715+
}
716+
}
717+
718+
fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream {
719+
let name = &f.ident;
720+
721+
let decimal_check = match &c.decimals {
722+
Some(decimals) => quote! {
723+
if #name.decimals != #decimals {
724+
return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into());
725+
}
726+
},
727+
None => quote! {},
728+
};
729+
let mint_authority_check = match &c.mint_authority {
730+
Some(mint_authority) => quote! {
731+
if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) {
732+
return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
733+
}
734+
},
735+
None => quote! {},
736+
};
737+
let freeze_authority_check = match &c.freeze_authority {
738+
Some(freeze_authority) => quote! {
739+
if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) {
740+
return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
741+
}
742+
},
743+
None => quote! {},
744+
};
745+
quote! {
746+
#decimal_check
747+
#mint_authority_check
748+
#freeze_authority_check
749+
}
750+
}
751+
685752
// Generated code to create an account with with system program with the
686753
// given `space` amount of data, owned by `owner`.
687754
//

lang/syn/src/lib.rs

+17
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ pub struct ConstraintGroup {
587587
close: Option<ConstraintClose>,
588588
address: Option<ConstraintAddress>,
589589
associated_token: Option<ConstraintAssociatedToken>,
590+
token_account: Option<ConstraintTokenAccountGroup>,
591+
mint: Option<ConstraintTokenMintGroup>,
590592
}
591593

592594
impl ConstraintGroup {
@@ -628,6 +630,8 @@ pub enum Constraint {
628630
State(ConstraintState),
629631
Close(ConstraintClose),
630632
Address(ConstraintAddress),
633+
TokenAccount(ConstraintTokenAccountGroup),
634+
Mint(ConstraintTokenMintGroup),
631635
}
632636

633637
// Constraint token is a single keyword in a `#[account(<TOKEN>)]` attribute.
@@ -832,6 +836,19 @@ pub struct ConstraintAssociatedToken {
832836
pub mint: Expr,
833837
}
834838

839+
#[derive(Debug, Clone)]
840+
pub struct ConstraintTokenAccountGroup {
841+
pub mint: Option<Expr>,
842+
pub authority: Option<Expr>,
843+
}
844+
845+
#[derive(Debug, Clone)]
846+
pub struct ConstraintTokenMintGroup {
847+
pub decimals: Option<Expr>,
848+
pub mint_authority: Option<Expr>,
849+
pub freeze_authority: Option<Expr>,
850+
}
851+
835852
// Syntaxt context object for preserving metadata about the inner item.
836853
#[derive(Debug, Clone)]
837854
pub struct Context<T> {

0 commit comments

Comments
 (0)