lib.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. use proc_macro::TokenStream;
  2. use quote::quote;
  3. use syn::{parse_macro_input, Fields, ItemStruct, LitStr};
  4. /// This macro attribute is used to define a BOLT system extra accounts.
  5. ///
  6. /// The extra account struct define the accounts (which are not components), that the system expect.
  7. ///
  8. ///
  9. /// # Example
  10. /// ```ignore
  11. ///#[extra_accounts]
  12. ///pub struct ExtraAccounts {
  13. /// #[account(address = bolt_lang::solana_program::sysvar::clock::id())]
  14. /// pub clock: AccountInfo<'info>,
  15. /// #[account(address = pubkey!("CbHEFbSQdRN4Wnoby9r16umnJ1zWbULBHg4yqzGQonU1"), signer)]
  16. /// pub my_account: AccountInfo<'info>,
  17. /// #[account(address = Metadata::id())]
  18. /// pub metadata_program: Program<'info, Metadata>,
  19. ///}
  20. ///
  21. /// ```
  22. #[proc_macro_attribute]
  23. pub fn extra_accounts(_attr: TokenStream, item: TokenStream) -> TokenStream {
  24. let input = parse_macro_input!(item as ItemStruct);
  25. let extra_accounts_struct_name = &input.ident;
  26. // Ensure the struct has named fields
  27. let fields = match &input.fields {
  28. Fields::Named(fields) => &fields.named,
  29. _ => panic!("extra_accounts macro only supports structs with named fields"),
  30. };
  31. // Transform fields for the struct definition
  32. let transformed_fields = fields.iter().map(|f| {
  33. let field_name = &f.ident;
  34. let attrs = &f.attrs;
  35. quote! {
  36. #(#attrs)*
  37. pub #field_name: AccountInfo<'info>,
  38. }
  39. });
  40. // Generate the new struct with the Accounts derive and transformed fields
  41. let output_struct = quote! {
  42. #[derive(Accounts)]
  43. pub struct #extra_accounts_struct_name<'info> {
  44. #(#transformed_fields)*
  45. }
  46. };
  47. // Generate the trait for the helper functions
  48. let helper_functions = fields.iter().map(|f| {
  49. let field_name = &f.ident;
  50. quote! {
  51. fn #field_name(&self) -> Result<&'c AccountInfo<'info>>;
  52. }
  53. });
  54. let output_trait = quote! {
  55. pub trait ContextExtensions<'a, 'b, 'c, 'info, T>
  56. {
  57. #(#helper_functions)*
  58. }
  59. };
  60. // Generate the helper functions for the struct
  61. let helper_functions_impl = fields.iter().enumerate().map(|(index, f)| {
  62. let field_name = &f.ident;
  63. let index = syn::Index::from(index); // Create a compile-time index representation
  64. quote! {
  65. fn #field_name(&self) -> Result<&'c AccountInfo<'info>> {
  66. self.remaining_accounts.get(Self::NUMBER_OF_COMPONENTS + #index).ok_or_else(|| ErrorCode::ConstraintAccountIsNone.into())
  67. }
  68. }
  69. });
  70. let output_trait_implementation = quote! {
  71. impl<'a, 'b, 'c, 'info, T: bolt_lang::Bumps> ContextExtensions<'a, 'b, 'c, 'info, T> for Context<'a, 'b, 'c, 'info, T> {
  72. #(#helper_functions_impl)*
  73. }
  74. };
  75. // Combine the struct definition and its implementation into the final TokenStream
  76. let output = quote! {
  77. #output_struct
  78. #output_trait
  79. #output_trait_implementation
  80. };
  81. TokenStream::from(output)
  82. }
  83. #[proc_macro]
  84. pub fn pubkey(input: TokenStream) -> TokenStream {
  85. let input_lit_str = parse_macro_input!(input as LitStr);
  86. let pubkey_str = input_lit_str.value();
  87. // Example using solana_program to create a Pubkey from a string
  88. let expanded = quote! {
  89. bolt_lang::pubkey_from_str(#pubkey_str)
  90. };
  91. TokenStream::from(expanded)
  92. }