
Hello this will be a quite different article where i share my notes while studying some topic/framework/language/technology in this section we will cover Solana Smart Contracts. So without any further ado let's get started!
Solana's high level overview
It's a opensource permissionless blockchain, that uses the unit of account as "SOL and Lamports. 1 Lamport = 0.000000001 SOL".
Approximately 1500 nodes capable of 50k TPS. Implements a number of real enhancements to achieve this level of thorughput
Useful link: https://medium.com/solana-labs/7-innovations-that-make-solana-the-first-web-scale-blockchain-ddc50b1defda
- Comprised of multiple clusters:
- Devnet
- Testnet
- Mainnet Beta
- Localnet / Test Validator Node
- Uses a BFT PoS (Proof of Stake) concensus mechanism and PoH (Proof of History) to maintain the state
Solana Smart Contracts x Account: Two total different things
- Smart Contracts
Solana is a total new concept of writing smart contracts so different than Ethereum that the contracts are Stateful solana uses a Stateless approach so the contracts only holds the logic and nothing more than that. And also each contract deployed has a Program Id that is the address/account the program is stored in.
- Accounts
So as we mentioned above the smart contract is stored inside the account and to store state. To create a state the smart contract need to create other accounts to store that data and the only one that can change the account is the user that holds the seed for that account, this is thanks for the PoH (Proof of History) that we mentioned above.
So Knowing that we can assume that:
- Account is actually a buffer
- Can be thought of like a Operating System File
- Includes metadata that tells the runtime who is allowed to access the data and how
- Held in Validator memory to pay the 'rent' to stay
- So In Solana the state from a Smart Contract get's stored into the account
- And users needs to be payed a 'rent' so the file can be kept into storage
Solana Instructions
So as we know the accounts store the state and the contract stores only the logic but how we can change the state? So different than Ethereum the state is not on the contract and as i mentioned above the accounts will hold a state inside a buffer, and to interact with the accounts you need to pass a instruction with the accounts that you guys are interacting with:
Like this:
# Instruction 1
ProgramID: ...
Accounts: [Acct1, Acct2]
Data: [1,2,3,4,5,6]
# Instruction 2
ProgramID: ...
Accounts: [Acct3, Acct4]
Data: [9,8,7,6,5,4]
# Transaction 1
[Instruction1, Instruction2]
So as we saw above a instruction os a conjunction of accounts, programID and also the data that is pretty much a ByteArray that is used to send input data to the on-chain programs written in Rust/C
This is the instruction Flow for the solana Blockchain:
Economics and Rent
Here goes a little bit of the complexity of Solana's Economics/Storage,
- Validators get paid transactions fees + inflationary rewards
- Stakers are rewarded for helping validate the ledger by delegating their stake to validator nodes
- Inflation currently set to Approximately 8%
- Each transaction submitted to the ledger imposes costs
- Tx Fees only covers processing the transaction, not storing it long term
As we told above accounts have a cost for existing regular accounts are covered by the fees but when it comes to storage comes into a whole new thing! So in solana we can pay to register data into the storage through other accounts and we pay that 'rent' fee that we mentioned above where we can pay and store to the on-chain data
- Storage Rent covers the cost of storing data in Accounts over time
- 2 Methods of storage rent:
- Set and Forget
- Pay per Byte
Data Serialization
Differently than Ethereum to interact with a Smart contract or a Program on the Solana blockchain you need to serialize that data into Bytes and deserialize into the smart contract!
- Serialization/Serialize: Converting an object in memory to a stream of Bytes
- Deserialization/Deserialize: Converting a stream of Bytes into a readable object in memory
So to send our data to/from a program via the JSON RPC requires serialization and deserialization
Anchor: Solana's Sealevel runtime framework
Anchor is a framework created by Armani Ferrante, to quickly build secure Solana Programs, it reduces the coding effort by generating various boilerplate's of code
- Documentation: https://www.anchor-lang.com/
- Github: https://github.com/coral-xyz/anchor
- Anchor Tools
- Rust Smart Contracts
- Client Generators
- CLI
- IDL: Interface Description Language (Similar to Abi files on Ethereum)
Anchor's Workflow:

Anchor's Sample Code:
use anchor_lang::prelude::*;
// The Address that this program is Located At
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
// Business Logic and new will go here with the initalize function
#[program]
pub mod raffle {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
// Validate the accounts for the initialize method
#[derive(Accounts)]
pub struct Initialize {}
Anchor Accounts
- Where you define which accounts your instruction expects, and which constraints these accounts should follow
- Types: Mostly Rust defined structs that derives from the
#[account]macro
#[account] #[derive(Default)] pub struct MyAccount { data: u64, mint: Pubkey }- Constraints
- you can use the same macro to define the account constraints example:
#[account(mut)]
- you can use the same macro to define the account constraints example:
- Types: Mostly Rust defined structs that derives from the
Read more about the constraints and safety checks here: https://www.anchor-lang.com/docs/the-accounts-struct
Anchor's Program
In rust anchor we have the part of the program that we call program where all the functions are defined. As we can see in the example below
#[program]
mod hello_anchor {
use super::*;
pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
ctx.accounts.my_account.data = data;
Ok(())
}
}
- The
ctxparameter is the deserialized accounts object - We can add parameters as arguments to the function as we saw above we have a parameter called
datathat is a additional parameter added- Anchor will automatically deserialize the instruction data into the arguments types so this way we can add parameters as String, Array and etc...
Anchor IDL: Interface Description Language
- Generates a IDL specification for the programs
- Similar to the EVM-based Abi
- Used by clients to be able to interact with deployed Anchor-based programs
- Greatly simplifies calling functions, sending and receiving data
- Acts as the connecting glue between on-chain program, and off chain clients
Lets make a simple contract
So now we are going to create a simple contract where we can set a Greeting message to a user
Before we start please follow the installation process on the anchor documentation: https://www.anchor-lang.com/docs/installation
NOTE: IF YOU ARE RUNNING ON APPLE SILICON DEVICES AND HAD ANY PROBLEM WHILE INSTALLING SOLANA CHECK THIS ARTICLE HERE
- First let's initialize our project by running the following command
anchor init greeting-anchor, this command will generate a base anchor project for us with a skeleton smart contract with tests passing - Let's build our rust code inside
programs > greeting-anchor > src > lib.rs
use anchor_lang::prelude::*;
// ID for our solana program
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod greeting_anchor {
use super::*;
// The function that will mutate the state of the greeting Account, with the message parameter
pub fn set_message(ctx: Context<SetMessage>, message: String) -> Result<()> {
let greeting_account = &mut ctx.accounts.greeting;
greeting_account.greeting = message;
msg!("Greeting updated, {}", greeting_account.greeting);
Ok(())
}
}
#[account]
struct GreetingAccount {
// Greeting message for that account
pub greeting: String,
}
#[derive(Accounts)]
pub struct SetMessage<'info> {
// The account that will be initialized by Solana to store our Contract State
// Here we are describint that this account will be initialized and that the one that will be paying for this account is the user
#[account(init, payer = user, space = 8 + 32)]
greeting: Account<'info, GreetingAccount>,
// The user account that will sign the transaction
#[account(mut)]
pub user: Signer<'info>,
// default for every instruction to include the system program from solana
pub system_program: Program<'info, System>,
}
- Let's write tests for our solana smart contract under
tests > greeting-anchor.ts
import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import { assert } from "chai";
import { GreetingAnchor } from "../target/types/greeting_anchor";
describe("greeting-anchor", () => {
const provider = anchor.AnchorProvider.env();
// Configure the client to use the local cluster.
anchor.setProvider(provider);
const program: Program<GreetingAnchor> = anchor.workspace.GreetingAnchor;
it("should execute the function call without any issues", async () => {
const greetingAccount = anchor.web3.Keypair.generate();
await program.methods
.setMessage("Hello World")
.accounts({
greetingAccount: greetingAccount.publicKey,
user: provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([greetingAccount])
.rpc({
commitment: "confirmed",
});
const { greeting } = await program.account.greetingAccount.fetch(
greetingAccount.publicKey,
);
assert(greeting === "Hello World", "Error Unexpected message");
});
});
- Run anchor test to be sure that everything is working fine and you have successfuly written your first smart contract using Rust + Solana
NOTE: I've got an issue by using the latest anchor version and it was not generating the account structs on the IDL so just use the 0.24.2 that is working fine
Useful Links
Hope that the information helps!





