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.

Account<T> is the standard way to declare a typed, program-owned data account in a Quasar instruction. During account parsing, Quasar validates that the account is owned by T::OWNER, that its data begins with T::DISCRIMINATOR, and that its data length is at least T::SPACE. Once all checks pass, the wrapper is available as Account<T>, which is #[repr(transparent)] over T — no heap allocation, no deserialization.

What Account<T> Validates

When Quasar’s #[derive(Accounts)] macro parses an Account<T> field, it performs three checks in order:
  1. Owner checkview.owner() == T::OWNER. Returns ProgramError::IllegalOwner if wrong.
  2. Discriminator check — the first bytes of account data must equal T::DISCRIMINATOR. Returns ProgramError::InvalidAccountData if missing.
  3. Data length check — account data must be at least T::SPACE bytes. Returns ProgramError::AccountDataTooSmall if too short.
These checks happen at parse time, before any handler code runs. The validated pointer is then reinterpreted as Account<T> via a #[repr(transparent)] cast over T.

Zero-Copy Deref

Account<T> implements Deref<Target = T> and DerefMut<Target = T>. Because T itself is a zero-copy wrapper generated by the #[account] macro, all field accesses dereference directly into the live account data — no copies, no allocations.
// Reading a field from a live account — zero copies
let maker: &Address = &ctx.accounts.escrow.maker;
let receive: u64 = ctx.accounts.escrow.receive;
The T here is the struct generated by #[account], and its fields are Pod types (PodU64, Address, etc.) accessed through raw pointer arithmetic to the account’s data region.

Declaring Account<T> in Accounts Structs

Use Account<T> inside any #[derive(Accounts)] struct. The #[account(mut)] attribute marks the account as writable; omitting it makes it read-only.
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,
}

#[derive(Accounts)]
pub struct IncrementCounter {
    #[account(mut)]
    pub authority: Signer,

    // Read-only account — no #[account(mut)]
    pub escrow: Account<Escrow>,
}

#[derive(Accounts)]
pub struct UpdateEscrow {
    #[account(mut)]
    pub maker: Signer,

    // Writable account — #[account(mut)]
    #[account(mut, has_one(maker), address = Escrow::seeds(maker.address()))]
    pub escrow: Account<Escrow>,
}

Read vs Write Access

Read-only: Account<T>

Omit #[account(mut)]. The account is only accessible via Deref. Quasar does not check is_writable at parse time, but the SVM will reject any attempt to modify the account.

Writable: #[account(mut)]

Add #[account(mut)]. Required if you call set_inner(), close(), or realloc(). The DerefMut impl is available, giving &mut T access.

Accessing Address and Lamports

Account<T> implements AsAccountView, which provides uniform access to the underlying AccountView. The address() method is inherited from this trait:
// Get the account's on-chain address (public key)
let addr: &Address = ctx.accounts.escrow.address();

// Get current lamport balance
let balance: u64 = ctx.accounts.escrow.to_account_view().lamports();

Writing Account Data with set_inner

When the #[account(..., set_inner)] attribute is present on an account struct, Quasar generates a set_inner method. This is the idiomatic way to write all fields of an account after initialization:
// From examples/escrow/src/instructions/make.rs
pub fn make_escrow(&mut self, receive: u64, bumps: &MakeBumps) -> Result<(), ProgramError> {
    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,
    });
    Ok(())
}
set_inner uses ptr::copy_nonoverlapping to write directly into the account’s data region, past the discriminator prefix.

Constraint Attributes

The #[account(...)] attribute on a field in #[derive(Accounts)] accepts several built-in constraints:
Marks the account as writable. Required for any handler that modifies account data or lamports.
Validates that account.field == field_account.address(). Quasar compares the stored Address inside the account data against the address of the named account in the same struct. Returns QuasarError::HasOneMismatch if the addresses do not match.
#[account(mut, has_one(maker), close(dest = taker))]
pub escrow: Account<Escrow>,
pub maker: UncheckedAccount,
After the handler returns, Quasar zeros the discriminator, drains all lamports to target, reassigns the account owner to System, and resizes to zero. The account is effectively closed in the epilogue.
// From examples/escrow/src/instructions/take.rs
#[account(
    mut,
    has_one(maker),
    has_one(maker_ta_b),
    constraints(escrow.receive > 0),
    close(dest = taker),
    address = Escrow::seeds(maker.address())
)]
pub escrow: Account<Escrow>,
Validates that the account’s address matches the result of expr. Typically used with PDA derivation helpers like Escrow::seeds(maker.address()).
Creates a new account via system program CPI, writes the discriminator, and returns an initialized Account<T>. Requires the account to be uninitialized (owned by System program).
Like init, but skips account creation if the account already exists and is initialized. Useful for token accounts or other accounts that may be created by an earlier instruction in the same transaction or a prior transaction.
#[account(init(idempotent), payer = maker, token(mint = mint_b, authority = maker, token_program = token_program))]
pub maker_ta_b: Account<Token>,

AccountLayout Trait

The AccountLayout trait associates three compile-time constants with an account type:
pub trait AccountLayout {
    /// The Pod schema type used for validation.
    type Schema: ZeroPodFixed;

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

    /// Byte offset where account data starts (after the discriminator).
    const DATA_OFFSET: usize;
}
Account<T> forwards AccountLayout to T. The DATA_OFFSET is the length of T::DISCRIMINATOR, ensuring that Deref skips past the discriminator bytes when returning a reference to the account’s payload.

Complete Escrow Example

The following shows how Account<T> is used end-to-end in the escrow example:
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,
}

Account<T> vs UncheckedAccount

Use Account<T>

  • You own the account type (it’s defined in your program)
  • You need validated owner + discriminator + data length checks
  • You want zero-copy field access via Deref
  • You need set_inner(), close(), or realloc()

Use UncheckedAccount

  • The account is from an external program with no Quasar type
  • You’re forwarding the account to a CPI without inspecting data
  • You need to perform fully custom validation logic
  • You want raw AccountView access
Account<T> does not check the is_writable flag during parsing unless you add #[account(mut)]. Always add the mut constraint when your handler modifies account state, or the SVM may silently ignore your writes.

Build docs developers (and LLMs) love