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.

Signer is a lightweight account wrapper that validates a single property: the is_signer flag in the SVM account header must be set. It performs no owner check, no data check, and no address check. If the flag is not set, parsing fails immediately with ProgramError::MissingRequiredSignature.

What Signer Validates

The Signer type has a trivially simple AccountLoad implementation:
impl AccountLoad for Signer {
    const IS_SIGNER: bool = true;

    fn check(_view: &AccountView) -> Result<(), ProgramError> {
        Ok(())
    }
}
The IS_SIGNER = true constant causes the generated account parser to call checks::Signer::check(view) before constructing the wrapper, which asserts view.is_signer(). The check body itself is a no-op because the signer flag is already validated by the generated code’s IS_SIGNER path.
Signer only validates the transaction-level signature. It does not verify that the signer is a specific address — combine it with #[account(address = ...)] or a has_one constraint when you need both.

What Signer Does NOT Check

Signer intentionally skips several checks that other wrappers perform:
  • Owner: any program can own a signer account
  • Data: account data is not read or validated
  • Executable: not checked
  • Address: not compared against any expected key
This keeps Signer general-purpose. Use it whenever you need “this account proved they authorized this transaction” and nothing more.

Usage Patterns

Read-only Signer (authority)

A Signer without #[account(mut)] provides address() access but does not allow lamport changes or data writes. This is the typical pattern for authority accounts that authorize an operation but don’t pay fees or hold state:
#[derive(Accounts)]
pub struct Increment {
    #[account(mut, has_one(authority))]
    pub counter: Account<Counter>,

    // authority proves consent; counter.authority must match
    pub authority: Signer,
}

Mutable Signer (payer)

Adding #[account(mut)] to a Signer marks it as writable, which is required for payer accounts that fund account creation via system program CPI:
#[derive(Accounts)]
pub struct Make {
    // Payer for new account creation — must be mut
    #[account(mut)]
    pub maker: Signer,

    #[account(init, payer = maker, address = Escrow::seeds(maker.address()))]
    pub escrow: Account<Escrow>,

    pub system_program: Program<SystemProgram>,
}

Getting the Signer’s Address

Signer implements AsAccountView, which provides the address() method via the blanket trait implementation:
pub fn handler(ctx: Ctx<Increment>) -> Result<(), ProgramError> {
    let authority_key: &Address = ctx.accounts.authority.address();
    // ...
    Ok(())
}

has_one Constraint with Signer

The has_one constraint is the idiomatic way to enforce that a stored address in an account matches the signer’s actual address. Quasar compares account_data.authority == authority.address() at parse time:
#[derive(Accounts)]
pub struct Increment {
    #[account(
        mut,
        has_one(authority)  // validates: counter.authority == authority.address()
    )]
    pub counter: Account<Counter>,

    pub authority: Signer,
}
If the addresses do not match, the instruction fails during account parsing — before any handler code runs.

Escrow Example

The escrow example uses Signer in two roles: a mutable payer (maker) and a mutable executor (taker):
// From examples/escrow/src/instructions/make.rs
#[derive(Accounts)]
pub struct Make {
    #[account(mut)]
    pub maker: Signer,   // funds escrow + vault creation
    #[account(init, payer = maker, address = Escrow::seeds(maker.address()))]
    pub escrow: Account<Escrow>,
    // ...
    pub system_program: Program<SystemProgram>,
}

Signer in Remaining Accounts

Signer implements RemainingItem, which means it can be used in typed remaining-account iteration. Duplicate accounts are allowed for Signer (i.e., the same signer may appear more than once):
// REJECT_DUPLICATES = false for Signer
impl<'input> RemainingItem<'input> for Signer {
    const COUNT: usize = 1;
    const REJECT_DUPLICATES: bool = false;
    // ...
}
This lets you pass a signer as both a declared account and a remaining account without triggering a duplicate error.

SystemAccount vs Signer

Signer

Validates only is_signer. Owner, data, and address are unchecked. Use when any signing account is acceptable.

SystemAccount

A dedicated account type that validates the account is owned by the System program (11111...). Does not validate is_signer. Use for wallets and SOL accounts without requiring them to sign.
If you need an account that is both a signer and owned by the System program (a typical user wallet), declare it as Signer — the is_signer check is already a stronger guarantee than an owner check for most authority use cases.

Build docs developers (and LLMs) love