#1 - A Starter Kit for New Solana Developer

Author: @ironaddicteddogarrow-up-right

[Updated at 2022.3.31]

TL; DR

  • What makes Solana so fast and efficient?

  • What should I learn at minimum to understand how Solana program works?

  • How can I setup my the environment quickly?

  • What is the best practice to develop Solana programs?

Before We Start

Prerequisites

Why Solana?

  • It's a new blockchain paradigm evolved from Ethereum (more or less)

  • It's blazing fast and efficient, meaning it will be cheap and affordable for average users

  • It's a good investment on modern web technology (Rust / Typescript, etc)

Why Rust?

Compare Solana to Ethereum, what are the pros and cons?

Pros

Cons

Structure of this Kit

  1. Hello world (for environment setup)

  2. Escrow program using vanilla Rust (for learning core concepts)

  3. Escrow program using Anchor (for learning the best practice)

1. Hello World!

Goal

  • Setup the development environment

  • Get familiar with tools such as:

    • cargo

      • cargo build-bpf

    • rustup

    • solana-cli

      • solana deploy

    • solana-test-validator

    • solana-web3.js

Install Rust and Solana Cli

Install rustup

Install solana-cli

Note: You may have to build from source if you are using Mac M1 machine. See this docarrow-up-right for more installation details.

Generate Keypair

Or, you can recover your key from your existing key phrase:

Config the keypath:

Config to Local Cluster

Install rust-analyzer, Better TOML and crates (Optional)

rust-analyzer can be very handy if you are using Visual Studio Code. For example, the analyzer can help download the missing dependencies for you automatically.

Install Additional Dependencies (Optional)

If you are using Linux, you may need to install these tools as well:

Build and Deploy

First, let's clone the repo:

Run solana-test-validator in another terminal session:

Next, let's compile the hello world program:

Deploy the program after compilation, :

If you encounter an insuffficient fund error, you may have to request for an aidrop:

Say Hello World

First, we need to modify the PROGRAM_PATH in src/client/hello_world.ts:

Finally, let's make the program say Hello by sending a transaction:

If we take a closer look to function sayHello, we can see how a solana transaction is constructed and sent:

2. Escrow Program (using vanilla Rust)

Goal

  • Learn Solana account model and core concepts such as:

    • Account model

    • Program Architecture

    • Program Derived Address (PDA)

    • Cross-Program Invocation (CPI)

      • invoke

      • invoke_signed

  • This section is extracted from this awesome tutorial: Programming on Solana - An Introduction (by paulx)arrow-up-right. Some of the explanations in this doc are more comprehensive and clearer in the original post. I strongly recommend you to read through the post at least once.

  • Program Architecture

    • lib.rs: registering modules

    • entrypoint.rs: entrypoint to the program

    • instruction.rs: program API, (de)serializing instruction data

    • processor.rs: program logic

    • state.rs: program objects, (de)serializing state

    • error.rs: program specific errors

  • See this repoarrow-up-right for full code base

Core Concepts

Account

  • Accounts are used to store state

  • Accounts are owned by programs

  • Only the account owner may debit an account and adjust its data

  • All accounts to be written to or read must be passed into the entrypoint

  • All internal Solana internal account information are saved into fields on the account (opens new window)but never into the data field which is solely meant for user space information

  • Developers should use the data field to save data inside accounts

Program

  • Solana programs are stateless

  • Each program is processed by its BPF Loader and has an entrypoint whose structure depends on which BPF Loader is used

  • In theory, programs have full autonomy over the accounts they own. It is up to the program's creator to limit this autonomy and up to the users of the program to verify the program's creator has really done so

  • The flow of a program using this structure looks like this:

    • Someone calls the entrypoint

    • The entrypoint forwards the arguments to the processor

    • The processor asks instruction module to decode the instruction_data argument from the entrypoint function.

    • Using the decoded data, the processor will now decide which processing function to use to process the request.

    • The processor may use state module to encode state into or decode the state of an account which has been passed into the entrypoint.

  • When writing Solana programs, be mindful of the fact that any accounts may be passed into the entrypoint, including different ones than those defined in the API inside instruction.rs. It's the program's responsibility to check that received accounts == expected accounts

Instruction

  • If you are familiar of Ethereum, think of Solana instructions as Ethereum transcations, while Solana transaction, which can wrap multiple instructions, is anologous to Ethereum multicallarrow-up-right

SPL token Program

  • The token program owns token accounts which inside their data field hold relevant information

  • the token program also owns token mint accounts with relevant data

  • each token account holds a reference to their token mint account, thereby stating which token mint they belong to

  • the token program allows the (user space) owner of a token account to transfer its ownership to another address

  • All internal Solana internal account information are saved into fields on the account but never into the data field which is solely meant for user space information

PDA

  • Program Derived Addresses do not lie on the ed25519 curve and therefore have no private key associated with them.

Cross-Program Invocation

  • When including a signed account in a program call, in all CPIs including that account made by that program inside the current instruction, the account will also be signed, i.e. the signature is extended to the CPIs.

  • when a program calls invoke_signed, the runtime uses the given seeds and the program id of the calling program to recreate the PDA and if it matches one of the given accounts inside invoke_signed's arguments, that account's signed property will be set to true

To spend Solana SPL, you don't need to approve. Why?

Rent

  • Rent is deducted from an account's balance according to their space requirements (i.e. the space an account and its fields take up in memory) regularly. An account can, however, be made rent-exempt if its balance is higher than some threshold that depends on the space it's consuming

  • If an account has no balance left, it will be purged from memory by the runtime after the transaction (you can see this when going navigating to an account that has been closed in the explorer)

  • "closing" instructions must set the data field properly, even if the intent is to have the account be purged from memory after the transaction

  • In any call to a program that is of the "close" kind, i.e. where you set an account's lamports to zero so it's removed from memory after the transaction, make sure to either clear the data field or leave the data in a state that would be OK to be recovered by a subsequent transaction.

  • Solana has sysvars that are parameters of the Solana cluster you are on. These sysvars can be accessed through accounts and store parameters such as what the current fee or rent is. As of solana-program version 1.6.5, sysvars can also be accessed without being passed into the entrypoint as an account.

Escrow Program Overview

Flow

Account Relations

Part 1

Fisrt, let's create a new project solana-escrow:

Next, we update the Cargo.toml manifest to as follows:

According to the program architecture, we will have five modules in the end. Let's create all these files at once before we start implementing them.

Next, define these modules in lib.rs:

Let's begin to implement these modules. First, we define instructions. Instructions are the APIs of program. Copy and paste the following snippet into your local instuction.rs:

You may notice that there are a few compile warning telling you InvalidInstruction is not resolved. Let's implement it in error.rs.

Update dependencies:

Update error.rs:

The main business logic locates in processor.rs. There will be two functions corresponding two instructions. Let's implement those one by one. Here we implement the process_init_escrow function which matches EscrowInstruction::InitEscrow case:

Update dependencies:

Update processor.rs:

Part 2

You will notice a warning raised due to unresolved state::Escrow.

What does state.rs do? It basically represents the data structure stored in the account owned by Escrow program. Also, it has the pack/unpack utils to convert the data format.

Update dependencies:

Update state.rs:

Let's further extend the business logic of process_init_escrow in processor.rs:

Here, we can see invoke is called to perform a CPI.

To make the first function process_init_escrow callable, let's put it in the entrypoint.rs:

Check if we can compile it successfully:

Part 3

Next, we can implement another instruction Exchange and its corresponding function process_exchange.

Update instruction.rs:

Also in processor.rs:

Here we can see that invoke_signed is called with seeds since the owner of escrow account is a PDA.

Finally, implement the missing error enums:

Check if we can compile successfully:

Interact with the escrow program

Basic setup

Now, we can write some client side code to interact with the escrow program.

First, let's install dependencies:

Next, let's generate the files to be filled in necessary code and data:

Generate Keypairs

We have to generate keypairs for alice, bob, and the transaction payer. This can be done via solana-keygen:

Next, we need to manually update the public keys for each. Retrieve the address for all of them and paste it to the *_pub.json files accordingly. For example:

Don't forget the double quotes

Add Code Base

Here we add the client code base. Copy and paste the following files to your local code base:

Again, I strongly recommend you to clone the original code base and run it

Compile, Depoly and Setup

First, let's start the validator:

Compile and deploy the program:

Before we execute the client code, we need to update the programId to be looked up:

Also, update the predefined terms.json as follows:

Fund the transaction payer in advance:

Run the Client Code

Finally, let's run the client code:

First, run setup.ts to mint the tokens to be exchanged:

Next, run alice.ts to initialize the escrow program:

You can see how an instruction is constructed. The interger 0 assined to the Uint8Array represents the instruction InitEscrow:

Then, run bob.ts to exchange and close the escrow account:

3. Escrow Program (using Anchor)

Goal

  • Learn the best practice

  • Why use Anchor?

  • A good framework reduces the mental pressure and keep the precious attention resource to important things

  • I actually wrote another post explaining the whole thing. See this docarrow-up-right to learn more.

More Advanced Topics

References

General

  • https://medium.com/@asmiller1989/solana-transactions-in-depth-1f7f7fe06ac2

  • https://hackmd.io/@adamisrusty/HkVyZHBoO

  • https://2501babe.github.io/posts/solana101.html

  • https://github.com/paul-schaaf/awesome-solana

  • https://github.com/project-serum/awesome-serum

  • https://solana.com/developers

Front-End Development

  • https://github.com/yihau/full-stack-solana-development

  • https://github.com/yihau/solana-web3-demo

  • https://github.com/raydium-io/raydium-ui

  • https://github.com/thuglabs/create-dapp-solana-nextjs

Program Development

  • https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/#instruction-rs-part-1-general-code-structure-and-the-beginning-of-the-escrow-program-flow

  • https://github.com/jstarry/solana-workshop-tw

  • https://jstarry.notion.site/Program-deploys-29780c48794c47308d5f138074dd9838

  • https://jstarry.notion.site/Transaction-Fees-f09387e6a8d84287aa16a34ecb58e239

Anchor Tutorials

  • https://hackmd.io/@ironaddicteddog/anchor_example_escrow

  • https://github.com/ironaddicteddog/anchor-escrow

  • https://github.com/ironaddicteddog/anchor-amm

  • https://dev.to/dabit3/the-complete-guide-to-full-stack-solana-development-with-react-anchor-rust-and-phantom-3291

  • https://2501babe.github.io/posts/anchor101.html

  • https://www.brianfriel.xyz/learning-how-to-build-on-solana/

  • https://project-serum.github.io/anchor/tutorials/tutorial-0.html

Core Technology

  • https://medium.com/solana-labs/proof-of-history-explained-by-a-water-clock-e682183417b8

  • https://medium.com/solana-labs/proof-of-history-a-clock-for-blockchain-cf47a61a9274

  • https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192

  • https://medium.com/solana-labs/solanas-network-architecture-8e913e1d5a40

  • https://medium.com/solana-labs/7-innovations-that-make-solana-the-first-web-scale-blockchain-ddc50b1defda

  • https://jito-labs.medium.com/solana-validator-101-transaction-processing-90bcdc271143

Twitters

  • https://twitter.com/ironaddicteddog

  • https://twitter.com/armaniferrante

  • https://twitter.com/therealchaseeb

  • https://twitter.com/jstrry

Last updated