learn.sol
Anchor Programs • Anchor Core Concepts

Anchor Core Concepts

Learn the Anchor model clearly: instructions, accounts, signers, and constraints, and how those pieces control what your program is allowed to do.

Before you write a real Anchor program, you need the mental model that makes the macros readable.

Without that model, Anchor looks like syntax.

With that model, Anchor looks like validation plus state changes.

That is the right way to think about it.

The Four Ideas That Control Everything

At the beginner level, Anchor is mostly four things working together:

  1. instructions
  2. accounts
  3. signers
  4. constraints

If you understand those four clearly, the first real program lesson becomes much easier.

1. Instruction

An instruction is one action your program exposes.

In Anchor, that usually means one function inside the #[program] module.

#[program]
pub mod counter {
    use super::*;

    pub fn increment(ctx: Context<Update>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count += 1;
        Ok(())
    }
}

What this means in plain language:

  • a client asked your program to run increment
  • the client had to provide the required accounts
  • Anchor validated those accounts first
  • only then did your Rust logic run

That order matters.

The validation layer runs before your handler logic.

2. Accounts

Solana programs do not keep normal server memory between requests.

They read and write accounts.

In Anchor, your persistent state is usually represented with structs marked by #[account].

#[account]
pub struct Counter {
    pub authority: Pubkey,
    pub count: u64,
}

This struct describes on-chain data.

It is not just a Rust convenience type.

It is part of the actual state model your program is allowed to read and write.

3. Signers

A signer is an account that approved the transaction.

In Anchor, you often represent that with Signer<'info>.

pub authority: Signer<'info>

This does one important thing.

It tells Anchor to reject the instruction unless that account really signed the transaction.

A signer is not automatically the same thing as an owner.

That confusion causes a lot of mistakes.

The signer authorizes the transaction.

Ownership and stored authority are separate things your program still needs to reason about.

4. Constraints

Constraints are the rules Anchor checks before your handler runs.

This is one of the main reasons Anchor exists.

Instead of pushing every validation rule deep into the function body, you can express many of them directly in the account context.

#[derive(Accounts)]
pub struct Update<'info> {
    pub authority: Signer<'info>,

    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
}

This says:

  • authority must sign
  • counter can be mutated
  • the counter.authority field must match the provided authority

If one of those checks fails, your handler does not run.

That is the critical idea.

The official Anchor docs frame account validation as a core security feature.

That is the right emphasis.

Most serious program bugs are not caused by missing syntax. They are caused by bad validation.

How These Pieces Fit Together

Look at this full shape:

#[program]
pub mod counter {
    use super::*;

    pub fn increment(ctx: Context<Update>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count += 1;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Update<'info> {
    pub authority: Signer<'info>,

    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
}

#[account]
pub struct Counter {
    pub authority: Pubkey,
    pub count: u64,
}

Now read it in order:

  1. the instruction is increment
  2. the account context is Update
  3. the required signer is authority
  4. the target state account is counter
  5. the constraints say who is allowed to mutate it
  6. the handler changes the value only after those rules pass

That is Anchor in one example.

Why Constraints Matter More Than Beginners Expect

The Anchor docs provide many built-in account constraints such as:

  • signer
  • mut
  • init
  • seeds
  • bump
  • has_one
  • constraint = ...

You do not need all of them right now.

But you do need the right mental model:

constraints are not decoration.

They are part of your security model.

One Small Example With init

When you create a new account, the context often looks like this:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,

    #[account(
        init,
        payer = authority,
        space = 8 + 40,
    )]
    pub counter: Account<'info, Counter>,

    pub system_program: Program<'info, System>,
}

What Anchor is doing here:

  • init means create the account
  • payer = authority means the signer funds the rent-exempt allocation
  • space = ... tells Anchor how much data space the account needs
  • system_program is required because account creation happens through the system program

This is still the same model.

Anchor is expressing validation and setup rules before your instruction logic runs.

The Most Important Beginner Distinctions

Instruction is not the same as transaction

A transaction can contain one instruction or many.

Your Anchor handler represents one instruction.

Signer is not the same as authority

A signer approves a transaction.

Authority is a rule your program may store in account data and enforce with constraints like has_one.

mut is not optional noise

If an account will be written to, Anchor needs to know that.

Without mut, state changes will not be persisted correctly.

Client checks are not enough

Even if the frontend verifies something, the program still has to enforce the real rule on-chain.

The program is the final authority.

One Good Reading Habit

Whenever you look at an Anchor instruction, ask these four questions in order:

  1. what instruction is being called?
  2. which accounts are involved?
  3. who must sign?
  4. what constraints must be true before the logic runs?

If you make that a habit, later Anchor code becomes much easier to read.

Quick Self-Check

Before moving on, make sure you can answer these clearly:

  1. If a has_one check fails, does the handler still run?
  2. Why is Signer<'info> different from a stored authority field?
  3. Why does init need a payer?
  4. Why is account validation part of security, not just convenience?

If any of those answers are fuzzy, reread the examples once more.

What Comes Next

The next lesson uses these ideas to build a real Anchor program slowly.

That is the point where the model stops being abstract and starts feeling practical.

Solana Assistant

AI-powered documentation helper

Welcome to Solana Assistant

Ask specific questions about Solana development:

Ask specific questions for better results400px
    Anchor Core Concepts | learn.sol