Expand description
A high-performance, trait-based Solana program framework for building fast, reliable, and type-safe programs.
Star Frame provides a modern approach to Solana program development with zero-cost abstractions, comprehensive compile-time validation, and an intuitive API design optimized for Solana’s compute unit constraints.
§Key Features
- Type Safety: Catch errors at compile time with zero-cost abstractions
- Performance: Optimized for Solana’s compute unit constraints
- Developer Experience: Intuitive APIs with comprehensive validation
- Modularity: Composable components for accounts, instructions, and program logic
§Getting Started
Add star_frame and bytemuck to your Cargo.toml:
cargo add star_frame bytemuck§Lifecycle of a Star Frame Transaction
Here’s what happens when you call a into a Star Frame program:
§Program Entrypoint
The StarFrameProgram derive macro generates the program entrypoint:
use star_frame::prelude::*;
#[derive(StarFrameProgram)]
#[program(
instruction_set = CounterInstructionSet,
id = "Coux9zxTFKZpRdFpE4F7Fs5RZ6FdaURdckwS61BUTMG",
)]
pub struct CounterProgram;The StarFrameProgram::entrypoint’s default implementation calls in to the
InstructionSet::dispatch method.
§Instruction Set Dispatch
The InstructionSet derive macro defines instruction discriminants for each instruction variant
(by default it is compatible with Anchor’s sighashes, but you can override it), and generates dispatch logic:
use star_frame::prelude::*;
#[derive(InstructionSet)]
pub enum CounterInstructionSet {
Initialize(Initialize),
Increment(Increment),
}
The derived InstructionSet::dispatch method matches on the instruction discriminant
from the instruction data, and calls Instruction::process_from_raw for the matched instruction.
§Instruction Processing
The Instruction trait provides the low-level interface for instruction processing,
but it’s rough and requires manual handling of raw account data and instruction bytes. In most cases, you should
implement the StarFrameInstruction and InstructionArgs
traits instead. The Instruction trait is implemented generically for all instructions that implement StarFrameInstruction.
Check the docs on StarFrameInstruction for how that implementation works.
§Instruction Data Parsing
Instructions implement BorshDeserialize (to parse the instruction data), and InstructionArgs
(to split the data into AccountSet lifecycle arguments). See the InstructionArgs trait for more information.
use star_frame::prelude::*;
#[derive(BorshSerialize, BorshDeserialize, Debug, InstructionArgs)]
pub struct Initialize {
#[ix_args(&run)]
pub start_at: Option<u64>,
}§Defining Program Accounts
Star Frame provides multiple ways to define program accounts for different use cases:
§Basic Account with Standard Derive
For statically sized accounts, you can use the ProgramAccount derive.
For the best performance, you can use bytemuck (with the zero_copy macro for convenience) with Account:
use star_frame::prelude::*;
#[zero_copy(pod)]
#[derive(Default, Debug, Eq, PartialEq, ProgramAccount)]
#[program_account(seeds = CounterSeeds)]
pub struct CounterAccount {
pub authority: Pubkey,
pub count: u64,
}
// Strongly typed seeds can be defined too!
#[derive(Debug, GetSeeds, Clone)]
#[get_seeds(seed_const = b"COUNTER")]
pub struct CounterSeeds {
pub authority: Pubkey,
}You can also use borsh with BorshAccount if you don’t like need performance.
§Unsized Accounts with the Unsized Type system
For accounts with variable-size data like vectors or dynamic strings, use unsized_type.
use star_frame::prelude::*;
#[derive(Debug, GetSeeds, Clone)]
#[unsized_type(program_account, seeds = CounterSeeds)]
pub struct CounterAccount {
pub authority: Pubkey,
#[unsized_start]
pub count_tracker: UnsizedMap<Pubkey, PackedValue<u64>>,
}Check out the unsize module for more details.
§Account Set Validation
Accounts are validated through AccountSet traits with compile-time and runtime checks:
use star_frame::prelude::*;
#[derive(AccountSet)]
pub struct InitializeAccounts {
#[validate(funder)]
pub authority: Signer<Mut<SystemAccount>>,
#[validate(arg = (
Create(()),
Seeds(CounterSeeds { authority: *self.authority.pubkey() }),
))]
pub counter: Init<Seeded<Account<CounterAccount>>>,
pub system_program: Program<System>,
}
#[derive(Align1, Pod, Zeroable, Default, Copy, Clone, Debug, Eq, PartialEq, ProgramAccount)]
§Instruction Processing
Finally, StarFrameInstruction::process executes the instruction logic:
use star_frame::prelude::*;
use star_frame::prelude::*;
impl StarFrameInstruction for Initialize {
type Accounts<'decode, 'arg> = InitializeAccounts;
type ReturnType = ();
fn process(
accounts: &mut Self::Accounts<'_, '_>,
start_at: &Option<u64>,
_ctx: &mut Context,
) -> Result<()> {
**accounts.counter.data_mut()? = CounterAccount {
authority: *accounts.authority.pubkey(),
count: start_at.unwrap_or(0),
};
Ok(())
}
}You can directly implement StarFrameInstruction with the process function using the
star_frame_instruction macro.
use star_frame::prelude::*;
#[star_frame_instruction]
fn Initialize(accounts: &mut InitializeAccounts, start_at: &Option<u64>) -> Result<()> {
**accounts.counter.data_mut()? = CounterAccount {
authority: *accounts.authority.pubkey(),
count: start_at.unwrap_or(0),
};
Ok(())
}§Putting it all together
You can check out our example programs for more information, and the simple counter example for how these steps are put together.
§Generating IDLs
Star Frame can automatically generate Codama IDL files for your programs when the idl feature flag is enabled.
IDLs are JSON files that describe your program’s interface, making it easier for clients to interact with your program.
Check out the Codama for more information on generating clients and using the IDL.
§Enabling IDL Generation
Add the idl feature to your Cargo.toml:
[dependencies]
star_frame = { version = "*", features = ["idl"] }§Generating an IDL
Programs that derive StarFrameProgram automatically implement
ProgramToIdl. You can create a test to generate the IDL:
#[cfg(feature = "idl")]
#[test]
fn generate_idl() -> Result<()> {
use star_frame::prelude::*;
let idl = CounterProgram::program_to_idl()?;
let codama_idl: ProgramNode = idl.try_into()?;
let idl_json = codama_idl.to_json()?;
std::fs::write("idl.json", &idl_json)?;
Ok(())
}And run it with:
cargo test --features idl -- generate_idl§Feature Flags
Star Frame provides several feature flags to customize functionality:
idl- Enables IDL generation for client librariestest_helpers- Provides utilities for testing programs and the unsized type systemcleanup_rent_warning- Emits a warning message if the account has more lamports than required by rent on cleanupaggressive_inline- Adds#[inline(always)]to more functions. Can be beneficial in some cases, but will likely increase binary size and may even reduce performance. This should only be used when you have thorough benchmarks and are confident in the performance impact.
Re-exports§
pub extern crate advancer;pub extern crate borsh;pub extern crate bytemuck;pub extern crate derive_more;pub extern crate derive_where;pub extern crate fixed;pub extern crate itertools;pub extern crate num_traits;pub extern crate paste;pub extern crate pinocchio;pub extern crate serde;pub extern crate solana_instruction;pub extern crate solana_pubkey;pub extern crate self as star_frame; pub extern crate static_assertions;pub extern crate typenum;
Modules§
- account_
set - Strongly typed and statically verified instruction accounts.
- align1
- Alignment to 1 byte. Much of the
crate::unsizemagic relies on packed alignment and no padding. - client
- Client-side utilities for working with Star Frame programs.
- context
- Context for instruction execution.
- cpi
- Cross program invocation (CPI) builders and utilities.
- data_
types - Utility data types for Star Frame programs.
- errors
- instruction
- Processing and handling of instructions from a
StarFrameProgram::entrypoint. - prelude
- Commonly used types and traits:
use star_frame::prelude::*. - program
- Core program definitions and utilities for Star Frame programs. Provides the foundational traits and macros needed to define and execute Solana programs with type safety.
- unsize
- Zero-copy, dynamically sized, CU-efficient types.
- util
- Useful miscellaneous functions.
Macros§
- bail
- Returns an
Err<Error> - borsh_
with_ bytemuck - Derives
BorshSerializeandBorshDeserializeforbytemucktypes. - create_
unit_ system - Creates a new unit system type.
- ensure
- Returns an
Err<Error>if the condition is false - ensure_
eq - Returns an
Err<Error>if left is not equal to right - ensure_
ne - Returns an
Err<Error>if left is equal to right - error
- Constructs an
Error - program_
setup - Defines useful top level items for a
star_frameprogram. - pubkey
- Compile time generation of a
Pubkeyfrom a base58 string literal. - sighash
- Compile time hashing of string literals.
- star_
frame_ entrypoint - Defines the entrypoint for a
star_frameprogram.
Functions§
- Ok
- Equivalent to
Ok::<_, Error>(value)
Type Aliases§
Attribute Macros§
- zero_
copy - Convenience wrapper around the common
bytemuckderives andreprattribute.