lib.rs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. use proc_macro::TokenStream;
  2. use proc_macro2::TokenStream as TokenStream2;
  3. use quote::quote;
  4. use syn::{parse_macro_input, AttributeArgs, ItemMod, NestedMeta, Type};
  5. /// This macro attribute is used to inject instructions and struct needed to delegate BOLT component.
  6. ///
  7. /// Components can be delegate in order to be updated in an Ephemeral Rollup
  8. ///
  9. /// # Example
  10. /// ```ignore
  11. ///
  12. /// #[component(delegate)]
  13. /// pub struct Position {
  14. /// pub x: i64,
  15. /// pub y: i64,
  16. /// pub z: i64,
  17. /// }
  18. /// ```
  19. #[proc_macro_attribute]
  20. pub fn delegate(args: TokenStream, input: TokenStream) -> TokenStream {
  21. let ast = parse_macro_input!(input as syn::ItemMod);
  22. let args = parse_macro_input!(args as syn::AttributeArgs);
  23. let component_type =
  24. extract_type_name(&args).expect("Expected a component type in macro arguments");
  25. let modified = modify_component_module(ast, &component_type);
  26. TokenStream::from(quote! {
  27. #modified
  28. })
  29. }
  30. /// Modifies the component module and adds the necessary functions and structs.
  31. fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMod {
  32. let (delegate_fn, delegate_struct) = generate_delegate(component_type);
  33. let (undelegate_fn, undelegate_struct) = generate_undelegate();
  34. let (allow_undelegate_fn, allow_undelegate_struct) = generate_allow_undelegate();
  35. module.content = module.content.map(|(brace, mut items)| {
  36. items.extend(
  37. vec![
  38. delegate_fn,
  39. delegate_struct,
  40. undelegate_fn,
  41. undelegate_struct,
  42. allow_undelegate_fn,
  43. allow_undelegate_struct,
  44. ]
  45. .into_iter()
  46. .map(|item| syn::parse2(item).unwrap())
  47. .collect::<Vec<_>>(),
  48. );
  49. (brace, items)
  50. });
  51. module
  52. }
  53. /// Generates the allow_undelegate function and struct.
  54. fn generate_allow_undelegate() -> (TokenStream2, TokenStream2) {
  55. (
  56. quote! {
  57. #[automatically_derived]
  58. pub fn allow_undelegation(ctx: Context<AllowUndelegation>) -> Result<()> {
  59. ::bolt_lang::allow_undelegation(
  60. &ctx.accounts.base_account,
  61. &ctx.accounts.delegation_record,
  62. &ctx.accounts.delegation_metadata,
  63. &ctx.accounts.buffer,
  64. &ctx.accounts.delegation_program,
  65. &id(),
  66. )?;
  67. Ok(())
  68. }
  69. },
  70. quote! {
  71. #[automatically_derived]
  72. #[derive(Accounts)]
  73. pub struct AllowUndelegation<'info> {
  74. #[account()]
  75. /// CHECK: The delegated component
  76. pub base_account: AccountInfo<'info>,
  77. #[account()]
  78. /// CHECK: delegation record
  79. pub delegation_record: AccountInfo<'info>,
  80. #[account(mut)]
  81. /// CHECK: delegation metadata
  82. pub delegation_metadata: AccountInfo<'info>,
  83. #[account()]
  84. /// CHECK: singer buffer to enforce CPI
  85. pub buffer: AccountInfo<'info>,
  86. #[account()]
  87. /// CHECK:`
  88. pub delegation_program: AccountInfo<'info>,
  89. }
  90. },
  91. )
  92. }
  93. /// Generates the undelegate function and struct.
  94. fn generate_undelegate() -> (TokenStream2, TokenStream2) {
  95. (
  96. quote! {
  97. #[automatically_derived]
  98. pub fn process_undelegation(ctx: Context<InitializeAfterUndelegation>, account_seeds: Vec<Vec<u8>>) -> Result<()> {
  99. let [delegated_account, buffer, payer, system_program] = [
  100. &ctx.accounts.base_account,
  101. &ctx.accounts.buffer,
  102. &ctx.accounts.payer,
  103. &ctx.accounts.system_program,
  104. ];
  105. ::bolt_lang::undelegate_account(
  106. delegated_account,
  107. &id(),
  108. buffer,
  109. payer,
  110. system_program,
  111. account_seeds,
  112. )?;
  113. Ok(())
  114. }
  115. },
  116. quote! {
  117. #[automatically_derived]
  118. #[derive(Accounts)]
  119. pub struct InitializeAfterUndelegation<'info> {
  120. /// CHECK:`
  121. #[account(mut)]
  122. pub base_account: AccountInfo<'info>,
  123. /// CHECK:`
  124. #[account()]
  125. pub buffer: AccountInfo<'info>,
  126. /// CHECK:
  127. #[account(mut)]
  128. pub payer: AccountInfo<'info>,
  129. /// CHECK:
  130. pub system_program: AccountInfo<'info>,
  131. }
  132. },
  133. )
  134. }
  135. /// Generates the delegate instruction and related structs to inject in the component.
  136. fn generate_delegate(component_type: &Type) -> (TokenStream2, TokenStream2) {
  137. (
  138. quote! {
  139. #[automatically_derived]
  140. pub fn delegate(ctx: Context<DelegateInput>, valid_until: i64, commit_frequency_ms: u32) -> Result<()> {
  141. let [payer, entity, account, owner_program, buffer, delegation_record, delegate_account_seeds, delegation_program, system_program] = [
  142. &ctx.accounts.payer,
  143. &ctx.accounts.entity.to_account_info(),
  144. &ctx.accounts.account,
  145. &ctx.accounts.owner_program,
  146. &ctx.accounts.buffer,
  147. &ctx.accounts.delegation_record,
  148. &ctx.accounts.delegate_account_seeds,
  149. &ctx.accounts.delegation_program,
  150. &ctx.accounts.system_program,
  151. ];
  152. let pda_seeds: &[&[u8]] = &[<#component_type>::seed(), &entity.key.to_bytes()];
  153. ::bolt_lang::delegate_account(
  154. payer,
  155. account,
  156. owner_program,
  157. buffer,
  158. delegation_record,
  159. delegate_account_seeds,
  160. delegation_program,
  161. system_program,
  162. pda_seeds,
  163. valid_until,
  164. commit_frequency_ms,
  165. )?;
  166. Ok(())
  167. }
  168. },
  169. quote! {
  170. #[automatically_derived]
  171. #[derive(Accounts)]
  172. pub struct DelegateInput<'info> {
  173. pub payer: Signer<'info>,
  174. #[account()]
  175. pub entity: Account<'info, Entity>,
  176. /// CHECK:
  177. #[account(mut)]
  178. pub account: AccountInfo<'info>,
  179. /// CHECK:`
  180. pub owner_program: AccountInfo<'info>,
  181. /// CHECK:
  182. #[account(mut)]
  183. pub buffer: AccountInfo<'info>,
  184. /// CHECK:`
  185. #[account(mut)]
  186. pub delegation_record: AccountInfo<'info>,
  187. /// CHECK:`
  188. #[account(mut)]
  189. pub delegate_account_seeds: AccountInfo<'info>,
  190. /// CHECK:`
  191. pub delegation_program: AccountInfo<'info>,
  192. /// CHECK:`
  193. pub system_program: AccountInfo<'info>,
  194. }
  195. },
  196. )
  197. }
  198. /// Extracts the type name from attribute arguments.
  199. fn extract_type_name(args: &AttributeArgs) -> Option<Type> {
  200. args.iter().find_map(|arg| {
  201. if let NestedMeta::Meta(syn::Meta::Path(path)) = arg {
  202. Some(Type::Path(syn::TypePath {
  203. qself: None,
  204. path: path.clone(),
  205. }))
  206. } else {
  207. None
  208. }
  209. })
  210. }