Raj

@rajgoesout

Accounts

Anchor → Pinocchio

Anchor Pinocchio
AccountInfo<'info> AccountView
#[account(mut)] Manual is_writable() check
#[account(signer)] Manual is_signer() check
#[account(owner = ...)] Manual owned_by() check
#[derive(Accounts)] TryFrom<&[AccountView]>

AccountView API

use pinocchio::{AccountView, Address, ProgramResult, entrypoint, error::ProgramError};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Address,
    accounts: &[AccountView],
    instruction_data: &[u8],
) -> ProgramResult {
    let account = &accounts[0];
    let data = account.try_borrow()?;

    // Read fields
    let addr: &Address = account.address();
    let lamports: u64 = account.lamports();
    let _bytes: &[u8] = &data;

    // Checks
    let is_signer: bool = account.is_signer();
    let is_writable: bool = account.is_writable();
    let owned_by_program: bool = account.owned_by(program_id);
    let _ = (addr, lamports, is_signer, is_writable, owned_by_program);
    Ok(())
}

Destructuring Accounts

pub fn process_instruction(
    program_id: &Address,
    accounts: &[AccountView],
    instruction_data: &[u8],
) -> ProgramResult {
    let [payer, counter, authority, system_program, ..] = accounts else {
        return Err(ProgramError::NotEnoughAccountKeys);
    };
    Ok(())
}

Anchor equivalent:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(mut)]
    pub counter: Account<'info, Counter>,
    pub authority: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}

Common Validations

// Signer check (#[account(signer)] in Anchor)
if !authority.is_signer() {
    return Err(ProgramError::MissingRequiredSignature);
}

// Owner check (#[account(owner = program)] in Anchor)
if !account.owned_by(program_id) {
    return Err(ProgramError::InvalidAccountOwner);
}

// Address check (#[account(address = EXPECTED)] in Anchor)
if account.address() != &expected_address {
    return Err(ProgramError::InvalidAccountData);
}

// System account (wallet)
if !account.owned_by(&pinocchio_system::ID) {
    return Err(ProgramError::InvalidAccountOwner);
}

// Token account
if !account.owned_by(&pinocchio_token::ID) {
    return Err(ProgramError::InvalidAccountOwner);
}

Mutable Data Access

fn update(account: &AccountView) -> ProgramResult {
    let mut data = account.try_borrow_mut()?;
    data[0..8].copy_from_slice(&42u64.to_le_bytes());
    Ok(())
}

TryFrom Pattern

Anchor's #[derive(Accounts)] generates validation logic. In Pinocchio, use TryFrom:

pub struct DepositAccounts<'a> {
    pub user: &'a AccountView,
    pub vault: &'a AccountView,
}

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

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

        if !user.is_signer() {
            return Err(ProgramError::MissingRequiredSignature);
        }

        Ok(Self { user, vault })
    }
}

// Usage
let accounts = DepositAccounts::try_from(accounts)?;

Anchor equivalent:

#[derive(Accounts)]
pub struct Deposit<'info> {
    #[account(mut, signer)]
    pub user: AccountInfo<'info>,
    #[account(mut)]
    pub vault: Account<'info, Vault>,
}