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:
- Fields with wrapper types that should be treated as their inner types
- Fields storing enum values as primitives
- 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. Usewritable
if the account will be modifiedsigner?
: Optional. Usesigner
if the account must sign the transactionname="account_name"
: Required. The name of the accountdesc="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
- Always use descriptive names in
#[account]
attributes - Include descriptions for better documentation
- Use
#[idl_type()]
sparingly - only when type overrides are necessary - Mark padding fields appropriately with
#[padding]
- Ensure all referenced types are properly annotated with Borsh traits
- 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.