3 min read

Anchor - Tutorial 0

I found the Anchor tutorial pretty hard to follow, so here's my walkthrough, (I was using v0.19.0):

First let's use anchor to create a project scaffold

anchor init base0
cd base0

Great. Then we can examine the contents. We've gotten a complete scaffold consisting of nodejs and rust files that we would normally start with. Lets go ahead and run test.

anchor test

And we should see the response

   base0
Your transaction signature 4b1og7drP9JXPw8y4NVT79xxZvYrmxEku1aTPaBHWsQwBKzoRXcouBQCPqpxkVZswx8ZnSeURFmU7PwCVyGpDc4z
    ✔ Is initialized! (528ms)


  1 passing (535ms)

Great! We have a passing test.

Now let's see if we can build this

anchor build
admin@ip-10-24-2-147:~/anchor_tutorials/base0$ anchor build
BPF SDK: /home/admin/solana-release/bin/sdk/bpf
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
warning: unused variable: `ctx`
 --> programs/base0/src/lib.rs:8:23
  |
8 |     pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
  |                       ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: `base0` (lib) generated 1 warning
    Finished release [optimized] target(s) in 0.55s
cargo-build-bpf child: /home/admin/solana-release/bin/sdk/bpf/dependencies/bpf-tools/llvm/bin/llvm-readelf --dyn-symbols /home/admin/anchor_tutorials/base0/target/deploy/base0.so

To deploy this program:
  $ solana program deploy /home/admin/anchor_tutorials/base0/target/deploy/base0.so
The program address will default to this keypair (override with --program-id):
  /home/admin/anchor_tutorials/base0/target/deploy/base0-keypair.json

So looks like it was already built during the test. Let's try to deploy

Note: To deploy, we need to interact with the blockchain, so on another terminal, we need to fire up a local chain first before calling deploy

admin@ip-10-24-2-147:~$ solana-test-validator
--faucet-sol argument ignored, ledger already exists
Ledger location: test-ledger
Log: test-ledger/validator.log
Identity: 5Nx2yqQgS9aNWMbPN1S4zsYJFgpyk7Uu9SiJaVjDad1E
Genesis Hash: 6FhQYjaCjHHAwMHX34tbQBWCztK7iYfQBEcUyY8k5DXn
Version: 1.8.5
Shred Version: 46084
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
⠈ 00:00:28 | Processed Slot: 2580 | Confirmed Slot: 2580 | Finalized Slot: 2548

Next deploy

admin@ip-10-24-2-147:~/anchor_tutorials/base0$ anchor deploy
Deploying workspace: http://localhost:8899
Upgrade authority: /home/admin/.config/solana/id.json
Deploying program "base0"...
Program path: /home/admin/anchor_tutorials/base0/target/deploy/base0.so...
Program Id: nrPmZ1tZCjXCgtLYx2W1RZzRwNRdVUUAoSaXGuYpgvC

Deploy success

For some reason this takes a long time on EC2 Linux, almost 5 minutes. The whole point of this exercise is to get the Program Id, which a client will need in order to interact with this contract.

So what does the contract actually do? Not much. Let's look at the code in programs/base0/src/lib.rs. I've commented the code below

/// the anchor_lang library
use anchor_lang::prelude::*;

/// This macro allows Anchor programs not to specify 
/// 'solana_program' as an additional crate dependency.
/// This should be used at the root of all Anchor 
/// based programs. The program's ID is a static public key 
/// Input: a single literal base58 string representation 
/// of a program's id. 
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

/// the program attribute, basically modified the base0
/// and provides RPC handlers that the program 
/// may invoke
#[program]
pub mod base0 {
    use super::*;
    
    /// the program has some non-argument inputs that
    /// it may need to use. Context struct contains
    /// a) program_id 
    /// b) accounts - deserialized accounts
    /// c) remaining accounts - not deserialized or validated
    /// Context is the first parameter of every RPC handler
    pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
        Ok(())
    }
}

/// A data structure of validated accounts that can be 
/// deserialized from the input to a Solana program. 
#[derive(Accounts)]
pub struct Initialize {}

I used the anchor rust docs, which are pretty good and probably better than the tutorial itself.

In effect the base0 program just creates a program on the blockchain which is initialized with the program_id and accounts, but otherwise doesn't do anything.

After the deploy, idl/base0.json

{
  "version": "0.1.0",
  "name": "base0",
  "instructions": [
    {
      "name": "initialize",
      "accounts": [],
      "args": []
    }
  ],
  "metadata": {
    "address": "nrPmZ1tZCjXCgtLYx2W1RZzRwNRdVUUAoSaXGuYpgvC"
  }
}

I am confused about the address, which is the deployed Program Id. Shouldn't it be the same as the anchor lang declare_id?  

Or is this something you change for the next deploy?

Now, this is the endpoint of the anchor init skeleton. In the basic-0 tutorial, they add a client.js in order to interact with the contract.

I added a client_code folder for use.

Run the program by invoking

solana config get keypair
ANCHOR_WALLET=/home/admin/.config/solana/id.json node client.js

So there you have it. A running skeleton. Whew, took me longer that I like to care to admit to figure this out.

  • git for the above here

Some other notes:

  • I think the program_id being used in declare! in the lib.rs does do something... but I'm not sure what. Seems likes its a dummy id which gets replaced in the idl by the actual generated id. However the solana-test-validator does not seem to allow two program with the same id to deploy. Restarting the validator resets this behavior
  • I had lots of trouble even developing on a t2.small and ended up upgrading to a t2.medium. Hard disk space also filled up quickly, so had to get at least 50GB.