Solana Development Tutorial: Program 101

Solong
6 min readNov 25, 2020

Before we start

Before diving deep into this tutorial, please make sure that you have your environment set up and tool suite installed following the steps in this tutorial. If you have already done it, then fix your seat belt, and we are ready to go 🚗

Connect to Devnet

If you do not have your own node, do not worry, we have devnet which is of same configuration with main net. So here let us first set out API endpoint to https://devnet.solana.com:

ubuntu@VM-0-12-ubuntu:~$ solana config set --url https://devnet.solana.com
Config File: /home/ubuntu/.config/solana/cli/config.yml
RPC URL: https://devnet.solana.com
WebSocket URL: wss://devnet.solana.com/ (computed)

Prepare your account

Every on-chain program is actually an Account, but it is marked “Executable: true”, meaning it is an executable file. In order to store this file, we need to create another Solana account which can pay the fee. So let us first do:

solana-keygen new

After input password and confirmation, our new account is saved in /home/ubuntu/.config/solana/id.json, and it will be used as our default Key.

We can check the public key by running:

ubuntu@VM-0-12-ubuntu:~$ solana-keygen pubkey /home/ubuntu/.config/solana/id.json
7FqW6xXE4sMmZSeVxFsoTr83He4MhhePvA1vRAv9zgQf

Well, it is time to show me the money, we can get some free SOL airdrop in devnet by running:

ubuntu@VM-0-12-ubuntu:~$ solana airdrop 10 7FqW6xXE4sMmZSeVxFsoTr83He4MhhePvA1vRAv9zgQf
Requesting airdrop of 10 SOL from 34.82.57.86:9900
10 SOL

Let us check our balance:

ubuntu@VM-0-12-ubuntu:~$ solana balance 7FqW6xXE4sMmZSeVxFsoTr83He4MhhePvA1vRAv9zgQf
10 SOL

Whooa, money is there. 💰

Then we create another account which will used to store our program file:

solana-keygen new -o solana_memo_program.json

Here we output our new key pairs to a new file solana_memo_program.json

by changing parameter -O in our command

Compile the program

Since version 1.4.x(published at 2020–10–22), Solana has provided tools like cargo-build-bpf and cargo-test-bpf which help us to compile our cargo project to BPF format file.

We can try this our using the memo program which provided by Solana official team. Let us clone it:

git clone https://github.com/solana-labs/solana-program-library.git

then we jump into folder solana-program-library/memo/program/ and run:

cargo build-bpf

This is a wrapper for program cargo-build-bpf we mentioned above.

If you see errors like:

= note: /usr/bin/ld: cannot find Scrt1.o: No such file or directory
/usr/bin/ld: cannot find crti.o: No such file or directory
collect2: error: ld returned 1 exit status

It is because you lack some 32-bit dependency packages, let us fix it by install:

sudo apt install gcc-multilib

Then we compile again, we get:

To deploy this program:
$ solana deploy /home/ubuntu/solana/solana-program-library/target/deploy/spl_memo.so

Ok, time to deploy, here we will deploy it to the account we created and exported to file solana_memo_program.json, let us get the key first:

solana-keygen pubkey ~/solana_memo_program.json
D8Cnv1UcThay2WijWP4SQ8G683UuVsKPaZEU7TNVKW1j

Then we run deploy:

solana deploy /home/ubuntu/solana/solana-program-library/target/deploy/spl_memo.so ~/solana_memo_program.json
{"programId":"D8Cnv1UcThay2WijWP4SQ8G683UuVsKPaZEU7TNVKW1j"}

Now we have successfully deployed a program to devnet with program address: D8Cnv1UcThay2WijWP4SQ8G683UuVsKPaZEU7TNVKW1j

To verify the command line does not lie to me, let’s check our program on Solana explorer, go to https://explorer.solana.com/ and change the network to Devnet, input your program address, you might see:

Yeah, command is right again, our program is there!

Test your program

To interact with our on-chain program, here we need our old friend @solana/web3.js again, let us create a new js project and add package @solana/web3.js

yarn init
yarn add @solana/web3.js

Then create a entry js file like index.js in your root folder and stuff in our test code:

var solana_web3 = require('@solana/web3.js');function testMemo(connection, account){
const instruction = new solana_web3.TransactionInstruction({
keys: [],
programId:new solana_web3.PublicKey('D8Cnv1UcThay2WijWP4SQ8G683UuVsKPaZEU7TNVKW1j'),
data: Buffer.from('cztest'),
});
console.log("account:", account.publicKey.toBase58())
solana_web3.sendAndConfirmTransaction(
connection,
new solana_web3.Transaction().add(instruction),
[account],
{
skipPreflight: true,
commitment: "singleGossip",
},
).then(()=>{console.log("done")}).catch((e)=>{console.log("error",e)});
}function main() {
connection = new solana_web3.Connection("https://devnet.solana.com", 'singleGossip');
const account = new solana_web3.Account()
const lamports = 10*1000000000
connection.requestAirdrop(account.publicKey, lamports).then(()=>{
console.log("airdrop done")
testMemo(connection, account)
});
}
main()

let’s see what has happened here, first we connect to the devnet endpoint, then we created a new account with new solana_web3.Account() function which is provided by solana_web3, this account will be used to interact with our program later.

Then we get some SOL airdrops from devnet, then we called this testMemo function, this function send a transaction to our program with parameters passed in as data stream(Here we pass a string “cztest”)

Now, let’s check our program using explorer again:

As the transaction showed above, we can see the data but in Hex format. We can decode this data using some tool like binascii:

>>> import binascii
>>> binascii.a2b_hex('637a74657374')
b'cztest'

Here we can see that the result is the same string as we sent to our program 🎊

Write your own program

We have just played with a program which we pulled from Solana official repo, so what shall we do to write a on-chain program from scratch ? As a normal Rust project, a Solana on-chain program is also managed by cargo.

First, let’s use cargo to init our new project :

cargo new onchain_program

Then open onchain_program/Cargo.toml with your favorite editor and add some basic info for your project:

[dependencies]
arrayref = "0.3.6"
num-derive = "0.3"
num-traits = "0.2"
num_enum = "0.5.1"
solana-program = "1.4.8"
thiserror = "1.0"[dev-dependencies]
solana-sdk = "1.4.8"[lib]
crate-type = ["cdylib", "lib"][package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

We also add a Xargo.toml file which used for cross platform support when compiling bpf file.

Then time to get our hand dirty, let’s write some Rust code, first we add an entrypoint in src/lib.rs:

#![deny(missing_docs)]//! A simple program that return success.#[cfg(not(feature = "no-entrypoint"))]
mod entrypoint;// Export current sdk types for downstream users building with a different sdk version
pub use solana_program;

Then we add some code in our entrypoint file entrypoint.rs:

//! Program entrypointuse solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, program_error::ProgramError,
pubkey::Pubkey,
};
use std::str::from_utf8;entrypoint!(process_instruction);
fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
Ok(())
}

This is a really simple program, we did not do anything here, just return success 😎

Function process_instruction is the whole entry function which we pass in an instruction structure. It contains all the info needed to execute this instruction: _program_id indicates the program, _accounts indicates all the accounts would be needed in this instruction, instruction_data indicates serialized data steam which we use to pass in other parameters. When the program runs well, we useOk(())to return success,or we useErr(error) to return failure.

If you are ready to deploy this program, as we do before we compile it and send the so file to devnet(or other nets at your choice):

cargo build-bpf...To deploy this program:
$ solana deploy /home/ubuntu/solana/memo_test/onchain_program/target/deploy/onchain_program.so

Conclusion

With all the tools and SDK Solana provides, we can use cargo to easily get our target files. Once you understand the whole process, you will see that it’s just a Rust project which compiles to BPF format files with LLVM provides by rustrc, then we deploy it on chain. One thing I want to mention is that not all Rust features can be used when developing Solana onchain program, details can be found here. If you use tool suite of version 1.3.x, you also need xargo-build.sh.

Chinese version see here

Reference

  1. example-helloworld
  2. solana-program-library
  3. cargo-build-bpf

--

--