pinocchio & steel
Pinocchio is a minimal Rust framework for native Solana programs emphasizing small binaries and low CU overhead on Agave 4.1.1. Steel layers structure (accounts, instructions, registry) atop Pinocchio-style patterns for teams that want conventions without full Anchor 0.32.1 codegen. Both target developers optimizing beyond anchor-lang defaults.
Recipe
Quick-reference recipe card - copy-paste ready.
[dependencies]
pinocchio = "0.8"
# steel = "4.0" # add when using Steel framework cratesuse pinocchio::{
account_info::AccountInfo,
entrypoint,
program_error::ProgramError,
pubkey::Pubkey,
ProgramResult,
};
entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
if data.is_empty() {
return Err(ProgramError::InvalidInstructionData);
}
let _ = accounts;
Ok(())
}When to reach for this:
- CU and binary size budgets are tight on hot instructions.
- You refuse Anchor macro magic but want safer patterns than raw
solana-program. - Steel registry models fit your multi-instruction program layout.
- Migrating performance-critical paths from Anchor with tests in LiteSVM 0.6.x.
Working Example
use pinocchio::{
account_info::AccountInfo,
entrypoint,
msg,
program_error::ProgramError,
pubkey::Pubkey,
ProgramResult,
};
entrypoint!(process_instruction);
enum MyInstruction {
Ping,
}
impl MyInstruction {
fn unpack(data: &[u8]) -> Result<Self, ProgramError> {
match data.first() {
Some(0) => Ok(MyInstruction::Ping),
_ => Err(ProgramError::InvalidInstructionData),
}
}
}
fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
match MyInstruction::unpack(data)? {
MyInstruction::Ping => {
let payer = accounts.get(0).ok_or(ProgramError::NotEnoughAccountKeys)?;
if !payer.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
msg!("ping from {}", payer.key());
Ok(())
}
}
}What this demonstrates:
- Manual instruction enum replaces Anchor discriminators.
- Explicit account indexing with signer validation.
msg!logging compatible with RPC log parsing.- No IDL generated - clients pack matching instruction bytes.
Deep Dive
How It Works
- Pinocchio supplies entrypoint helpers and lightweight account accessors.
- Steel adds account registries, typed handlers, and testing utilities.
- Both compile with
cargo build-sbflike native programs. - Clients must hand-pack or use Codama/Shank IDLs if you publish one separately.
Rust Crate Table
| Crate | Role | IDL |
|---|---|---|
pinocchio | Minimal program runtime helpers | Manual |
pinocchio-log | Efficient logging | N/A |
steel | Framework structure on Pinocchio | Optional conventions |
steel-cli | Scaffolding | N/A |
TypeScript / NPM Table
| Package | Role | Notes |
|---|---|---|
@solana/kit | Client tx building | 7.0.0 |
| Codama | If you publish an IDL | From Shank/Anchor export |
| Hand-packed ix | Simple programs | Document byte layout in docs |
Rust Notes
// Measure CU before/after migrating from Anchor
// pinocchio::cpi helpers exist for common programs - check docs for coverageGotchas
- No Anchor account constraints - easy to miss owner checks. Fix: checklist per instruction; LiteSVM tests.
- Instruction layout undocumented - client bugs. Fix: publish IDL or strict spec in repo.
- Steel version skew with Pinocchio - build failures. Fix: follow Steel release compatibility table.
- Over-optimizing prematurely - slower delivery. Fix: profile Anchor first; migrate hot paths only.
- SPL CPI verbosity - more boilerplate than
anchor-spl. Fix: thin internal helpers.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
anchor-lang 0.32.1 | IDL + velocity | Extreme size limits |
Raw solana-program | Total control | Need framework guardrails |
solana-nostd-entrypoint | Tiny entry | Want Pinocchio ergonomics |
| Off-chain compute | Heavy logic | Must be trustless on-chain |
FAQs
Anchor vs Pinocchio CU savings?
Instruction-dependent - benchmark with simulate_transaction and LiteSVM.
Steel without Pinocchio?
Steel is designed around Pinocchio-style programs - follow upstream guidance.
IDL options?
Shank or manual JSON; feed Codama for @solana/kit clients.
Testing?
LiteSVM 0.6.x and Mollusk are common; Bankrun for TS integration tests.
Upgrade path from Anchor?
Extract hot instructions incrementally; keep IDL clients during transition.
Token CPI?
Use pinocchio token helpers or raw spl-token instructions.
Binary size?
Compare ELF sizes with solana program show after deploy.
Team skill floor?
Higher than Anchor - budget review time for security checks.
Interop with Anchor programs?
CPI works at program level - framework choice is per program binary.
Logging cost?
Still spend CU - gate msg! behind feature flags in production builds if needed.
Related
- Pinocchio Basics - framework intro
- Steel Framework - Steel guide
- When to Go Native - decision framing
- solana-program & solana-sdk - base crates
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.