ephemeral_rollups_sdk_attribute_delegate/
lib.rsuse proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{parse_macro_input, ItemStruct};
#[proc_macro_attribute]
pub fn delegate(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemStruct);
let struct_name = &input.ident;
let fields = &input.fields;
let original_attrs = &input.attrs;
let mut new_fields = Vec::new();
let mut delegate_methods = Vec::new();
let mut has_owner_program = false;
let mut has_delegation_program = false;
let mut has_system_program = false;
for field in fields.iter() {
let mut field_attrs = field.attrs.clone();
let field_name = match &field.ident {
Some(name) => name,
None => {
return syn::Error::new_spanned(
field,
"Unnamed fields are not supported in this macro",
)
.to_compile_error()
.into();
}
};
let has_del = field_attrs
.iter()
.any(|attr| attr.path.is_ident("account") && attr.tokens.to_string().contains("del"));
if has_del {
let buffer_field = syn::Ident::new(&format!("buffer_{}", field_name), field.span());
let delegation_record_field =
syn::Ident::new(&format!("delegation_record_{}", field_name), field.span());
let delegation_metadata_field =
syn::Ident::new(&format!("delegation_metadata_{}", field_name), field.span());
for attr in &mut field_attrs {
if attr.path.is_ident("account") {
let tokens = attr.tokens.to_string();
if tokens.contains("del") {
let new_tokens = tokens
.replace(", del", "")
.replace("del, ", "")
.replace("del", "");
attr.tokens = syn::parse_str(&new_tokens).unwrap();
}
}
}
new_fields.push(quote! {
#[account(
mut, seeds = [ephemeral_rollups_sdk_v2::consts::BUFFER, #field_name.key().as_ref()],
bump, seeds::program = crate::id()
)]
pub #buffer_field: AccountInfo<'info>,
});
new_fields.push(quote! {
#[account(
mut, seeds = [ephemeral_rollups_sdk_v2::consts::DELEGATION_RECORD, #field_name.key().as_ref()],
bump, seeds::program = delegation_program.key()
)]
pub #delegation_record_field: AccountInfo<'info>,
});
new_fields.push(quote! {
#[account(
mut, seeds = [ephemeral_rollups_sdk_v2::consts::DELEGATION_METADATA, #field_name.key().as_ref()],
bump, seeds::program = delegation_program.key()
)]
pub #delegation_metadata_field: AccountInfo<'info>,
});
let delegate_method_name =
syn::Ident::new(&format!("delegate_{}", field_name), field.span());
delegate_methods.push(quote! {
pub fn #delegate_method_name<'a>(
&'a self,
payer: &'a Signer<'info>,
seeds: &[&[u8]],
config: ::ephemeral_rollups_sdk_v2::cpi::DelegateConfig,
) -> anchor_lang::solana_program::entrypoint::ProgramResult {
let del_accounts = ::ephemeral_rollups_sdk_v2::cpi::DelegateAccounts {
payer,
pda: &self.#field_name.to_account_info(),
owner_program: &self.owner_program,
buffer: &self.#buffer_field,
delegation_record: &self.#delegation_record_field,
delegation_metadata: &self.#delegation_metadata_field,
delegation_program: &self.delegation_program,
system_program: &self.system_program,
};
::ephemeral_rollups_sdk_v2::cpi::delegate_account(del_accounts, seeds, config)
}
});
}
let field_type = &field.ty;
new_fields.push(quote! {
#(#field_attrs)*
pub #field_name: #field_type,
});
if field_name.eq("owner_program") {
has_owner_program = true;
}
if field_name.eq("delegation_program") {
has_delegation_program = true;
}
if field_name.eq("system_program") {
has_system_program = true;
}
}
if !has_owner_program {
new_fields.push(quote! {
#[account(address = crate::id())]
pub owner_program: AccountInfo<'info>,
});
}
if !has_delegation_program {
new_fields.push(quote! {
#[account(address = ::ephemeral_rollups_sdk_v2::id())]
pub delegation_program: AccountInfo<'info>,
});
}
if !has_system_program {
new_fields.push(quote! {
pub system_program: Program<'info, System>,
});
}
let expanded = quote! {
#(#original_attrs)*
pub struct #struct_name<'info> {
#(#new_fields)*
}
impl<'info> #struct_name<'info> {
#(#delegate_methods)*
}
};
TokenStream::from(expanded)
}