DEV Community

metadevdigital
metadevdigital

Posted on

How to Build Your First Solana dApp: A Practical Guide for UK Developers

How to Build Your First Solana dApp: A Practical Guide for UK Developers

So you want to build on Solana but don't know where to start. Fair enough—the Solana ecosystem can feel overwhelming with all the different tools, frameworks, and standards floating around. But here's the thing: getting your first dApp up and running is actually way more straightforward than you think.

I'm going to walk you through building a simple but functional dApp on Solana from scratch. By the end of this, you'll have a working application that can interact with the blockchain, and you'll understand the core concepts well enough to build more complex stuff.

What You're Going to Build

We're going to create a basic counter program. Yes, it's simple, but it'll teach you the fundamentals you need. The program will:

  • Store a count value on-chain
  • Allow users to increment the counter
  • Persist data between transactions

This covers the essential patterns: state management, program logic, and client-side interactions. Everything else builds from here.

Prerequisites

You'll need:

  • Node.js (v16 or higher)
  • Rust (for writing the Solana program)
  • The Solana CLI
  • Basic understanding of blockchain concepts
  • A text editor (VS Code is fine)

If you haven't installed the Solana CLI yet, grab it from solana.com. Follow their install guide—it's dead simple.

Setting Up Your Development Environment

First, let's get Rust installed. You'll need it for writing Solana programs.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup component add rustfmt
Enter fullscreen mode Exit fullscreen mode

Next, initialize a new Solana program using Anchor. Anchor is a framework that makes building Solana programs significantly easier. It abstracts away a lot of boilerplate.

cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest
avm use latest
anchor init counter_program
cd counter_program
Enter fullscreen mode Exit fullscreen mode

Perfect. You've got the basic scaffolding set up.

Writing Your First Solana Program

Inside your programs/counter_program/src/lib.rs, you'll see some template code. Replace it with this:

use anchor_lang::prelude::*;

declare_id!("11111111111111111111111111111111");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count = 0;
        counter.authority = ctx.accounts.user.key();
        Ok(())
    }

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

    pub fn decrement(ctx: Context<Decrement>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count -= 1;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = user, space = 8 + 40)]
    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, has_one = authority)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
}

#[derive(Accounts)]
pub struct Decrement<'info> {
    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
}

#[account]
pub struct Counter {
    pub count: u64,
    pub authority: Pubkey,
}
Enter fullscreen mode Exit fullscreen mode

Let me break down what's happening here:

  • Initialize: Creates a new counter account and sets the initial count to 0
  • Increment: Increases the counter by 1
  • Decrement: Decreases the counter by 1
  • Counter struct: Defines what data we're storing on-chain

The #[account(...)] macros tell Anchor how to manage accounts. The has_one = authority constraint ensures only the account owner can modify their counter.

Building and Deploying

Build your program:

anchor build
Enter fullscreen mode Exit fullscreen mode

Before deploying, set your RPC endpoint. Let's use Devnet for testing:

solana config set --url devnet
Enter fullscreen mode Exit fullscreen mode

Create a keypair if you don't have one:

solana-keygen new
Enter fullscreen mode Exit fullscreen mode

Get some devnet SOL (you'll need this for transactions):

solana airdrop 2
Enter fullscreen mode Exit fullscreen mode

Now deploy:

anchor deploy
Enter fullscreen mode Exit fullscreen mode

Your program is live on Devnet. You'll see a program ID in the output—copy that and update the declare_id!() macro in your code.

Building the Client-Side Application

Now let's create a simple client to interact with your program. Create a client.js file:

const anchor = require("@project-serum/anchor");
const { PublicKey } = require("@solana/web3.js");

const programID = new PublicKey("YOUR_PROGRAM_ID_HERE");

async function main() {
  const connection = new anchor.web3.Connection(
    anchor.web3.clusterApiUrl("devnet"),
    "processed"
  );

  const wallet = new anchor.Wallet(anchor.web3.Keypair.fromSecretKey(
    Buffer.from(JSON.parse(require("fs").readFileSync(
      process.env.HOME + "/.config/solana/id.json",
      "utf-8"
    )))
  ));

  const provider = new anchor.AnchorProvider(connection, wallet, {});
  anchor.setProvider(provider);

  const program = new anchor.Program(IDL, programID, provider);

  // Derive the counter PDA
  const [counterPDA] = await PublicKey.findProgramAddress(
    [Buffer.from("counter")],
    program.programId
  );

  // Initialize counter
  await program.methods
    .initialize()
    .accounts({
      counter: counterPDA,
      user: wallet.publicKey,
      systemProgram: anchor.web3.SystemProgram.programId,
    })
    .rpc();

  console.log("Counter initialized!");

  // Increment counter
  await program.methods
    .increment()
    .accounts({
      counter: counterPDA,
      authority: wallet.publicKey,
    })
    .rpc();

  console.log("Counter incremented!");

  // Fetch and display counter
  const counter = await program.account.counter.fetch(counterPDA);
  console.log("Count:", counter.count.toString());
}

main();
Enter fullscreen mode Exit fullscreen mode

Testing It Out

Install dependencies:

npm install @project-serum/anchor @solana/web3.js
Enter fullscreen mode Exit fullscreen mode

Run your client:

node client.js
Enter fullscreen mode Exit fullscreen mode

You should see your counter initialize and increment. That's it—you've built and deployed a working Solana dApp.

What's Next?

You've got the basics down, but there's plenty more to explore:

  • Add more complex state management
  • Implement token transfers
  • Build a frontend with React
  • Add proper error handling
  • Write tests for your program

The Solana documentation and the Anchor book are excellent resources for diving deeper. The community is also incredibly helpful if you get stuck.

Keep building. This is just the beginning.

Top comments (0)