123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- use proc_macro::TokenStream;
- use proc_macro2::TokenStream as TokenStream2;
- use quote::quote;
- use syn::{parse_macro_input, AttributeArgs, ItemMod, NestedMeta, Type};
- /// This macro attribute is used to inject instructions and struct needed to delegate BOLT component.
- ///
- /// Components can be delegate in order to be updated in an Ephemeral Rollup
- ///
- /// # Example
- /// ```ignore
- ///
- /// #[component(delegate)]
- /// pub struct Position {
- /// pub x: i64,
- /// pub y: i64,
- /// pub z: i64,
- /// }
- /// ```
- #[proc_macro_attribute]
- pub fn delegate(args: TokenStream, input: TokenStream) -> TokenStream {
- let ast = parse_macro_input!(input as syn::ItemMod);
- let args = parse_macro_input!(args as syn::AttributeArgs);
- let component_type =
- extract_type_name(&args).expect("Expected a component type in macro arguments");
- let modified = modify_component_module(ast, &component_type);
- TokenStream::from(quote! {
- #modified
- })
- }
- /// Modifies the component module and adds the necessary functions and structs.
- fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMod {
- let (delegate_fn, delegate_struct) = generate_delegate(component_type);
- let (undelegate_fn, undelegate_struct) = generate_undelegate();
- let (allow_undelegate_fn, allow_undelegate_struct) = generate_allow_undelegate();
- module.content = module.content.map(|(brace, mut items)| {
- items.extend(
- vec![
- delegate_fn,
- delegate_struct,
- undelegate_fn,
- undelegate_struct,
- allow_undelegate_fn,
- allow_undelegate_struct,
- ]
- .into_iter()
- .map(|item| syn::parse2(item).unwrap())
- .collect::<Vec<_>>(),
- );
- (brace, items)
- });
- module
- }
- /// Generates the allow_undelegate function and struct.
- fn generate_allow_undelegate() -> (TokenStream2, TokenStream2) {
- (
- quote! {
- #[automatically_derived]
- pub fn allow_undelegation(ctx: Context<AllowUndelegation>) -> Result<()> {
- ::bolt_lang::allow_undelegation(
- &ctx.accounts.base_account,
- &ctx.accounts.delegation_record,
- &ctx.accounts.delegation_metadata,
- &ctx.accounts.buffer,
- &ctx.accounts.delegation_program,
- &id(),
- )?;
- Ok(())
- }
- },
- quote! {
- #[automatically_derived]
- #[derive(Accounts)]
- pub struct AllowUndelegation<'info> {
- #[account()]
- /// CHECK: The delegated component
- pub base_account: AccountInfo<'info>,
- #[account()]
- /// CHECK: delegation record
- pub delegation_record: AccountInfo<'info>,
- #[account(mut)]
- /// CHECK: delegation metadata
- pub delegation_metadata: AccountInfo<'info>,
- #[account()]
- /// CHECK: singer buffer to enforce CPI
- pub buffer: AccountInfo<'info>,
- #[account()]
- /// CHECK:`
- pub delegation_program: AccountInfo<'info>,
- }
- },
- )
- }
- /// Generates the undelegate function and struct.
- fn generate_undelegate() -> (TokenStream2, TokenStream2) {
- (
- quote! {
- #[automatically_derived]
- pub fn process_undelegation(ctx: Context<InitializeAfterUndelegation>, account_seeds: Vec<Vec<u8>>) -> Result<()> {
- let [delegated_account, buffer, payer, system_program] = [
- &ctx.accounts.base_account,
- &ctx.accounts.buffer,
- &ctx.accounts.payer,
- &ctx.accounts.system_program,
- ];
- ::bolt_lang::undelegate_account(
- delegated_account,
- &id(),
- buffer,
- payer,
- system_program,
- account_seeds,
- )?;
- Ok(())
- }
- },
- quote! {
- #[automatically_derived]
- #[derive(Accounts)]
- pub struct InitializeAfterUndelegation<'info> {
- /// CHECK:`
- #[account(mut)]
- pub base_account: AccountInfo<'info>,
- /// CHECK:`
- #[account()]
- pub buffer: AccountInfo<'info>,
- /// CHECK:
- #[account(mut)]
- pub payer: AccountInfo<'info>,
- /// CHECK:
- pub system_program: AccountInfo<'info>,
- }
- },
- )
- }
- /// Generates the delegate instruction and related structs to inject in the component.
- fn generate_delegate(component_type: &Type) -> (TokenStream2, TokenStream2) {
- (
- quote! {
- #[automatically_derived]
- pub fn delegate(ctx: Context<DelegateInput>, valid_until: i64, commit_frequency_ms: u32) -> Result<()> {
- let [payer, entity, account, owner_program, buffer, delegation_record, delegate_account_seeds, delegation_program, system_program] = [
- &ctx.accounts.payer,
- &ctx.accounts.entity.to_account_info(),
- &ctx.accounts.account,
- &ctx.accounts.owner_program,
- &ctx.accounts.buffer,
- &ctx.accounts.delegation_record,
- &ctx.accounts.delegate_account_seeds,
- &ctx.accounts.delegation_program,
- &ctx.accounts.system_program,
- ];
- let pda_seeds: &[&[u8]] = &[<#component_type>::seed(), &entity.key.to_bytes()];
- ::bolt_lang::delegate_account(
- payer,
- account,
- owner_program,
- buffer,
- delegation_record,
- delegate_account_seeds,
- delegation_program,
- system_program,
- pda_seeds,
- valid_until,
- commit_frequency_ms,
- )?;
- Ok(())
- }
- },
- quote! {
- #[automatically_derived]
- #[derive(Accounts)]
- pub struct DelegateInput<'info> {
- pub payer: Signer<'info>,
- #[account()]
- pub entity: Account<'info, Entity>,
- /// CHECK:
- #[account(mut)]
- pub account: AccountInfo<'info>,
- /// CHECK:`
- pub owner_program: AccountInfo<'info>,
- /// CHECK:
- #[account(mut)]
- pub buffer: AccountInfo<'info>,
- /// CHECK:`
- #[account(mut)]
- pub delegation_record: AccountInfo<'info>,
- /// CHECK:`
- #[account(mut)]
- pub delegate_account_seeds: AccountInfo<'info>,
- /// CHECK:`
- pub delegation_program: AccountInfo<'info>,
- /// CHECK:`
- pub system_program: AccountInfo<'info>,
- }
- },
- )
- }
- /// Extracts the type name from attribute arguments.
- fn extract_type_name(args: &AttributeArgs) -> Option<Type> {
- args.iter().find_map(|arg| {
- if let NestedMeta::Meta(syn::Meta::Path(path)) = arg {
- Some(Type::Path(syn::TypePath {
- qself: None,
- path: path.clone(),
- }))
- } else {
- None
- }
- })
- }
|