Account Types
Reference for all built-in account types in Quasar.
Every field in a #[derive(Accounts)] struct must use one of these wrapper types. Each validates the account during instruction parsing.
Summary
| Type | Validates | Typical Use |
|---|---|---|
Account<T> | Owner + discriminator | Program-owned data accounts |
Signer | is_signer flag | Fee payers, authorities |
UncheckedAccount | Nothing | CPI passthrough, manual checks |
SystemAccount | Owner is system program | SOL-holding wallets |
Program<T> | Executable + address | Program accounts for CPI |
Interface<T> | Executable + multi-address | Token program (SPL / Token-2022) |
InterfaceAccount<T> | Multi-owner + discriminator | Token/Mint accounts across programs |
Sysvar<T> | Address matches sysvar ID | Clock, Rent |
Option<&'info T> | Presence check via sentinel | Optional accounts |
Account<T>
Typed wrapper for program-owned data accounts. T is your #[account] struct.
Validates:
- Owner matches
T::OWNER(your program ID) -- returnsIllegalOwner - Discriminator matches
T::DISCRIMINATOR-- returnsInvalidAccountData - Data is large enough for the type -- returns
AccountDataTooSmall
Implements: Deref<Target = T> and DerefMut for direct field access.
Type signature:
#[repr(transparent)]
pub struct Account<T> {
pub(crate) inner: T,
}Example:
#[account(discriminator = 1)]
pub struct Vault {
pub authority: Address,
pub balance: u64,
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut, has_one = authority)]
pub vault: &'info mut Account<Vault>,
pub authority: &'info Signer,
}Methods:
| Method | Description |
|---|---|
realloc(new_space, payer, rent) | Resize account data, adjusting lamports for rent exemption |
close(destination) | Zero discriminator, drain lamports, reassign to system program |
owner() | Returns the expected owner Address (requires T: Owner) |
Static vs dynamic accounts
Fixed-size accounts (all fixed-width fields) are constructed via pointer cast with no deserialization cost.
Dynamic accounts (containing String or Vec fields) walk the length prefixes once to cache byte offsets, then provide O(1) field access:
#[account(discriminator = 5)]
pub struct Profile<'a> {
pub owner: Address,
pub name: String, // dynamic field
pub bio: String<u16, 4096>, // custom prefix + max
}
#[derive(Accounts)]
pub struct UpdateProfile<'info> {
#[account(mut)]
pub profile: &'info mut Account<Profile<'info>>,
}Dynamic accounts have additional methods:
| Method | Description |
|---|---|
field_name() | Read accessor returning &str or &[T] |
field_name_raw() | Raw bytes including length prefix (for CPI passthrough) |
set_field_name(payer, value) | Write accessor with auto-realloc |
Signer
An account that must be a transaction signer.
Validates:
is_signerflag is set -- returnsMissingRequiredSignature
Type signature:
#[repr(transparent)]
pub struct Signer {
view: AccountView,
}Example:
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: &'info Signer,
pub system_program: &'info Program<System>,
}UncheckedAccount
No validation. Use for accounts passed through to CPI calls or validated manually in the handler.
Type signature:
#[repr(transparent)]
pub struct UncheckedAccount {
view: AccountView,
}Example:
#[derive(Accounts)]
pub struct ForwardCpi<'info> {
/// CHECK: Validated by the target program via CPI.
pub target_account: &'info UncheckedAccount,
pub target_program: &'info UncheckedAccount,
}SystemAccount
An account owned by the System program. Used for SOL-holding wallets.
Validates:
- Account owner is the all-zeros address (System program) -- returns
IllegalOwner
Type signature:
#[repr(transparent)]
pub struct SystemAccount {
view: AccountView,
}Example:
#[derive(Accounts)]
pub struct TransferSol<'info> {
#[account(mut)]
pub from: &'info Signer,
#[account(mut)]
pub to: &'info SystemAccount,
pub system_program: &'info Program<System>,
}Program<T>
Validates the executable flag and address. T must implement Id (providing const ID: Address).
Validates:
- Account is executable -- returns
InvalidAccountData - Account address matches
T::ID-- returnsIncorrectProgramId
Type signature:
#[repr(transparent)]
pub struct Program<T: Id> {
view: AccountView,
_marker: PhantomData<T>,
}Example:
#[derive(Accounts)]
pub struct CreateAccount<'info> {
#[account(mut)]
pub payer: &'info Signer,
pub system_program: &'info Program<System>,
}Methods:
| Method | Description |
|---|---|
emit_event(event, event_authority, bump) | Emit an event via self-CPI to this program |
Common program marker types: System, Token, Token2022, AssociatedTokenProgram, MetadataProgram.
Interface<T>
A program account wrapper that accepts multiple program IDs. T must implement ProgramInterface.
Validates:
- Account is executable -- returns
InvalidAccountData T::matches(address)returns true -- returnsIncorrectProgramId
Type signature:
#[repr(transparent)]
pub struct Interface<T: ProgramInterface> {
view: AccountView,
_marker: PhantomData<T>,
}Example:
use quasar_spl::interface::TokenInterface;
#[derive(Accounts)]
pub struct Transfer<'info> {
#[account(mut)]
pub from: &'info InterfaceAccount<Token>,
#[account(mut)]
pub to: &'info InterfaceAccount<Token>,
pub token_program: &'info Interface<TokenInterface>,
}The built-in TokenInterface accepts both SPL Token and Token-2022 program IDs.
InterfaceAccount<T>
A typed account wrapper that accepts accounts owned by SPL Token or Token-2022. Provides zero-copy Deref/DerefMut to the inner data layout.
Validates:
- Account owner is SPL Token or Token-2022 -- returns
IllegalOwner T::check()passes (discriminator/data validation) -- returnsInvalidAccountData
Implements: Deref<Target = T::Target> and DerefMut when T: ZeroCopyDeref.
Type signature:
#[repr(transparent)]
pub struct InterfaceAccount<T> {
view: AccountView,
_marker: PhantomData<T>,
}Example:
use quasar_spl::{token::Token, mint::Mint};
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut, token::mint = mint, token::authority = authority)]
pub vault: &'info mut InterfaceAccount<Token>,
pub mint: &'info InterfaceAccount<Mint>,
pub authority: &'info Signer,
pub token_program: &'info Interface<TokenInterface>,
}Methods:
| Method | Description |
|---|---|
resolve() | Dispatch to program-specific resolved type based on runtime owner (when T: InterfaceResolve) |
Sysvar<T>
A read-only wrapper for sysvar accounts. Validates the account address matches T::ID and provides zero-copy access via Deref.
Validates:
- Account address matches the sysvar ID -- returns
IncorrectProgramId
Implements: Deref<Target = T>, so sysvar fields are accessed directly.
Supported sysvars:
| Sysvar | Type | Fields |
|---|---|---|
| Clock | Sysvar<Clock> | slot, epoch, unix_timestamp, epoch_start_timestamp, leader_schedule_epoch |
| Rent | Sysvar<Rent> | minimum_balance_unchecked(data_len), try_minimum_balance(data_len) |
Type signature:
#[repr(transparent)]
pub struct Sysvar<T: sysvars::Sysvar> {
view: AccountView,
_marker: PhantomData<T>,
}Example:
#[derive(Accounts)]
pub struct ReadClock<'info> {
pub clock: &'info Sysvar<Clock>,
}
impl ReadClock<'_> {
pub fn handler(&self) -> Result<(), ProgramError> {
let slot = self.clock.slot.get();
let timestamp = self.clock.unix_timestamp.get();
Ok(())
}
}You can also fetch sysvars via syscall without an account:
let clock = Clock::get()?;
let rent = Rent::get()?;Option<&'info T>
An optional account. When the client passes the program ID as the account address, the field resolves to None. Otherwise it resolves to Some(&T) with full validation for type T.
Validates:
- If address equals the program ID: field is
None(no further checks) - Otherwise: full validation for the inner type
T
Example:
#[derive(Accounts)]
pub struct OptionalDeposit<'info> {
#[account(mut)]
pub vault: &'info mut Account<Vault>,
pub optional_authority: Option<&'info Signer>,
}
impl OptionalDeposit<'_> {
pub fn handler(&self) -> Result<(), ProgramError> {
if let Some(authority) = self.optional_authority {
// authority is present and validated as Signer
}
Ok(())
}
}Traits
All account types implement AsAccountView, which provides access to the underlying AccountView:
pub trait AsAccountView {
fn to_account_view(&self) -> &AccountView;
fn address(&self) -> &Address;
}For program-owned data accounts, the #[account] macro automatically implements these traits on your type:
| Trait | Purpose |
|---|---|
Owner | const OWNER: Address -- your program ID |
Discriminator | const DISCRIMINATOR: &[u8] -- byte prefix |
Space | const SPACE: usize -- total account size |
AccountCheck | Runtime validation (discriminator check) |
StaticView | Marks type safe for pointer-cast construction |
