@@ -121,6 +121,33 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
121121 _ => return Err ( ParseError :: new ( ident. span ( ) , "Invalid attribute" ) ) ,
122122 }
123123 }
124+ "associated_token" => {
125+ stream. parse :: < Token ! [ : ] > ( ) ?;
126+ stream. parse :: < Token ! [ : ] > ( ) ?;
127+ let kw = stream. call ( Ident :: parse_any) ?. to_string ( ) ;
128+ stream. parse :: < Token ! [ =] > ( ) ?;
129+
130+ let span = ident
131+ . span ( )
132+ . join ( stream. span ( ) )
133+ . unwrap_or_else ( || ident. span ( ) ) ;
134+
135+ match kw. as_str ( ) {
136+ "mint" => ConstraintToken :: AssociatedTokenMint ( Context :: new (
137+ span,
138+ ConstraintTokenMint {
139+ mint : stream. parse ( ) ?,
140+ } ,
141+ ) ) ,
142+ "authority" => ConstraintToken :: AssociatedTokenAuthority ( Context :: new (
143+ span,
144+ ConstraintTokenAuthority {
145+ auth : stream. parse ( ) ?,
146+ } ,
147+ ) ) ,
148+ _ => return Err ( ParseError :: new ( ident. span ( ) , "Invalid attribute" ) ) ,
149+ }
150+ }
124151 "bump" => {
125152 let bump = {
126153 if stream. peek ( Token ! [ =] ) {
@@ -246,6 +273,8 @@ pub struct ConstraintGroupBuilder<'ty> {
246273 pub address : Option < Context < ConstraintAddress > > ,
247274 pub token_mint : Option < Context < ConstraintTokenMint > > ,
248275 pub token_authority : Option < Context < ConstraintTokenAuthority > > ,
276+ pub associated_token_mint : Option < Context < ConstraintTokenMint > > ,
277+ pub associated_token_authority : Option < Context < ConstraintTokenAuthority > > ,
249278 pub mint_authority : Option < Context < ConstraintMintAuthority > > ,
250279 pub mint_decimals : Option < Context < ConstraintMintDecimals > > ,
251280 pub bump : Option < Context < ConstraintTokenBump > > ,
@@ -273,6 +302,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
273302 address : None ,
274303 token_mint : None ,
275304 token_authority : None ,
305+ associated_token_mint : None ,
306+ associated_token_authority : None ,
276307 mint_authority : None ,
277308 mint_decimals : None ,
278309 bump : None ,
@@ -307,7 +338,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
307338 // When initializing a non-PDA account, the account being
308339 // initialized must sign to invoke the system program's create
309340 // account instruction.
310- if self . signer . is_none ( ) && self . seeds . is_none ( ) {
341+ if self . signer . is_none ( ) && self . seeds . is_none ( ) && self . associated_token_mint . is_none ( )
342+ {
311343 self . signer
312344 . replace ( Context :: new ( i. span ( ) , ConstraintSigner { } ) ) ;
313345 }
@@ -425,6 +457,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
425457 address,
426458 token_mint,
427459 token_authority,
460+ associated_token_mint,
461+ associated_token_authority,
428462 mint_authority,
429463 mint_decimals,
430464 bump,
@@ -469,6 +503,17 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
469503 ) ) ,
470504 } ,
471505 }
506+ } else if let Some ( tm) = & associated_token_mint {
507+ InitKind :: AssociatedToken {
508+ mint : tm. clone ( ) . into_inner ( ) . mint ,
509+ owner : match & associated_token_authority {
510+ Some ( a) => a. clone ( ) . into_inner ( ) . auth ,
511+ None => return Err ( ParseError :: new (
512+ tm. span ( ) ,
513+ "authority must be provided to initialize a token program derived address"
514+ ) ) ,
515+ } ,
516+ }
472517 } else if let Some ( d) = & mint_decimals {
473518 InitKind :: Mint {
474519 decimals : d. clone ( ) . into_inner ( ) . decimals ,
@@ -522,6 +567,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
522567 ConstraintToken :: Address ( c) => self . add_address ( c) ,
523568 ConstraintToken :: TokenAuthority ( c) => self . add_token_authority ( c) ,
524569 ConstraintToken :: TokenMint ( c) => self . add_token_mint ( c) ,
570+ ConstraintToken :: AssociatedTokenAuthority ( c) => self . add_associated_token_authority ( c) ,
571+ ConstraintToken :: AssociatedTokenMint ( c) => self . add_associated_token_mint ( c) ,
525572 ConstraintToken :: MintAuthority ( c) => self . add_mint_authority ( c) ,
526573 ConstraintToken :: MintDecimals ( c) => self . add_mint_decimals ( c) ,
527574 ConstraintToken :: Bump ( c) => self . add_bump ( c) ,
@@ -585,6 +632,12 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
585632 if self . token_mint . is_some ( ) {
586633 return Err ( ParseError :: new ( c. span ( ) , "token mint already provided" ) ) ;
587634 }
635+ if self . associated_token_mint . is_some ( ) {
636+ return Err ( ParseError :: new (
637+ c. span ( ) ,
638+ "associated token mint already provided" ,
639+ ) ) ;
640+ }
588641 if self . init . is_none ( ) {
589642 return Err ( ParseError :: new (
590643 c. span ( ) ,
@@ -595,6 +648,26 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
595648 Ok ( ( ) )
596649 }
597650
651+ fn add_associated_token_mint ( & mut self , c : Context < ConstraintTokenMint > ) -> ParseResult < ( ) > {
652+ if self . associated_token_mint . is_some ( ) {
653+ return Err ( ParseError :: new (
654+ c. span ( ) ,
655+ "associated token mint already provided" ,
656+ ) ) ;
657+ }
658+ if self . token_mint . is_some ( ) {
659+ return Err ( ParseError :: new ( c. span ( ) , "token mint already provided" ) ) ;
660+ }
661+ if self . init . is_none ( ) {
662+ return Err ( ParseError :: new (
663+ c. span ( ) ,
664+ "init must be provided before token" ,
665+ ) ) ;
666+ }
667+ self . associated_token_mint . replace ( c) ;
668+ Ok ( ( ) )
669+ }
670+
598671 fn add_bump ( & mut self , c : Context < ConstraintTokenBump > ) -> ParseResult < ( ) > {
599672 if self . bump . is_some ( ) {
600673 return Err ( ParseError :: new ( c. span ( ) , "bump already provided" ) ) ;
@@ -626,6 +699,32 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
626699 Ok ( ( ) )
627700 }
628701
702+ fn add_associated_token_authority (
703+ & mut self ,
704+ c : Context < ConstraintTokenAuthority > ,
705+ ) -> ParseResult < ( ) > {
706+ if self . associated_token_authority . is_some ( ) {
707+ return Err ( ParseError :: new (
708+ c. span ( ) ,
709+ "associated token authority already provided" ,
710+ ) ) ;
711+ }
712+ if self . token_authority . is_some ( ) {
713+ return Err ( ParseError :: new (
714+ c. span ( ) ,
715+ "token authority already provided" ,
716+ ) ) ;
717+ }
718+ if self . init . is_none ( ) {
719+ return Err ( ParseError :: new (
720+ c. span ( ) ,
721+ "init must be provided before token authority" ,
722+ ) ) ;
723+ }
724+ self . associated_token_authority . replace ( c) ;
725+ Ok ( ( ) )
726+ }
727+
629728 fn add_mint_authority ( & mut self , c : Context < ConstraintMintAuthority > ) -> ParseResult < ( ) > {
630729 if self . mint_authority . is_some ( ) {
631730 return Err ( ParseError :: new ( c. span ( ) , "mint authority already provided" ) ) ;
0 commit comments