Skip to main content
A simple counter program demonstrating how to use native Rust (without Anchor) with Ephemeral Rollups. This example shows the low-level implementation of delegation, transaction execution, and state management using Borsh serialization.

What You’ll Learn

  • How to implement Ephemeral Rollups delegation in native Rust
  • How to use Borsh serialization for instruction data
  • How to manually handle CPI calls to the delegation program
  • How to structure a native Solana program with multiple instructions
  • How to commit and undelegate accounts without Anchor macros

Program Structure

The Rust counter program includes the following instructions:
  • 0: InitializeCounter - Initialize the counter PDA to 0
  • 1: IncreaseCounter - Increase the counter by a specified amount
  • 2: Delegate - Delegate the counter account to Ephemeral Rollups
  • 3: CommitAndUndelegate - Commit and undelegate the account
  • 4: Commit - Commit changes to the base layer
  • 5: IncrementAndCommit - Increment and commit in one instruction
  • 6: IncrementAndUndelegate - Increment and undelegate in one instruction
  • 7: Undelegate - Undelegate with custom PDA seeds

Software Requirements

Ensure you have the following software packages installed before building the program.

Build and Test

1

Build the program

Build the Solana program using the BPF toolchain:
2

Deploy the program

Deploy the compiled program to your chosen cluster:
3

Configure environment

Add your wallet private key and RPC endpoints to .env:
4

Run tests

Install dependencies and run the test suite:

Program Implementation

Project Structure

The native Rust program is organized into modules:

Instruction Enum

Instructions are defined as an enum with associated data:

State Definition

The counter state uses Borsh for serialization:

Delegation Implementation

The delegation function shows how to manually handle CPI calls:
Without Anchor macros, you must manually retrieve and pass all required accounts for delegation.

Increment Implementation

The increment function uses Borsh for deserialization and serialization:

Commit and Undelegate

Committing and undelegating requires manual account handling:

TypeScript Client Usage

Delegate to Ephemeral Rollups

Execute on Ephemeral Rollups

Key Features

Native Rust

Pure Rust implementation without framework dependencies

Borsh Serialization

Efficient binary serialization for instruction data and accounts

Manual Control

Full control over account validation and CPI calls

Lightweight

Minimal dependencies and smaller program size
Native Rust programs require more boilerplate code compared to Anchor, but offer maximum flexibility and control.

Source Code

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