ephemeral_rollups_sdk_attribute_delegate/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::spanned::Spanned;
4use syn::{parse_macro_input, ItemStruct};
5
6#[proc_macro_attribute]
7pub fn delegate(_attr: TokenStream, item: TokenStream) -> TokenStream {
8 let input = parse_macro_input!(item as ItemStruct);
9
10 let struct_name = &input.ident;
12 let fields = &input.fields;
13 let original_attrs = &input.attrs;
14
15 let mut new_fields = Vec::new();
17 let mut delegate_methods = Vec::new();
18 let mut has_owner_program = false;
19 let mut has_delegation_program = false;
20 let mut has_system_program = false;
21
22 for field in fields.iter() {
23 let mut field_attrs = field.attrs.clone();
24
25 let field_name = match &field.ident {
26 Some(name) => name,
27 None => {
28 return syn::Error::new_spanned(
29 field,
30 "Unnamed fields are not supported in this macro",
31 )
32 .to_compile_error()
33 .into();
34 }
35 };
36
37 let has_del = field_attrs
39 .iter()
40 .any(|attr| attr.path.is_ident("account") && attr.tokens.to_string().contains("del"));
41
42 if has_del {
43 let buffer_field = syn::Ident::new(&format!("buffer_{field_name}"), field.span());
44 let delegation_record_field =
45 syn::Ident::new(&format!("delegation_record_{field_name}"), field.span());
46 let delegation_metadata_field =
47 syn::Ident::new(&format!("delegation_metadata_{field_name}"), field.span());
48
49 for attr in &mut field_attrs {
51 if attr.path.is_ident("account") {
52 let tokens = attr.tokens.to_string();
53 if tokens.contains("del") {
54 let new_tokens = tokens
55 .replace(", del", "")
56 .replace("del, ", "")
57 .replace("del", "");
58 attr.tokens = syn::parse_str(&new_tokens).unwrap();
59 }
60 }
61 }
62
63 new_fields.push(quote! {
65 #[account(
67 mut, seeds = [ephemeral_rollups_sdk::consts::BUFFER, #field_name.key().as_ref()],
68 bump, seeds::program = crate::id()
69 )]
70 pub #buffer_field: AccountInfo<'info>,
71 });
72
73 new_fields.push(quote! {
74 #[account(
76 mut, seeds = [ephemeral_rollups_sdk::consts::DELEGATION_RECORD, #field_name.key().as_ref()],
77 bump, seeds::program = delegation_program.key()
78 )]
79 pub #delegation_record_field: AccountInfo<'info>,
80 });
81
82 new_fields.push(quote! {
83 #[account(
85 mut, seeds = [ephemeral_rollups_sdk::consts::DELEGATION_METADATA, #field_name.key().as_ref()],
86 bump, seeds::program = delegation_program.key()
87 )]
88 pub #delegation_metadata_field: AccountInfo<'info>,
89 });
90
91 let delegate_method_name =
93 syn::Ident::new(&format!("delegate_{field_name}"), field.span());
94 delegate_methods.push(quote! {
95 pub fn #delegate_method_name<'a>(
96 &'a self,
97 payer: &'a Signer<'info>,
98 seeds: &[&[u8]],
99 config: ::ephemeral_rollups_sdk::cpi::DelegateConfig,
100 ) -> anchor_lang::solana_program::entrypoint::ProgramResult {
101 let del_accounts = ::ephemeral_rollups_sdk::cpi::DelegateAccounts {
102 payer,
103 pda: &self.#field_name.to_account_info(),
104 owner_program: &self.owner_program,
105 buffer: &self.#buffer_field,
106 delegation_record: &self.#delegation_record_field,
107 delegation_metadata: &self.#delegation_metadata_field,
108 delegation_program: &self.delegation_program,
109 system_program: &self.system_program,
110 };
111 ::ephemeral_rollups_sdk::cpi::delegate_account(del_accounts, seeds, config)
112 }
113 });
114 }
115
116 let field_type = &field.ty;
118 new_fields.push(quote! {
119 #(#field_attrs)*
120 pub #field_name: #field_type,
121 });
122
123 if field_name.eq("owner_program") {
125 has_owner_program = true;
126 }
127 if field_name.eq("delegation_program") {
128 has_delegation_program = true;
129 }
130 if field_name.eq("system_program") {
131 has_system_program = true;
132 }
133 }
134
135 if !has_owner_program {
137 new_fields.push(quote! {
138 #[account(address = crate::id())]
140 pub owner_program: AccountInfo<'info>,
141 });
142 }
143 if !has_delegation_program {
144 new_fields.push(quote! {
145 #[account(address = ::ephemeral_rollups_sdk::id())]
147 pub delegation_program: AccountInfo<'info>,
148 });
149 }
150 if !has_system_program {
151 new_fields.push(quote! {
152 pub system_program: Program<'info, System>,
153 });
154 }
155
156 let expanded = quote! {
158 #(#original_attrs)*
159 pub struct #struct_name<'info> {
160 #(#new_fields)*
161 }
162
163 impl<'info> #struct_name<'info> {
164 #(#delegate_methods)*
165 }
166 };
167
168 TokenStream::from(expanded)
169}