# pinocchio builds Solana programs without dragging a crate tree behind it

> pinocchio is a Rust library for writing Solana programs whose only dependencies are types from the Solana SDK itself, with no standard library and no allocations in the entrypoint path. The design pays for itself in compute units and binary size, and the repository is also a set of reference programs, from a system program to a token program to a transfer hook interface.

**anza-xyz/pinocchio** — Create Solana programs with no external dependencies attached

- Repository: https://github.com/anza-xyz/pinocchio
- Stars: 975 · Forks: 236
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/anza-xyz-pinocchio

## What dropping the dependency tree actually buys

The pitch in the README is one line, create Solana programs with no external dependencies attached. The overview expands it. Pinocchio is a no external dependencies library to create Solana programs in Rust, and the only dependencies are types from the Solana SDK specifically designed for on-chain programs. The stated reason is that this mitigates dependency issues, and the second benefit is that it offers an efficient zero copy library to write programs, optimised in terms of both compute unit consumption and binary size. Both of those are on-chain constraints rather than developer preferences. A program that ships to a validator pays for its binary and for its compute, and a dependency tree in a program is a dependency tree in the audit, in the build and in the supply chain. The features list makes the same point in four items, a no_std crate, only dependencies to Solana SDK types, an efficient entrypoint macro with no copies or allocations, and a lightweight lazy entrypoint that gives more control over how the input is parsed.

## The entrypoint macro sets up three things at once

Getting started is one command, `cargo add pinocchio`, and then the entrypoint. A Solana program has to define an entrypoint, the function the runtime calls to begin execution, and the `entrypoint!` macro emits the boilerplate for it. The macro is a convenience that invokes three other macros, and it is worth knowing which three, because you can use them individually. `program_entrypoint!` declares the program entrypoint itself. `default_allocator!` declares the default bump global allocator. `default_panic_handler!` declares the default panic hook, which works in combination with the standard library panic handler. There is a fourth case, and it is the one that catches people out. When all of your dependencies are no_std, there is no standard library panic handler to cooperate with, so you should use `nostd_panic_handler!` instead of `default_panic_handler!`. The README also notes that you do not need this when any dependency is std, since the Rust compiler will emit a panic handler in that case.

## Parsing the input up front is what the lazy entrypoint avoids

The input a program receives is split into three entities, the program id, which is the address of the program being called, the accounts received by the instruction, and the instruction data. The standard entrypoint parses the whole input and hands you those three separately, and the documentation is explicit about the cost, that this consumes compute units before the program begins its execution. In some cases it is better for a program to have more control over when the parsing happens, including whether parsing is needed at all, and that is the purpose of the `lazy_program_entrypoint!` macro. Where the standard version parses everything up front, the lazy version only wraps the program input and provides methods to parse it on demand. So the choice is a real one rather than a stylistic one, and it depends on your program's shape. A program that only sometimes needs the accounts can avoid paying for the parse, and a program that always needs them is paying for a lazy path it does not benefit from.

## process_program_input is the escape hatch for a fast path

For programs that want maximum control over the entrypoint, the library exposes the `process_program_input` function. The documentation explains the relationship precisely, it is the same function used by the `program_entrypoint!` macro, exposed as a public API so it can be called directly from a custom entrypoint, which allows you to implement fast path optimisations or custom pre-processing logic before falling back to standard input processing. The example makes the shape of that concrete. A custom `extern "C"` entrypoint reads the account count straight out of the program input, returns immediately when there are no accounts, and otherwise delegates to `process_program_input` with the instruction handler:

```rust
#[no_mangle]
pub unsafe extern "C" fn entrypoint(
    program_input: *mut u8,
    instruction_data: *mut u8,
) -> u64 {
    // Fast path: check the number of accounts
    let num_accounts = unsafe { *(program_input as *const u64) };
    if num_accounts == 0 {
        log("Fast path - no accounts!");
        return 0;
    }

    // Standard path: delegate to `process_program_input`
    unsafe { process_program_input(program_input, instruction_data, process_instruction) }
}
```

So the escape hatch is not a different parsing model, it is the same one with your own prologue in front of it. The related variations are the `lazy_program_entrypoint` and a `no_allocator` option, and when you use those you have to specify the program entrypoint, the global allocator and the panic handler individually.

## The workspace is a set of reference programs

The Cargo workspace members list is a good description of what the project is for. There is the `sdk` directory, which is the library itself, and then a `programs/` directory with associated token account, memo, system, token, token-2022 and transfer hook interface. Those are not toy examples, they are the program shapes a Solana developer actually has to write, and having the system program and two generations of the token program implemented in this style gives you a reference for the account layouts and the zero copy access patterns. The workspace dependencies show the surface the library sits on, with `solana-account-view`, `solana-address`, `solana-define-syscall`, `solana-instruction-view`, `solana-nullable`, `solana-program-error` and `solana-zero-copy`, each pinned to a major version, and the library itself referenced with `default-features = false` so a consumer chooses what it gets. The `token-2022` and `transfer-hook-interface` crates are published separately, which is worth noticing, since it means the parts a program needs are separately versioned from the core.

## The build scripts are TypeScript driving cargo

The repository is a Cargo workspace with a Node side, and the split is unusual enough to be worth describing. The root `package.json` has a set of scripts that each run a TypeScript file through tsx, with `cargo-audit`, `build-sbf`, `clippy`, `doc`, `format`, `hack`, `lint`, `miri`, `semver`, `spellcheck` and `test`, and the spellcheck one is a direct call to `cargo spellcheck` with a single job and a single code. The Node dependencies are minimal, `@iarna/toml` for reading the manifest, tsx to run the TypeScript, TypeScript itself and zx, which is the shell scripting library that makes the orchestration readable. The toolchain metadata in the Cargo manifest is the other interesting part, with the build and test toolchains pinned to 1.89.0 while the format and lint toolchains are pinned to a nightly, `nightly-2026-01-22`, which is the usual arrangement since rustfmt and clippy lints move on nightly. The workspace metadata also pins the Solana CLI to 3.1.8 and records the release commit and tag message templates.

## What to check before you build a program on it

The practical checklist is short. The licence is Apache-2.0 at the workspace level, and the edition is 2021 with a Rust version of 1.89.0 as the minimum, so your toolchain has to be recent. The library is published on crates.io and documented on docs.rs, and the recent release list shows the crates moving independently, with the transfer hook interface at 0.1.0 on 2026-09-30, the token 2022 crate at 0.4.0 on 2026-08-03 and the core library at 0.11.2 on 2026-06-11. That last number is the one to watch, because a zero copy library is exactly the kind of thing where a version bump can change how a field is read. The repository also carries a `SECURITY.MD`, which is where a report about a program built on it belongs. And the last push to the repository was on 2026-10-16, so the tree is moving, which for a dependency this central is a reason to pin rather than to track the branch.

## Conclusion

pinocchio suits a team writing an on-chain program where compute unit consumption and binary size are real constraints, since the entrypoint path allocates nothing and copies nothing. It is a poor fit if you want the convenience of the wider Rust ecosystem, since the whole point is that you give up external crates, and it is a poor fit if your program depends on something like a borsh derive or a token library that pulls in more than you want. Before you build on it, check three things: which Solana SDK types you need, since the workspace pins specific major versions of the account view, address, instruction view, program error and zero copy crates, whether you want the standard entrypoint or the lazy one, since the choice is about whether you pay the parse cost up front, and the toolchain requirements, with Rust 1.89 as the workspace minimum and a nightly pinned separately for formatting and linting.

## FAQ

### What is pinocchio and what does it avoid?

It is a Rust library for writing Solana programs whose only dependencies are types from the Solana SDK designed for on-chain programs. The README calls it a no external dependencies library, and the stated benefits are fewer dependency issues and an efficient zero copy approach optimised for compute unit consumption and binary size.

### How do I add pinocchio to a Solana program?

With `cargo add pinocchio` from the project folder. The entrypoint is then defined with the `entrypoint!` macro, which invokes `program_entrypoint!` to declare the entrypoint, `default_allocator!` for the bump global allocator, and `default_panic_handler!` for the panic hook.

### What is the difference between the standard and lazy entrypoints?

The standard entrypoint parses the whole input and provides the program id, the accounts and the instruction data separately, which consumes compute units before the program starts. The lazy version only wraps the program input and provides methods to parse it on demand, which is useful when a program does not always need the accounts.

### What does process_program_input let me do?

It is the same function the program_entrypoint! macro uses, exposed as a public API so a custom entrypoint can call it directly. That lets you implement fast path optimisations or custom pre-processing before falling back to standard input processing, as in the example that checks the account count and returns early.

### Which Rust and Solana versions does pinocchio need?

The workspace requires Rust 1.89.0 as the minimum and pins the Solana CLI to 3.1.8, with separate nightly toolchains for formatting and linting. The core library is at 0.11.2 from 2026-06-11, and the repository was last pushed on 2026-10-16.

## Sources

- [anza-xyz/pinocchio on GitHub](https://github.com/anza-xyz/pinocchio)
- [Issues](https://github.com/anza-xyz/pinocchio/issues)
- [License: Apache-2.0](https://github.com/anza-xyz/pinocchio/blob/main/LICENSE)
- [README](https://github.com/anza-xyz/pinocchio/blob/main/README.md)
- [Releases](https://github.com/anza-xyz/pinocchio/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/anza-xyz-pinocchio
