Liam Vu's Blog

Liam Vu's Blog

Solana for Beginners

Solana for Beginners

Development Guide

Solana development tutorial covers core concepts (programming models and terminology), smart contract, rust language keywords, contract deployment, and contract interaction on Solana cluster.

Content

content.png

Presentation file: docs.google.com/presentation/d/1EAQ300mamB1..

1. Programing Model

Reference

Basic terms

Cluster: A set of validators maintaining a single instance of the Solana blockchain, e.g. localhost, Testnet, Mainnet Beta, etc

Accounts: A file living on chain with address (pubkey) and must pay rent for space usage on chain

Programs: Accounts that are marked executable (smart contracts)

Account Ownership: Accounts are owned by programs which are indicated by a program id in the metadata owner field.

Instructions: The smallest unit of a program that a client can include in a transaction.

Native Programs:

  • System Program: Create accounts, assign account ownership.
  • BPF Loader: For deployment, upgrades, instruction execution

2. Smart Contract

Fundamental

  • Solana smart contracts are called programs → read-only
  • Stored on an account → account public key is program id
  • Another account for storing app data

smart-contract-fundamental (1).png

Memory Management

  • appAccount has fixed amount of memory for raw data
  • Data structure is defined in program

smart-contract-flow.png

Programming Languages

  • Can use Rust or C
  • Quickly learn Rust with: doc.rust-lang.org/rust-by-example
  • Rust keywords to focus: Primitives, custom type, flow of control, functions, modules, cargo, scoping rules, traits, error handling, testing

Hello World Program

project
|- src           : source code
|- Cargo.toml    : package dependency
`- Xargo.toml    : sysroot manager
  • src/lib.rs:

Include library for unpack/pack raw data → Include standard libraries → Define entry point:

use borsh::{BorshDeserialize, BorshSerialize};

use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
};

entrypoint!(process_instruction);

Define data structure → Get app account in entry function:

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    pub counter: u32,
}

pub fn process_instruction(
    program_id: &Pubkey        // program acc pub key
    accounts: &[AccountInfo],  // app acc pub key
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("Hello World Rust program entrypoint");
    let accounts_iter = &mut accounts.iter();
    let account = next_account_info(accounts_iter)?;
    ...

Check ownership:

    if account.owner != program_id {
        return Err(ProgramError::IncorrectProgramId);
    }

Unpack data and increase counter by 1:

    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    greeting_account.counter += 1;

Pack data and push to app account:

    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
    msg!("Greeted {} time(s)!", greeting_account.counter);
    Ok(())

3. Contract Deployment

Establish connection to RPC → Init payer account → Request 1 SOL airdrop to pay the fee → Init program account:

const connection = new solanaWeb3.Connection('http://localhost:8899');

const payerAccount = new solanaWeb3.Account();
const res = await connection.requestAirdrop(payerAccount.publicKey, 1000000000);

const programAccount = new solanaWeb3.Account();

Read compiled contract → Load code on chain:

const data = await fs.readFile('./directory/file.so');

await solanaWeb3.BpfLoader.load(
  connection,
  payerAccount,
  programAccount,
  data,
  solanaWeb3.BPF_LOADER_PROGRAM_ID,
);

const programId = programAccount.publicKey;

Setup app account with: required space, lamports to rent space, and program id for ownership

const appAccount = new solanaWeb3.Account();

const transaction = new solanaWeb3.Transaction().add(
  solanaWeb3.SystemProgram.createAccount({
    fromPubkey: payerAccount.publicKey,
    newAccountPubkey: appAccount.publicKey,
    lamports: 5000000000,
    space: DATA_STRUCT_SIZE,
    programId,
  }),
);

await solanaWeb3.sendAndConfirmTransaction(
  connection, transaction,
  [payerAccount, appAccount],
);

4. Contract Interaction

Create an instruction with no data and send it the program

  • client.js:
const instruction = new solanaWeb3.TransactionInstruction({
  keys: [{pubkey: appAccount.publicKey, isSigner: false, isWritable: true}],
  programId: app.programId,
  data: Buffer.alloc(0),
});

const confirmation = await solanaWeb3.sendAndConfirmTransaction(
  connection,
  new solanaWeb3.Transaction().add(instruction),
  [payerAccount],
);
 
Share this