Solana for Beginners

Solana for Beginners

Development Guide

·

3 min read

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],
);