# A Minimal Example

Here, we introduce Anchor's core syntax elements and project workflow. This tutorial assumes all prerequisites are installed.

# Clone the Repo

To get started, clone the repo.

git clone https://github.com/project-serum/anchor

Next, checkout the tagged branch of the same version of the anchor cli you have installed.

git checkout tags/<version>

And change directories to the example (opens new window).

cd anchor/examples/tutorial/basic-0

# Starting a Localnet

In a separate terminal, start a local network. If you're running solana for the first time, generate a wallet.

solana-keygen new

Then run

solana-test-validator

Then, shut it down.

The test validator will be used when testing Anchor programs. Make sure to turn off the validator before you begin testing Anchor programs.

DETAILS

As you'll see later, starting a localnet manually like this is not necessary when testing with Anchor, but is done for educational purposes in this tutorial.

# Defining a Program

We define the minimum viable program as follows.

use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_0 {
    use super::*;
    pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
        Ok(())
    }
}
#[derive(Accounts)]
pub struct Initialize {}
  • #[program] First, notice that a program is defined with the #[program] attribute, where each inner method defines an RPC request handler, or, in Solana parlance, an "instruction" handler. These handlers are the entrypoints to your program that clients may invoke, as we will see soon.

  • Context<Initialize> The first parameter of every RPC handler is the Context struct, which is a simple container for the currently executing program_id generic over Accounts--here, the Initialize struct.

  • #[derive(Accounts)] The Accounts derive macro marks a struct containing all the accounts that must be specified for a given instruction. To understand Accounts on Solana, see the docs (opens new window). In subsequent tutorials, we'll demonstrate how an Accounts struct can be used to specify constraints on accounts given to your program. Since this example doesn't touch any accounts, we skip this (important) detail.

# Building and Emitting an IDL

After creating a program, you can use the anchor CLI to build and emit an IDL, from which clients can be generated.

anchor build
DETAILS

The build command is a convenience combining two steps.

  1. cargo build-bpf
  2. anchor idl parse -f program/src/lib.rs -o target/idl/basic_0.json.

Once run, you should see your build artifacts, as usual, in your target/ directory. Additionally, a target/idl/basic_0.json file is created. Inspecting its contents you should see

{
  "version": "0.0.0",
  "name": "basic",
  "instructions": [
    {
      "name": "initialize",
      "accounts": [],
      "args": []
    }
  ]
}

From this file a client can be generated. Note that this file is created by parsing the src/lib.rs file in your program's crate.

TIP

If you've developed on Ethereum, the IDL is analogous to the abi.json.

# Deploying

Once built, we can deploy the program by running

anchor deploy

Take note of the program's deployed address. We'll use it next.

# Generating a Client

Now that we've built a program, deployed it to a local cluster, and generated an IDL, we can use the IDL to generate a client to speak to our on-chain program. For example, see client.js (opens new window).

// Read the generated IDL.
const idl = JSON.parse(require('fs').readFileSync('./target/idl/basic_0.json', 'utf8'));
// Address of the deployed program.
const programId = new anchor.web3.PublicKey('<YOUR-PROGRAM-ID>');
// Generate the program client from IDL.
const program = new anchor.Program(idl, programId);
// Execute the RPC.
await program.rpc.initialize();

Notice how we dynamically created the initialize method under the rpc namespace.

Now, make sure to plugin your program's address into <YOUR-PROGRAM-ID> (a mild annoyance that we'll address next), and run

node client.js

You just successfully created a client and executed a transaction on your localnet.

# Workspaces

So far we've seen the basics of how to create, deploy, and make RPCs to a program, but deploying a program, copy and pasting the address, and explicitly reading an IDL is all a bit tedious, and can easily get out of hand the more tests and the more programs you have. For this reason, we introduce the concept of a workspace.

Inspecting tests/basic_0.js (opens new window), we see the above example can be reduced to

// Read the deployed program from the workspace.
const program = anchor.workspace.Basic0;
// Execute the RPC.
await program.rpc.initialize();

The workspace namespace provides access to all programs in the local project and is automatically updated to reflect the latest deployment, making it easy to change your program, update your JavaScript, and run your tests in a fast feedback loop.

NOTE

For now, the workspace feature is only available when running the anchor test command, which will automatically build, deploy, and test all programs against a localnet in one command.

Finally, we can run the test. Don't forget to kill the local validator started earlier. We won't need to start one manually for any future tutorials.

anchor test

# Next Steps

We've introduced the basic syntax of writing programs in Anchor along with a productive workflow for building and testing. However, programs aren't all that interesting without interacting with persistent state. We'll cover that next.