Skip to content

lava-404/Hello-Sol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

6 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

day 1

πŸ§ͺ Experiment 1 (exp1)


🟩 1. Creating / Generating a New Keypair

Import

solana_sdk::signer::{keypair::Keypair, Signer}

Generate a new keypair

let keypair = Keypair.generate();

This is used to create a brand-new Solana keypair.


🟩 2. Working with PDAs (Program Derived Addresses)

πŸ”Ή pubkey! macro

  • The pubkey! macro takes in a base58 string and converts it into a Pubkey.

  • Example:

    pubkey!("1" * 32)

    This converts the System Program ID into a Pubkey.


πŸ”Ή Seeds

  • Seeds are used to derive PDAs.
  • Seeds must be in bytes, not strings.

Example:

let seeds = [b"helloWorld".as_ref()];
  • The b prefix indicates that the value is byte-encoded, not an &str (which is commonly assumed).

  • PDA seeds must be of type:

    &[&[u8]]
  • This is why .as_ref() is used β€” to convert the byte array into a byte slice.


πŸ”Ή Pubkey::find_program_address

  • Pubkey::find_program_address:

    • Takes in the seeds and the program ID
    • Hashes them
    • Finds a PDA that does not have a private key
  • This is required because PDAs CANNOT HAVE PRIVATE KEYS.


🟩 3. Creating Instructions, Transactions, and Mint Accounts

Step-by-step flow:

  1. Create a connection.

  2. Generate a keypair to pay for the transaction.

  3. Request an airdrop to fund the keypair.

  4. Generate a keypair for the mint account.

    • The public key will be used as the mint account’s address.
  5. Calculate the minimum lamports required for a mint account.

    • getMinimumBalanceForRentExemptMint calculates how many lamports must be allocated for the mint account’s data.

Instructions involved:

πŸ”Ή First instruction (System Program – createAccount)

This instruction:

  • Allocates the number of bytes needed to store the mint data
  • Transfers lamports from the wallet to fund the new account
  • Assigns ownership of the account to the Token Extensions Program

πŸ”Ή Second instruction (Token Extensions Program – createInitializeMint2Instruction)

This instruction initializes the mint account with:

  • 2 decimals

  • Wallet as both:

    • Mint authority
    • Freeze authority

Signing and sending the transaction

Two signatures are required:

  1. Wallet account

    • Signs as the payer for transaction fees
    • Signs for account creation

day 2:

🧩 PDA Message Program (Solana + Anchor)

This is a simple Solana program built using Anchor that demonstrates how to:

  • Create a Program Derived Account (PDA)
  • Store user-specific data on-chain
  • Update dynamically sized data (String)
  • Close accounts and reclaim lamports
  • Interact with the program using an Anchor test (TypeScript)

This project was built as a learning exercise to understand:

  • PDAs
  • Anchor Context
  • Account constraints
  • How client code talks to on-chain Rust programs

πŸ“„ On-chain Program (lib.rs)

use anchor_lang::prelude::*;

declare_id!("AKL2d5ktgDVn6p2LYrKY3DPU3ZdoMC7SJ6AaQyomfAXw");

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

    pub fn create(ctx: Context<Create>, message: String) -> Result<()> {
        msg!("Create Message: {}", message);
        let account_data = &mut ctx.accounts.message_account;
        account_data.user = ctx.accounts.user.key();
        account_data.message = message;
        account_data.bump = ctx.bumps.message_account;
        Ok(())
    }

    pub fn update(ctx: Context<Update>, message: String) -> Result<()> {
        msg!("Update Message: {}", message);
        let account_data = &mut ctx.accounts.message_account;
        account_data.message = message;
        Ok(())
    }

    pub fn delete(_ctx: Context<Delete>) -> Result<()> {
        msg!("Delete Message");
        Ok(())
    }


#[derive(Accounts)]
#[instruction(message: String)]
pub struct Create<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        init,
        seeds = [b"message", user.key().as_ref()],
        bump,
        payer = user,
        space = 8 + 32 + 4 + message.len() + 1
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(message: String)]
pub struct Update<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        realloc = 8 + 32 + 4 + message.len() + 1,
        realloc::payer = user,
        realloc::zero = true,
    )]
    pub message_account: Account<'info, MessageAccount>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Delete<'info> {
    #[account(mut)]
    pub user: Signer<'info>,

    #[account(
        mut,
        seeds = [b"message", user.key().as_ref()],
        bump = message_account.bump,
        close = user,
    )]
    pub message_account: Account<'info, MessageAccount>,
}

#[account]
pub struct MessageAccount {
    pub user: Pubkey,
    pub message: String,
    pub bump: u8,
}

🧠 What this program does (in plain English)

πŸ”Ή Core idea

Each wallet (user) can have one message account, stored as a PDA.

The PDA address is derived using:

["message", user_public_key]

This guarantees:

  • One message per user
  • Deterministic account address
  • No collisions between users

πŸ”‘ Instructions Explained

🟒 create(message)

  • Creates a PDA for the user

  • Stores:

    • the user’s public key
    • the message string
    • the PDA bump
  • User pays for account creation

⚠️ Can only be called once per user (because PDA is deterministic).


🟑 update(message)

  • Updates the existing message
  • Uses realloc to resize account if message length changes
  • User pays extra lamports if account grows

This is where dynamic data on Solana really shows up.


πŸ”΄ delete()

  • Closes the PDA
  • Refunds remaining lamports back to the user
  • Frees on-chain storage

🧱 Account Structure

pub struct MessageAccount {
    pub user: Pubkey,   // Owner of the message
    pub message: String, // Stored message
    pub bump: u8,       // PDA bump
}

Why the space math matters

8   // Anchor discriminator
32  // Pubkey
4   // String length prefix
N   // Message bytes
1   // bump

Solana accounts have fixed size, so this calculation must be exact.


πŸ§ͺ Client Test (anchor.test.ts)

import { PublicKey } from "@solana/web3.js";

describe("pda", () => {
  const program = pg.program;
  const wallet = pg.wallet;

  const [pda, bump] = PublicKey.findProgramAddressSync(
    [Buffer.from("message"), wallet.publicKey.toBuffer()],
    program.programId
  );

  it("Update Message", async () => {
    const message = "I will win!";

    const transactionSignature = await program.methods
      .update(message)
      .accounts({ messageAccount: pda })
      .rpc({ commitment: "confirmed" });

    const messageAccount = await program.account.messageAccount.fetch(
      pda,
      "confirmed"
    );

    console.log(JSON.stringify(messageAccount, null, 2));
    console.log(
      "Transaction Signature:",
      `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
    );
  });
});

🧠 How the test works

  • pg.wallet acts as the user
  • PDA is derived off-chain using the same seeds as Rust
  • .accounts({ messageAccount: pda }) tells Anchor which address to use
  • .update(message) calls the on-chain instruction
  • .fetch(pda) reads the on-chain account state

This test simulates a real user updating their on-chain data.


🧩 Key Concepts Learned

  • What a PDA is and why it exists

  • Difference between:

    • instruction execution (program.methods)
    • account reading (program.account)
  • How Anchor maps:

    • message_account (Rust) β†’ messageAccount (TS)
  • Why accounts must be explicitly passed

  • Why realloc is needed for strings

  • How Solana enforces correctness via constraints


🧠 Mental Model (the one that finally clicked)

The wallet is the user, the PDA is the user’s storage, and the program is just the rulebook.


πŸš€ What this can grow into

This pattern is used in real protocols for:

  • profiles
  • posts
  • vaults
  • escrows
  • configs
  • counters
  • NFTs
  • governance

Day 3

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors