Drop-in SPL Token on Pinocchio. no_std, zero-copy, byte-for-byte compatible.
gENFYvwL1qN8qFJxewhCzyvNXonEHreFXiE6AK8pino
[dependencies]
pinocchio-token = "0.4"
pinocchio = "0.7"
# Drop-in replacement — same program ID, same instruction layout.
# Your existing clients keep working. You just pay less compute.
Streaming p-token transactions as they confirm on mainnet-beta. Every CU saved against the legacy SPL Token program is real and accruing right now.
Methodology: CU is read from meta.computeUnitsConsumed on every confirmed signature returned by getSignaturesForAddress(TokenkegQ…). Savings update with every batch and extrapolate forward at the observed CU-per-second rate between batches.
Minimal Solana framework that skips solana-program entirely. No heap. No bincode. Just syscalls and pointers.
Account data is read in place. No deserialization passes, no allocations — just pointers and offsets. Pinocchio types are sized exactly like their on-chain bytes.
Same program ID, same instruction tags, same account layout. Wallets, indexers, RPCs, and existing clients keep working without a single change.
Compiled without the standard library and without the solana-program crate. Fuzzed continuously against SPL Token regression fixtures. Audited by OtterSec.
Measured against spl-token v3.5.0 on mainnet-beta fixtures. Lower is better. Compute unit numbers are deterministic across runs.
Reproduce: cargo bench -p p-token · fixtures pinned in /pinocchio/program/fuzz/program-mb.so.
Deploys to the canonical SPL Token program ID. Every instruction tag, every account, every error code preserved. Wallets and indexers don't know the difference.
use pinocchio::{
account_info::AccountInfo,
program_error::ProgramError,
ProgramResult,
};
use crate::state::{Account, Mint};
pub fn process_transfer(
accounts: &[AccountInfo],
amount: u64,
) -> ProgramResult {
let [src, dst, auth, rest @ ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Zero-copy borrow — no deserialize, no allocation.
let mut src_acc = Account::from_account_info_mut(src)?;
let mut dst_acc = Account::from_account_info_mut(dst)?;
check_authority(&src_acc, auth, rest)?;
src_acc.amount = src_acc.amount
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
dst_acc.amount = dst_acc.amount
.checked_add(amount)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(())
}
$ cargo add pinocchio-token
$ cargo build-sbf
Compiling p-token v0.4.0
Finished release in 18.2s
$ solana program deploy ./p_token.so
$ spl-token create-token # works unchanged
Entrypoint dispatches on the first instruction byte. One file per processor. State maps directly to on-chain bytes.
src/
├── entrypoint.rs
├── lib.rs
└── processor/
├── transfer.rs
├── transfer_checked.rs
├── mint_to.rs
├── mint_to_checked.rs
├── burn.rs
├── burn_checked.rs
├── approve.rs
├── approve_checked.rs
├── revoke.rs
├── set_authority.rs
├── close_account.rs
├── freeze_account.rs
├── thaw_account.rs
├── sync_native.rs
├── batch.rs // new
├── …
└── shared/
├── authority.rs
└── multisig.rs
Every processor is fuzz-tested against the reference SPL Token program. Behavior matches bit-for-bit: same logs, same account writes, same error codes.
A replacement. p-token deploys to the canonical SPL Token program ID and exposes the exact same interface. The validator runtime treats it as a drop-in alternative implementation — clients don't have to change anything.
Not yet. p-token implements the classic SPL Token surface (program ID Tokenkeg…). Token-2022 extensions like transfer fees and confidential transfers live in a separate program and are not part of this crate.
Three main reasons: (1) no solana-program dependency, so no generic deserialization paths; (2) zero-copy account access through Pinocchio's AccountInfo; (3) hand-tuned dispatch and validation. Net effect: roughly half the compute on hot paths.
Yes — the instruction format is identical. Use the same spl_token::instruction::* builders you already use. CPI through p-token is also cheaper, so your callers see savings too.