Theme

Reference

Shank Macros Reference

Shank provides several macros used to annotate Solana Rust programs for IDL extraction:

ShankAccount

Annotates a struct that shank will consider an account containing de/serializable data.

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct Metadata {
    pub update_authority: Pubkey,
    pub mint: Pubkey,
    pub primary_sale_happened: bool,
}

Field Attributes

#[idl_type(...)] Attribute

This attribute allows overriding how Shank interprets a field's type when generating the IDL. Useful for:

  1. Fields with wrapper types that should be treated as their inner types
  2. Fields storing enum values as primitives
  3. Fields with complex types needing simpler representations

Supports two formats:

  • String literal: #[idl_type("TypeName")]
  • Direct type: #[idl_type(TypeName)]
#[derive(Clone, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct MyAccount {
    // Field stored as u8 but representing an enum
    #[idl_type("MyEnum")]
    pub enum_as_byte: u8,

    // Field with a wrapper type treated as a simpler type
    #[idl_type("u64")]
    pub wrapped_u64: CustomU64Wrapper,
}

#[padding] Attribute

Indicates that a field is used for padding and should be marked as such in the IDL.

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct PaddedAccount {
    pub active_field: u64,
    
    #[padding]
    pub unused_space: [u8; 32],
    
    pub another_field: String,
}

Note: The fields of a ShankAccount struct can reference other types as long as they are annotated with BorshSerialize, BorshDeserialize, or ShankType.

ShankInstruction

Annotates the program Instruction Enum to include #[account] attributes.

#[derive(Debug, Clone, ShankInstruction, BorshSerialize, BorshDeserialize)]
#[rustfmt::skip]
pub enum MyProgramInstruction {
    /// Creates a new account with the given name
    #[account(0, writable, signer, name="user", desc="User account")]
    #[account(1, writable, name="account", desc="Account to create")]
    #[account(2, name="system_program", desc="System program")]
    CreateAccount {
        name: String,
        space: u64,
    },
    
    /// Updates an existing account
    #[account(0, writable, signer, name="authority", desc="Account authority")]
    #[account(1, writable, name="account", desc="Account to update")]
    UpdateAccount {
        new_name: String,
    },
}

#[account] Attribute

Configures accounts for each instruction variant. The attribute follows this format:

#[account(index, mutability?, signer?, name="account_name", desc="Account description")]

Where:

  • index: The position of the account in the accounts array (0-based)
  • mutability?: Optional. Use writable if the account will be modified
  • signer?: Optional. Use signer if the account must sign the transaction
  • name="account_name": Required. The name of the account
  • desc="Account description": Optional. A description of the account's purpose

Account Attribute Examples

// Read-only account
#[account(0, name="mint", desc="Mint account")]

// Writable account
#[account(1, writable, name="token_account", desc="Token account to modify")]

// Signer account
#[account(2, signer, name="owner", desc="Account owner")]

// Writable signer account
#[account(3, writable, signer, name="authority", desc="Program authority")]

// Optional account
#[account(4, optional, name="delegate", desc="Optional delegate account")]

ShankType

Marks structs or enums with serializable data that are used as custom types in accounts or instructions.

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankType)]
pub enum TokenState {
    Uninitialized,
    Initialized,
    Frozen,
}

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankType)]
pub struct Creator {
    pub address: Pubkey,
    pub verified: bool,
    pub share: u8,
}

ShankBuilder

Generates instruction builders for each annotated instruction, creating builder pattern implementations that simplify instruction construction.

#[derive(Debug, Clone, ShankInstruction, ShankBuilder, BorshSerialize, BorshDeserialize)]
pub enum MyInstruction {
    CreateAccount { name: String, space: u64 },
}

This generates builder methods that allow for fluent instruction creation.

ShankContext

Creates account structs for instructions, generating context structures for program instructions that integrate with Anchor framework patterns.

#[derive(Debug, Clone, ShankInstruction, ShankContext, BorshSerialize, BorshDeserialize)]
pub enum MyInstruction {
    #[account(0, writable, signer, name="payer")]
    #[account(1, writable, name="account")]
    CreateAccount { name: String },
}

This generates context structs that match the account requirements defined in the instruction.

Best Practices

  1. Always use descriptive names in #[account] attributes
  2. Include descriptions for better documentation
  3. Use #[idl_type()] sparingly - only when type overrides are necessary
  4. Mark padding fields appropriately with #[padding]
  5. Ensure all referenced types are properly annotated with Borsh traits
  6. Group related macros when they work together (e.g., ShankInstruction + ShankBuilder)

Common Patterns

Account with Custom Types

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankAccount)]
pub struct TokenAccount {
    pub mint: Pubkey,
    pub owner: Pubkey,
    pub amount: u64,
    pub state: TokenState, // References ShankType
}

#[derive(Clone, BorshSerialize, BorshDeserialize, ShankType)]
pub enum TokenState {
    Uninitialized,
    Initialized,
    Frozen,
}

Complete Instruction Definition

#[derive(Debug, Clone, ShankInstruction, BorshSerialize, BorshDeserialize)]
#[rustfmt::skip]
pub enum TokenInstruction {
    /// Transfer tokens between accounts
    #[account(0, writable, name="source", desc="Source token account")]
    #[account(1, writable, name="destination", desc="Destination token account")]
    #[account(2, signer, name="owner", desc="Owner of source account")]
    Transfer {
        amount: u64,
    },
}

This reference covers all the essential Shank macros and their usage patterns for effective IDL generation from Solana programs.

Previous
Getting Started