delegation_sdk_attribute_delegate/
lib.rs

1use proc_macro::TokenStream;
2
3use proc_macro2::TokenStream as TokenStream2;
4use quote::quote;
5use syn::{parse_macro_input, ItemMod};
6
7/// This macro attribute is used to inject instructions and struct needed from the delegation program.
8///
9/// Components can be delegate and undelegated to allow fast udpates in the Ephemeral Rollups.
10///
11/// # Example
12/// ```ignore
13///
14/// pub fn delegate(ctx: Context<DelegateInput>) -> Result<()> {
15///     let pda_seeds: &[&[u8]] = &[TEST_PDA_SEED];
16///
17///     let [payer, pda, owner_program, buffer, delegation_record, delegation_metadata, delegation_program, system_program] = [
18///         &ctx.accounts.payer,
19///         &ctx.accounts.pda,
20///         &ctx.accounts.owner_program,
21///         &ctx.accounts.buffer,
22///         &ctx.accounts.buffer,
23///         &ctx.accounts.delegation_record,
24///         &ctx.accounts.delegation_metadata,
25///         &ctx.accounts.delegation_program,
26///         &ctx.accounts.system_program,
27///     ];
28///
29///     delegate_account(
30///         payer,
31///         pda,
32///         owner_program,
33///         buffer,
34///         delegation_record,
35///         delegation_metadata,
36///         delegation_program,
37///         system_program,
38///         pda_seeds,
39///         0,
40///         30000
41///     )?;
42///
43///     Ok(())
44/// }
45///
46/// #[derive(Accounts)]
47/// pub struct DelegateInput<'info> {
48///     pub payer: Signer<'info>,
49///     /// CHECK: The pda to delegate
50///     #[account(mut)]
51///     pub pda: AccountInfo<'info>,
52///     /// CHECK: The owner program of the pda
53///     pub owner_program: AccountInfo<'info>,
54///     /// CHECK: The buffer to temporarily store the pda data
55///     #[account(mut)]
56///     pub buffer: AccountInfo<'info>,
57///     /// CHECK: The delegation record
58///     #[account(mut)]
59///     pub delegation_record: AccountInfo<'info>,
60///     /// CHECK: The delegation account seeds
61///     #[account(mut)]
62///     pub delegation_metadata: AccountInfo<'info>,
63///     /// CHECK: The delegation program
64///     pub delegation_program: AccountInfo<'info>,
65///     /// CHECK: The system program
66///     pub system_program: AccountInfo<'info>,
67/// }
68/// ```
69#[proc_macro_attribute]
70pub fn delegate(_args: TokenStream, input: TokenStream) -> TokenStream {
71    let ast = parse_macro_input!(input as syn::ItemMod);
72    let modified = modify_component_module(ast);
73    TokenStream::from(quote! {
74        #modified
75    })
76}
77
78/// Modifies the component module and adds the necessary functions and structs.
79fn modify_component_module(mut module: ItemMod) -> ItemMod {
80    let (imports, undelegate_fn, undelegate_struct) = generate_undelegate();
81    module.content = module.content.map(|(brace, mut items)| {
82        items.extend(
83            vec![imports, undelegate_fn, undelegate_struct]
84                .into_iter()
85                .map(|item| {
86                    syn::parse2(item).unwrap_or_else(|e| panic!("Failed to parse item: {}", e))
87                })
88                .collect::<Vec<_>>(),
89        );
90        (brace, items)
91    });
92    module
93}
94
95/// Generates the undelegate function and struct.
96fn generate_undelegate() -> (TokenStream2, TokenStream2, TokenStream2) {
97    (
98        quote! {
99            use delegation_program_sdk::undelegate_account;
100        },
101        quote! {
102            #[automatically_derived]
103            pub fn process_undelegation(ctx: Context<InitializeAfterUndelegation>, account_seeds: Vec<Vec<u8>>) -> Result<()> {
104                let [delegated_account, buffer, payer, system_program] = [
105                    &ctx.accounts.base_account,
106                    &ctx.accounts.buffer,
107                    &ctx.accounts.payer,
108                    &ctx.accounts.system_program,
109                ];
110                undelegate_account(
111                    delegated_account,
112                    &id(),
113                    buffer,
114                    payer,
115                    system_program,
116                    account_seeds,
117                )?;
118                Ok(())
119            }
120        },
121        quote! {
122            #[automatically_derived]
123            #[derive(Accounts)]
124                pub struct InitializeAfterUndelegation<'info> {
125                /// CHECK:`
126                #[account(mut)]
127                pub base_account: AccountInfo<'info>,
128                /// CHECK:`
129                #[account()]
130                pub buffer: AccountInfo<'info>,
131                /// CHECK:`
132                #[account(mut)]
133                pub payer: AccountInfo<'info>,
134                /// CHECK:`
135                pub system_program: AccountInfo<'info>,
136            }
137        },
138    )
139}