bolt_anchor_attribute_program/lib.rs
1extern crate proc_macro;
2
3mod declare_program;
4
5use declare_program::DeclareProgram;
6use quote::ToTokens;
7use syn::parse_macro_input;
8
9/// The `#[program]` attribute defines the module containing all instruction
10/// handlers defining all entries into a Solana program.
11#[proc_macro_attribute]
12pub fn program(
13 _args: proc_macro::TokenStream,
14 input: proc_macro::TokenStream,
15) -> proc_macro::TokenStream {
16 parse_macro_input!(input as bolt_anchor_syn::Program)
17 .to_token_stream()
18 .into()
19}
20
21/// Declare an external program based on its IDL.
22///
23/// The IDL of the program must exist in a directory named `idls`. This directory can be at any
24/// depth, e.g. both inside the program's directory (`<PROGRAM_DIR>/idls`) and inside Anchor
25/// workspace root directory (`<PROGRAM_DIR>/../../idls`) are valid.
26///
27/// # Usage
28///
29/// ```rs
30/// declare_program!(program_name);
31/// ```
32///
33/// This generates a module named `external_program` that can be used to interact with the program
34/// without having to add the program's crate as a dependency.
35///
36/// Both on-chain and off-chain usage is supported.
37///
38/// Use `cargo doc --open` to see the generated modules and their documentation.
39///
40/// # Note
41///
42/// Re-defining the same program to use the same definitions should be avoided since this results
43/// in larger binary size.
44///
45/// A program should only be defined once. If you have multiple programs that depend on the same
46/// definition, you should consider creating a separate crate for the external program definition
47/// and reuse it in your programs.
48#[proc_macro]
49pub fn declare_program(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
50 parse_macro_input!(input as DeclareProgram)
51 .to_token_stream()
52 .into()
53}
54
55/// The `#[interface]` attribute is used to mark an instruction as belonging
56/// to an interface implementation, thus transforming its discriminator to the
57/// proper bytes for that interface instruction.
58///
59/// # Example
60///
61/// ```rust,ignore
62/// use anchor_lang::prelude::*;
63///
64/// // SPL Transfer Hook Interface: `Execute` instruction.
65/// //
66/// // This instruction is invoked by Token-2022 when a transfer occurs,
67/// // if a mint has specified this program as its transfer hook.
68/// #[interface(spl_transfer_hook_interface::execute)]
69/// pub fn execute_transfer(ctx: Context<Execute>, amount: u64) -> Result<()> {
70/// // Check that all extra accounts were provided
71/// let data = ctx.accounts.extra_metas_account.try_borrow_data()?;
72/// ExtraAccountMetaList::check_account_infos::<ExecuteInstruction>(
73/// &ctx.accounts.to_account_infos(),
74/// &TransferHookInstruction::Execute { amount }.pack(),
75/// &ctx.program_id,
76/// &data,
77/// )?;
78///
79/// // Or maybe perform some custom logic
80/// if ctx.accounts.token_metadata.mint != ctx.accounts.token_account.mint {
81/// return Err(ProgramError::IncorrectAccount);
82/// }
83///
84/// Ok(())
85/// }
86/// ```
87#[cfg(feature = "interface-instructions")]
88#[proc_macro_attribute]
89pub fn interface(
90 _args: proc_macro::TokenStream,
91 input: proc_macro::TokenStream,
92) -> proc_macro::TokenStream {
93 // This macro itself is a no-op, but must be defined as a proc-macro
94 // attribute to be used on a function as the `#[interface]` attribute.
95 //
96 // The `#[program]` macro will detect this attribute and transform the
97 // discriminator.
98 input
99}