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.

Every persistent piece of state in a Quasar program lives in an on-chain account described by the #[account] attribute macro. Unlike frameworks that deserialize account data into heap-allocated Rust structs, Quasar pointer-casts the raw SVM input buffer directly into a #[repr(C)] companion struct — zero copies, zero allocations, zero deserialization overhead.

The #[account] Attribute

Annotate a Rust struct with #[account(discriminator = N)] to turn it into a typed on-chain account. The macro automatically generates:
  • Discriminator trait impl with DISCRIMINATOR: &'static [u8]
  • Owner trait impl binding the account to crate::ID
  • Space trait impl summing the discriminator length and all field sizes
  • A #[repr(C)] zero-copy companion struct (the “Zc” type) used for direct pointer access
use quasar_lang::prelude::*;

#[account(discriminator = 1)]
pub struct Counter {
    pub authority: Address,
    pub count: u64,
}
The integer 1 becomes a single-byte discriminator [1u8] prepended to the account’s data. Setting discriminator to 0 (all-zero bytes) is banned at compile time — the macro emits an error to prevent uninitialized data from accidentally passing validation.

Core Traits Generated

Discriminator

DISCRIMINATOR: &'static [u8] — the byte prefix stored at offset 0. Checked during every account parse.

Owner

OWNER: Address = crate::ID — the program that must own the account. Validated when Account<T> is parsed.

Space

SPACE: usize — total byte length including the discriminator. Used by init to size the create_account CPI.

Account Data Layout

┌─────────────────────────────────────────┐
│  discriminator (1..N bytes)             │  ← checked on every parse
│  field 0 bytes                          │
│  field 1 bytes                          │
│  …                                      │
└─────────────────────────────────────────┘
The discriminator occupies the first bytes; all fields follow contiguously in C struct order.

Zero-Copy Access and #[repr(C)]

Quasar generates a #[repr(C)] companion struct (the “Zc” type) for each #[account] struct. Accessing account fields goes through a pointer cast into the account’s data region — no heap allocation and no copying. The Account<T> wrapper implements Deref and DerefMut to this Zc type.
// Reading a field — no deserialization, just a pointer read
let count: u64 = ctx.accounts.counter.count;

// Writing a field — direct pointer write
ctx.accounts.counter.count += 1;
All field types used in an #[account] struct must be Pod (plain-old-data) types with alignment 1. Use the zeropod types provided by Quasar (PodU64, PodBool, etc.) or Address (a 32-byte array) for every field.

Pod Types from zeropod

Quasar re-exports alignment-safe integer types from the zeropod crate under quasar_lang::pod:
Quasar typeRust primitive
PodU64u64
PodU32u32
PodU16u16
PodU8 / u8u8
PodBoolbool
PodString (aliased as quasar_lang::String)variable-length string
PodVec (aliased as quasar_lang::Vec)variable-length bytes
Address[u8; 32] — a Solana public key
Standard u64, u32, etc. are also valid when they already have the right alignment in a #[repr(C)] struct. Use Address directly for public keys.

PDA Seeds with #[seeds]

Combine #[account] with a #[seeds] attribute to declare typed PDA derivation:
#[account(discriminator = 1, set_inner)]
#[seeds(b"escrow", maker: Address)]
pub struct Escrow {
    pub maker: Address,
    pub mint_a: Address,
    pub mint_b: Address,
    pub maker_ta_b: Address,
    pub receive: u64,
    pub bump: u8,
}
This generates an Escrow::seeds(maker: &Address) -> impl SeedSpec method used by the address = constraint:
#[account(init, payer = maker, address = Escrow::seeds(maker.address()))]
pub escrow: Account<Escrow>,
When the struct contains a bump: u8 field, Discriminator::BUMP_OFFSET is automatically set so that PDA verification reads the bump from account data (~200 CU) instead of re-deriving it (~544 CU).

set_inner for Account Initialization

When set_inner is included in the #[account] attribute, the macro generates a set_inner method on the account wrapper. This is the idiomatic way to write all fields at once after init:
// From the escrow example — writing initial state after #[account(init)]
self.escrow.set_inner(EscrowInner {
    maker: *self.maker.address(),
    mint_a: *self.mint_a.address(),
    mint_b: *self.mint_b.address(),
    maker_ta_b: *self.maker_ta_b.address(),
    receive,
    bump: bumps.escrow,
});
set_inner accepts an EscrowInner value (a plain Rust struct with the same fields) and writes each field into the account’s data region through the zero-copy companion.

AccountView Methods

Every account wrapper exposes its underlying AccountView through the AsAccountView trait. The most commonly used methods are:
account.address()    // &Address — the account's public key
account.owner()      // &Address — the program that owns this account
account.lamports()   // u64 — current lamport balance
account.data_len()   // usize — byte length of the data region

Escrow State: Full Example

Here is the complete state definition from the escrow example program:
use quasar_lang::prelude::*;

#[account(discriminator = 1, set_inner)]
#[seeds(b"escrow", maker: Address)]
pub struct Escrow {
    pub maker: Address,
    pub mint_a: Address,
    pub mint_b: Address,
    pub maker_ta_b: Address,
    pub receive: u64,
    pub bump: u8,
}
And the counter example from the README:
declare_id!("22222222222222222222222222222222222222222222");

#[account(discriminator = 1)]
pub struct Counter {
    pub authority: Address,
    pub count: u64,
}
Keep discriminator values unique across every account type in your program. The macro cannot enforce uniqueness across multiple types, but the IDL generator will warn about collisions.

All-Zero Discriminator Ban

Quasar bans all-zero discriminators at compile time. A freshly created account’s data buffer is zeroed, so an all-zero discriminator would allow uninitialized accounts to pass the discriminator check. The macro emits a compile error:
error: discriminator must not be all-zero (would match uninitialized accounts)
Always start user-defined account discriminators at 1 or higher.

Build docs developers (and LLMs) love