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 directory | Source files |
|---|---|
web | 26 |
anchor | 18 |
(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.
anchor/programs/solana-button/src/instructions/initialize_global_state.rs L1–24 @ 47b7974 · MIT · originaluse 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>,}- 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. - 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. - 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. - Line 21
pub authority: Signer<'info>,: Requires this account to have signed the transaction.
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,- Line 53
init,: Creates and pays for a new account. Space and payer must be specified. - 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. - Line 57
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 63
pub user: Signer<'info>,: Requires this account to have signed the transaction.
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(())}- Line 26
require!(ctx.accounts.user.lamports() >= amount, ClickButtonError::InsufficientFunds);: An explicit validation that aborts the instruction with an error. - Line 34
game_state.last_click_timestamp = Some(clock.unix_timestamp);: Reads on-chain time from the Clock sysvar. - Line 43
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);: Makes a cross-program invocation into another program.
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, ]);- Line 37
const latestBlockhashInfo = await provider.connection.getLatestBlockhash("confirmed");: Fetches a recent blockhash. Transactions expire if it gets too old. - 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.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | nx serve web |
. | npm run build | nx build web |
anchor | anchor build | compile programs and generate IDLs |
anchor | anchor test | local validator + test suite |
anchor | anchor run test | ../node_modules/.bin/nx run anchor:jest |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)