bolt_attribute_bolt_extra_accounts/
lib.rs

1use proc_macro::TokenStream;
2
3use quote::quote;
4use syn::{parse_macro_input, Fields, ItemStruct, LitStr};
5
6/// This macro attribute is used to define a BOLT system extra accounts.
7///
8/// The extra account struct define the accounts (which are not components), that the system expect.
9///
10///
11/// # Example
12/// ```ignore
13///#[extra_accounts]
14///pub struct ExtraAccounts {
15/// #[account(address = bolt_lang::solana_program::sysvar::clock::id())]
16/// pub clock: AccountInfo<'info>,
17/// #[account(address = pubkey!("CbHEFbSQdRN4Wnoby9r16umnJ1zWbULBHg4yqzGQonU1"), signer)]
18/// pub my_account: AccountInfo<'info>,
19/// #[account(address = Metadata::id())]
20/// pub metadata_program: Program<'info, Metadata>,
21///}
22///
23/// ```
24#[proc_macro_attribute]
25pub fn extra_accounts(_attr: TokenStream, item: TokenStream) -> TokenStream {
26    let input = parse_macro_input!(item as ItemStruct);
27    let extra_accounts_struct_name = &input.ident;
28
29    // Ensure the struct has named fields
30    let fields = match &input.fields {
31        Fields::Named(fields) => &fields.named,
32        _ => panic!("extra_accounts macro only supports structs with named fields"),
33    };
34
35    // Transform fields for the struct definition
36    let transformed_fields = fields.iter().map(|f| {
37        let field_name = &f.ident;
38        let attrs = &f.attrs;
39        quote! {
40            #(#attrs)*
41            pub #field_name: AccountInfo<'info>,
42        }
43    });
44
45    // Generate the new struct with the Accounts derive and transformed fields
46    let output_struct = quote! {
47        #[derive(Accounts)]
48        pub struct #extra_accounts_struct_name<'info> {
49            #(#transformed_fields)*
50        }
51    };
52
53    // Generate the trait for the helper functions
54    let helper_functions = fields.iter().map(|f| {
55        let field_name = &f.ident;
56        quote! {
57            fn #field_name(&self) -> Result<&'c AccountInfo<'info>>;
58        }
59    });
60
61    let output_trait = quote! {
62        pub trait ContextExtensions<'a, 'b, 'c, 'info, T>
63        {
64            #(#helper_functions)*
65        }
66    };
67
68    // Generate the helper functions for the struct
69    let helper_functions_impl = fields.iter().enumerate().map(|(index, f)| {
70        let field_name = &f.ident;
71        let index = syn::Index::from(index); // Create a compile-time index representation
72        quote! {
73            fn #field_name(&self) -> Result<&'c AccountInfo<'info>> {
74                self.remaining_accounts.get(Self::NUMBER_OF_COMPONENTS + #index).ok_or_else(|| ErrorCode::ConstraintAccountIsNone.into())
75            }
76        }
77    });
78
79    let output_trait_implementation = quote! {
80        impl<'a, 'b, 'c, 'info, T: bolt_lang::Bumps> ContextExtensions<'a, 'b, 'c, 'info, T> for Context<'a, 'b, 'c, 'info, T> {
81            #(#helper_functions_impl)*
82        }
83    };
84
85    // Combine the struct definition and its implementation into the final TokenStream
86    let output = quote! {
87        #output_struct
88        #output_trait
89        #output_trait_implementation
90    };
91
92    TokenStream::from(output)
93}
94
95#[proc_macro]
96pub fn pubkey(input: TokenStream) -> TokenStream {
97    let input_lit_str = parse_macro_input!(input as LitStr);
98    let pubkey_str = input_lit_str.value();
99
100    // Example using solana_program to create a Pubkey from a string
101    let expanded = quote! {
102        bolt_lang::pubkey_from_str(#pubkey_str)
103    };
104
105    TokenStream::from(expanded)
106}