nox.im · All Posts · All in Solana · All in Rust

Dev Setup for the Solana Blockchain Platform

The design of Solana shows a deep understanding of distributed systems, operating systems and hardware. This article is the first in a series of communicating from the Web3 frontends via JSON RPC to Solana nodes, building and deploying on-chain programs and understanding the fundamental concepts.

Here, I’m going through the basics of setting up a web3 development environment with Rust, NodeJS and the Solana SDK.

Solana

Getting started with Solana on the Devnet

We start by downloading the latest Solana release from Github. If you’re on a Mac like me, get solana-release-x86_64-apple-darwin.tar.bz2 and set it up:

tar jxf solana-release-x86_64-apple-darwin.tar.bz2
cd solana-release/
export PATH=$PWD/bin:$PATH

Create a Solana Wallet

Generate key and save the output in a safe place

solana-keygen new -o ./validator-keypair.json
Wrote new keypair to ./validator-keypair.json
pubkey: 8RJ66oyximASYmucMXL5LDx6kSbqUgfesuDEm2wmXCwY
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
# ...

it prints a pubkey which enables us to airdrop ourselves play tokens on the devnet:

solana airdrop 1 8RJ66oyximASYmucMXL5LDx6kSbqUgfesuDEm2wmXCwY --url https://api.devnet.solana.com
Requesting airdrop of 1 SOL

Signature: 5deUJ1tWRZb3oNoAsJ9NSfb7F2EEMPgTKYxFEL1FGSeNYktHh2PDU46J7958x1MV4aWSFWXB5VRaryNw1CCmVPd1

1 SOL

we can find the transaction signature on the Solana explorer 5deUJ1tWRZb3oNoAsJ9NSfb7F2EEMPgTKYxFEL1FGSeNYktHh2PDU46J7958x1MV4aWSFWXB5VRaryNw1CCmVPd1

check the balance to confirm the airdrop was successful

solana balance 8RJ66oyximASYmucMXL5LDx6kSbqUgfesuDEm2wmXCwY --url https://api.devnet.solana.com
1 SOL

Transfer funds

solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> <AMOUNT> --fee-payer <KEYPAIR>

We create a second wallet and transfer funds to it

solana-keygen new --no-passphrase --no-outfile
Generating a new keypair
pubkey: 62PFC8bjfti96Aig3sY41o8t5n8v91c7gDS252JJMJZg
Save this seed phrase to recover your new keypair:
# ...
solana transfer --from ./validator-keypair.json 62PFC8bjfti96Aig3sY41o8t5n8v91c7gDS252JJMJZg 0.1 --allow-unfunded-recipient --url https://api.devnet.solana.com --fee-payer ./validator-keypair.json

Signature: 2qJoBsVfB2d7XaovdcuNCaxTVhRAoJvcgrA6bAHRDhKqGxS9YbPYY3GLiqyHGgYcG71ryGQPk2DwLGFaCtCgDMu5

Solana On-Chain Programs

I create notes on the technical details on how Solana smart contracts work under the hood in a separate article. Here we will stick to pragmatic examples and how to deploy and execute on-chain programs.

Install Rust

We’re installing rust via rustup. This installs the Rust Programming Language from the official release channels and enables us to switch between stable, beta, and nightly compilers and keep them updated.

brew install rustup-init
rustup-init

Notes on Rust

Programs are constrained to run deterministically, so random numbers are not available.

For logging, Rust’s println! macro is computationally expensive and not supported. Instead the helper macro msg! is provided.

Web3 example and Solana Hello World

My environment at this point looks like this:

rustc --version
rustc 1.57.0 (f1edd0429 2021-11-29)
solana --version
solana-cli 1.8.5 (src:76c5c94a; feat:52865992)
node --version
v15.8.0

Set CLI config url to localhost cluster

solana config set --url localhost
Config File: /Users/noxim/.config/solana/cli/config.yml
RPC URL: http://localhost:8899 
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /Users/noxim/.config/solana/id.json

Start a local Solana cluster:

solana-test-validator

Notice! No wallet available. `solana airdrop` localnet SOL after creating one

Ledger location: test-ledger
Log: test-ledger/validator.log
Identity: 5JjzuqdS9B36QxTxokq8QA2MNxP5GZRehEsfY9q8JJTW
Genesis Hash: 2tW5hFVULXwyznwZRPMzPyUPPjWvochGb9trQ2xLFwVU
Version: 1.8.5
Shred Version: 16631
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:14 | Processed Slot: 27 | Confirmed Slot: 27 | Finalized Slot: 0 | Snaps

Ctrl+C will not delete the data on this validator, should something go wrong with this validator or should you desire to reset your localnet blockchain, start it with solana-test-validator --reset.

Grab the hello world example from Solana labs and compile the rust program

git clone https://github.com/solana-labs/example-helloworld
cd example-helloworld
npm run build:program-rust

create a default signer and deploy the on-chain program, note that this will fail initially if we don’t airdrop ourselves funds. I’ve included this example as I’ve seen it commonly encountered online with local clusters.

solana-keygen new -o /Users/noxim/.config/solana/id.json

solana program deploy dist/program/helloworld.so
Error: Account 8jz3nUqvoxBwxeNSJtcPEnu1X65gaVF9Gab5qecuGET1 has insufficient funds for spend (0.4223676 SOL) + fee (0.00032 SOL)

solana aidrop 1
Requesting airdrop of 1 SOL

Signature: 4X5JbjNooagQ4Q73NvPKPP1EMuURmjsGLmNh81EGA1g14e97TMsaYxKKRfpdvuM5yfAYBgKTz4HrccU3u9JJa81z

solana program deploy dist/program/helloworld.so
Program Id: 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM

We can inspect the file type and see we got a Executable and Linkable Format (ELF), a common standard for executables and shared objects, in the eBPF instruction set.

We can inspect the program with the Solana toolchain as follows:

solana program show 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM       

Program Id: 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM
Owner: BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: 6hskN36DBFFx4GRE4Ux1sDr5mzt2UhHe8gUfAhigZDsy
Authority: 8jz3nUqvoxBwxeNSJtcPEnu1X65gaVF9Gab5qecuGET1
Last Deployed In Slot: 17583
Data Length: 121024 (0x1d8c0) bytes
Balance: 0.84353112 SOL

An account in Solana has an owner. This owner refers to the program that owns and can make changes to the data in this account (more on storage later). We can inspect the account of the program with:

solana account 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM

Public Key: 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM
Balance: 0.00114144 SOL
Owner: BPFLoaderUpgradeab1e11111111111111111111111
Executable: true
Rent Epoch: 0
Length: 36 (0x24) bytes
0000:   02 00 00 00  54 c3 0c 2a  a7 b2 bd 4b  84 49 d6 b0   ....T..*...K.I..
0010:   da 9b 10 98  27 ba 62 d8  09 4c 79 06  75 8c 60 d1   ....'.b..Ly.u.`.
0020:   25 e2 82 cc                                          %...
file dist/program/helloworld.so: ELF 64-bit LSB shared object, eBPF, version 1 (SYSV), dynamically linked, stripped

run the web3 Javascript client

npm install
npm run start
Let's say hello to a Solana account...
Connection to cluster established: http://localhost:8899 { 'feature-set': 52865992, 'solana-core': '1.8.5' }
Using account 8jz3nUqvoxBwxeNSJtcPEnu1X65gaVF9Gab5qecuGET1 containing 0.15500744 SOL to pay for fees
Using program 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM
Creating account 8qz4R9s3WSWsuFZikiGSnbG19vfkF8qPqM2RGFVZVjmW to say hello to
Saying hello to 8qz4R9s3WSWsuFZikiGSnbG19vfkF8qPqM2RGFVZVjmW
8qz4R9s3WSWsuFZikiGSnbG19vfkF8qPqM2RGFVZVjmW has been greeted 1 time(s)
Success

Running solana logs in a second window will show us the transaction on another run

Transaction executed in slot 2566:
  Signature: 4n6VL8i5FjUgefe3bZXNiuZJGs87D19iByxnAseimNGATRdYUeDKFbs4hPSHLwH6hHSWGZNbgPNZtSpMU1E7Hj3k
  Status: Ok
  Log Messages:
    Program 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM invoke [1]
    Program log: Hello World Rust program entrypoint
    Program log: Greeted 2 time(s)!
    Program 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM consumed 1174 of 200000 compute units
    Program 2YStkyjmtn6mpTfH8xE5q74wGtPKwhXm9RvuVrue36zM success

The web3 app does these five things:

async function main() {
  console.log("Let's say hello to a Solana account...");

  // Establish connection to the cluster
  await establishConnection();

  // Determine who pays for the fees
  await establishPayer();

  // Check if the program has been deployed
  await checkProgram();

  // Say hello to an account
  await sayHello();

  // Find out how many times that account has been greeted
  await reportGreetings();

  console.log('Success');
}

Don’t forget to point your Solana CLI again to the devnet if we want to move development to staging:

solana config set --url devnet

Anchor

Anchor is a development framework that will save us a ton of time and takes away some low level details with macros. See the anchor introductory guide for a more in-depth tutorial. The source code can be found on project-serum/anchor.

Install anchor and yarn which anchor depends on;

npm install -g yarn
npm i -g @project-serum/anchor-cli

if you’re on MacOS or any of their unsupported operating systems, build from source

cargo install --git https://github.com/project-serum/anchor --tag v0.19.0 anchor-cli --locked

Start a project

anchor init <new programm>

to run anchor build I had to set the following

export ANCHOR_WALLET=~/.config/solana/id.json
export ANCHOR_PROVIDER_URL=http://127.0.0.1:8899

The first time we build this program with anchor build, anchor will generate a key pair for it. This pair will be stored in the /target directory (target/deploy/<my program>-keypair.json). The pubkey will become the unique identifier of your program, the program ID. Note that we need to update our program ID. It will be displayed on deployment, but we can also access it with the Solana tools:

solana address -k target/deploy/myapp-keypair.json
8Xcscw4KgUtHGVqCRNDKFRXMo2jPPs974VSj9JH9eYQv

Update Anchor.toml

[programs.localnet]
my_solana_app = "8Xcscw4KgUtHGVqCRNDKFRXMo2jPPs974VSj9JH9eYQv"

and lib.rs

declare_id!("8Xcscw4KgUtHGVqCRNDKFRXMo2jPPs974VSj9JH9eYQv");

anchor deploy takes the BPF and deploys it with a transaction on the selected cluster. The command anchor test takes care of all steps including spinning up a local ledger that terminates after execution. If we do run a local test validator already and want to test closer to what we’d get on dev and main net, we run the steps anchor test --skip-local-validator which does:

anchor build
anchor deploy
anchor test

This may fail with

tsconfig.json" needs an import assertion of type "json"

You will need to install ts-mocha:

yarn add ts-mocha

The last step executes the “test” script from the Anchor.toml file. You can also invoke it individually with anchor run test. You’re very likely to require packages such as spl-token that you can install with:

yarn add @solana/spl-token

To continue the journey and go into more details, I’ve since added another article on Solana On-Chain Programs.

Anchor Account directives

You’ll find plenty of directives in generated and popular anchor programs such as #[account(mut)] for writable accounts, #[account(zero)] to assert program accounts, #[account(signer)] to assert that the given account signed the transaction and many others as well as combinations of the above.

See Derive Macro anchor_lang::Accounts to quickly know what’s what.

Specifying a program ID for deployment

To deploy with the solana command to the same program ID, specify the keypair in the deploy command to deploy to a specific program id:

solana program deploy --program-id <KEYPAIR_FILEPATH> <PROGRAM_FILEPATH>
solana program deploy --program-id project-name-keypair.json project-name.so

Forcing a change in the program id

Under certain scenarios during testing we want to deploy a second version of our on-chain program without destroying or overwriting the first version. For example when we have a web client and want to test against two different versions of the same code base. I started to rotate anchor program IDs for this as follows:

mv target/deploy/<project-name>-keypair.json target/deploy/<project-name>-keypair.json.6uaD9SSV12fLxiENKzzqUvZ7t8k2QzLxnpbLXqzKwKeH
anchor build
anchor deploy

assuming 6uaD9SSV12fLxiENKzzqUvZ7t8k2QzLxnpbLXqzKwKeH was my previous program ID for quick references. We then need a new keypair and plug the program ID into a few places:

Running anchor test --skip-local-validator now should work again.

Troubleshooting

I couldn’t deploy to my test validator one day and got this error:

Error: Account allocation failed: RPC response error -32002: Transaction simulation failed: Error processing Instruction 1: Unsupported program id [2 log messages]

Resetting the validator with solana-test-validator -r helped and resolved this issue.

A lot of built errors I found clean out when reinstalling/rebuilding the local toolchain and sometimes just removing the old Solana SDK via rm -rf ~/.cache/solana. It’s the early days.

Ensure you have set the anchor wallet environment variable.

export ANCHOR_WALLET=~/.config/solana.id.json

Published on Sunday, Dec 12, 2021. Last modified on Saturday, Jun 18, 2022.
Go back

If you’d like to support me, follow me on Twitter or buy me a coffee. Use Bitcoin
BTC address: bc1q6zjzekdjhp44aws36hdavzc5hhf9p9xnx9j7cv