Solana SPL tokens

June 30, 2022

Solana Logo

Intro

I was interested how Solana blockchain is structured. Basically how to create own ERC20 and ERC721 token analog in Solana. My findings show that Solana provides ready to use implementation for Fingible and Nonfingible tokens. Comparing to Ethereum and other blockchain platforms, Solana is more flexible and has more features. Smart Contracts are also available in Solana. I will try to cover all the features of Solana.

Solana Programs

Smart Contracts in Solana called as Programs. Programs are usually written on C++ or Rust prograaming languages. I will focus on Rust programming language because i want myself to dive deep into Rust language. There not much difference in terms of how Smart contracs deployed on Solana and Ethereum, you will get similair use eperience using platfom tools for deployment on both blockchain networks. However, there are some differences in how programs are written.

Rust

What is Rust programming language? Rust is a modern, safe, concurrent, compiled programming language that is used to write applications for the web and other platforms. Rust is a superset of the C programming language, and is also a superset of the Go programming language. Rust is a toolchain for the Rust programming language.

Official website of Rust: https://www.rust-lang.org/

Smart Contract Architecture

Solana offers a different smart contract model to traditional EVM-based blockchains. In traditional EVM-based chains, contract code/logic and state are combined into a single contract deployed on-chain. With Solana, a smart contract (or program) is read-only or stateless and contains just program logic. Once deployed, smart contracts can be interacted with by external accounts. The accounts that interact with the programs store data related to program interaction. This creates a logical separation of state (accounts) and contract logic (programs). This is the crucial difference between Solana and EVM-based smart contracts. Accounts on Ethereum are not the same as accounts on Solana. Solana accounts can store data (including wallet information) as opposed to Ethereum accounts, which are references to people’s wallets.

In addition to this, Solana offers a CLI and JSON RPC API that can be used by decentralized applications to interact with the Solana blockchain. They can also use one of the existing SDKs, which allow clients to talk to the blockchain and Solana programs.

Architecture

Solana SPL

Solana SPL (Standard Program Library) is a collection of programs written in Rust. It is a collection of programs that are used to create smart contracts. You can find SPL on Github as well as on Official Solana website.

Below you will find basic description of programs available in SPL. In next blog posts i will cover how to create your own programs in Rust and provede some Rust insights.

Token program

A token program on the Solana blockchain, usable for fungible and non-fungible tokens. This program provides an interface and implementation that third parties can utilize to create and use their tokens. Full documentation is available at Official Solana website JavaScript bindings are available in the ./js directory. `

Token Swap Program

A Uniswap-like exchange for the Token program on the Solana blockchain. Full documentation is available at Official Solana website JavaScript bindings are available in the ./js directory.

Token Lending program

A lending protocol for the Token program on the Solana blockchain inspired by Aave and Compound. Full documentation is available at Official Solana website Web3 bindings are available in the ./js directory. `

Stateless Offer

Simple program to make token offers to any bidder that can satisfy the constraints.

This program is stateless. It is up to the maker to advertise. It uses the PDA as a one way hash of the offer that the maker wants to create. The maker then only needs to approve their token to the PDA address for the taker to receive the items. The maker doesn’t need to be online to complete the transaction, but needs to advertise the offer off-chain.

Maker compute the offer PDA approve the token delegation for the amount to the PDA publish the offer off-chain Taker Create the offer TX Submit the TX to the stateless-offer program To cancel, the maker simply needs to cancel the delegation.

stake-pool program

Full documentation is available at https://spl.solana.com/stake-pool The command-line interface tool is available in the ./cli directory. Javascript bindings are available in the ./js directory. Python bindings are available in the ./py directory.

Shared memory program

A shared-memory program on the Solana blockchain, usable for sharing data between programs or within cross-program invocations. Full documentation is available at https://spl.solana.com/shared-memory

Name Service program

A spl program for issuing and managing ownership of: domain names, Solana Pubkeys, URLs, twitter handles, ipfs cid’s, metadata, etc.. This program provides an interface and implementation that third parties can utilize to create and use their own version of a name service of any kind. Full documentation is available at https://spl.solana.com/name-service JavaScript binding are available in the ./js directory.

Memo Program

A simple program that validates a string of UTF-8 encoded characters and logs it in the transaction log. The program also verifies that any accounts provided are signers of the transaction, and if so, logs their addresses. It can be used to record a string on-chain, stored in the instruction data of a successful transaction, and optionally verify the originator.

Full documentation is available at https://spl.solana.com/memo

SPL Governance

SPL Governance is a program the chief purpose of which is to provide core building blocks and primitives to create Decentralized Autonomous Organizations (DAOs) on Solana blockchain. The program is DAO type and asset type agnostic and can be used to build any type of DAOs which can own and manage any type of assets. For example it can be used as an authority provider for mints, token accounts and other forms of access control where we may want a voting population to vote on disbursement of funds collectively. It can also control upgrades of itself and other programs through democratic means. In the simplest form the program can be used for Multisig control over a shared wallet (treasury account) or as a Multisig upgrade authority for Solana programs

The program is modular and uses open/close architecture where individual parts of the program’s behavior can be customized through external plugins. For example the default implementation of the program takes deposits of the governance tokens in exchange for voting power but it can be swapped with a custom program implementation which can implement any custom requirements like token locking, token escrows, NFT voting or multi token governance structures.

The plugins are ordinary Solana programs and can be written using any supporting technology like Anchor framework for example.

Solana Yield Farming

Solana Yield Farming is a set of easy-to-use tools and blockchain contracts for yield optimization strategies.

It is powered by Solana blockchain to allow for frequent automatic compounding, staking, and rebalancing.

One of the distinct features of this platform is the On-chain Reference Database. Metadata for all objects: Tokens, Pools, Farms, Vaults, etc., is stored in the blockchain, so clients don’t need any state or hard-coded data.

Solana Yield Farming provides an unified interface to Vaults, regular AMM Pools, Farms, and basic operations on tokens and accounts. Currently, Raydium, Saber, and Orca protocols are supported, but others are under development.

The HelloWorld Program

The HelloWorld program is a smart contract that prints output to the console and counts the number of times the program has been called for a given account, storing the number on-chain. Let’s break down the code into separate sections.

The first section defines some standard Solana program parameters and defines an entry point for the program (the ‘process_instruction’ function). In addition to this, it uses borsh for serializing and deserializing parameters being passed to and from the deployed program.

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

/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    /// number of greetings
    pub counter: u32,
}

// Declare and export the program's entrypoint
entrypoint!(process_instruction);

The process_instruction function accepts the program_id, which is the public key where the program is deployed to, and accountInfo, which is the account to say hello to.

pub fn process_instruction(
    program_id: &Pubkey, // Public key of the account the hello world program was loaded into
    accounts: &[AccountInfo], // The account to say hello to
    _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
    ) -> ProgramResult {
    msg!("Hello World Rust program entrypoint");

    // Iterating accounts is safer then indexing
    let accounts_iter = &mut accounts.iter();

    // Get the account to say hello to
    let account = next_account_info(accounts_iter)?;

The ProgramResult is where the main logic of the program resides. In this case, it simply prints a message, then selects the accounts by looping through ‘accounts’. However, in our example there will only be one account.

Next, the program checks to see if the account has permission to modify the data for the specified account.

    // The account must be owned by the program in order to modify its data
    if account.owner != program_id {
        msg!("Greeted account does not have the correct program id");
        return Err(ProgramError::IncorrectProgramId);
    }

Finally, the function takes the existing account’s stored number, increases the value by one, writes the result back, and displays a message.

    // Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    greeting_account.counter += 1;
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

    msg!("Greeted {} time(s)!", greeting_account.counter);

    Ok(())

    }