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}