# Solana Development Utilities A collection of utilities and macros to enhance Solana program development, focusing on compute unit logging and instruction discriminant generation. ## Features - Log CU for functions and code blocks - Parse logs, with correction for measurement overhead* - Utility functions for Solana-specific operations - Generate const instruction discriminants for Anchor programs *Read below for important notes on CU measurement correction. ### Recommended Usage ```toml [dependencies] sol_dev_proc_macros = "0.1.5" [features] compute-fn = [] ``` ```rust use sol_dev_proc_macros::compute_fn; #[cfg_attr(feature = "compute-fn", compute_fn)] fn my_function() { // Function body } ``` with `Cargo.toml`: Then if you want to enable logging, compile with: ```bash cargo build-sbf --features compute-fn ``` If you build normally, the `compute_fn` attribute will be stripped from the code and introduces **no overhead**. Then ```bash cargo install sol-dev-cli sol-dev-cli parse dir ``` parses all your logs to `log_parsed.json`. ## sol-dev-proc-macros [![Crates.io](https://img.shields.io/crates/v/sol-dev-proc-macros.svg)](https://crates.io/crates/sol-dev-proc-macros) [![Crates.io](https://img.shields.io/crates/d/sol-dev-proc-macros.svg)](https://crates.io/crates/sol-dev-proc-macros) [![Crates.io](https://img.shields.io/crates/l/sol-dev-proc-macros.svg)](https://crates.io/crates/sol-dev-proc-macros) ```toml [dependencies] sol_dev_proc_macros = "0.1.5" ``` ### Usage ```rust use sol_dev_proc_macros::compute_fn; #[compute_fn] fn my_function() { // Function body } ``` Produces log entries: ```json [ "Program log: my_function {{", "Program consumption: 196528 units remaining", "Program consumption: 196528 units consumed", "Program log: }} my_function" ] ``` ## sol-dev-macros [![Crates.io](https://img.shields.io/crates/v/sol-dev-macros.svg)](https://crates.io/crates/sol-dev-macros) [![Crates.io](https://img.shields.io/crates/d/sol-dev-macros.svg)](https://crates.io/crates/sol-dev-macros) [![Crates.io](https://img.shields.io/crates/l/sol-dev-macros.svg)](https://crates.io/crates/sol-dev-macros) ```toml [dependencies] sol_dev_macros = "0.1.5" ``` ### Usage ```rust use sol_dev_macros::compute_fn; compute_fn!("My Block" => { // Your code here some_fn(); }); ``` ```json [ "Program log: My Block {{", "Program consumption: 196528 units remaining", "Program consumption: 196528 units consumed", "Program log: }} My Block" ] ``` ## sol-dev-cli [![Crates.io](https://img.shields.io/crates/v/sol-dev-cli.svg)](https://crates.io/crates/sol-dev-cli) [![Crates.io](https://img.shields.io/crates/d/sol-dev-cli.svg)](https://crates.io/crates/sol-dev-cli) [![Crates.io](https://img.shields.io/crates/l/sol-dev-cli.svg)](https://crates.io/crates/sol-dev-cli) ```bash cargo install sol-dev-cli ``` ### Usage The *CLI* can be used to parse the logs generated by the compute unit logging macros. The logs can be parsed from a file or a directory containing multiple log files. ```bash sol-dev-cli parse dir sol-dev-cli parse file ``` This parses logs into JSON that looks like this: ```json [ { "type": "invoke" "id": "11111111111111111111111111111111111111111111" depth: 1, children: [ { "type": "function", "name": "my_function", "naive_local": 726, "naive_global": 2971, "local": 418, "global": 11.5, "children": [] } ], ] ``` | Measurement | Description | |-------------|-------------| | naive_local | Raw CU consumed within the function, excluding its children, including measurement overhead | | naive_global | Raw CU consumed within the function, excluding its children, including measurement overhead | | local | Adjusted CU consumed within the function, excluding its children, excluding measurement overhead | | global | Adjusted CU consumed within the function, excluding its children, excluding measurement overhead | #### Important Notes on CU measurement Of course measuring CU *itself* costs CU. Some of this extra cost we can account for, some of it we can't. **What we can account for** is the cost of the `msg!` macro and `sol_log_compute_units!` macro. Some of this cost goes to the caller of the function, and some of it is internalized by the function itself. We can correct for this cost with some simple arithmetic, which is reflected in the 'local' and 'global' measurements. **What we can't account for** is that this macro will cause the compiler to generate different code, which will have a different CU cost. This unaccounted difference is why we provide both naive and adjusted measurements. Example: ```rust use sol_dev_macros::compute_fn; fn my_inner_function() { // Function body } #[compute_fn] fn my_function() { // Function body my_inner_function(); } ``` Normally, `my_inner_function` would likely be inlined into `my_function`, turning this into a single function call. Using `compute_fn` will prevent inlining, causing two function calls. We cannot account for this difference easily, so user discretion is advised. In practice, these differences **are significant**, so be weary of this. My advice is to only use measurements to quantify the *relative* cost of different code paths, not the *absolute* cost. ## sol-dev-utils [![Crates.io](https://img.shields.io/crates/v/sol-dev-utils.svg)](https://crates.io/crates/sol-dev-utils) [![Crates.io](https://img.shields.io/crates/d/sol-dev-utils.svg)](https://crates.io/crates/sol-dev-utils) [![Crates.io](https://img.shields.io/crates/l/sol-dev-utils.svg)](https://crates.io/crates/sol-dev-utils) ```toml [dependencies] sol_dev_utils = "0.1.5" ``` ### Usage ```rust use sol_dev_proc_macros::anchor_discriminant; match discriminant { anchor_discriminant![initialize] => initialize(), // This is equivalent to: // anchor_discriminant![global:initialize] => initialize(), anchor_discriminant![process] => process(), anchor_discriminant![custom:finalize] => finalize(), _ => return Err(ProgramError::InvalidInstructionData.into()), } ``` Handles the global namespace automatically if no namespace is provided. ## Contributing Contributions are welcome! Please open an issue or submit a pull request. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.