State
Anchor → Pinocchio
| Anchor | Pinocchio |
|---|---|
#[account] macro |
#[repr(C)] struct + manual impl |
#[derive(InitSpace)] |
Manual const LEN calculation |
| Borsh serialization | Zero-copy pointer cast |
Account<'info, T> |
unsafe pointer cast to &T |
| 8-byte discriminator | 1-byte discriminator (optional) |
Zero-Copy Struct
Anchor uses Borsh serialization. Pinocchio casts raw bytes directly to structs:
use pinocchio::account::{Ref, RefMut};
#[repr(C)]
pub struct Counter {
pub authority: [u8; 32],
pub value: u64,
pub bump: u8,
}
impl Counter {
pub const LEN: usize = 32 + 8 + 1; // 41 bytes
pub fn from_account(account: &AccountView) -> Result<Ref<Self>, ProgramError> {
if account.data_len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
Ok(Ref::map(account.try_borrow()?, |data| unsafe {
&*(data.as_ptr() as *const Self)
}))
}
pub fn from_account_mut(account: &AccountView) -> Result<RefMut<Self>, ProgramError> {
let data = account.try_borrow_mut()?;
if data.len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
Ok(RefMut::map(data, |data| unsafe {
&mut *(data.as_mut_ptr() as *mut Self)
}))
}
pub fn authority(&self) -> Address {
Address::from(self.authority)
}
}
Anchor equivalent:
#[account]
pub struct Counter {
pub authority: Pubkey,
pub value: u64,
pub bump: u8,
}
Reading State
fn read(account: &AccountView) -> Result<u64, ProgramError> {
let counter = Counter::from_account(account)?;
Ok(counter.value)
}
Writing State
fn initialize(account: &AccountView, authority: &Address, bump: u8) -> ProgramResult {
let counter = Counter::from_account_mut(account)?;
counter.authority.copy_from_slice(authority.as_ref());
counter.value = 0;
counter.bump = bump;
Ok(())
}
fn increment(account: &AccountView) -> ProgramResult {
let mut counter = Counter::from_account_mut(account)?;
counter.value = counter
.value
.checked_add(1)
.ok_or(ProgramError::ArithmeticOverflow)?;
Ok(())
}
Field Ordering (Minimize Padding)
Order fields from largest to smallest to minimize padding:
// GOOD: Largest to smallest
#[repr(C)]
struct Good {
big: u64, // 8 bytes
medium: u16, // 2 bytes
small: u8, // 1 byte
} // = 16 bytes (5 padding)
// BAD: Wastes space
#[repr(C)]
struct Bad {
small: u8, // 1 + 7 padding
big: u64, // 8 bytes
medium: u16, // 2 + 6 padding
} // = 24 bytes (13 padding)
Zero-Padding (Maximum Efficiency)
Store integers as byte arrays to eliminate all padding:
#[repr(C)]
struct Compact {
amount: [u8; 8], // Store u64 as bytes
fee: [u8; 2], // Store u16 as bytes
flag: u8,
} // = 11 bytes exactly
impl Compact {
pub fn amount(&self) -> u64 { u64::from_le_bytes(self.amount) }
pub fn set_amount(&mut self, v: u64) { self.amount = v.to_le_bytes(); }
pub fn fee(&self) -> u16 { u16::from_le_bytes(self.fee) }
pub fn set_fee(&mut self, v: u16) { self.fee = v.to_le_bytes(); }
}
Discriminator for Account Types
Anchor auto-generates 8-byte discriminators. In Pinocchio, use a 1-byte type tag:
#[repr(C)]
pub struct Vault {
pub discriminator: u8, // 1 = Vault, 2 = UserRecord, etc.
pub authority: [u8; 32],
// ...
}
impl Vault {
pub const DISCRIMINATOR: u8 = 1;
pub fn from_account(account: &AccountView) -> Result<Ref<Self>, ProgramError> {
let data = account.try_borrow()?;
if data.len() < Self::LEN || data[0] != Self::DISCRIMINATOR {
return Err(ProgramError::InvalidAccountData);
}
Ok(Ref::map(data, |data| unsafe { &*(data.as_ptr() as *const Self) }))
}
}
Creating Accounts
Anchor's init constraint handles account creation. In Pinocchio, use CreateAccount CPI:
use pinocchio_system::instructions::CreateAccount;
use pinocchio::sysvars::Rent;
fn create(payer: &AccountView, new_account: &AccountView, program_id: &Address) -> ProgramResult {
let rent = Rent::get()?;
let lamports = rent.try_minimum_balance(Counter::LEN)?;
CreateAccount {
from: payer,
to: new_account,
lamports,
space: Counter::LEN as u64,
owner: program_id,
}.invoke()
}
Anchor equivalent:
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(
init,
payer = payer,
space = 8 + Counter::INIT_SPACE
)]
pub counter: Account<'info, Counter>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}