Skip to main content
In this quickstart guide, you’ll build, test, and deploy a simple counter program using the Anchor framework. This hands-on tutorial will teach you the fundamentals of Anchor program development.

What you’ll build

You’ll create a counter program that:
  • Creates a counter account to store data
  • Increments the counter value
  • Validates account ownership and authority
  • Demonstrates Anchor’s account constraints

Prerequisites

Before you begin, ensure you have installed:
  • Rust
  • Solana CLI
  • Anchor CLI
  • Node.js and Yarn
See the Installation guide if you need to install these dependencies. Verify Anchor is installed:
anchor --version
Expected output:
anchor-cli 0.32.1

Build the counter program

1
Create a new project
2
Create a new Anchor project:
3
anchor init counter
cd counter
4
This creates a project with the following structure:
5
  • /programs/counter/src/ - Program source code
  • /tests/ - TypeScript tests
  • Anchor.toml - Project configuration
  • 6
    Write the program
    7
    Open programs/counter/src/lib.rs and replace the contents with this counter program:
    8
    use anchor_lang::prelude::*;
    
    declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
    
    #[program]
    pub mod counter {
        use super::*;
    
        pub fn create(ctx: Context<Create>, authority: Pubkey) -> Result<()> {
            let counter = &mut ctx.accounts.counter;
            counter.authority = authority;
            counter.count = 0;
            msg!("Counter created. Authority: {}", authority);
            Ok(())
        }
    
        pub fn increment(ctx: Context<Increment>) -> Result<()> {
            let counter = &mut ctx.accounts.counter;
            counter.count += 1;
            msg!("Counter incremented. Current count: {}", counter.count);
            Ok(())
        }
    }
    
    #[derive(Accounts)]
    pub struct Create<'info> {
        #[account(init, payer = user, space = 8 + 32 + 8)]
        pub counter: Account<'info, Counter>,
        #[account(mut)]
        pub user: Signer<'info>,
        pub system_program: Program<'info, System>,
    }
    
    #[derive(Accounts)]
    pub struct Increment<'info> {
        #[account(mut, has_one = authority)]
        pub counter: Account<'info, Counter>,
        pub authority: Signer<'info>,
    }
    
    #[account]
    pub struct Counter {
        pub authority: Pubkey,
        pub count: u64,
    }
    
    9
    Understanding the code:
    • declare_id!() specifies the program’s on-chain address
    • #[program] marks the module containing instruction handlers
    • create instruction initializes a new counter account
    • increment instruction increases the counter value
    • #[derive(Accounts)] validates accounts for each instruction
    • #[account] defines the counter’s data structure
    10
    Understanding account validation
    11
    Anchor uses constraints to validate accounts:
    12
    Create instruction:
    13
    #[derive(Accounts)]
    pub struct Create<'info> {
        #[account(init, payer = user, space = 8 + 32 + 8)]
        pub counter: Account<'info, Counter>,
        #[account(mut)]
        pub user: Signer<'info>,
        pub system_program: Program<'info, System>,
    }
    
    14
  • init - Creates a new account owned by the program
  • payer = user - The user pays for account creation
  • space = 8 + 32 + 8 - Allocates space for discriminator (8) + authority (32) + count (8)
  • mut - The user account must be mutable to pay rent
  • 15
    Increment instruction:
    16
    #[derive(Accounts)]
    pub struct Increment<'info> {
        #[account(mut, has_one = authority)]
        pub counter: Account<'info, Counter>,
        pub authority: Signer<'info>,
    }
    
    17
  • mut - The counter account must be mutable to update the count
  • has_one = authority - Validates that counter.authority matches the authority account
  • 18
    Build the program
    19
    Build the program:
    20
    anchor build
    
    21
    This compiles your program and generates:
    22
  • Program binary at /target/deploy/counter.so
  • IDL (Interface Description Language) at /target/idl/counter.json
  • TypeScript types at /target/types/counter.ts
  • 23
    After building, sync the program ID with anchor keys sync to update declare_id!() with the generated program address.
    24
    Write tests
    25
    Create a test file at tests/counter.ts:
    26
    import * as anchor from "@anchor-lang/core";
    import { Program } from "@anchor-lang/core";
    import { Counter } from "../target/types/counter";
    import { expect } from "chai";
    
    describe("counter", () => {
      // Configure the client to use the local cluster
      const provider = anchor.AnchorProvider.env();
      anchor.setProvider(provider);
    
      const program = anchor.workspace.Counter as Program<Counter>;
      const counter = anchor.web3.Keypair.generate();
    
      it("Creates a counter", async () => {
        const tx = await program.methods
          .create(provider.wallet.publicKey)
          .accounts({
            counter: counter.publicKey,
            user: provider.wallet.publicKey,
            systemProgram: anchor.web3.SystemProgram.programId,
          })
          .signers([counter])
          .rpc();
    
        console.log("Create transaction signature:", tx);
    
        const counterAccount = await program.account.counter.fetch(
          counter.publicKey
        );
    
        expect(counterAccount.authority.toString()).to.equal(
          provider.wallet.publicKey.toString()
        );
        expect(counterAccount.count.toNumber()).to.equal(0);
      });
    
      it("Increments the counter", async () => {
        const tx = await program.methods
          .increment()
          .accounts({
            counter: counter.publicKey,
            authority: provider.wallet.publicKey,
          })
          .rpc();
    
        console.log("Increment transaction signature:", tx);
    
        const counterAccount = await program.account.counter.fetch(
          counter.publicKey
        );
    
        expect(counterAccount.count.toNumber()).to.equal(1);
      });
    
      it("Increments the counter again", async () => {
        await program.methods
          .increment()
          .accounts({
            counter: counter.publicKey,
            authority: provider.wallet.publicKey,
          })
          .rpc();
    
        const counterAccount = await program.account.counter.fetch(
          counter.publicKey
        );
    
        expect(counterAccount.count.toNumber()).to.equal(2);
      });
    });
    
    27
    Run tests
    28
    Test your program:
    29
    anchor test
    
    30
    You should see output similar to:
    31
      counter
        ✔ Creates a counter (462ms)
        ✔ Increments the counter (421ms)
        ✔ Increments the counter again (415ms)
    
      3 passing (1s)
    
    32
    anchor test automatically starts a local validator, deploys your program, runs tests, and stops the validator.
    33
    Deploy to devnet
    34
    To deploy to devnet, update Anchor.toml:
    35
    [provider]
    cluster = "Devnet"
    wallet = "~/.config/solana/id.json"
    
    36
    Ensure you have devnet SOL:
    37
    solana config set -ud
    solana airdrop 2
    
    38
    Deploy the program:
    39
    anchor deploy
    
    40
    You should see output like:
    41
    Deploying cluster: devnet
    Upgrade authority: YourPublicKeyHere
    Deploying program "counter"...
    Program path: /path/to/counter/target/deploy/counter.so
    Program Id: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
    
    Deploy success
    
    42
    Interact with your program
    43
    Once deployed to devnet, you can interact with your program using the Anchor client, Solana Explorer, or build a frontend application.
    44
    View your program on Solana Explorer by searching for your program ID.

    Understanding the code

    Let’s break down the key components:

    Program structure

    #[program]
    pub mod counter {
        use super::*;
        
        // Instruction handlers
        pub fn create(ctx: Context<Create>, authority: Pubkey) -> Result<()> {
            // Implementation
        }
        
        pub fn increment(ctx: Context<Increment>) -> Result<()> {
            // Implementation
        }
    }
    

    Space calculation

    When initializing accounts, you must specify the space:
    #[account(init, payer = user, space = 8 + 32 + 8)]
    
    • 8 bytes: Account discriminator (automatically added by Anchor)
    • 32 bytes: Pubkey for authority
    • 8 bytes: u64 for count
    Total: 48 bytes

    Account constraints

    Anchor provides powerful constraints for security:
    • init - Create and initialize an account
    • mut - Account data can be modified
    • has_one - Validate account relationships
    • constraint - Custom validation logic
    • signer - Account must sign the transaction
    See the Account Constraints reference for a complete list.

    Next steps

    Congratulations! You’ve built and deployed your first Anchor program. Here’s what to explore next:

    Program structure

    Learn about Anchor macros and program architecture

    Account constraints

    Explore all available account validation constraints

    TypeScript client

    Build frontend applications with the Anchor TS client

    Examples

    Browse more example programs and tutorials

    Build docs developers (and LLMs) love