#module #modular #instructions #macro #modular-program

macro anchor-modular-program

Replacement #[program] macro that allows specifying additional instruction modules

4 releases

new 0.1.3 Apr 25, 2025
0.1.2 Apr 25, 2025
0.1.1 Apr 25, 2025
0.1.0 Apr 24, 2025

#691 in Procedural macros

32 downloads per month

MIT license

13KB
157 lines

Anchor Modular Program

Replacement #[program] macro that allows specifying additional instruction modules

Usage

Lets say you want to import instructions from your module extra::instructions, and you have the required types (contexts, instruction argument types) at extra::types:


use anchor_lang::prelude::*;
use anchor_modular_program::*;

declare_id!("...");

mod extra;
use extra::types::*;

#[modular_program(
    modules=[
        extra::instructions
    ]
)]
mod my_program {
    use super::*;
}

Instructions from extra::instructions will be included (forwarded, rather than included directly), prefixed with extra_.

Overrides

As well as specifying a module path in the local program, you can provide a spec:

#[modular_program(
    modules=[
        {
            module: etc,                   // rust module path to instructions module
            prefix: "etc",                 // prefixed with "etc_", or "" for no prefix
            file_path: "./src/etc/mod.rs", // path to instructions
            wrapper: path::to::macro       // see [#wrapper-macros](Wrapper Macros)
        }
    ]
)

Examples

See the Test Program for examples.

Wrapper Macros

It's possible to specify a macro that will define how the instruction is called, the macro takes the path to the function and the instruction parameters, i.e.:

macro_rules! call_instruction_macro {
    ($ix:path, $ctx:ident: $ctx_type:ty $(, $arg:ident: $arg_type:ty )*) => {
      { msg!("Before");
        // If you want to use a custom context wrapper, you need to
        // `use` it `as Context` in your instructions module
        let ctx = MyContextWrapper::new($ctx);
        let out = $ix(ctx $(, $arg))?;
        msg!("After");
        Ok(out)
      }
    };
}

How it works

  1. In the above example, extra::instructions is converted to the file path src/extra/instructions.rs.
  2. The file is read and parsed, using anchor's own Program parsing.
  3. Relay instructions are created, and appended to the TokenStream of the main program module.
  4. The final program is built from the modified TokenStream.

Contributing

PRs welcome

Dependencies

~4MB
~91K SLoC