lib.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. extern crate proc_macro;
  2. #[cfg(feature = "event-cpi")]
  3. use anchor_syn::parser::accounts::event_cpi::{add_event_cpi_accounts, EventAuthority};
  4. use quote::quote;
  5. use syn::parse_macro_input;
  6. /// The event attribute allows a struct to be used with
  7. /// [emit!](./macro.emit.html) so that programs can log significant events in
  8. /// their programs that clients can subscribe to. Currently, this macro is for
  9. /// structs only.
  10. ///
  11. /// See the [`emit!` macro](emit!) for an example.
  12. #[proc_macro_attribute]
  13. pub fn event(
  14. _args: proc_macro::TokenStream,
  15. input: proc_macro::TokenStream,
  16. ) -> proc_macro::TokenStream {
  17. let event_strct = parse_macro_input!(input as syn::ItemStruct);
  18. let event_name = &event_strct.ident;
  19. let discriminator: proc_macro2::TokenStream = {
  20. let discriminator_preimage = format!("event:{event_name}");
  21. let mut discriminator = [0u8; 8];
  22. discriminator.copy_from_slice(
  23. &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8],
  24. );
  25. format!("{discriminator:?}").parse().unwrap()
  26. };
  27. let ret = quote! {
  28. #[derive(anchor_lang::__private::EventIndex, AnchorSerialize, AnchorDeserialize)]
  29. #event_strct
  30. impl anchor_lang::Event for #event_name {
  31. fn data(&self) -> Vec<u8> {
  32. let mut d = #discriminator.to_vec();
  33. d.append(&mut self.try_to_vec().unwrap());
  34. d
  35. }
  36. }
  37. impl anchor_lang::Discriminator for #event_name {
  38. const DISCRIMINATOR: [u8; 8] = #discriminator;
  39. }
  40. };
  41. #[cfg(feature = "idl-build")]
  42. {
  43. let idl_gen = anchor_syn::idl::build::gen_idl_print_function_for_event(&event_strct);
  44. return proc_macro::TokenStream::from(quote! {
  45. #ret
  46. #idl_gen
  47. });
  48. }
  49. #[allow(unreachable_code)]
  50. proc_macro::TokenStream::from(ret)
  51. }
  52. // EventIndex is a marker macro. It functionally does nothing other than
  53. // allow one to mark fields with the `#[index]` inert attribute, which is
  54. // used to add metadata to IDLs.
  55. #[proc_macro_derive(EventIndex, attributes(index))]
  56. pub fn derive_event(_item: proc_macro::TokenStream) -> proc_macro::TokenStream {
  57. proc_macro::TokenStream::from(quote! {})
  58. }
  59. /// Logs an event that can be subscribed to by clients.
  60. /// Uses the [`sol_log_data`](https://docs.rs/solana-program/latest/solana_program/log/fn.sol_log_data.html)
  61. /// syscall which results in the following log:
  62. /// ```ignore
  63. /// Program data: <Base64EncodedEvent>
  64. /// ```
  65. /// # Example
  66. ///
  67. /// ```rust,ignore
  68. /// use anchor_lang::prelude::*;
  69. ///
  70. /// // handler function inside #[program]
  71. /// pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
  72. /// emit!(MyEvent {
  73. /// data: 5,
  74. /// label: [1,2,3,4,5],
  75. /// });
  76. /// Ok(())
  77. /// }
  78. ///
  79. /// #[event]
  80. /// pub struct MyEvent {
  81. /// pub data: u64,
  82. /// pub label: [u8; 5],
  83. /// }
  84. /// ```
  85. #[proc_macro]
  86. pub fn emit(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  87. let data: proc_macro2::TokenStream = input.into();
  88. proc_macro::TokenStream::from(quote! {
  89. {
  90. anchor_lang::solana_program::log::sol_log_data(&[&anchor_lang::Event::data(&#data)]);
  91. }
  92. })
  93. }
  94. /// Log an event by making a self-CPI that can be subscribed to by clients.
  95. ///
  96. /// This way of logging events is more reliable than [`emit!`](emit!) because RPCs are less likely
  97. /// to truncate CPI information than program logs.
  98. ///
  99. /// Uses a [`invoke_signed`](https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html)
  100. /// syscall to store the event data in the ledger, which results in the data being stored in the
  101. /// transaction metadata.
  102. ///
  103. /// This method requires the usage of an additional PDA to guarantee that the self-CPI is truly
  104. /// being invoked by the same program. Requiring this PDA to be a signer during `invoke_signed`
  105. /// syscall ensures that the program is the one doing the logging.
  106. ///
  107. /// The necessary accounts are added to the accounts struct via [`#[event_cpi]`](event_cpi)
  108. /// attribute macro.
  109. ///
  110. /// # Example
  111. ///
  112. /// ```ignore
  113. /// use anchor_lang::prelude::*;
  114. ///
  115. /// #[program]
  116. /// pub mod my_program {
  117. /// use super::*;
  118. ///
  119. /// pub fn my_instruction(ctx: Context<MyInstruction>) -> Result<()> {
  120. /// emit_cpi!(MyEvent { data: 42 });
  121. /// Ok(())
  122. /// }
  123. /// }
  124. ///
  125. /// #[event_cpi]
  126. /// #[derive(Accounts)]
  127. /// pub struct MyInstruction {}
  128. ///
  129. /// #[event]
  130. /// pub struct MyEvent {
  131. /// pub data: u64,
  132. /// }
  133. /// ```
  134. ///
  135. /// **NOTE:** This macro requires `ctx` to be in scope.
  136. ///
  137. /// *Only available with `event-cpi` feature enabled.*
  138. #[cfg(feature = "event-cpi")]
  139. #[proc_macro]
  140. pub fn emit_cpi(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  141. let event_struct = parse_macro_input!(input as syn::Expr);
  142. let authority = EventAuthority::get();
  143. let authority_name = authority.name_token_stream();
  144. let authority_name_str = authority.name;
  145. let authority_seeds = authority.seeds;
  146. proc_macro::TokenStream::from(quote! {
  147. {
  148. let authority_info = ctx.accounts.#authority_name.to_account_info();
  149. let authority_bump = *ctx.bumps.get(#authority_name_str).unwrap();
  150. let disc = anchor_lang::event::EVENT_IX_TAG_LE;
  151. let inner_data = anchor_lang::Event::data(&#event_struct);
  152. let ix_data: Vec<u8> = disc.into_iter().chain(inner_data.into_iter()).collect();
  153. let ix = anchor_lang::solana_program::instruction::Instruction::new_with_bytes(
  154. crate::ID,
  155. &ix_data,
  156. vec![
  157. anchor_lang::solana_program::instruction::AccountMeta::new_readonly(
  158. *authority_info.key,
  159. true,
  160. ),
  161. ],
  162. );
  163. anchor_lang::solana_program::program::invoke_signed(
  164. &ix,
  165. &[authority_info],
  166. &[&[#authority_seeds, &[authority_bump]]],
  167. )
  168. .map_err(anchor_lang::error::Error::from)?;
  169. }
  170. })
  171. }
  172. /// An attribute macro to add necessary event CPI accounts to the given accounts struct.
  173. ///
  174. /// Two accounts named `event_authority` and `program` will be appended to the list of accounts.
  175. ///
  176. /// # Example
  177. ///
  178. /// ```ignore
  179. /// #[event_cpi]
  180. /// #[derive(Accounts)]
  181. /// pub struct MyInstruction<'info> {
  182. /// pub signer: Signer<'info>,
  183. /// }
  184. /// ```
  185. ///
  186. /// The code above will be expanded to:
  187. ///
  188. /// ```ignore
  189. /// #[derive(Accounts)]
  190. /// pub struct MyInstruction<'info> {
  191. /// pub signer: Signer<'info>,
  192. /// /// CHECK: Only the event authority can invoke self-CPI
  193. /// #[account(seeds = [b"__event_authority"], bump)]
  194. /// pub event_authority: AccountInfo<'info>,
  195. /// /// CHECK: Self-CPI will fail if the program is not the current program
  196. /// pub program: AccountInfo<'info>,
  197. /// }
  198. /// ```
  199. ///
  200. /// See [`emit_cpi!`](emit_cpi!) for a full example.
  201. ///
  202. /// *Only available with `event-cpi` feature enabled.*
  203. #[cfg(feature = "event-cpi")]
  204. #[proc_macro_attribute]
  205. pub fn event_cpi(
  206. _attr: proc_macro::TokenStream,
  207. input: proc_macro::TokenStream,
  208. ) -> proc_macro::TokenStream {
  209. let accounts_struct = parse_macro_input!(input as syn::ItemStruct);
  210. let accounts_struct = add_event_cpi_accounts(&accounts_struct).unwrap();
  211. proc_macro::TokenStream::from(quote! {#accounts_struct})
  212. }