Creating a Staking Smart Contract on Solana using Anchor

Posted By : Siddharth

Apr 30, 2023

This article explores a concise yet comprehensive guide on creating a staking smart contract on Solana utilizing Anchor. Explore the vast potential of Solana blockchain development services by leveraging Anchor's capabilities to build robust staking mechanisms. 

 

What is Solana Blockchain

 

Solana is a high-performance blockchain platform that provides fast, secure, and scalable transactions for decentralized applications (dApps) and marketplaces. Solana uses a unique consensus algorithm called Proof of History (PoH), which allows the network to verify transactions and reach consensus quickly and efficiently.

 

Solana also provides a suite of developer tools, including a web-based wallet, smart contract language, and decentralized exchange (DEX) for developers to build on its platform. Its native cryptocurrency, SOL, is used for transaction fees, staking, and exchange within the Solana ecosystem.

 

Suggested Read | What Makes Solana Blockchain Development Stand Out

 

What is a Smart Contract

 

A smart contract is a self-executing program that automatically enforces the rules and regulations of a contract when certain pre-defined conditions are met. Smart contracts are usually written in programming languages such as Solidity for Ethereum or Rust for Solana and are stored on a blockchain, making them immutable and tamper-proof.

 

Smart contracts are an integral part of decentralized applications (dApps) that operate on blockchain networks. Smart contracts are transparent and secure, and provide a trustless environment for parties to interact with each other.

 

Also, Explore | Top 5 Smart Contract Development Companies

 

What is a Staking Contract

 

A staking contract is a type of smart contract that allows users to earn rewards by staking their tokens or cryptocurrencies in a particular blockchain network. By staking their tokens, users can participate in the consensus process and earn rewards in return.

 

Staking contracts can also include various features such as slashing mechanisms, where a portion of a staker's tokens is taken away as a penalty for malicious behavior or failure to perform network duties. Staking contracts can also implement additional features, such as delegation, slashing, and governance, to improve the staking experience and incentivize participation.

 

Check It Out | An Explainer to Liquidity Staking Solution

 

Installation and Versions Used in Code

 

  1. Rust - 1.69.0
  2. Solana - 1.15.2
  3. Anchor - 0.27.0

 

You can follow the link to install all the dependencies.

 

Also, Discover | Exploring the Potential of Liquid Staking Derivatives (LSD)

 

Code

 

use anchor_lang::prelude::*;
use anchor_spl::token::{self, MintTo, Transfer};
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
declare_id!("7MHr6ZPGTWZkRk6m52GfEWoMxSV7EoDjYyoXAYf3MBwS");
#[program]
pub mod solana_staking_blog {
    use super::*;
    pub fn initialize(ctx: Context<Initialize>, start_slot: u64, end_slot: u64) -> Result<()> {
        msg!("Instruction: Initialize");
        let pool_info = &mut ctx.accounts.pool_info;
        pool_info.admin = ctx.accounts.admin.key();
        pool_info.start_slot = start_slot;
        pool_info.end_slot = end_slot;
        pool_info.token = ctx.accounts.staking_token.key();
        Ok(())
    }
    pub fn stake(ctx: Context<Stake>, amount: u64) -> Result<()> {
        msg!("Instruction: Stake");
        let user_info = &mut ctx.accounts.user_info;
        let clock = Clock::get()?;
        if user_info.amount > 0 {
            let reward = (clock.slot - user_info.deposit_slot) - user_info.reward_debt;
            let cpi_accounts = MintTo {
                mint: ctx.accounts.staking_token.to_account_info(),
                to: ctx.accounts.user_staking_wallet.to_account_info(),
                authority: ctx.accounts.admin.to_account_info(),
            };
            let cpi_program = ctx.accounts.token_program.to_account_info();
            let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
            token::mint_to(cpi_ctx, reward)?;
        }
        let cpi_accounts = Transfer {
            from: ctx.accounts.user_staking_wallet.to_account_info(),
            to: ctx.accounts.admin_staking_wallet.to_account_info(),
            authority: ctx.accounts.user.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::transfer(cpi_ctx, amount)?;
        user_info.amount += amount;
        user_info.deposit_slot = clock.slot;
        user_info.reward_debt = 0;
        Ok(())
    }
    pub fn unstake(ctx: Context<Unstake>) -> Result<()> {
        msg!("Instruction: Unstake");
        let user_info = &mut ctx.accounts.user_info;
        let clock = Clock::get()?;
        let reward = (clock.slot - user_info.deposit_slot) - user_info.reward_debt;
        let cpi_accounts = MintTo {
            mint: ctx.accounts.staking_token.to_account_info(),
            to: ctx.accounts.user_staking_wallet.to_account_info(),
            authority: ctx.accounts.admin.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::mint_to(cpi_ctx, reward)?;
        let cpi_accounts = Transfer {
            from: ctx.accounts.admin_staking_wallet.to_account_info(),
            to: ctx.accounts.user_staking_wallet.to_account_info(),
            authority: ctx.accounts.admin.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::transfer(cpi_ctx, user_info.amount)?;
        user_info.amount = 0;
        user_info.deposit_slot = 0;
        user_info.reward_debt = 0;
        Ok(())
    }
    pub fn claim_reward(ctx: Context<ClaimReward>) -> Result<()> {
        msg!("Instruction: Claim Reward");
        let user_info = &mut ctx.accounts.user_info;
        let clock = Clock::get()?;
        let reward = (clock.slot - user_info.deposit_slot) - user_info.reward_debt;
        let cpi_accounts = MintTo {
            mint: ctx.accounts.staking_token.to_account_info(),
            to: ctx.accounts.user_staking_wallet.to_account_info(),
            authority: ctx.accounts.admin.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        token::mint_to(cpi_ctx, reward)?;
        user_info.reward_debt += reward;
        Ok(())
    }
}
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub admin: Signer<'info>,
    #[account(init, payer = admin, space = 8 + PoolInfo::LEN)]
    pub pool_info: Account<'info, PoolInfo>,
    #[account(mut)]
    pub staking_token: InterfaceAccount<'info, Mint>,
    #[account(mut)]
    pub admin_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Stake<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    /// CHECK:
    #[account(mut)]
    pub admin: AccountInfo<'info>,
    #[account(init, payer = user, space = 8 + UserInfo::LEN)]
    pub user_info: Account<'info, UserInfo>,
    #[account(mut)]
    pub user_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub admin_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub staking_token: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Unstake<'info> {
    /// CHECK:
    #[account(mut)]
    pub user: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub admin: AccountInfo<'info>,
    #[account(mut)]
    pub user_info: Account<'info, UserInfo>,
    #[account(mut)]
    pub user_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub admin_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub staking_token: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
}
#[derive(Accounts)]
pub struct ClaimReward<'info> {
    /// CHECK:
    #[account(mut)]
    pub user: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub admin: AccountInfo<'info>,
    #[account(mut)]
    pub user_info: Account<'info, UserInfo>,
    #[account(mut)]
    pub user_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub admin_staking_wallet: InterfaceAccount<'info, TokenAccount>,
    #[account(mut)]
    pub staking_token: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
}
#[account]
pub struct PoolInfo {
    pub admin: Pubkey,
    pub start_slot: u64,
    pub end_slot: u64,
    pub token: Pubkey,
}
#[account]
pub struct UserInfo {
    pub amount: u64,
    pub reward_debt: u64,
    pub deposit_slot: u64,
}
impl UserInfo {
    pub const LEN: usize = 8 + 8 + 8;
}
impl PoolInfo {
    pub const LEN: usize = 32 + 8 + 8 + 32;
}

 

You May Also Like | NFT Staking Platform Development Explained

 

Steps to Run the Test Case

 

  1. NPM/Yarn install dependencies ("npm i" or "yarn")
  2. Run "solana-test-validator" in the new terminal
  3. Run "anchor test --skip-local-validator"

 

Output

 

solana-staking-blog

 

   ✔ Initialize

   ✔ Stake

   ✔ Claim Reward

   ✔ Unstake

 

The complete code can be found here.

 

If you want more information about Solana blockchain development or want to get started with a project, connect with our skilled blockchain developers.

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

April 4, 2024 at 08:10 am

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text