1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
extern crate proc_macro;
extern crate quote;
extern crate syn;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};
/// Generates a discriminant for a given function name within a global namespace.
///
/// This macro is used to create a unique identifier (discriminant) for a function
/// within the Anchor framework. It's typically used for generating unique
/// instruction identifiers in Solana programs.
///
/// Anchor typically requires adds the namespace to the function name to generate
/// a unique identifier. In this macro, we default to the global namespace if no
/// namespace is provided.
///
/// # Arguments
///
/// * `input` - A `TokenStream` containing the function name to generate a discriminant for.
///
/// # Returns
///
/// A `TokenStream` representing an array of bytes, which is the generated discriminant.
///
/// # Example
///
/// ```rust
/// use sol_dev_proc_macros::anchor_discriminant;
/// const DISCRIMINANT: [u8; 8] = anchor_discriminant!(initialize);
/// const DISCRIMINANT_WITH_NAMESPACE: [u8; 8] = anchor_discriminant!(global:initialize);
/// assert_eq!(
/// DISCRIMINANT,
/// [175, 175, 109, 31, 13, 152, 155, 237]
/// );
/// assert_eq!(
/// DISCRIMINANT_WITH_NAMESPACE,
/// DISCRIMINANT
/// );
/// ```
#[proc_macro]
pub fn anchor_discriminant(input: TokenStream) -> TokenStream {
const NAMESPACE: &str = "global";
let function_name = input.to_string();
// If the function does not contain a namespace, we add the global namespace.
let full_name = if function_name.contains(':') {
function_name
} else {
format!("{}:{}", NAMESPACE, function_name)
};
let arr = sol_dev_utils::anchor_discriminant(&full_name);
let expanded = quote::quote! {
[#(#arr),*]
};
TokenStream::from(expanded)
}
/// Attribute macro for instrumenting functions with compute unit logging.
///
/// This macro wraps the decorated function with additional logging statements
/// that print the function name and the number of compute units used before and after
/// the function execution.
///
/// # Usage
///
/// ```rust,ignore
/// #[compute_fn]
/// fn my_function() {
/// // Function body
/// }
/// ```
///
/// # Effects
///
/// - Adds a log message with the function name at the start of execution.
/// - Logs the number of compute units before and after the function execution.
/// - Adds a closing log message with the function name at the end of execution.
///
/// # Note
///
/// Total extra compute units used per `compute_fn!` call: 409 CU
/// For more details, see:
/// - https://github.com/anza-xyz/agave/blob/d88050cda335f87e872eddbdf8506bc063f039d3/programs/bpf_loader/src/syscalls/logging.rs#L70
/// - https://github.com/anza-xyz/agave/blob/d88050cda335f87e872eddbdf8506bc063f039d3/program-runtime/src/compute_budget.rs#L150
#[proc_macro_attribute]
pub fn compute_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let block = &input.block;
input.block = syn::parse_quote!({
::solana_program::msg!(concat!(stringify!(#fn_name), " {"));
::solana_program::log::sol_log_compute_units();
let __result = (|| #block)();
::solana_program::log::sol_log_compute_units();
::solana_program::msg!(concat!("} // ", stringify!(#fn_name)));
__result
});
quote!(#input).into()
}