Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/blueshift-gg/quasar/llms.txt

Use this file to discover all available pages before exploring further.

Quasar’s performance guarantees are not magic — they follow directly from the way the Solana Virtual Machine serializes data and from deliberate design choices in how Quasar reads that data back. This page explains the SVM input buffer format, how Quasar turns raw bytes into typed Rust references without copying, what discriminators are and how they differ from Anchor’s approach, how the alignment-1 guarantee makes pointer casts sound, the account validation pipeline, how PDA bumps are cached for cheap verification, the no_std constraint, and what all of this means for your program’s compute-unit budget.

The SVM Input Buffer

When the Solana runtime invokes a program, it builds a single contiguous byte region — the input buffer — and passes a pointer to it as the program’s only argument. The layout looks like this:
[u64 num_accounts]
[RuntimeAccount header 0] [account data 0] [padding to 8-byte align]
[RuntimeAccount header 1] [account data 1] [padding to 8-byte align]
...
[u64 ix_data_len] [instruction data bytes]
[u8; 32 program_id]
Each non-duplicate account entry starts with a RuntimeAccount header struct. That header contains the borrow state, signer/writable/executable flags, the account’s address, its owner, lamport balance, and a data_len field. Immediately after the header come exactly data_len bytes of raw account data, followed by enough zero padding to realign the pointer to an 8-byte boundary. Duplicate accounts (the same account passed more than once) are represented as a single u64 whose low byte holds the index of the original entry — saving buffer space and preventing double-parse. Quasar computes the stride past each account with:
pub const fn account_stride(data_len: usize) -> usize {
    align_up_8(ACCOUNT_HEADER.wrapping_add(data_len))
}
where ACCOUNT_HEADER is size_of::<RuntimeAccount>() + MAX_PERMITTED_DATA_INCREASE + size_of::<u64>(). The MAX_PERMITTED_DATA_INCREASE padding region exists so that realloc has room to grow account data in-place without moving the buffer.
Quasar carries a compile-time assertion that size_of::<usize>() >= size_of::<u64>(). This guarantees that data_len as usize casts are lossless, which is only true on 64-bit targets — exactly where the SVM runs.

Zero-Copy Access: Pointer Casts from the Buffer

Traditional Solana frameworks read the raw bytes described above and call a deserialization function to fill in a Rust struct. Quasar skips that step entirely. After validating that an account is correctly sized, owned, and discriminated, Quasar performs a direct pointer cast:
buffer address  ──►  &RuntimeAccount header
                ──►  (header + ACCOUNT_HEADER offset)  ──►  &YourAccountStruct
This works because Quasar’s #[account] macro generates a companion type with #[repr(C)]. A #[repr(C)] struct has a stable, predictable memory layout: fields appear in declaration order, with sizes and strides determined by the C ABI rather than by the Rust compiler’s optimizer. Given that the SVM serializes account data as a plain byte array, and given that the companion struct’s layout exactly mirrors how that data was written, a pointer cast is both correct and free. The AccountLayout trait captures this relationship:
pub trait AccountLayout {
    /// The schema type that implements `ZeroPodFixed`.
    type Schema: crate::__zeropod::ZeroPodFixed;

    /// The zero-copy companion type that Deref targets.
    type Target;

    /// Byte offset where account data begins (after discriminator, if any).
    const DATA_OFFSET: usize;

    /// Size of the schema data in bytes.
    const DATA_SIZE: usize = <Self::Schema as crate::__zeropod::ZeroPodFixed>::SIZE;
}
DATA_OFFSET is 0 for external (non-program-owned) accounts and disc_len for program accounts, so the discriminator bytes are skipped before the cast. The StaticView marker trait makes the soundness requirement explicit:
/// # Safety
///
/// The implementor must be `#[repr(transparent)]` over `AccountView` (possibly
/// through a chain of transparent wrappers). This guarantees that a pointer
/// cast from `&AccountView` to `&Self` is sound.
pub unsafe trait StaticView {}
Only types that the #[account] macro generates — and that therefore provably satisfy the layout contract — implement StaticView.

Discriminators

Every Quasar account type carries a discriminator: a short byte prefix stored at the very start of the account’s data region. When Quasar parses an account, it reads those bytes and compares them against the expected constant before touching anything else. An account whose discriminator does not match is rejected with ProgramError::InvalidAccountData. Quasar discriminators are developer-specified, not SHA-256-derived. You write:
#[account(discriminator = 1)]
pub struct Counter {
    pub authority: Address,
    pub count: u64,
}
and the byte 1 becomes the single-byte discriminator for Counter. Instructions use the same mechanism:
#[instruction(discriminator = 0)]
pub fn increment(ctx: Ctx<Increment>) -> Result<(), ProgramError> { ... }
This is intentionally different from Anchor, which computes discriminators as sha256("account:TypeName")[..8]. Developer-specified constants are shorter (often one byte), fully predictable, and do not require a hash computation at build time.
All-zero discriminators are banned at compile time. Uninitialized Solana accounts have their data zeroed by the runtime. Allowing an all-zero discriminator would mean that a freshly-created, never-initialized account could pass discriminator validation — a serious security flaw. The #[account] and #[instruction] macros emit a compile_error! if the provided discriminator is all zeros.
The Discriminator trait in quasar-lang captures this contract:
pub trait Discriminator {
    const DISCRIMINATOR: &'static [u8];

    /// Byte offset of a stored `bump: u8` field from the start of account data.
    ///
    /// When `Some(offset)`, PDA validation can read the bump directly from
    /// account data and use `verify_program_address` (~200 CU) instead of
    /// `find_program_address` (~544 CU).
    const BUMP_OFFSET: Option<usize> = None;
}

The Alignment-1 Guarantee

A pointer cast is only sound if the target type’s alignment requirement is satisfied by the pointer’s actual alignment. On x86, misaligned reads are often harmless (though slower). On some architectures they cause hardware faults. On the SVM, the buffer pointer is 8-byte aligned at the start of each account entry, but field offsets inside the data region depend entirely on how many bytes precede them. Quasar’s solution is to require that every field type in a zero-copy account have alignment 1. This is enforced at compile time via assert! calls generated by the #[account] macro. Pod integer types from the zeropod crate (PodU64, PodI32, PodU16, etc.) are defined with #[repr(C, align(1))] to make this possible. With alignment-1 types, a cast to *const YourStruct is valid for any byte-aligned pointer — including the middle of the SVM input buffer — regardless of field order or struct size.
// Address must be [u8; 32] with alignment 1.
const _: () = {
    assert!(core::mem::size_of::<solana_address::Address>() == 32);
    assert!(core::mem::align_of::<solana_address::Address>() == 1);
};
This pattern appears throughout quasar-lang wherever a new type is introduced that will be used inside account data.

Account Validation Pipeline

When Quasar’s generated parse_accounts code walks the SVM input buffer, each account passes through a fixed sequence of checks before a typed reference is handed to your instruction logic:
1
Discriminator Check
2
The first N bytes of account data are compared against T::DISCRIMINATOR. Mismatch → ProgramError::InvalidAccountData. This runs before any field is accessed.
3
Owner Check
4
account.owner == T::OWNER is verified using the CheckOwner trait, which uses a branchless four-word u64 comparison for 32-byte addresses. Mismatch → ProgramError::IllegalOwner.
5
Length Check
6
account.data_len >= T::SPACE is verified. Mismatch → ProgramError::InvalidAccountData. The pointer cast only happens after this succeeds.
7
Flag Check (Signer / Writable)
8
The 4-byte header word is read once and compared against the expected constant. The constants NODUP_SIGNER, NODUP_MUT, NODUP_MUT_SIGNER, etc. encode borrow state and required flags in a single comparison. Extra flags (e.g. signer when not required) are accepted silently by the mask-based cold path.
9
Constraint Evaluation
10
Constraints declared in #[derive(Accounts)] — such as has_one = authority, seeds, or init — run after the above checks. These are generated as ordinary Rust comparisons on the already-validated account data.
The ParseAccounts trait drives this pipeline:
pub trait ParseAccounts<'input>: Sized {
    type Bumps: Copy;
    fn parse(
        accounts: &'input mut [AccountView],
        program_id: &Address,
    ) -> Result<(Self, Self::Bumps), ProgramError>;
}
parse returns both the validated account struct and a Bumps companion — a generated struct that holds every PDA bump discovered during validation.

Bumps: Cheap PDA Verification

Program Derived Addresses (PDAs) cannot have a private key, so signing for them at runtime requires re-deriving the address. The naive approach calls find_program_address, which iterates bump values from 255 downward and hashes on each attempt — costing up to ~544 CU per bump tried. Quasar avoids this by storing the bump byte in the account data itself. When a bump: u8 field is declared in an #[account] struct, the macro sets Discriminator::BUMP_OFFSET to the byte offset of that field. At PDA verification time, instead of calling find_program_address, Quasar:
  1. Reads the bump directly from the (already validated) account data at BUMP_OFFSET.
  2. Calls verify_program_address once with the known seeds and bump — a single hash operation costing ~200 CU.
This saves roughly 300 CU compared to a find_program_address call, with no extra developer work beyond adding a bump: u8 field to the account struct. The BUMP_OFFSET constant in the Discriminator trait enables this pattern:
/// Byte offset of a stored `bump: u8` field from the start of account data.
///
/// When `Some(offset)`, PDA validation can read the bump directly from
/// account data and use `verify_program_address` (~200 CU) instead of
/// `find_program_address` (~544 CU). Automatically set by
/// `#[account]` when the struct contains a `bump: u8` field.
const BUMP_OFFSET: Option<usize> = None;

The no_std Constraint

Quasar’s lang/ crate is declared #![no_std] at the top of lib.rs. On-chain BPF programs do not have access to the Rust standard library’s OS-dependent features — no file system, no threads, no system time. More importantly for CUs, std pulls in a global allocator by default, and heap allocation on the SVM is slow (the BPF heap is a simple bump allocator with no free). Quasar reaches for alloc — and therefore heap allocation — only in two places:
  • The idl-build feature gate, which generates IDL JSON at compile time, not on-chain.
  • The debug feature gate, which enables sol_log diagnostics for development.
Neither feature is active in a production .so. On-chain, Quasar never allocates.
#![no_std]
#[cfg(any(feature = "debug", feature = "idl-build"))]
extern crate alloc;
This means your entire program — account parsing, validation, instruction dispatch, CPI — runs entirely from the stack and from pointer casts into the SVM-owned input buffer.

Compute Unit Model

Understanding what costs CUs in Solana helps explain why Quasar’s design choices matter:
OperationApproximate CU cost
sol_sha256 syscall (PDA hash attempt)~544 CU
verify_program_address (known bump)~200 CU
sol_invoke_signed (CPI call)~1,000 CU base + accounts
sol_log_data (event emission)~100 CU
Borsh deserialization (varies by struct size)~50–500+ CU
Pointer cast (zero-copy account access)~0 CU (compile-time math)
u64 field read via read_unaligned~1 CU
Quasar’s zero-copy access eliminates deserialization cost entirely. The validation pipeline (discriminator + owner + length + flags) costs a handful of comparisons — far cheaper than decoding bytes into a heap struct. Storing bumps in account data converts expensive find_program_address searches into single-hash verifications.
Use quasar profile to measure your program’s actual CU consumption with a per-function flamegraph. Use quasar profile --diff <program-name> to compare your local build against a deployed on-chain program.
The ZeroCopyDeref trait rounds out the model by providing safe Deref/DerefMut to the #[repr(C)] target type for programs that use interface accounts (like SPL Token mints):
pub trait ZeroCopyDeref {
    type Target;

    /// # Safety
    /// The caller must ensure `view.data_len()` is large enough for the
    /// zero-copy target and that the underlying bytes match the layout.
    unsafe fn deref_from(view: &AccountView) -> &Self::Target;

    /// # Safety
    /// Same as `deref_from`, plus the account must be writable with no
    /// conflicting aliases.
    unsafe fn deref_from_mut(view: &mut AccountView) -> &mut Self::Target;
}
Every field access through ZeroCopyDeref is a load from the original SVM buffer — no intermediate storage, no copies.

Build docs developers (and LLMs) love