logo
logo
Sign in

A Guide to Counter DApp Development on Solana Blockchain

avatar
Oodles Blockchain
A Guide to Counter DApp Development on Solana Blockchain

This blog gives a comprehensive guide to smart contract development using Anchor for counter dApp development on the Solana blockchain.


About Solana

Solana is a high-performance blockchain known for its remarkable speed and scalability. It uses proof-of-history (PoH) and proof-of-stake (PoS) to process thousands of transactions per second, offering low fees. Solana’s native cryptocurrency is SOL.


Decentralised apps, or dApps, can flourish there thanks to its expanding ecosystem. Priorities for the network are decentralisation and interoperability. Solana is a strong platform for blockchain innovation in spite of a few challenges.


Check It Out: What Makes Solana Blockchain Development Stand Out


Writing Smart Contracts on Solana using Anchor


Set Up Environment

Install Rust, Solana CLI, and Anchor CLI for development, for more details refer to https://www.anchor-lang.com/docs/installation.


Initialize Project

Use Anchor CLI to create a project with smart contract templates.


Define Contract

Write your Rust smart contract code in the lib.rs file, initialising accounts and defining state structures.


Compile and Deploy

Use Anchor CLI to compile your code into a Solana program and deploy it to the network.


Interact

By submitting transactions to its entry points, users can communicate with your contract.


Testing

Test your contract and use Anchor for upgrades.


Suggested Read: Exploring the Potential of Solana Smart Contract Development


Creating an Update Counter Smart Contract


About Smart Contract

This is a simple smart contract built using Anchor. This smart contract allows you to initialize a counter account and then increment and decrement it. The counter account can only be increased or decreased by the creator, who can also decide to delete it.


Concepts Covered:

  1. PDA — Program Derived Address
  2. Structs
  3. Modify the state of the smart contract
  4. Add Signers for signing the transaction
  5. Error Code
  6. InitSpace for default spacing of account struct
  7. Closing a created PDA and getting the Rent SOL back


Dependencies Version

Anchor 0.28.0


Solana CLI 1.16.9


Project Creation Command

anchor init solana-counter


Functions and Functionality

First of all, we’ll create an account struct that will store data.


#[account]
#[derive(Default, InitSpace)]
pub struct Counter {
    pub owner: Pubkey,
    pub counter: u64,
    pub bump: u8,
}


Explanation

  1. Owner: To store the owner who created the counter account
  2. Counter: To store the count of a particular counter account
  3. Bump: To store the canonical bump of the account


Then we’ll manage this created account as follows:

1. Initialize: Here is the code to initialise a counter account so that it can hold the information mentioned above.


Account

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = initializer,
        seeds = [
            COUNTER_SEED.as_bytes(),
            initializer.key.as_ref()
        ],
        bump,
        space = 8 + Counter::INIT_SPACE
      )]
    pub counter_account: Account<'info, Counter>,
    #[account(mut)]
    pub initializer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Function

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let counter_account = &mut ctx.accounts.counter_account;
        counter_account.owner = ctx.accounts.initializer.key();
        counter_account.counter = 0;
        counter_account.bump = *ctx.bumps.get("counter_account").unwrap();
        Ok(())
}

Explanation

  • Counter account: a PDA type account that will be initialized first with the seeds (COUNTER_SEED and initializer account) so that for every initializer there can be a different counter account, we’ll store the owner, set the counter to 0 and the canonical bump.
  • Initializer: a Signer-type account that will pay for the account creation.
  • System Program: an account with the System Programme that will be used to register for the System Programme.

2. Increment Counter: To increment the counter account, below is the code to increment a counter account.

Account

#[derive(Accounts)]
pub struct Update<'info> {
    #[account(
        mut,
        seeds = [
            COUNTER_SEED.as_bytes(),
            updater.key.as_ref()
        ],
        bump = counter_account.bump,
      )]
    pub counter_account: Account<'info, Counter>,
    #[account(
        mut,
        constraint = updater.key() == counter_account.owner @ ErrorCode::AccessDenied
    )]
    pub updater: Signer<'info>,
}

Function

pub fn increment_counter(ctx: Context<Update>) -> Result<()> {
        let counter_account = &mut ctx.accounts.counter_account;
        counter_account.counter += 1;

        Ok(())
}

Explanation

  • Counter account: the same PDA account that we created before will be passed so that we can access the data from that account.
  • Updater: a Signer-type account that will pay for the account updation.


3. Decrement Counter: To decrement the counter account, below is the code to decrement a counter account.

Function

pub fn decrement_counter(ctx: Context<Update>) -> Result<()> {
        let counter_account = &mut ctx.accounts.counter_account;
        require!(counter_account.counter > 0, ErrorCode::InvalidCount);

        counter_account.counter -= 1;
        Ok(())
}

Explanation

  • Counter account: the same PDA account that we created before will be passed so that we can access the data from that account.
  • Updater: a Signer-type account that will pay for the account updation.


Note: We can use the same Update Account in Increment for decrement as well.


4. Remove Counter: To remove the counter account, below is the code to remove a counter account.

Account

#[derive(Accounts)]
pub struct Remove<'info> {
    #[account(
        mut,
        seeds = [
            COUNTER_SEED.as_bytes(),
            remover.key.as_ref()
        ],
        bump = counter_account.bump,
        close = remover
      )]
    pub counter_account: Account<'info, Counter>,
    #[account(
        mut,
        constraint = remover.key() == counter_account.owner @ ErrorCode::AccessDenied
    )]
    pub remover: Signer<'info>,
}

Function

pub fn remove_counter(_ctx: Context<Remove>) -> Result<()> {
        Ok(())
}

Explanation

  • Counter account: the same PDA account that we created before will be passed so that we can access the data from that account.
  • Remover: a Signer type account, who will get the SOL for the account removal.


Steps to Deploy on Localhost

“solana config get” — Make sure it shows localhost configuration, if not run the command below.


“solana config set –url localhost”

After the instance is set to localhost, run the command below.

After the instance is set to localhost, run the command below.

“anchor localnet” — This will start a local validator so that you can deploy and run the smart contract.

“anchor build” — This will build the smart contract.

“anchor keys list” — Copy the program ID that is returned and paste it in lib.rs and Anchor.toml also check if the cluster is set to “localnet”.

“anchor build” — This will build the smart contract again with the updated program ID.

“anchor deploy” — This will deploy the smart contract on the localhost.

“anchor run test” — This will run the smart contract test cases.


Complete code — https://github.com/siddharth-oodles/solana-counter


Explore More: Why Develop DApps on Solana


Solana DApp Development with Oodles

Oodles Blockchain offers a wide range of dApp development services for the Solana blockchain. We enable you to develop robust Solana dApps for fintech, non-fungible token marketplaces, gaming, and beyond. Connect with our Solana developers to discuss your project needs.

collect
0
avatar
Oodles Blockchain
guide
Zupyak is the world’s largest content marketing community, with over 400 000 members and 3 million articles. Explore and get your content discovered.
Read more