Skip to main content
A lightweight counter program using the Pinocchio framework with Ephemeral Rollups. This example demonstrates a more efficient alternative to Borsh serialization, using manual serialization with fixed-size types for reduced compute and size overhead.

What You’ll Learn

  • How to use Pinocchio for lightweight Solana programs
  • How to implement manual serialization without Borsh
  • How to delegate accounts using the Pinocchio SDK
  • How to work with fixed-size arrays and primitives
  • How to minimize program size and compute units

Program Structure

The Pinocchio counter program includes the following instructions:
  • 0: InitializeCounter - Initialize a counter PDA to 0 (payload: bump u8)
  • 1: IncreaseCounter - Increase counter by specified amount (payload: bump u8 + increase_by u64)
  • 2: Delegate - Delegate the counter to Ephemeral Rollups (payload: bump u8)
  • 3: CommitAndUndelegate - Commit and undelegate the counter
  • 4: Commit - Commit changes to base layer
  • 5: IncrementAndCommit - Increment and commit in one instruction (payload: bump u8 + increase_by u64)
  • 6: IncrementAndUndelegate - Increment and undelegate in one instruction (payload: bump u8 + increase_by u64)

Software Requirements

Build and Test

1

Build the program

2

Run tests

Run tests with logging enabled:

Key Differences from Rust Counter

No Borsh

Uses manual serialization with to_le_bytes() and from_le_bytes() for simplicity

No Vec

All types use fixed-size arrays or primitives

Pinocchio Framework

Leverages Pinocchio’s lightweight instruction handling

Direct State Management

Simple Counter struct with manual memory management

Program Implementation

State Definition

The counter state is a simple struct with manual serialization:
Using #[repr(C)] ensures the struct has a predictable memory layout, allowing direct pointer casting.

Initialize Counter

Increment Counter

Pinocchio uses AccountView instead of AccountInfo, providing a more lightweight abstraction.

Delegation Implementation

Commit and Undelegate

TypeScript Client Usage

Initialize with Bump

Pinocchio counter requires passing the bump seed in the instruction data.

Delegate to Ephemeral Rollups

Execute on Ephemeral Rollups

Performance Benefits

Smaller Program Size

No Borsh dependency reduces compiled program size

Lower Compute Units

Direct memory access is more efficient than serialization

Minimal Dependencies

Pinocchio is lightweight with fewer external crates

Fixed Allocations

No Vec types means predictable memory usage
Pinocchio requires manual memory management and is more error-prone than frameworks like Anchor. Use it when you need maximum performance and minimal size.

Account Structure

The Counter account is simple:
  • Size: 8 bytes
  • Layout: Single u64 count value
  • Serialization: Direct memory casting (no Borsh)

Source Code

View the complete source code on GitHub: pinocchio-counter on GitHub