Full lesson
CryptoPlant
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 `pnpm`, eg: `pnpm anchor`.
- Original
- giangtv270304/CryptoPlant
- SuperStack fork
- Blockchains/CryptoPlant @
70e7884 - Stars
- 0
- Last push (original)
- 2025-05-16
- Languages
- TypeScript, JavaScript, Rust, CSS
- Detected stack
- AnchorMetaplexNext.jsweb3.js
- Categories
- Other
- Licence
- MIT
Architecture
62 source files. By extension: .tsx ×21, .ts ×16, .rs ×14, .js ×11.
| Top-level directory | Source files |
|---|---|
src | 22 |
anchor | 19 |
server | 11 |
(root) | 10 |
On-chain programs: anchor/programs/cryptoplant
Dapp: devnet deploy pending
⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/cryptoplant/. Until then, the lesson and step-by-step guide below are complete, and you can 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/cryptoplant/src/instructions/player.rs L25–48 @ 70e7884 · MIT · original// Cấu trúc chứa thông tin các tài khoản cần thiết cho việc tạo người chơi#[derive(Accounts)]pub struct CreatePlayer<'info> { // Admin thực hiện ký và trả phí tạo tài khoản #[account(mut)] pub admin: Signer<'info>, // Tài khoản lưu thông tin người chơi (PDA) #[account( init, payer = admin, // Admin trả phí space = 8 + Player::INIT_SPACE, seeds = [b"player", authority.key().as_ref()], bump )] pub player: Account<'info, Player>, // Authority lưu public key người chơi (không cần ký) pub authority: SystemAccount<'info>, pub system_program: Program<'info, System>,} // Cấu trúc chứa thông tin các tài khoản cần thiết cho việc cập nhật điểm số- Line 30
pub admin: Signer<'info>,: Requires this account to have signed the transaction. - Line 34
init,: Creates and pays for a new account. Space and payer must be specified. - Line 37
seeds = [b"player", authority.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 38
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
anchor/programs/cryptoplant/src/instructions/swap_sol_to_ctc.rs L13–36 @ 70e7884 · MIT · original amount_sol, ); anchor_lang::solana_program::program::invoke( &transfer_sol_ix, &[ ctx.accounts.user.to_account_info(), ctx.accounts.sol_vault.to_account_info(), ], )?; let ctc_amount = amount_sol .checked_mul(exchange.rate) .ok_or(ErrorCode::NumberOverflow)?; let seeds = &[b"exchange", exchange.admin.as_ref(), &[ctx.bumps.exchange]]; let signer = &[&seeds[..]]; let cpi_accounts = Transfer { from: ctx.accounts.ctc_vault.to_account_info(), to: ctx.accounts.user_ctc_ata.to_account_info(), authority: ctx.accounts.exchange.to_account_info(), }; let cpi_ctx = CpiContext::new_with_signer(- Line 15
anchor_lang::solana_program::program::invoke(: Makes a cross-program invocation into another program. - Line 24
.checked_mul(exchange.rate): Uses checked arithmetic so overflow fails instead of wrapping. - Line 27
let seeds = &[b"exchange", exchange.admin.as_ref(), &[ctx.bumps.exchange]];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 36
let cpi_ctx = CpiContext::new_with_signer(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
anchor/programs/cryptoplant/src/instructions/initialize_exchange.rs L13–35 @ 70e7884 · MIT · original exchange.ctc_mint = ctx.accounts.ctc_mint.key(); // Lưu public key của token mint exchange.ctc_vault = ctx.accounts.ctc_vault.key(); // Lưu public key của ví CTC exchange.sol_vault = ctx.accounts.sol_vault.key(); // Lưu public key của ví SOL exchange.rate = rate; // Lưu tỷ giá quy đổi Ok(())} // Cấu trúc chứa thông tin các tài khoản cần thiết cho việc khởi tạo sàn#[derive(Accounts)]pub struct InitializeExchange<'info> { // Tài khoản sàn giao dịch (PDA) #[account( init, // Khởi tạo tài khoản mới payer = admin, // Admin trả phí khởi tạo seeds = [b"exchange", admin.key().as_ref()], // Seeds để tạo PDA bump, // Lưu bump seed space = 8 + 32 + 8, // Không gian cần thiết cho tài khoản )] pub exchange: Account<'info, Exchange>, // Tài khoản admin (người khởi tạo sàn) #[account(mut)] pub admin: Signer<'info>,- Line 25
init, // Khởi tạo tài khoản mới: Creates and pays for a new account. Space and payer must be specified. - Line 27
seeds = [b"exchange", admin.key().as_ref()], // Seeds để tạo PDA: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 28
bump, // Lưu bump seed: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 35
pub admin: Signer<'info>,: Requires this account to have signed the transaction.
anchor/programs/cryptoplant/src/instructions/use_item.rs L1–24 @ 70e7884 · MIT · originaluse anchor_lang::prelude::*;use anchor_spl::token::{self, Burn, Mint, Token, TokenAccount}; use crate::state::Player; #[derive(Accounts)]pub struct UseItem<'info> { #[account( mut, seeds = [b"player", authority.key().as_ref()], bump, has_one = authority, )] pub player: Account<'info, Player>, pub authority: Signer<'info>, pub item_mint: Account<'info, Mint>, #[account( mut, associated_token::mint = item_mint, associated_token::authority = authority )]- Line 10
seeds = [b"player", authority.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 11
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 12
has_one = authority,: Checks that a stored pubkey field matches the passed account. - Line 16
pub authority: Signer<'info>,: Requires this account to have signed the transaction.
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 | next dev |
. | npm run start | next start |
. | npm run build | next build |
. | npm run lint | next lint |
server | npm run dev | node ./app.js |
server | npm run start | node ./app.js |
anchor | anchor build | compile programs and generate IDLs |
anchor | anchor test | local validator + test suite |
anchor | anchor run test | ../node_modules/.bin/jest --preset ts-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)