lib.rs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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 (reinit_undelegate_fn, reinit_undelegate_struct) = generate_reinit_after_undelegate();
  34. let (undelegate_fn, undelegate_struct) = generate_undelegate();
  35. module.content = module.content.map(|(brace, mut items)| {
  36. items.extend(
  37. vec![
  38. delegate_fn,
  39. delegate_struct,
  40. reinit_undelegate_fn,
  41. reinit_undelegate_struct,
  42. undelegate_fn,
  43. 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_undelegate() -> (TokenStream2, TokenStream2) {
  55. (
  56. quote! {
  57. #[automatically_derived]
  58. pub fn undelegate(ctx: Context<Undelegate>) -> Result<()> {
  59. ::bolt_lang::commit_and_undelegate_accounts(
  60. &ctx.accounts.payer,
  61. vec![&ctx.accounts.delegated_account.to_account_info()],
  62. &ctx.accounts.magic_context,
  63. &ctx.accounts.magic_program,
  64. )?;
  65. Ok(())
  66. }
  67. },
  68. quote! {
  69. #[automatically_derived]
  70. #[derive(Accounts)]
  71. pub struct Undelegate<'info> {
  72. #[account(mut)]
  73. pub payer: Signer<'info>,
  74. #[account(mut)]
  75. /// CHECK: The delegated component
  76. pub delegated_account: AccountInfo<'info>,
  77. #[account(mut, address = ::bolt_lang::MAGIC_CONTEXT_ID)]
  78. /// CHECK:`
  79. pub magic_context: AccountInfo<'info>,
  80. #[account()]
  81. /// CHECK:`
  82. pub magic_program: Program<'info, MagicProgram>
  83. }
  84. },
  85. )
  86. }
  87. /// Generates the undelegate function and struct.
  88. fn generate_reinit_after_undelegate() -> (TokenStream2, TokenStream2) {
  89. (
  90. quote! {
  91. #[automatically_derived]
  92. pub fn process_undelegation(ctx: Context<InitializeAfterUndelegation>, account_seeds: Vec<Vec<u8>>) -> Result<()> {
  93. let [delegated_account, buffer, payer, system_program] = [
  94. &ctx.accounts.delegated_account,
  95. &ctx.accounts.buffer,
  96. &ctx.accounts.payer,
  97. &ctx.accounts.system_program,
  98. ];
  99. ::bolt_lang::undelegate_account(
  100. delegated_account,
  101. &id(),
  102. buffer,
  103. payer,
  104. system_program,
  105. account_seeds,
  106. )?;
  107. Ok(())
  108. }
  109. },
  110. quote! {
  111. #[automatically_derived]
  112. #[derive(Accounts)]
  113. pub struct InitializeAfterUndelegation<'info> {
  114. /// CHECK:`
  115. #[account(mut)]
  116. pub delegated_account: AccountInfo<'info>,
  117. /// CHECK:`
  118. #[account()]
  119. pub buffer: AccountInfo<'info>,
  120. /// CHECK:
  121. #[account(mut)]
  122. pub payer: AccountInfo<'info>,
  123. /// CHECK:
  124. pub system_program: AccountInfo<'info>,
  125. }
  126. },
  127. )
  128. }
  129. /// Generates the delegate instruction and related structs to inject in the component.
  130. fn generate_delegate(component_type: &Type) -> (TokenStream2, TokenStream2) {
  131. (
  132. quote! {
  133. #[automatically_derived]
  134. pub fn delegate(ctx: Context<DelegateInput>, valid_until: i64, commit_frequency_ms: u32) -> Result<()> {
  135. let [payer, entity, account, owner_program, buffer, delegation_record, delegate_account_seeds, delegation_program, system_program] = [
  136. &ctx.accounts.payer,
  137. &ctx.accounts.entity.to_account_info(),
  138. &ctx.accounts.account,
  139. &ctx.accounts.owner_program,
  140. &ctx.accounts.buffer,
  141. &ctx.accounts.delegation_record,
  142. &ctx.accounts.delegate_account_seeds,
  143. &ctx.accounts.delegation_program,
  144. &ctx.accounts.system_program,
  145. ];
  146. let pda_seeds: &[&[u8]] = &[<#component_type>::seed(), &entity.key.to_bytes()];
  147. ::bolt_lang::delegate_account(
  148. payer,
  149. account,
  150. owner_program,
  151. buffer,
  152. delegation_record,
  153. delegate_account_seeds,
  154. delegation_program,
  155. system_program,
  156. pda_seeds,
  157. valid_until,
  158. commit_frequency_ms,
  159. )?;
  160. Ok(())
  161. }
  162. },
  163. quote! {
  164. #[automatically_derived]
  165. #[derive(Accounts)]
  166. pub struct DelegateInput<'info> {
  167. pub payer: Signer<'info>,
  168. #[account()]
  169. pub entity: Account<'info, Entity>,
  170. /// CHECK:
  171. #[account(mut)]
  172. pub account: AccountInfo<'info>,
  173. /// CHECK:`
  174. pub owner_program: AccountInfo<'info>,
  175. /// CHECK:
  176. #[account(mut)]
  177. pub buffer: AccountInfo<'info>,
  178. /// CHECK:`
  179. #[account(mut)]
  180. pub delegation_record: AccountInfo<'info>,
  181. /// CHECK:`
  182. #[account(mut)]
  183. pub delegate_account_seeds: AccountInfo<'info>,
  184. /// CHECK:`
  185. pub delegation_program: AccountInfo<'info>,
  186. /// CHECK:`
  187. pub system_program: AccountInfo<'info>,
  188. }
  189. },
  190. )
  191. }
  192. /// Extracts the type name from attribute arguments.
  193. fn extract_type_name(args: &AttributeArgs) -> Option<Type> {
  194. args.iter().find_map(|arg| {
  195. if let NestedMeta::Meta(syn::Meta::Path(path)) = arg {
  196. Some(Type::Path(syn::TypePath {
  197. qself: None,
  198. path: path.clone(),
  199. }))
  200. } else {
  201. None
  202. }
  203. })
  204. }