Quick Start
Basic Counter Program

How to Build a Counter Program with Anchor

https://beta.solpg.io/66624e49cffcf4b13384d14b

Starter Code

  • open solana playground
  • starter code
lib.ts
use anchor_lang::prelude::*;
 
declare_id!("");
 
#[program]
pub mod counter {
    use super::*;
 
    pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
 
    pub fn increment(_ctx: Context<Increment>) -> Result<()> {
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct Initialize {}
 
#[derive(Accounts)]
pub struct Increment {}
 
#[account]
pub struct Counter {}
Terminal
build
Output
$ build
Building...
Build successful. Completed in 3.82s.

Define Counter Account Type

lib.ts
#[account]
pub struct Counter {
    pub count: u64,
}
Diff
+ #[account]
+ pub struct Counter {
+    pub count: u64,
+ }
 
- #[account]
- pub struct Counter {}
Terminal
build

Implement Initialize Instruction

  • define required accounts
lib.ts
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
 
    #[account(
        init,
        payer = user,
        space = 8 + 8
    )]
    pub counter: Account<'info, Counter>,
    pub system_program: Program<'info, System>,
}
Diff
- #[derive(Accounts)]
- pub struct Initialize {}
 
+ #[derive(Accounts)]
+ pub struct Initialize<'info> {
+    #[account(mut)]
+    pub user: Signer<'info>,
+
+    #[account(
+        init,
+        payer = user,
+        space = 8 + 8
+    )]
+    pub counter: Account<'info, Counter>,
+    pub system_program: Program<'info, System>,
+ }
  • implement instruction
lib.ts
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
    let counter = &ctx.accounts.counter;
    msg!("Counter account created! Current count: {}", counter.count);
    Ok(())
}
Diff
- pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
-     Ok(())
- }
 
+ pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
+     let counter = &ctx.accounts.counter;
+     msg!("Counter account created! Current count: {}", counter.count);
+     Ok(())
+ }
Terminal
build

Implement Increment Instruction

  • define required accounts
lib.ts
#[derive(Accounts)]
pub struct Increment<'info> {
    #[account(mut)]
    pub counter: Account<'info, Counter>,
}
Diff
- #[derive(Accounts)]
- pub struct Increment {}
 
+ #[derive(Accounts)]
+ pub struct Increment<'info> {
+    #[account(mut)]
+    pub counter: Account<'info, Counter>,
+ }
  • implement instruction
lib.ts
pub fn increment(ctx: Context<Increment>) -> Result<()> {
    let counter = &mut ctx.accounts.counter;
    msg!("Previous counter: {}", counter.count);
 
    // Increment the count value stored on the counter account by 1
    counter.count = counter.count + 1;
    msg!("Counter incremented! Current count: {}", counter.count);
    Ok(())
}
Diff
- pub fn increment(_ctx: Context<Increment>) -> Result<()> {
-     Ok(())
- }
 
+ pub fn increment(ctx: Context<Increment>) -> Result<()> {
+     // Mutable reference to the counter account from the Increment struct
+     let counter = &mut ctx.accounts.counter;
+     msg!("Previous counter: {}", counter.count);
+
+     // Increment the count value stored on the counter account by 1
+     counter.count = counter.count + 1;
+     msg!("Counter incremented! Current count: {}", counter.count);
+     Ok(())
+ }
Terminal
build

Deploy Program

  • deploy program, airdrop if needed
Terminal
deploy
Output
$ deploy
Deploying... This could take a while depending on the program size and network conditions.
Deployment successful. Completed in 17s.

Test File Setup

anchor.test.ts
import { Keypair } from "@solana/web3.js";
 
describe("counter", () => {
  const program = pg.program;
 
  // Generate a new keypair to use as the address the counter account
  const counterAccount = new Keypair();
 
  it("Is initialized!", async () => {});
 
  it("Increment", async () => {});
});

Invoke Initialize Instruction

anchor.test.ts
it("Is initialized!", async () => {
  // Invoke the initialize instruction
  const transactionSignature = await program.methods
    .initialize()
    .accounts({
      counter: counterAccount.publicKey,
    })
    .signers([counterAccount]) // include counter keypair as additional signer
    .rpc({ skipPreflight: true });
 
  // Fetch the counter account data
  const accountData = await program.account.counter.fetch(
    counterAccount.publicKey,
  );
 
  console.log(`Transaction Signature: ${transactionSignature}`);
  console.log(`Count: ${accountData.count}`);
});
Diff
- it("Is initialized!", async () => {});
 
+ it("Is initialized!", async () => {
+   // Invoke the initialize instruction
+   const transactionSignature = await program.methods
+     .initialize()
+     .accounts({
+       counter: counterAccount.publicKey,
+     })
+     .signers([counterAccount]) // include counter keypair as additional signer
+     .rpc({ skipPreflight: true });
+
+   // Fetch the counter account data
+   const accountData = await program.account.counter.fetch(
+     counterAccount.publicKey,
+   );
+
+   console.log(`Transaction Signature: ${transactionSignature}`);
+   console.log(`Count: ${accountData.count}`);
+ });

Invoke Increment Instruction

anchor.test.ts
it("Increment", async () => {
  // Invoke the increment instruction
  const transactionSignature = await program.methods
    .increment()
    .accounts({
      counter: counterAccount.publicKey,
    })
    .rpc();
 
  // Fetch the counter account data
  const accountData = await program.account.counter.fetch(
    counterAccount.publicKey,
  );
 
  console.log(`Transaction Signature: ${transactionSignature}`);
  console.log(`Count: ${accountData.count}`);
});
Diff
- it("Increment", async () => {});
 
+ it("Increment", async () => {
+   // Invoke the increment instruction
+   const transactionSignature = await program.methods
+     .increment()
+     .accounts({
+       counter: counterAccount.publicKey,
+     })
+     .rpc();
+
+   // Fetch the counter account data
+   const accountData = await program.account.counter.fetch(
+     counterAccount.publicKey,
+   );
+
+   console.log(`Transaction Signature: ${transactionSignature}`);
+   console.log(`Count: ${accountData.count}`);
+ });

Run Test

  • run test
Terminal
test
Output
Running tests...
  anchor.test.ts:
  counter
    Transaction Signature: 3pVEPm3SEzr64eLqkauMQBLWHQQ5aiZUoNDnwUWfSe1TGeTEP7njy7sGFgnG6mcEWE7BfAnLhSbZRhdCxyHvGDF9
    Count: 0
 Is initialized! (2406ms)
    Transaction Signature: 56aQPsuWjyDV2vhuvhtfZ5NkbnGmTuiQVQakUEM8mEE3HWGir9rufxKYaN8m6byX2urEwSDdNF8SHDkdzyEVtxc9
    Count: 1
 Increment (897ms)
  2 passing (3s)

Close Program

  • run test
Terminal
solana program close <program_id>
Terminal
solana program close J2WA6mGXrutGo1ZSRijNWh7tDPmLbZm8UmQfffwVEHhq
Output
$ solana program close J2WA6mGXrutGo1ZSRijNWh7tDPmLbZm8UmQfffwVEHhq
Loading Solana CLI...
Success.
 
 
Closed Program Id J2WA6mGXrutGo1ZSRijNWh7tDPmLbZm8UmQfffwVEHhq, 2.85580632 SOL reclaimed