pinocchio-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pinocchio Development Guide

Pinocchio开发指南

Build blazing-fast Solana programs with Pinocchio - a zero-dependency, zero-copy framework that delivers 88-95% compute unit reduction and 40% smaller binaries compared to traditional approaches.
使用Pinocchio构建极速Solana程序——这是一款零依赖、零拷贝框架,与传统方案相比,可减少88-95%的计算单元(CU),并生成缩小40%的二进制文件

Overview

概述

Pinocchio is Anza's minimalist Rust library for writing Solana programs without the heavyweight
solana-program
crate. It treats incoming transaction data as a single byte slice, reading it in-place via zero-copy techniques.
Pinocchio是Anza推出的极简Rust库,用于编写Solana程序,无需依赖庞大的
solana-program
crate。它将传入的交易数据视为单个字节切片,通过零拷贝技术就地读取数据。

Performance Comparison

性能对比

MetricAnchorNative (solana-program)Pinocchio
Token Transfer CU~6,000~4,500~600-800
Binary SizeLargeMediumSmall (-40%)
Heap AllocationRequiredRequiredOptional
DependenciesManySeveralZero*
*Only Solana SDK types for on-chain execution
指标Anchor原生(solana-program)Pinocchio
代币转账CU~6,000~4,500~600-800
二进制文件大小小(缩小40%)
堆内存分配必需必需可选
依赖项众多若干零*
仅依赖Solana SDK类型用于链上执行

When to Use Pinocchio

何时使用Pinocchio

Use Pinocchio When:
  • Building high-throughput programs (DEXs, orderbooks, games)
  • Compute units are a bottleneck
  • Binary size matters (program deployment costs)
  • You need maximum control over memory
  • Building infrastructure (tokens, vaults, escrows)
Consider Anchor Instead When:
  • Rapid prototyping / MVPs
  • Team unfamiliar with low-level Rust
  • Complex account relationships
  • Need extensive ecosystem tooling
  • Audit timeline is tight (more auditors know Anchor)
推荐使用Pinocchio的场景:
  • 构建高吞吐量程序(去中心化交易所、订单簿、游戏)
  • 计算单元成为性能瓶颈
  • 关注二进制文件大小(程序部署成本)
  • 需要对内存进行最大程度的控制
  • 构建基础设施类程序(代币、金库、托管)
推荐使用Anchor的场景:
  • 快速原型开发/最小可行产品(MVP)
  • 团队不熟悉底层Rust开发
  • 存在复杂的账户关系
  • 需要丰富的生态工具支持
  • 审计时间紧张(更多审计人员熟悉Anchor)

Quick Start

快速开始

1. Project Setup

1. 项目设置

toml
undefined
toml
undefined

Cargo.toml

Cargo.toml

[package] name = "my-program" version = "0.1.0" edition = "2021"
[lib] crate-type = ["cdylib", "lib"]
[features] default = [] bpf-entrypoint = []
[dependencies] pinocchio = "0.10" pinocchio-system = "0.4" # System Program CPI helpers pinocchio-token = "0.4" # Token Program CPI helpers bytemuck = { version = "1.14", features = ["derive"] }
[profile.release] overflow-checks = true lto = "fat" codegen-units = 1 opt-level = 3
undefined
[package] name = "my-program" version = "0.1.0" edition = "2021"
[lib] crate-type = ["cdylib", "lib"]
[features] default = [] bpf-entrypoint = []
[dependencies] pinocchio = "0.10" pinocchio-system = "0.4" # System Program CPI 工具 pinocchio-token = "0.4" # Token Program CPI 工具 bytemuck = { version = "1.14", features = ["derive"] }
[profile.release] overflow-checks = true lto = "fat" codegen-units = 1 opt-level = 3
undefined

2. Basic Program Structure

2. 基础程序结构

rust
use pinocchio::{
    account_info::AccountInfo,
    entrypoint,
    program_error::ProgramError,
    pubkey::Pubkey,
    ProgramResult,
};

// Declare entrypoint
entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Route instructions by discriminator (first byte)
    match instruction_data.first() {
        Some(0) => initialize(accounts, &instruction_data[1..]),
        Some(1) => execute(accounts, &instruction_data[1..]),
        _ => Err(ProgramError::InvalidInstructionData),
    }
}
rust
use pinocchio::{
    account_info::AccountInfo,
    entrypoint,
    program_error::ProgramError,
    pubkey::Pubkey,
    ProgramResult,
};

// 声明入口点
entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // 通过 discriminator(第一个字节)路由指令
    match instruction_data.first() {
        Some(0) => initialize(accounts, &instruction_data[1..]),
        Some(1) => execute(accounts, &instruction_data[1..]),
        _ => Err(ProgramError::InvalidInstructionData),
    }
}

3. Account Definition with Bytemuck

3. 使用Bytemuck定义账户

rust
use bytemuck::{Pod, Zeroable};

// Single-byte discriminator for account type
pub const VAULT_DISCRIMINATOR: u8 = 1;

#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Vault {
    pub discriminator: u8,
    pub owner: [u8; 32],      // Pubkey as bytes
    pub balance: u64,
    pub bump: u8,
    pub _padding: [u8; 6],    // Align to 8 bytes
}

impl Vault {
    pub const LEN: usize = std::mem::size_of::<Self>();

    pub fn from_account(account: &AccountInfo) -> Result<&Self, ProgramError> {
        let data = account.try_borrow_data()?;
        if data.len() < Self::LEN {
            return Err(ProgramError::InvalidAccountData);
        }
        if data[0] != VAULT_DISCRIMINATOR {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(bytemuck::from_bytes(&data[..Self::LEN]))
    }

    pub fn from_account_mut(account: &AccountInfo) -> Result<&mut Self, ProgramError> {
        let mut data = account.try_borrow_mut_data()?;
        if data.len() < Self::LEN {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(bytemuck::from_bytes_mut(&mut data[..Self::LEN]))
    }
}
rust
use bytemuck::{Pod, Zeroable};

// 账户类型的单字节 discriminator
pub const VAULT_DISCRIMINATOR: u8 = 1;

#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Vault {
    pub discriminator: u8,
    pub owner: [u8; 32],      // Pubkey 字节形式
    pub balance: u64,
    pub bump: u8,
    pub _padding: [u8; 6],    // 对齐到8字节
}

impl Vault {
    pub const LEN: usize = std::mem::size_of::<Self>();

    pub fn from_account(account: &AccountInfo) -> Result<&Self, ProgramError> {
        let data = account.try_borrow_data()?;
        if data.len() < Self::LEN {
            return Err(ProgramError::InvalidAccountData);
        }
        if data[0] != VAULT_DISCRIMINATOR {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(bytemuck::from_bytes(&data[..Self::LEN]))
    }

    pub fn from_account_mut(account: &AccountInfo) -> Result<&mut Self, ProgramError> {
        let mut data = account.try_borrow_mut_data()?;
        if data.len() < Self::LEN {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(bytemuck::from_bytes_mut(&mut data[..Self::LEN]))
    }
}

Instructions

指令开发

Step 1: Define Account Validation

步骤1:定义账户验证逻辑

Create a struct to hold validated accounts:
rust
pub struct InitializeAccounts<'a> {
    pub vault: &'a AccountInfo,
    pub owner: &'a AccountInfo,
    pub system_program: &'a AccountInfo,
}

impl<'a> InitializeAccounts<'a> {
    pub fn parse(accounts: &'a [AccountInfo]) -> Result<Self, ProgramError> {
        let [vault, owner, system_program, ..] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        // Validate owner is signer
        if !owner.is_signer() {
            return Err(ProgramError::MissingRequiredSignature);
        }

        // Validate system program
        if system_program.key() != &pinocchio_system::ID {
            return Err(ProgramError::IncorrectProgramId);
        }

        Ok(Self {
            vault,
            owner,
            system_program,
        })
    }
}
创建结构体存储已验证的账户:
rust
pub struct InitializeAccounts<'a> {
    pub vault: &'a AccountInfo,
    pub owner: &'a AccountInfo,
    pub system_program: &'a AccountInfo,
}

impl<'a> InitializeAccounts<'a> {
    pub fn parse(accounts: &'a [AccountInfo]) -> Result<Self, ProgramError> {
        let [vault, owner, system_program, ..] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        // 验证owner是签名者
        if !owner.is_signer() {
            return Err(ProgramError::MissingRequiredSignature);
        }

        // 验证系统程序
        if system_program.key() != &pinocchio_system::ID {
            return Err(ProgramError::IncorrectProgramId);
        }

        Ok(Self {
            vault,
            owner,
            system_program,
        })
    }
}

Step 2: Implement Instruction Handler

步骤2:实现指令处理函数

rust
use pinocchio_system::instructions::CreateAccount;

pub fn initialize(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
    let ctx = InitializeAccounts::parse(accounts)?;

    // Derive PDA
    let (pda, bump) = Pubkey::find_program_address(
        &[b"vault", ctx.owner.key().as_ref()],
        &crate::ID,
    );

    // Verify PDA matches
    if ctx.vault.key() != &pda {
        return Err(ProgramError::InvalidSeeds);
    }

    // Create account via CPI
    let space = Vault::LEN as u64;
    let rent = pinocchio::sysvar::rent::Rent::get()?;
    let lamports = rent.minimum_balance(space as usize);

    CreateAccount {
        from: ctx.owner,
        to: ctx.vault,
        lamports,
        space,
        owner: &crate::ID,
    }
    .invoke_signed(&[&[b"vault", ctx.owner.key().as_ref(), &[bump]]])?;

    // Initialize account data
    let vault = Vault::from_account_mut(ctx.vault)?;
    vault.discriminator = VAULT_DISCRIMINATOR;
    vault.owner = ctx.owner.key().to_bytes();
    vault.balance = 0;
    vault.bump = bump;

    Ok(())
}
rust
use pinocchio_system::instructions::CreateAccount;

pub fn initialize(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
    let ctx = InitializeAccounts::parse(accounts)?;

    // 推导PDA
    let (pda, bump) = Pubkey::find_program_address(
        &[b"vault", ctx.owner.key().as_ref()],
        &crate::ID,
    );

    // 验证PDA匹配
    if ctx.vault.key() != &pda {
        return Err(ProgramError::InvalidSeeds);
    }

    // 通过CPI创建账户
    let space = Vault::LEN as u64;
    let rent = pinocchio::sysvar::rent::Rent::get()?;
    let lamports = rent.minimum_balance(space as usize);

    CreateAccount {
        from: ctx.owner,
        to: ctx.vault,
        lamports,
        space,
        owner: &crate::ID,
    }
    .invoke_signed(&[&[b"vault", ctx.owner.key().as_ref(), &[bump]]])?;

    // 初始化账户数据
    let vault = Vault::from_account_mut(ctx.vault)?;
    vault.discriminator = VAULT_DISCRIMINATOR;
    vault.owner = ctx.owner.key().to_bytes();
    vault.balance = 0;
    vault.bump = bump;

    Ok(())
}

Entrypoint Options

入口点选项

Pinocchio provides three entrypoint macros with different trade-offs:
Pinocchio提供三个入口点宏,各有不同的权衡:

1. Standard Entrypoint (Recommended for most cases)

1. 标准入口点(大多数场景推荐)

rust
use pinocchio::entrypoint;

entrypoint!(process_instruction);
  • Sets up heap allocator
  • Configures panic handler
  • Deserializes accounts automatically
rust
use pinocchio::entrypoint;

entrypoint!(process_instruction);
  • 配置堆内存分配器
  • 设置panic处理程序
  • 自动反序列化账户

2. Lazy Entrypoint (Best for single-instruction programs)

2. 延迟入口点(单指令程序最佳选择)

rust
use pinocchio::lazy_entrypoint;

lazy_entrypoint!(process_instruction);

pub fn process_instruction(mut context: InstructionContext) -> ProgramResult {
    // Accounts parsed on-demand
    let account = context.next_account()?;
    let data = context.instruction_data();
    Ok(())
}
  • Defers parsing until needed
  • Best CU savings for simple programs
  • 80-87% CU reduction in memo program benchmarks
rust
use pinocchio::lazy_entrypoint;

lazy_entrypoint!(process_instruction);

pub fn process_instruction(mut context: InstructionContext) -> ProgramResult {
    // 按需解析账户
    let account = context.next_account()?;
    let data = context.instruction_data();
    Ok(())
}
  • 延迟解析直到需要时
  • 为简单程序带来最大的计算单元节省
  • 在备忘录程序基准测试中减少80-87%的计算单元

3. No Allocator (Maximum optimization)

3. 无分配器(极致优化)

rust
use pinocchio::{entrypoint, no_allocator};

no_allocator!();
entrypoint!(process_instruction);
  • Disables heap entirely
  • Cannot use
    String
    ,
    Vec
    ,
    Box
  • Best for statically-sized operations
rust
use pinocchio::{entrypoint, no_allocator};

no_allocator!();
entrypoint!(process_instruction);
  • 完全禁用堆内存
  • 无法使用
    String
    Vec
    Box
    等类型
  • 最适合静态大小的操作

CPI Patterns

CPI模式

System Program CPI

系统程序CPI

rust
use pinocchio_system::instructions::{CreateAccount, Transfer};

// Create account
CreateAccount {
    from: payer,
    to: new_account,
    lamports: rent_lamports,
    space: account_size,
    owner: &program_id,
}.invoke()?;

// Transfer SOL
Transfer {
    from: source,
    to: destination,
    lamports: amount,
}.invoke()?;

// Transfer with PDA signer
Transfer {
    from: pda_account,
    to: destination,
    lamports: amount,
}.invoke_signed(&[&[b"vault", owner.as_ref(), &[bump]]])?;
rust
use pinocchio_system::instructions::{CreateAccount, Transfer};

// 创建账户
CreateAccount {
    from: payer,
    to: new_account,
    lamports: rent_lamports,
    space: account_size,
    owner: &program_id,
}.invoke()?;

// 转账SOL
Transfer {
    from: source,
    to: destination,
    lamports: amount,
}.invoke()?;

// 使用PDA签名者转账
Transfer {
    from: pda_account,
    to: destination,
    lamports: amount,
}.invoke_signed(&[&[b"vault", owner.as_ref(), &[bump]]])?;

Token Program CPI

代币程序CPI

rust
use pinocchio_token::instructions::{Transfer, MintTo, Burn};

// Transfer tokens
Transfer {
    source: from_token_account,
    destination: to_token_account,
    authority: owner,
    amount: token_amount,
}.invoke()?;

// Mint tokens (with PDA authority)
MintTo {
    mint: mint_account,
    token_account: destination,
    authority: mint_authority_pda,
    amount: mint_amount,
}.invoke_signed(&[&[b"mint_auth", &[bump]]])?;
rust
use pinocchio_token::instructions::{Transfer, MintTo, Burn};

// 转账代币
Transfer {
    source: from_token_account,
    destination: to_token_account,
    authority: owner,
    amount: token_amount,
}.invoke()?;

// 铸造代币(使用PDA权限)
MintTo {
    mint: mint_account,
    token_account: destination,
    authority: mint_authority_pda,
    amount: mint_amount,
}.invoke_signed(&[&[b"mint_auth", &[bump]]])?;

Custom CPI (Third-party programs)

自定义CPI(第三方程序)

rust
use pinocchio::{
    instruction::{AccountMeta, Instruction},
    program::invoke,
};

// Build instruction manually
let accounts = vec![
    AccountMeta::new(*account1.key(), false),
    AccountMeta::new_readonly(*account2.key(), true),
];

let ix = Instruction {
    program_id: &external_program_id,
    accounts: &accounts,
    data: &instruction_data,
};

invoke(&ix, &[account1, account2])?;
rust
use pinocchio::{
    instruction::{AccountMeta, Instruction},
    program::invoke,
};

// 手动构建指令
let accounts = vec![
    AccountMeta::new(*account1.key(), false),
    AccountMeta::new_readonly(*account2.key(), true),
];

let ix = Instruction {
    program_id: &external_program_id,
    accounts: &accounts,
    data: &instruction_data,
};

invoke(&ix, &[account1, account2])?;

Account Validation Patterns

账户验证模式

Pattern 1: TryFrom Trait

模式1:TryFrom trait

rust
pub struct DepositAccounts<'a> {
    pub vault: &'a AccountInfo,
    pub owner: &'a AccountInfo,
    pub system_program: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
    type Error = ProgramError;

    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
        let [vault, owner, system_program, ..] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        // Validations
        require!(owner.is_signer(), ProgramError::MissingRequiredSignature);
        require!(vault.is_writable(), ProgramError::InvalidAccountData);

        Ok(Self { vault, owner, system_program })
    }
}

// Usage
let ctx = DepositAccounts::try_from(accounts)?;
rust
pub struct DepositAccounts<'a> {
    pub vault: &'a AccountInfo,
    pub owner: &'a AccountInfo,
    pub system_program: &'a AccountInfo,
}

impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
    type Error = ProgramError;

    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
        let [vault, owner, system_program, ..] = accounts else {
            return Err(ProgramError::NotEnoughAccountKeys);
        };

        // 验证逻辑
        require!(owner.is_signer(), ProgramError::MissingRequiredSignature);
        require!(vault.is_writable(), ProgramError::InvalidAccountData);

        Ok(Self { vault, owner, system_program })
    }
}

// 使用方式
let ctx = DepositAccounts::try_from(accounts)?;

Pattern 2: Builder Pattern

模式2:构建器模式

rust
pub struct AccountValidator<'a> {
    account: &'a AccountInfo,
}

impl<'a> AccountValidator<'a> {
    pub fn new(account: &'a AccountInfo) -> Self {
        Self { account }
    }

    pub fn is_signer(self) -> Result<Self, ProgramError> {
        if !self.account.is_signer() {
            return Err(ProgramError::MissingRequiredSignature);
        }
        Ok(self)
    }

    pub fn is_writable(self) -> Result<Self, ProgramError> {
        if !self.account.is_writable() {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(self)
    }

    pub fn has_owner(self, owner: &Pubkey) -> Result<Self, ProgramError> {
        if self.account.owner() != owner {
            return Err(ProgramError::IllegalOwner);
        }
        Ok(self)
    }

    pub fn build(self) -> &'a AccountInfo {
        self.account
    }
}

// Usage
let owner = AccountValidator::new(&accounts[0])
    .is_signer()?
    .is_writable()?
    .build();
rust
pub struct AccountValidator<'a> {
    account: &'a AccountInfo,
}

impl<'a> AccountValidator<'a> {
    pub fn new(account: &'a AccountInfo) -> Self {
        Self { account }
    }

    pub fn is_signer(self) -> Result<Self, ProgramError> {
        if !self.account.is_signer() {
            return Err(ProgramError::MissingRequiredSignature);
        }
        Ok(self)
    }

    pub fn is_writable(self) -> Result<Self, ProgramError> {
        if !self.account.is_writable() {
            return Err(ProgramError::InvalidAccountData);
        }
        Ok(self)
    }

    pub fn has_owner(self, owner: &Pubkey) -> Result<Self, ProgramError> {
        if self.account.owner() != owner {
            return Err(ProgramError::IllegalOwner);
        }
        Ok(self)
    }

    pub fn build(self) -> &'a AccountInfo {
        self.account
    }
}

// 使用方式
let owner = AccountValidator::new(&accounts[0])
    .is_signer()?
    .is_writable()?
    .build();

Pattern 3: Macro-based Validation

模式3:基于宏的验证

rust
macro_rules! require {
    ($cond:expr, $err:expr) => {
        if !$cond {
            return Err($err);
        }
    };
}

macro_rules! require_signer {
    ($account:expr) => {
        require!($account.is_signer(), ProgramError::MissingRequiredSignature)
    };
}

macro_rules! require_writable {
    ($account:expr) => {
        require!($account.is_writable(), ProgramError::InvalidAccountData)
    };
}
rust
macro_rules! require {
    ($cond:expr, $err:expr) => {
        if !$cond {
            return Err($err);
        }
    };
}

macro_rules! require_signer {
    ($account:expr) => {
        require!($account.is_signer(), ProgramError::MissingRequiredSignature)
    };
}

macro_rules! require_writable {
    ($account:expr) => {
        require!($account.is_writable(), ProgramError::InvalidAccountData)
    };
}

PDA Operations

PDA操作

Deriving PDAs

推导PDA

rust
use pinocchio::pubkey::Pubkey;

// Find PDA with bump
let (pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key().as_ref()],
    program_id,
);

// Create PDA with known bump (cheaper)
let pda = Pubkey::create_program_address(
    &[b"vault", user.key().as_ref(), &[bump]],
    program_id,
)?;
rust
use pinocchio::pubkey::Pubkey;

// 查找带bump值的PDA
let (pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key().as_ref()],
    program_id,
);

// 使用已知bump值创建PDA(成本更低)
let pda = Pubkey::create_program_address(
    &[b"vault", user.key().as_ref(), &[bump]],
    program_id,
)?;

PDA Signing for CPI

用于CPI的PDA签名

rust
// Single seed set
let signer_seeds = &[b"vault", owner.as_ref(), &[bump]];

Transfer {
    from: vault_pda,
    to: destination,
    lamports: amount,
}.invoke_signed(&[signer_seeds])?;

// Multiple PDA signers
let signer1 = &[b"vault", owner.as_ref(), &[bump1]];
let signer2 = &[b"authority", &[bump2]];

invoke_signed(&ix, &accounts, &[signer1, signer2])?;
rust
// 单种子集
let signer_seeds = &[b"vault", owner.as_ref(), &[bump]];

Transfer {
    from: vault_pda,
    to: destination,
    lamports: amount,
}.invoke_signed(&[signer_seeds])?;

// 多个PDA签名者
let signer1 = &[b"vault", owner.as_ref(), &[bump1]];
let signer2 = &[b"authority", &[bump2]];

invoke_signed(&ix, &accounts, &[signer1, signer2])?;

Data Serialization

数据序列化

Fixed-Size with Bytemuck (Recommended)

固定大小数据使用Bytemuck(推荐)

rust
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct GameState {
    pub discriminator: u8,
    pub player: [u8; 32],
    pub score: u64,
    pub level: u8,
    pub _padding: [u8; 6],
}

// Zero-copy read
let state: &GameState = bytemuck::from_bytes(&data);

// Zero-copy write
let state: &mut GameState = bytemuck::from_bytes_mut(&mut data);
rust
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct GameState {
    pub discriminator: u8,
    pub player: [u8; 32],
    pub score: u64,
    pub level: u8,
    pub _padding: [u8; 6],
}

// 零拷贝读取
let state: &GameState = bytemuck::from_bytes(&data);

// 零拷贝写入
let state: &mut GameState = bytemuck::from_bytes_mut(&mut data);

Variable-Size with Borsh

可变大小数据使用Borsh

rust
use borsh::{BorshDeserialize, BorshSerialize};

#[derive(BorshSerialize, BorshDeserialize)]
pub struct Metadata {
    pub name: String,
    pub symbol: String,
    pub uri: String,
}

// Deserialize (allocates)
let metadata = Metadata::try_from_slice(data)?;

// Serialize
let mut buffer = Vec::new();
metadata.serialize(&mut buffer)?;
rust
use borsh::{BorshDeserialize, BorshSerialize};

#[derive(BorshSerialize, BorshDeserialize)]
pub struct Metadata {
    pub name: String,
    pub symbol: String,
    pub uri: String,
}

// 反序列化(会分配内存)
let metadata = Metadata::try_from_slice(data)?;

// 序列化
let mut buffer = Vec::new();
metadata.serialize(&mut buffer)?;

Manual Parsing (Maximum control)

手动解析(最大控制权)

rust
pub fn parse_u64(data: &[u8]) -> Result<u64, ProgramError> {
    if data.len() < 8 {
        return Err(ProgramError::InvalidInstructionData);
    }
    Ok(u64::from_le_bytes(data[..8].try_into().unwrap()))
}

pub fn parse_pubkey(data: &[u8]) -> Result<Pubkey, ProgramError> {
    if data.len() < 32 {
        return Err(ProgramError::InvalidInstructionData);
    }
    Ok(Pubkey::new_from_array(data[..32].try_into().unwrap()))
}
rust
pub fn parse_u64(data: &[u8]) -> Result<u64, ProgramError> {
    if data.len() < 8 {
        return Err(ProgramError::InvalidInstructionData);
    }
    Ok(u64::from_le_bytes(data[..8].try_into().unwrap()))
}

pub fn parse_pubkey(data: &[u8]) -> Result<Pubkey, ProgramError> {
    if data.len() < 32 {
        return Err(ProgramError::InvalidInstructionData);
    }
    Ok(Pubkey::new_from_array(data[..32].try_into().unwrap()))
}

IDL Generation with Shank

使用Shank生成IDL

Since Pinocchio doesn't auto-generate IDLs, use Shank:
rust
use shank::{ShankAccount, ShankInstruction};

#[derive(ShankAccount)]
pub struct Vault {
    pub owner: Pubkey,
    pub balance: u64,
}

#[derive(ShankInstruction)]
pub enum ProgramInstruction {
    #[account(0, writable, signer, name = "vault")]
    #[account(1, signer, name = "owner")]
    #[account(2, name = "system_program")]
    Initialize,

    #[account(0, writable, name = "vault")]
    #[account(1, signer, name = "owner")]
    Deposit { amount: u64 },
}
Generate IDL:
bash
shank idl -o idl.json -p src/lib.rs
由于Pinocchio不会自动生成IDL,可使用Shank工具:
rust
use shank::{ShankAccount, ShankInstruction};

#[derive(ShankAccount)]
pub struct Vault {
    pub owner: Pubkey,
    pub balance: u64,
}

#[derive(ShankInstruction)]
pub enum ProgramInstruction {
    #[account(0, writable, signer, name = "vault")]
    #[account(1, signer, name = "owner")]
    #[account(2, name = "system_program")]
    Initialize,

    #[account(0, writable, name = "vault")]
    #[account(1, signer, name = "owner")]
    Deposit { amount: u64 },
}
生成IDL:
bash
shank idl -o idl.json -p src/lib.rs

Guidelines

开发指南

  1. Always use single-byte discriminators for instructions and accounts
  2. Prefer bytemuck over Borsh for fixed-size data
  3. Use
    lazy_entrypoint!
    for single-instruction programs
  4. Validate all accounts before processing
  5. Use
    invoke_signed
    for PDA-owned account operations
  6. Add padding to align structs to 8 bytes
  7. Test with
    solana-program-test
    or Bankrun
  1. 始终为指令和账户使用单字节discriminator
  2. 对于固定大小数据,优先使用bytemuck而非Borsh
  3. 单指令程序使用
    lazy_entrypoint!
  4. 处理前验证所有账户
  5. PDA所属账户操作使用
    invoke_signed
  6. 添加填充使结构体对齐到8字节
  7. 使用
    solana-program-test
    或Bankrun进行测试

Files in This Skill

本技能包含的文件

pinocchio-development/
├── SKILL.md                           # This file
├── scripts/
│   ├── scaffold-program.sh            # Project generator
│   └── benchmark-cu.sh                # CU benchmarking
├── resources/
│   ├── account-patterns.md            # Validation patterns
│   ├── cpi-reference.md               # CPI quick reference
│   ├── optimization-checklist.md      # Performance tips
│   └── anchor-comparison.md           # Side-by-side comparison
├── examples/
│   ├── counter/                       # Basic counter program
│   ├── vault/                         # PDA vault with deposits
│   ├── token-operations/              # Token minting/transfers
│   └── transfer-hook/                 # Token-2022 hook
├── templates/
│   └── program-template.rs            # Starter template
└── docs/
    ├── migration-from-anchor.md       # Anchor migration guide
    └── edge-cases.md                  # Gotchas and solutions
pinocchio-development/
├── SKILL.md                           # 本文档
├── scripts/
│   ├── scaffold-program.sh            # 项目生成脚本
│   └── benchmark-cu.sh                # 计算单元基准测试脚本
├── resources/
│   ├── account-patterns.md            # 验证模式文档
│   ├── cpi-reference.md               # CPI速查手册
│   ├── optimization-checklist.md      # 性能优化技巧
│   └── anchor-comparison.md           # 对比Anchor的文档
├── examples/
│   ├── counter/                       # 基础计数器程序
│   ├── vault/                         # 带存款功能的PDA金库
│   ├── token-operations/              # 代币铸造/转账程序
│   └── transfer-hook/                 # Token-2022钩子程序
├── templates/
│   └── program-template.rs            # 程序模板
└── docs/
    ├── migration-from-anchor.md       # Anchor迁移指南
    └── edge-cases.md                  # 注意事项与解决方案

Performance Benchmarks (2025)

性能基准测试(2025年)

Latest benchmarks demonstrate Pinocchio's efficiency:
ProgramAnchor CUPinocchio CUReduction
Token Transfer~6,000~600-80088-95%
Memo Program~650~10883%
Counter~800~10487%
Assembly implementation: 104 CU, Pinocchio: 108 CU, Basic Anchor: 649 CU
最新基准测试展示了Pinocchio的效率:
程序Anchor计算单元Pinocchio计算单元减少比例
代币转账~6,000~600-80088-95%
备忘录程序~650~10883%
计数器~800~10487%
汇编实现:104 CU,Pinocchio:108 CU,基础Anchor:649 CU

SDK Roadmap (Anza Plans)

SDK路线图(Anza计划)

The Anza team has announced plans for SDK v3:
Anza团队已宣布SDK v3的计划:

Coming Improvements

即将到来的改进

  • Unified Base Types: Reusable types across Anchor and Pinocchio
  • New Serialization Library: Zero-copy, simpler enums, variable-length types
  • ATA Program Optimization: Pinocchio-optimized Associated Token Account
  • Token22 Optimization: Full Token Extensions support with minimal CU usage
  • 统一基础类型:Anchor与Pinocchio之间可复用的类型
  • 新序列化库:零拷贝、更简洁的枚举、可变长度类型
  • ATA程序优化:Pinocchio优化的关联代币账户
  • Token22优化:完整支持代币扩展,同时最小化计算单元使用

Integration Progress

集成进展

  • Pinocchio types are being integrated into the core Solana SDK
  • Improved interoperability between Anchor and Pinocchio programs
  • Pinocchio类型正被整合到核心Solana SDK中
  • 提升Anchor与Pinocchio程序之间的互操作性

Notes

注意事项

  • Pinocchio is unaudited - use with caution in production
  • Version 0.10.x is current (latest:
    pinocchio = "0.10"
    )
  • pinocchio-system = "0.4"
    and
    pinocchio-token = "0.4"
    for CPI helpers
  • Token-2022 support via
    pinocchio-token
    is under active development
  • For client generation, use Codama with your Shank-generated IDL
  • Maintained by Anza (Solana Agave client developers)
  • Pinocchio未经过审计——生产环境使用请谨慎
  • 当前版本为0.10.x(最新版本:
    pinocchio = "0.10"
  • CPI工具使用
    pinocchio-system = "0.4"
    pinocchio-token = "0.4"
  • pinocchio-token
    对Token-2022的支持正在积极开发中
  • 客户端生成可使用Codama配合Shank生成的IDL
  • 由Anza(Solana Agave客户端开发者)维护