DeveloperBreeze

Building a Simple Solana Smart Contract with Anchor

Introduction

Solana is known for its high-performance blockchain infrastructure, which supports scalable decentralized applications (dApps). Anchor is a framework that streamlines smart contract development on Solana by providing tools and abstractions that simplify coding and deployment. In this tutorial, we'll create a basic Solana smart contract using Anchor to store and update a counter value. You'll learn how to set up your development environment, write a smart contract, and deploy it to the Solana Devnet.

Objectives

By the end of this tutorial, you will:

  • Set up a Solana development environment with Anchor.
  • Write a simple smart contract to manage a counter.
  • Deploy the contract to the Solana Devnet.
  • Interact with the deployed contract.

Prerequisites

  • Ubuntu system with Rust installed.
  • Basic understanding of Rust programming.
  • Solana CLI installed (as outlined in previous tutorials).
  • Node.js and npm installed for Anchor.

Step 1: Install Anchor

  1. Install Node.js and npm:

Anchor requires Node.js for its CLI tool. Install it using the following commands:

   sudo apt update
   sudo apt install -y nodejs npm
  1. Install Anchor CLI:

Use npm to install the Anchor CLI:

   npm install -g @project-serum/anchor-cli
  1. Verify Anchor Installation:

Check that Anchor is installed correctly:

   anchor --version

You should see the version number of the Anchor CLI.

Step 2: Set Up a New Anchor Project

  1. Create a New Project:

Use Anchor to create a new project named counter:

   anchor init counter

This command creates a new directory named counter with the basic project structure.

  1. Navigate to the Project Directory:
   cd counter

Step 3: Write the Smart Contract

  1. Edit the lib.rs File:

Open the lib.rs file located in the programs/counter/src directory:

   nano programs/counter/src/lib.rs

Replace the contents with the following code to create a counter program:

   use anchor_lang::prelude::*;

   declare_id!("Fg6PaFpoGXkYsidMpWxTWG9AAM9QK2ZrhQWk5raUB7Uq");

   #[program]
   pub mod counter {
       use super::*;
       pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
           let counter = &mut ctx.accounts.counter;
           counter.count = 0;
           Ok(())
       }

       pub fn increment(ctx: Context<Increment>) -> ProgramResult {
           let counter = &mut ctx.accounts.counter;
           counter.count += 1;
           Ok(())
       }
   }

   #[derive(Accounts)]
   pub struct Initialize<'info> {
       #[account(init, payer = user, space = 8 + 8)]
       pub counter: Account<'info, Counter>,
       #[account(mut)]
       pub user: Signer<'info>,
       pub system_program: Program<'info, System>,
   }

   #[derive(Accounts)]
   pub struct Increment<'info> {
       #[account(mut)]
       pub counter: Account<'info, Counter>,
   }

   #[account]
   pub struct Counter {
       pub count: u64,
   }
  • This code defines a Solana program with two functions: initialize and increment.
  • The initialize function sets up the counter with an initial value of zero.
  • The increment function increases the counter's value by one.
  1. Save and Exit:

Save the file and exit the editor (Ctrl+X, then Y, then Enter).

Step 4: Build and Deploy the Contract

  1. Build the Project:

Run the following command to build the smart contract:

   anchor build
  1. Deploy the Contract to Devnet:

Use Anchor to deploy the contract to the Solana Devnet:

   anchor deploy
  • Make sure your Solana CLI is configured to use the Devnet (solana config set --url https://api.devnet.solana.com).

Step 5: Interact with the Deployed Contract

  1. Run a Test:

Create a simple test script in the tests directory to interact with the contract:

   nano tests/counter.js

Add the following JavaScript code:

   const anchor = require("@project-serum/anchor");

   describe("counter", () => {
     // Configure the client to use the local cluster.
     const provider = anchor.AnchorProvider.env();
     anchor.setProvider(provider);

     it("Initializes and increments the counter", async () => {
       const program = anchor.workspace.Counter;

       // Create a new account to hold the counter state.
       const counter = anchor.web3.Keypair.generate();

       // Initialize the counter.
       await program.rpc.initialize({
         accounts: {
           counter: counter.publicKey,
           user: provider.wallet.publicKey,
           systemProgram: anchor.web3.SystemProgram.programId,
         },
         signers: [counter],
       });

       // Increment the counter.
       await program.rpc.increment({
         accounts: {
           counter: counter.publicKey,
         },
       });

       // Fetch the account details.
       const account = await program.account.counter.fetch(counter.publicKey);
       console.log("Count:", account.count.toString());
     });
   });
  1. Run the Test:

Use the following command to run the test and interact with your contract:

   anchor test

You should see output indicating that the counter was initialized and incremented successfully.

Conclusion

Congratulations! You've successfully set up a Solana development environment using Anchor, written a basic smart contract, and deployed it to the Solana Devnet. This tutorial introduced you to the fundamentals of smart contract development on Solana and demonstrated how Anchor simplifies the process. You can now explore more complex smart contract designs and leverage Solana's capabilities to build scalable decentralized applications.

Related Posts

More content you might like

Tutorial

Etherscan vs Infura: Choosing the Right API for Your Blockchain Application

You should use Etherscan when you need to read data from the Ethereum blockchain, such as querying transaction details, wallet balances, or token transfers. Etherscan is a powerful tool for building blockchain explorers or applications that focus on data analytics.

const axios = require('axios');

// Replace with your actual Etherscan API key
const apiKey = 'YOUR_ETHERSCAN_API_KEY';

// Replace with the Ethereum address you want to query
const address = '0xYourEthereumAddress';

// Etherscan API URL to fetch wallet balance
const url = `https://api.etherscan.io/api?module=account&action=balance&address=${address}&tag=latest&apikey=${apiKey}`;

async function getWalletBalance() {
  try {
    const response = await axios.get(url);
    const balanceInWei = response.data.result;

    // Convert balance from Wei to Ether
    const balanceInEther = balanceInWei / 1e18;
    console.log(`Wallet Balance: ${balanceInEther} ETH`);
  } catch (error) {
    console.error('Error fetching balance:', error);
  }
}

getWalletBalance();

Oct 24, 2024
Read More
Tutorial

ETH vs WETH: Understanding the Difference and Their Roles in Ethereum

Ethereum predates the ERC-20 token standard, which was introduced to standardize token creation on the blockchain. Since ETH doesn't conform to this standard, it cannot be directly used in many decentralized applications that rely on ERC-20 tokens. WETH solves this problem by allowing ETH holders to wrap their ETH into an ERC-20 compatible token that can be used across a wide range of DeFi protocols and dApps.

WETH essentially bridges the gap between ETH and the broader ERC-20 ecosystem, allowing ETH to function seamlessly in DeFi, token swaps, and other smart contract interactions.

Oct 24, 2024
Read More
Tutorial
rust

Using Solana's Program Library: Building Applications with Pre-Built Functions

Open the lib.rs file inside the programs/solana-spl-tutorial/src directory and replace its content with the following code:

use anchor_lang::prelude::*;
use spl_token::instruction::mint_to;
use solana_program::program::invoke;

declare_id!("YourProgramID");

#[program]
pub mod solana_spl_tutorial {
    use super::*;

    pub fn mint_token(ctx: Context<MintToken>, amount: u64) -> Result<()> {
        let cpi_accounts = MintTo {
            mint: ctx.accounts.mint.to_account_info(),
            to: ctx.accounts.token_account.to_account_info(),
            authority: ctx.accounts.authority.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

        invoke(
            &mint_to(
                ctx.accounts.token_program.key,
                ctx.accounts.mint.key,
                ctx.accounts.token_account.key,
                ctx.accounts.authority.key,
                &[],
                amount,
            )?,
            &[
                ctx.accounts.mint.to_account_info(),
                ctx.accounts.token_account.to_account_info(),
                ctx.accounts.authority.to_account_info(),
            ],
        )?;

        Ok(())
    }
}

#[derive(Accounts)]
pub struct MintToken<'info> {
    #[account(mut)]
    pub mint: AccountInfo<'info>,
    #[account(mut)]
    pub token_account: AccountInfo<'info>,
    #[account(signer)]
    pub authority: AccountInfo<'info>,
    pub token_program: AccountInfo<'info>,
}

Aug 27, 2024
Read More
Tutorial
solidity

Understanding Gas and Optimization in Smart Contracts

  • Reduce redundant calculations or operations within your smart contract. Precompute values where possible and reuse them.
  • Example: Store the result of a complex computation in a variable rather than recalculating it multiple times.

Several tools can help you analyze and optimize gas consumption in your smart contracts:

Aug 22, 2024
Read More

Discussion 0

Please sign in to join the discussion.

No comments yet. Be the first to share your thoughts!