Sol.College

Full lesson

solana-button

What it does

From the project README:

You can use any normal anchor commands. Either move to the `anchor` directory and run the `anchor` command or prefix the command with `npm run`, eg: `npm run anchor`.
Original
AlexScotte/solana-button
SuperStack fork
Blockchains/solana-button @ 47b7974
Stars
0
Last push (original)
2024-10-06
Languages
TypeScript, Rust, JavaScript, Makefile, CSS
Detected stack
AnchorNext.jsweb3.js
Categories
Other
Licence
MIT

Architecture

46 source files. By extension: .tsx ×20, .ts ×14, .rs ×7, .js ×5.

Top-level directorySource files
web26
anchor18
(root)2

On-chain programs: anchor/programs/solana-button

Dapp: deploying

This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/solana-button/, once it is deployed and checked on devnet. Until then, run it locally with the commands below.

Key code, explained

Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.

AlexScotte/solana-button · anchor/programs/solana-button/src/instructions/initialize_global_state.rs L1–24 @ 47b7974 · MIT · original
use anchor_lang::prelude::*; pub fn initialize_global_state(ctx: Context<InitializeGlobalStateData>) -> Result<()>{     let global_state = &mut ctx.accounts.global_state;    global_state.authority = *ctx.accounts.authority.key;    global_state.next_game_id = 0;    global_state.active_game_id = None;     Ok(())}  #[derive(Accounts)]pub struct InitializeGlobalStateData<'info> {     #[account(init, payer = authority, space = 8 + 32 + 8 + (1 + 8), seeds = [b"global".as_ref()], bump)]    pub global_state: Account<'info, GlobalState>,     #[account(mut)]    pub authority: Signer<'info>,        pub system_program: Program<'info, System>,}
  1. Line 17 #[account(init, payer = authority, space = 8 + 32 + 8 + (1 + 8), seeds = [b"global".as_ref: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  2. Line 17 #[account(init, payer = authority, space = 8 + 32 + 8 + (1 + 8), seeds = [b"global".as_ref: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  3. Line 17 #[account(init, payer = authority, space = 8 + 32 + 8 + (1 + 8), seeds = [b"global".as_ref: Creates and pays for a new account. Space and payer must be specified.
  4. Line 21 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
AlexScotte/solana-button · anchor/programs/solana-button/src/instructions/create_new_game.rs L49–72 @ 47b7974 · MIT · original
    )]    pub game_state: Account<'info, GameState>,     #[account(        init,        payer = user,        space = 8 + 8 + 8 + 8,        seeds = [b"vault".as_ref(), &global_state.next_game_id.to_le_bytes()],         bump    )]    pub vault: Account<'info, Vault>,      #[account(mut)]    pub user: Signer<'info>,     pub system_program: Program<'info, System>,} #[account]pub struct GameState {    pub last_clicker: Pubkey,    pub game_id: u64,    pub click_number: u64,
  1. Line 53 init,: Creates and pays for a new account. Space and payer must be specified.
  2. Line 56 seeds = [b"vault".as_ref(), &global_state.next_game_id.to_le_bytes()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 57 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 63 pub user: Signer<'info>,: Requires this account to have signed the transaction.
AlexScotte/solana-button · anchor/programs/solana-button/src/instructions/click_button.rs L25–47 @ 47b7974 · MIT · original
    // Should user have sufficient money    require!(ctx.accounts.user.lamports() >= amount, ClickButtonError::InsufficientFunds);     // Update vault    vault.balance += amount;     // Update game state    let game_state = &mut ctx.accounts.game_state;    game_state.last_clicker = *ctx.accounts.user.key;    game_state.last_click_timestamp = Some(clock.unix_timestamp);    game_state.click_number += 1;     // Transfer SOL from USER account to VAULT    let cpi_accounts = anchor_lang::system_program::Transfer {        from: ctx.accounts.user.to_account_info(),        to: ctx.accounts.vault.to_account_info(),    };    let cpi_program = ctx.accounts.system_program.to_account_info();    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);    transfer(cpi_ctx, amount)?;     Ok(())}
  1. Line 26 require!(ctx.accounts.user.lamports() >= amount, ClickButtonError::InsufficientFunds);: An explicit validation that aborts the instruction with an error.
  2. Line 34 game_state.last_click_timestamp = Some(clock.unix_timestamp);: Reads on-chain time from the Clock sysvar.
  3. Line 43 let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);: Makes a cross-program invocation into another program.
AlexScotte/solana-button · anchor/migrations/initialize_global_state.ts L25–47 @ 47b7974 · MIT · original
    //     .rpc();     const initializeGlobalStateInstruction = await program.instruction.initializeGlobalState(        {            accounts: {                globalState: globalStatePda,                authority: wallet.publicKey,                systemProgram: anchor.web3.SystemProgram.programId,            },        }    );     const latestBlockhashInfo = await provider.connection.getLatestBlockhash("confirmed");     const txInitializeGlobalState = new anchor.web3.Transaction({        blockhash: latestBlockhashInfo.blockhash,        feePayer: wallet.publicKey,        lastValidBlockHeight: latestBlockhashInfo.lastValidBlockHeight,    }).add(initializeGlobalStateInstruction);     const signedTx = await sendAndConfirmTransaction(provider.connection, txInitializeGlobalState, [        wallet.payer,    ]);
  1. Line 37 const latestBlockhashInfo = await provider.connection.getLatestBlockhash("confirmed");: Fetches a recent blockhash. Transactions expire if it gets too old.
  2. Line 45 const signedTx = await sendAndConfirmTransaction(provider.connection, txInitializeGlobalSt: Submits the signed transaction to the cluster.

How to run, build and test

These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.

DirectoryCommandRuns
.npm run devnx serve web
.npm run buildnx build web
anchoranchor buildcompile programs and generate IDLs
anchoranchor testlocal validator + test suite
anchoranchor run test../node_modules/.bin/nx run anchor:jest

What you could build from it