Skip to main content

ephemeral_rollups_sdk_attribute_ephemeral/
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 ephemeral(_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        #[allow(unused_imports)]
75        use ephemeral_rollups_sdk::anchor::MagicProgram;
76
77        #modified
78    })
79}
80
81/// Modifies the component module and adds the necessary functions and structs.
82fn modify_component_module(mut module: ItemMod) -> ItemMod {
83    // Add common imports for MagicIntentBundleBuilder for user
84    let builder_imports = quote! {
85        #[allow(unused_imports)]
86        use ephemeral_rollups_sdk::ephem::{
87            FoldableIntentBuilder,
88            FoldableCommitIntentBuilder,
89            FoldableCauIntentBuilder,
90        };
91    };
92
93    let (imports, undelegate_fn, undelegate_struct) = generate_undelegate();
94    module.content = module.content.map(|(brace, mut items)| {
95        items.extend(
96            vec![builder_imports, imports, undelegate_fn, undelegate_struct]
97                .into_iter()
98                .map(|item| {
99                    syn::parse2(item).unwrap_or_else(|e| panic!("Failed to parse item: {e}"))
100                })
101                .collect::<Vec<_>>(),
102        );
103        (brace, items)
104    });
105    module
106}
107
108/// Generates the undelegate function and struct.
109fn generate_undelegate() -> (TokenStream2, TokenStream2, TokenStream2) {
110    (
111        quote! {
112            use ephemeral_rollups_sdk::cpi::undelegate_account;
113        },
114        quote! {
115            #[automatically_derived]
116            pub fn process_undelegation(ctx: Context<InitializeAfterUndelegation>, account_seeds: Vec<Vec<u8>>) -> Result<()> {
117                let [delegated_account, buffer, payer, system_program] = [
118                    &ctx.accounts.base_account,
119                    &ctx.accounts.buffer,
120                    &ctx.accounts.payer,
121                    &ctx.accounts.system_program,
122                ];
123                undelegate_account(
124                    delegated_account,
125                    &id(),
126                    buffer,
127                    payer,
128                    system_program,
129                    account_seeds,
130                )?;
131                Ok(())
132            }
133        },
134        quote! {
135            #[automatically_derived]
136            #[derive(Accounts)]
137                pub struct InitializeAfterUndelegation<'info> {
138                /// CHECK:`
139                #[account(mut)]
140                pub base_account: AccountInfo<'info>,
141                /// CHECK:`
142                #[account()]
143                pub buffer: AccountInfo<'info>,
144                /// CHECK:`
145                #[account(mut)]
146                pub payer: AccountInfo<'info>,
147                /// CHECK:`
148                pub system_program: AccountInfo<'info>,
149            }
150        },
151    )
152}