diff --git a/Cargo.lock b/Cargo.lock index c4869f266..21f6282ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -748,27 +748,14 @@ dependencies = [ "cfg_aliases", ] -[[package]] -name = "borsh-derive" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" -dependencies = [ - "borsh-derive-internal 0.9.3", - "borsh-schema-derive-internal 0.9.3", - "proc-macro-crate 0.1.5", - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "borsh-derive" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" dependencies = [ - "borsh-derive-internal 0.10.4", - "borsh-schema-derive-internal 0.10.4", + "borsh-derive-internal", + "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", @@ -787,17 +774,6 @@ dependencies = [ "syn 2.0.118", ] -[[package]] -name = "borsh-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "borsh-derive-internal" version = "0.10.4" @@ -809,17 +785,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "borsh-schema-derive-internal" version = "0.10.4" @@ -894,18 +859,6 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -[[package]] -name = "carnival" -version = "0.1.0" -dependencies = [ - "anchor-lang", - "anchor-v2-testing", - "solana-fee", - "solana-kite", - "solana-program-runtime", - "wincode", -] - [[package]] name = "cc" version = "1.2.65" @@ -3779,35 +3732,6 @@ dependencies = [ "solana-transaction", ] -[[package]] -name = "repository-layout-pinocchio-program" -version = "0.1.0" -dependencies = [ - "litesvm", - "pinocchio 0.10.2", - "pinocchio-log", - "solana-instruction 3.5.1", - "solana-keypair", - "solana-native-token 3.0.0", - "solana-pubkey 4.3.0", - "solana-transaction", -] - -[[package]] -name = "repository-layout-program" -version = "0.1.0" -dependencies = [ - "borsh 1.7.0", - "borsh-derive 0.9.3", - "litesvm", - "solana-instruction 3.5.1", - "solana-keypair", - "solana-native-token 3.0.0", - "solana-program 4.0.0", - "solana-pubkey 4.3.0", - "solana-transaction", -] - [[package]] name = "rfc6979" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index c6f046762..4178f7f73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,9 +57,6 @@ members = [ "basics/favorites/pinocchio/program", "basics/favorites/anchor/programs/favorites", "basics/pyth/anchor/programs/pythexample", - "basics/repository-layout/native/program", - "basics/repository-layout/pinocchio/program", - "basics/repository-layout/anchor/programs/*", "basics/transaction-v1/anchor/programs/transaction-v1", "basics/transfer-sol/native/program", "basics/transfer-sol/pinocchio/program", diff --git a/README.md b/README.md index a0df6f6ea..8e9a3a17a 100644 --- a/README.md +++ b/README.md @@ -179,12 +179,6 @@ Calculate an account's size to determine the minimum rent-exempt balance. [⚓ Anchor v1](./basics/rent/anchor-v1) [⚓ Anchor v2](./basics/rent/anchor) [💫 Quasar](./basics/rent/quasar) [🤥 Pinocchio](./basics/rent/pinocchio) [🦀 Native](./basics/rent/native) -### Repository Layout - -Structure a larger Solana program across multiple files and modules. - -[⚓ Anchor v1](./basics/repository-layout/anchor-v1) [⚓ Anchor v2](./basics/repository-layout/anchor) [💫 Quasar](./basics/repository-layout/quasar) [🦀 Native](./basics/repository-layout/native) - ### Transfer SOL Send SOL between two accounts. diff --git a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs index 695296069..88d17ba10 100644 --- a/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs +++ b/basics/cross-program-invocation/quasar/hand/src/instructions/pull_lever.rs @@ -24,9 +24,9 @@ pub fn handle_pull_lever( // // The lever's switch_power instruction takes `String<50>`, which Quasar // serialises with a single-byte length prefix (matching every other - // Quasar program: account-data, close-account, rent, realloc, - // repository-layout). An earlier version of this builder used a u32 - // length prefix, which sent a malformed payload on every CPI call. + // Quasar program: account-data, close-account, rent, realloc). An earlier + // version of this builder used a u32 length prefix, which sent a malformed + // payload on every CPI call. // // 128 bytes is enough for any reasonable name (max 50 + 1 + 1 = 52). let mut data = [0u8; 128]; diff --git a/basics/repository-layout/README.md b/basics/repository-layout/README.md deleted file mode 100644 index c0eadd757..000000000 --- a/basics/repository-layout/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Recommended Program Layout - -A typical layout for a Solana [program](https://solana.com/docs/terminology#program) as it grows in size and starts to need multiple Rust files. Many programs follow this shape. - -> You can structure your `src` folder however you like, as long as it follows Cargo's conventions. This layout is shown so that the patterns in other programs are recognizable. - -The `native` and `anchor` layouts are similar. The main difference is the `processor.rs` file in the `native` setup - one of the things [Anchor](https://solana.com/docs/terminology#anchor) abstracts away for you. diff --git a/basics/repository-layout/anchor-v1/Anchor.toml b/basics/repository-layout/anchor-v1/Anchor.toml deleted file mode 100644 index 411ce3df7..000000000 --- a/basics/repository-layout/anchor-v1/Anchor.toml +++ /dev/null @@ -1,16 +0,0 @@ -[toolchain] -# Match the repo package manager (pnpm-lock.yaml at root); avoids Anchor's yarn default. -package_manager = "pnpm" -solana_version = "3.1.8" - -[features] -seeds = false -[programs.localnet] -carnival = "8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "cargo test" diff --git a/basics/repository-layout/anchor-v1/CHANGELOG.md b/basics/repository-layout/anchor-v1/CHANGELOG.md deleted file mode 100644 index 4f1163881..000000000 --- a/basics/repository-layout/anchor-v1/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## 2026-07-07 - -Added this changelog. Changes prior to this date were tracked in git history only. diff --git a/basics/repository-layout/anchor-v1/Cargo.toml b/basics/repository-layout/anchor-v1/Cargo.toml deleted file mode 100644 index f39770481..000000000 --- a/basics/repository-layout/anchor-v1/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[workspace] -members = [ - "programs/*" -] -resolver = "2" - -[profile.release] -overflow-checks = true -lto = "fat" -codegen-units = 1 -[profile.release.build-override] -opt-level = 3 -incremental = false -codegen-units = 1 diff --git a/basics/repository-layout/anchor-v1/README.md b/basics/repository-layout/anchor-v1/README.md deleted file mode 100644 index 8c966bc5e..000000000 --- a/basics/repository-layout/anchor-v1/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Repository Layout (Anchor) - -> [!NOTE] -> This is the **Anchor v1** copy of this example, on Anchor 1.2.1, the current -> stable Anchor release. Every `anchor` command on this page needs the v1 CLI: -> `avm install 1.2.1 && avm use 1.2.1`. The Anchor v2 version of this example is in -> [`../anchor`](../anchor/). - -Organize a larger program across modules (state, instructions, errors) instead of a single file. - -See also: [Repository Layout overview](../README.md) and the [repository catalog](../../../README.md). - -## Major concepts - -- Multi-file Anchor layout -- Separating concerns by module - -## Setup - -From this directory (`basics/repository-layout/anchor/`): - -```bash -anchor build -``` - -Prerequisites: [Agave](https://docs.anza.xyz/) CLI (version in `Anchor.toml` `[toolchain]`), [Anchor](https://www.anchor-lang.com/docs). - -## Testing - -Tests run in-process with [LiteSVM](https://www.anchor-lang.com/docs/testing/litesvm). No local validator. - -```bash -anchor test -``` - -This runs `cargo test` as configured in `Anchor.toml`. Tests call instruction handlers and check onchain state. - -## Usage - -Read the program `programs/` source and `Anchor.toml` for deployed program IDs. For deployment, use `anchor build && anchor deploy` against your target cluster. diff --git a/basics/repository-layout/anchor-v1/programs/carnival/Cargo.toml b/basics/repository-layout/anchor-v1/programs/carnival/Cargo.toml deleted file mode 100644 index d4a9dc968..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "carnival" -version = "0.1.0" -description = "Created with Anchor" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "carnival" - -[features] -default = [] -cpi = ["no-entrypoint"] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -idl-build = ["anchor-lang/idl-build"] -anchor-debug = [] -custom-heap = [] -custom-panic = [] - -[dependencies] -anchor-lang = "1.2.1" - -[dev-dependencies] -litesvm = "0.16.0" -solana-signer = "3.0.0" -solana-keypair = "3.0.1" -solana-kite = "0.6.1" - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] } diff --git a/basics/repository-layout/anchor-v1/programs/carnival/Xargo.toml b/basics/repository-layout/anchor-v1/programs/carnival/Xargo.toml deleted file mode 100644 index 475fb71ed..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/error.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/error.rs deleted file mode 100644 index fc136669d..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/error.rs +++ /dev/null @@ -1,17 +0,0 @@ -use anchor_lang::prelude::*; - -// Real failure modes for the carnival examples. Previously this file was empty -// and the rejection branches in the instruction handlers just logged a message -// and returned Ok(()), which made it impossible for callers (or tests) to -// distinguish "the ride happened" from "the ride refused service". -#[error_code] -pub enum CarnivalError { - // The rider/gamer/eater did not bring enough tickets for the requested - // attraction. - #[msg("Not enough tickets for the requested attraction")] - NotEnoughTickets, - - // The rider is below the minimum height required for the ride. - #[msg("Rider is below the minimum height for this ride")] - RiderTooShort, -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/eat_food.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/eat_food.rs deleted file mode 100644 index 2e3a62a06..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/eat_food.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::food}; - -// Instruction Data - -pub struct EatFoodInstructionData { - pub eater_name: String, - pub eater_ticket_count: u32, - pub food_stand: String, -} - -pub fn eat_food(ix: EatFoodInstructionData) -> Result<()> { - let food_stands_list = food::get_food_stands(); - - for food_stand in food_stands_list.iter() { - if ix.food_stand.eq(&food_stand.name) { - msg!("Welcome to {}! What can I get you?", food_stand.name); - - if ix.eater_ticket_count < food_stand.tickets { - msg!( - " Sorry {}, our {} is {} tickets!", - ix.eater_name, - food_stand.food_type, - food_stand.tickets - ); - // Refuse service rather than logging and returning Ok(()), so - // callers can distinguish a sale from a refusal. - return Err(CarnivalError::NotEnoughTickets.into()); - } - - msg!(" Enjoy your {}!", food_stand.food_type); - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData.into()) -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/get_on_ride.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/get_on_ride.rs deleted file mode 100644 index 0427f0c36..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/get_on_ride.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::ride}; - -// Instruction Data - -pub struct GetOnRideInstructionData { - pub rider_name: String, - pub rider_height: u32, - pub rider_ticket_count: u32, - pub ride: String, -} - -pub fn get_on_ride(ix: GetOnRideInstructionData) -> Result<()> { - let rides_list = ride::get_rides(); - - for ride in rides_list.iter() { - if ix.ride.eq(&ride.name) { - msg!("You're about to ride the {}!", ride.name); - - // Refuse service with a real error so callers and tests can - // distinguish a refused ride from a successful one. - if ix.rider_ticket_count < ride.tickets { - msg!( - " Sorry {}, you need {} tickets to ride the {}!", - ix.rider_name, - ride.tickets, - ride.name - ); - return Err(CarnivalError::NotEnoughTickets.into()); - }; - - if ix.rider_height < ride.min_height { - msg!( - " Sorry {}, you need to be {}\" tall to ride the {}!", - ix.rider_name, - ride.min_height, - ride.name - ); - return Err(CarnivalError::RiderTooShort.into()); - }; - - msg!(" Welcome aboard the {}!", ride.name); - - if ride.upside_down { - msg!(" Btw, this ride goes upside down. Hold on tight!"); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData.into()) -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/mod.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/mod.rs deleted file mode 100644 index ee6ea6d1e..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod eat_food; -pub mod get_on_ride; -pub mod play_game; diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/play_game.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/play_game.rs deleted file mode 100644 index 4867f98e8..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/instructions/play_game.rs +++ /dev/null @@ -1,44 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::game}; - -// Instruction Data - -pub struct PlayGameInstructionData { - pub gamer_name: String, - pub gamer_ticket_count: u32, - pub game: String, -} - -pub fn play_game(ix: PlayGameInstructionData) -> Result<()> { - let games_list = game::get_games(); - - for game in games_list.iter() { - if ix.game.eq(&game.name) { - msg!("You're about to play {}!", game.name); - - if ix.gamer_ticket_count < game.tickets { - msg!( - " Sorry {}, you need {} tickets to play {}!", - ix.gamer_name, - game.tickets, - game.name - ); - // Refuse service rather than logging and returning Ok(()), so - // callers can tell a successful play from a refusal. - return Err(CarnivalError::NotEnoughTickets.into()); - } - - msg!(" Let's see what you got!"); - msg!( - " You get {} attempts and the prize is a {}!", - game.tries, - game.prize - ); - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData.into()) -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/lib.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/lib.rs deleted file mode 100644 index 9d0bea35f..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -use anchor_lang::prelude::*; - -pub mod error; -pub mod instructions; -pub mod state; - -use crate::instructions::{eat_food, get_on_ride, play_game}; - -// For setting up modules & configs - -declare_id!("8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP"); - -#[program] -pub mod carnival { - use super::*; - - pub fn go_on_ride( - _context: Context, - name: String, - height: u32, - ticket_count: u32, - ride_name: String, - ) -> Result<()> { - get_on_ride::get_on_ride(get_on_ride::GetOnRideInstructionData { - rider_name: name, - rider_height: height, - rider_ticket_count: ticket_count, - ride: ride_name, - }) - } - - pub fn play_game( - _context: Context, - name: String, - ticket_count: u32, - game_name: String, - ) -> Result<()> { - play_game::play_game(play_game::PlayGameInstructionData { - gamer_name: name, - gamer_ticket_count: ticket_count, - game: game_name, - }) - } - - pub fn eat_food( - _context: Context, - name: String, - ticket_count: u32, - food_stand_name: String, - ) -> Result<()> { - eat_food::eat_food(eat_food::EatFoodInstructionData { - eater_name: name, - eater_ticket_count: ticket_count, - food_stand: food_stand_name, - }) - } -} - -#[derive(Accounts)] -pub struct CarnivalAccountConstraints<'info> { - #[account(mut)] - pub payer: Signer<'info>, -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/state/food.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/state/food.rs deleted file mode 100644 index 62b5e04d6..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/state/food.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Objects - -pub struct FoodStand { - pub name: String, - pub food_type: String, - pub tickets: u32, -} - -impl FoodStand { - pub fn new(name: String, food_type: String, tickets: u32) -> FoodStand { - FoodStand { - name, - food_type, - tickets, - } - } -} - -pub fn get_food_stands() -> Vec { - vec![ - FoodStand::new("Larry's Pizza".to_string(), "pizza".to_string(), 3), - FoodStand::new("Taco Shack".to_string(), "taco".to_string(), 2), - FoodStand::new("Dough Boy's".to_string(), "fried dough".to_string(), 1), - ] -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/state/game.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/state/game.rs deleted file mode 100644 index 185547230..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/state/game.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Objects - -pub struct Game { - pub name: String, - pub tickets: u32, - pub tries: u32, - pub prize: String, -} - -const DEFAULT_TICKETS_TO_PLAY: u32 = 3; - -impl Game { - pub fn new(name: String, tries: u32, prize: String) -> Game { - Game { - name, - tickets: DEFAULT_TICKETS_TO_PLAY, - tries, - prize, - } - } -} - -pub fn get_games() -> Vec { - vec![ - Game::new("Ring Toss".to_string(), 5, "teddy bear".to_string()), - Game::new("I Got It!".to_string(), 12, "goldfish".to_string()), - Game::new("Ladder Climb".to_string(), 1, "popcorn bucket".to_string()), - ] -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/state/mod.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/state/mod.rs deleted file mode 100644 index 12a92a2cf..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/state/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod food; -pub mod game; -pub mod ride; diff --git a/basics/repository-layout/anchor-v1/programs/carnival/src/state/ride.rs b/basics/repository-layout/anchor-v1/programs/carnival/src/state/ride.rs deleted file mode 100644 index a3af23969..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/src/state/ride.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Objects - -pub struct Ride { - pub name: String, - pub upside_down: bool, - pub tickets: u32, - pub min_height: u32, -} - -impl Ride { - pub fn new(name: String, upside_down: bool, tickets: u32, min_height: u32) -> Ride { - Ride { - name, - upside_down, - tickets, - min_height, - } - } -} - -pub fn get_rides() -> Vec { - vec![ - Ride::new("Tilt-a-Whirl".to_string(), false, 3, 48), - Ride::new("Scrambler".to_string(), false, 3, 48), - Ride::new("Ferris Wheel".to_string(), false, 5, 55), - Ride::new("Zero Gravity".to_string(), true, 5, 60), - ] -} diff --git a/basics/repository-layout/anchor-v1/programs/carnival/tests/test_carnival.rs b/basics/repository-layout/anchor-v1/programs/carnival/tests/test_carnival.rs deleted file mode 100644 index 242ef42bf..000000000 --- a/basics/repository-layout/anchor-v1/programs/carnival/tests/test_carnival.rs +++ /dev/null @@ -1,169 +0,0 @@ -use { - anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}, - litesvm::LiteSVM, - solana_kite::{create_wallet, send_transaction_from_instructions}, - solana_signer::Signer, -}; - -fn setup() -> (LiteSVM, solana_keypair::Keypair) { - let program_id = carnival::id(); - let mut svm = LiteSVM::new(); - let bytes = include_bytes!("../../../target/deploy/carnival.so"); - svm.add_program(program_id, bytes).unwrap(); - let payer = create_wallet(&mut svm, 10_000_000_000).unwrap(); - (svm, payer) -} - -fn go_on_ride_ix( - payer: &solana_keypair::Keypair, - name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::GoOnRide { - name: name.to_string(), - height, - ticket_count, - ride_name: ride_name.to_string(), - } - .data(), - accounts, - ) -} - -fn play_game_ix( - payer: &solana_keypair::Keypair, - name: &str, - ticket_count: u32, - game_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::PlayGame { - name: name.to_string(), - ticket_count, - game_name: game_name.to_string(), - } - .data(), - accounts, - ) -} - -fn eat_food_ix( - payer: &solana_keypair::Keypair, - name: &str, - ticket_count: u32, - food_stand_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::EatFood { - name: name.to_string(), - ticket_count, - food_stand_name: food_stand_name.to_string(), - } - .data(), - accounts, - ) -} - -// Riders that have enough tickets and meet the height requirement succeed. -#[test] -fn test_go_on_ride_succeeds() { - let (mut svm, payer) = setup(); - // Scrambler costs 3 tickets and requires 48" min height; Alice has 15 - // tickets and is 56" tall. - let ix = go_on_ride_ix(&payer, "Alice", 56, 15, "Scrambler"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -// Insufficient tickets must produce a real error (NotEnoughTickets), not a -// silent Ok(). -#[test] -fn test_go_on_ride_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Tilt-a-Whirl costs 3 tickets; Bob only has 1. - let ix = go_on_ride_ix(&payer, "Bob", 49, 1, "Tilt-a-Whirl"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "ride must reject insufficient tickets"); -} - -// A rider below the minimum height must be rejected with RiderTooShort. -#[test] -fn test_go_on_ride_rejects_too_short() { - let (mut svm, payer) = setup(); - // Ferris Wheel requires 55" min height; Jimmy is 36" tall but has plenty - // of tickets. - let ix = go_on_ride_ix(&payer, "Jimmy", 36, 15, "Ferris Wheel"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "ride must reject riders below min height"); -} - -// Unknown attraction name routes to ProgramError::InvalidInstructionData. -#[test] -fn test_go_on_ride_rejects_unknown_ride() { - let (mut svm, payer) = setup(); - let ix = go_on_ride_ix(&payer, "Alice", 60, 100, "Quantum Coaster"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "unknown ride must be rejected"); -} - -#[test] -fn test_play_game_succeeds() { - let (mut svm, payer) = setup(); - // Ring Toss costs 3 tickets; Alice has 15. - let ix = play_game_ix(&payer, "Alice", 15, "Ring Toss"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -#[test] -fn test_play_game_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Ring Toss costs 3 tickets; Mary only has 1. - let ix = play_game_ix(&payer, "Mary", 1, "Ring Toss"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "game must reject insufficient tickets"); -} - -#[test] -fn test_eat_food_succeeds() { - let (mut svm, payer) = setup(); - // Dough Boy's costs 1 ticket; Bob has 6. - let ix = eat_food_ix(&payer, "Bob", 6, "Dough Boy's"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -#[test] -fn test_eat_food_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Larry's Pizza costs 3 tickets; Mary has 1. - let ix = eat_food_ix(&payer, "Mary", 1, "Larry's Pizza"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!( - result.is_err(), - "food stand must reject insufficient tickets" - ); -} diff --git a/basics/repository-layout/anchor/Anchor.toml b/basics/repository-layout/anchor/Anchor.toml deleted file mode 100644 index 411ce3df7..000000000 --- a/basics/repository-layout/anchor/Anchor.toml +++ /dev/null @@ -1,16 +0,0 @@ -[toolchain] -# Match the repo package manager (pnpm-lock.yaml at root); avoids Anchor's yarn default. -package_manager = "pnpm" -solana_version = "3.1.8" - -[features] -seeds = false -[programs.localnet] -carnival = "8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "cargo test" diff --git a/basics/repository-layout/anchor/CHANGELOG.md b/basics/repository-layout/anchor/CHANGELOG.md deleted file mode 100644 index 4f1163881..000000000 --- a/basics/repository-layout/anchor/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## 2026-07-07 - -Added this changelog. Changes prior to this date were tracked in git history only. diff --git a/basics/repository-layout/anchor/Cargo.toml b/basics/repository-layout/anchor/Cargo.toml deleted file mode 100644 index f39770481..000000000 --- a/basics/repository-layout/anchor/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[workspace] -members = [ - "programs/*" -] -resolver = "2" - -[profile.release] -overflow-checks = true -lto = "fat" -codegen-units = 1 -[profile.release.build-override] -opt-level = 3 -incremental = false -codegen-units = 1 diff --git a/basics/repository-layout/anchor/README.md b/basics/repository-layout/anchor/README.md deleted file mode 100644 index b71da90e3..000000000 --- a/basics/repository-layout/anchor/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Repository Layout (Anchor) - -> [!NOTE] -> This is the **Anchor v2** copy of this example. Every `anchor` command on this page -> needs the v2 CLI: `cargo install anchor-cli --version 2.0.0-rc.2 --locked` (avm has -> no prebuilt binary for this pre-release). The Anchor v1 version of this example is in -> [`../anchor-v1`](../anchor-v1/). - -Organize a larger program across modules (state, instructions, errors) instead of a single file. - -See also: [Repository Layout overview](../README.md) and the [repository catalog](../../../README.md). - -## Major concepts - -- Multi-file Anchor layout -- Separating concerns by module - -## Setup - -From this directory (`basics/repository-layout/anchor/`): - -```bash -anchor build -``` - -Prerequisites: [Agave](https://docs.anza.xyz/) CLI (version in `Anchor.toml` `[toolchain]`), [Anchor](https://www.anchor-lang.com/docs). - -## Testing - -Tests run in-process with [LiteSVM](https://www.anchor-lang.com/docs/testing/litesvm). No local validator. - -```bash -anchor test -``` - -This runs `cargo test` as configured in `Anchor.toml`. Tests call instruction handlers and check onchain state. - -## Usage - -Read the program `programs/` source and `Anchor.toml` for deployed program IDs. For deployment, use `anchor build && anchor deploy` against your target cluster. diff --git a/basics/repository-layout/anchor/programs/carnival/Cargo.toml b/basics/repository-layout/anchor/programs/carnival/Cargo.toml deleted file mode 100644 index 0f4ad9a2c..000000000 --- a/basics/repository-layout/anchor/programs/carnival/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "carnival" -version = "0.1.0" -description = "Created with Anchor" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "carnival" - -[features] -default = [] -cpi = ["no-entrypoint"] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -idl-build = ["anchor-lang/idl-build"] -anchor-debug = [] -custom-heap = [] -custom-panic = [] -# Forwards to the harness so `anchor test --profile` can turn tracing on. -profile = ["anchor-v2-testing/profile"] - -[dependencies] -anchor-lang = "2.0.0-rc.2" -# The `#[program]` macro expands to `wincode` paths for instruction-data -# (de)serialization, so the crate has to be a direct dependency. -wincode = { version = "0.6", features = ["derive"] } - -[dev-dependencies] -# Anchor v2's own LiteSVM harness. `svm()` is `LiteSVM::new()` until the -# `profile` feature is on, when it also records the SBF register traces that -# `anchor test --profile`, `anchor debugger` and `anchor coverage` read. Tests -# that call `LiteSVM::new()` directly still pass but produce no traces, so -# those three tools see nothing. -# -# Published only from git: crates.io has no `anchor-v2-testing`. Pinned to a -# revision rather than tracking `anchor-next`, so a CI run cannot change -# behaviour because upstream moved between builds. -# -# Bump the rev deliberately, never automatically: `anchor-next` is where v2 -# development happens, so a later revision can carry breakage as easily as -# fixes. When `anchor-v2-testing` reaches crates.io, drop the git source for -# an ordinary version. -anchor-v2-testing = { git = "https://github.com/otter-sec/anchor.git", rev = "a20660219882af5f199d56ea3f9326f387f89d8b" } -# LiteSVM 0.18.1 asks for the Agave crates with `^4.3` ranges but does not -# compile against Agave 4.4.0. These two hold the Agave crates at 4.3; the rest -# follow, because Agave pins its own crates to one another exactly. Remove both -# once a LiteSVM release builds against Agave 4.4. -solana-program-runtime = "~4.3" -solana-fee = "~4.3" -solana-kite = "0.7.0" - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] } diff --git a/basics/repository-layout/anchor/programs/carnival/Xargo.toml b/basics/repository-layout/anchor/programs/carnival/Xargo.toml deleted file mode 100644 index 475fb71ed..000000000 --- a/basics/repository-layout/anchor/programs/carnival/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/basics/repository-layout/anchor/programs/carnival/src/error.rs b/basics/repository-layout/anchor/programs/carnival/src/error.rs deleted file mode 100644 index fc136669d..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/error.rs +++ /dev/null @@ -1,17 +0,0 @@ -use anchor_lang::prelude::*; - -// Real failure modes for the carnival examples. Previously this file was empty -// and the rejection branches in the instruction handlers just logged a message -// and returned Ok(()), which made it impossible for callers (or tests) to -// distinguish "the ride happened" from "the ride refused service". -#[error_code] -pub enum CarnivalError { - // The rider/gamer/eater did not bring enough tickets for the requested - // attraction. - #[msg("Not enough tickets for the requested attraction")] - NotEnoughTickets, - - // The rider is below the minimum height required for the ride. - #[msg("Rider is below the minimum height for this ride")] - RiderTooShort, -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/instructions/eat_food.rs b/basics/repository-layout/anchor/programs/carnival/src/instructions/eat_food.rs deleted file mode 100644 index 555c6623a..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/instructions/eat_food.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::food}; - -// Instruction Data - -pub struct EatFoodInstructionData { - pub eater_name: String, - pub eater_ticket_count: u32, - pub food_stand: String, -} - -pub fn eat_food(ix: EatFoodInstructionData) -> Result<()> { - let food_stands_list = food::get_food_stands(); - - for food_stand in food_stands_list.iter() { - if ix.food_stand.eq(&food_stand.name) { - msg!("Welcome to {}! What can I get you?", food_stand.name); - - if ix.eater_ticket_count < food_stand.tickets { - msg!( - " Sorry {}, our {} is {} tickets!", - ix.eater_name, - food_stand.food_type, - food_stand.tickets - ); - // Refuse service rather than logging and returning Ok(()), so - // callers can distinguish a sale from a refusal. - return Err(CarnivalError::NotEnoughTickets.into()); - } - - msg!(" Enjoy your {}!", food_stand.food_type); - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/instructions/get_on_ride.rs b/basics/repository-layout/anchor/programs/carnival/src/instructions/get_on_ride.rs deleted file mode 100644 index 43fb2f10b..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/instructions/get_on_ride.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::ride}; - -// Instruction Data - -pub struct GetOnRideInstructionData { - pub rider_name: String, - pub rider_height: u32, - pub rider_ticket_count: u32, - pub ride: String, -} - -pub fn get_on_ride(ix: GetOnRideInstructionData) -> Result<()> { - let rides_list = ride::get_rides(); - - for ride in rides_list.iter() { - if ix.ride.eq(&ride.name) { - msg!("You're about to ride the {}!", ride.name); - - // Refuse service with a real error so callers and tests can - // distinguish a refused ride from a successful one. - if ix.rider_ticket_count < ride.tickets { - msg!( - " Sorry {}, you need {} tickets to ride the {}!", - ix.rider_name, - ride.tickets, - ride.name - ); - return Err(CarnivalError::NotEnoughTickets.into()); - }; - - if ix.rider_height < ride.min_height { - msg!( - " Sorry {}, you need to be {}\" tall to ride the {}!", - ix.rider_name, - ride.min_height, - ride.name - ); - return Err(CarnivalError::RiderTooShort.into()); - }; - - msg!(" Welcome aboard the {}!", ride.name); - - if ride.upside_down { - msg!(" Btw, this ride goes upside down. Hold on tight!"); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/instructions/mod.rs b/basics/repository-layout/anchor/programs/carnival/src/instructions/mod.rs deleted file mode 100644 index ee6ea6d1e..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/instructions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod eat_food; -pub mod get_on_ride; -pub mod play_game; diff --git a/basics/repository-layout/anchor/programs/carnival/src/instructions/play_game.rs b/basics/repository-layout/anchor/programs/carnival/src/instructions/play_game.rs deleted file mode 100644 index 5613fc778..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/instructions/play_game.rs +++ /dev/null @@ -1,44 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{error::CarnivalError, state::game}; - -// Instruction Data - -pub struct PlayGameInstructionData { - pub gamer_name: String, - pub gamer_ticket_count: u32, - pub game: String, -} - -pub fn play_game(ix: PlayGameInstructionData) -> Result<()> { - let games_list = game::get_games(); - - for game in games_list.iter() { - if ix.game.eq(&game.name) { - msg!("You're about to play {}!", game.name); - - if ix.gamer_ticket_count < game.tickets { - msg!( - " Sorry {}, you need {} tickets to play {}!", - ix.gamer_name, - game.tickets, - game.name - ); - // Refuse service rather than logging and returning Ok(()), so - // callers can tell a successful play from a refusal. - return Err(CarnivalError::NotEnoughTickets.into()); - } - - msg!(" Let's see what you got!"); - msg!( - " You get {} attempts and the prize is a {}!", - game.tries, - game.prize - ); - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/lib.rs b/basics/repository-layout/anchor/programs/carnival/src/lib.rs deleted file mode 100644 index 4a542c28a..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -use anchor_lang::prelude::*; - -pub mod error; -pub mod instructions; -pub mod state; - -use crate::instructions::{eat_food, get_on_ride, play_game}; - -// For setting up modules & configs - -declare_id!("8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP"); - -#[program] -pub mod carnival { - use super::*; - - pub fn go_on_ride( - _context: &mut Context, - name: String, - height: u32, - ticket_count: u32, - ride_name: String, - ) -> Result<()> { - get_on_ride::get_on_ride(get_on_ride::GetOnRideInstructionData { - rider_name: name, - rider_height: height, - rider_ticket_count: ticket_count, - ride: ride_name, - }) - } - - pub fn play_game( - _context: &mut Context, - name: String, - ticket_count: u32, - game_name: String, - ) -> Result<()> { - play_game::play_game(play_game::PlayGameInstructionData { - gamer_name: name, - gamer_ticket_count: ticket_count, - game: game_name, - }) - } - - pub fn eat_food( - _context: &mut Context, - name: String, - ticket_count: u32, - food_stand_name: String, - ) -> Result<()> { - eat_food::eat_food(eat_food::EatFoodInstructionData { - eater_name: name, - eater_ticket_count: ticket_count, - food_stand: food_stand_name, - }) - } -} - -#[derive(Accounts)] -pub struct CarnivalAccountConstraints { - #[account(mut)] - pub payer: Signer, -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/state/food.rs b/basics/repository-layout/anchor/programs/carnival/src/state/food.rs deleted file mode 100644 index 62b5e04d6..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/state/food.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Objects - -pub struct FoodStand { - pub name: String, - pub food_type: String, - pub tickets: u32, -} - -impl FoodStand { - pub fn new(name: String, food_type: String, tickets: u32) -> FoodStand { - FoodStand { - name, - food_type, - tickets, - } - } -} - -pub fn get_food_stands() -> Vec { - vec![ - FoodStand::new("Larry's Pizza".to_string(), "pizza".to_string(), 3), - FoodStand::new("Taco Shack".to_string(), "taco".to_string(), 2), - FoodStand::new("Dough Boy's".to_string(), "fried dough".to_string(), 1), - ] -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/state/game.rs b/basics/repository-layout/anchor/programs/carnival/src/state/game.rs deleted file mode 100644 index 185547230..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/state/game.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Objects - -pub struct Game { - pub name: String, - pub tickets: u32, - pub tries: u32, - pub prize: String, -} - -const DEFAULT_TICKETS_TO_PLAY: u32 = 3; - -impl Game { - pub fn new(name: String, tries: u32, prize: String) -> Game { - Game { - name, - tickets: DEFAULT_TICKETS_TO_PLAY, - tries, - prize, - } - } -} - -pub fn get_games() -> Vec { - vec![ - Game::new("Ring Toss".to_string(), 5, "teddy bear".to_string()), - Game::new("I Got It!".to_string(), 12, "goldfish".to_string()), - Game::new("Ladder Climb".to_string(), 1, "popcorn bucket".to_string()), - ] -} diff --git a/basics/repository-layout/anchor/programs/carnival/src/state/mod.rs b/basics/repository-layout/anchor/programs/carnival/src/state/mod.rs deleted file mode 100644 index 12a92a2cf..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/state/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod food; -pub mod game; -pub mod ride; diff --git a/basics/repository-layout/anchor/programs/carnival/src/state/ride.rs b/basics/repository-layout/anchor/programs/carnival/src/state/ride.rs deleted file mode 100644 index a3af23969..000000000 --- a/basics/repository-layout/anchor/programs/carnival/src/state/ride.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Objects - -pub struct Ride { - pub name: String, - pub upside_down: bool, - pub tickets: u32, - pub min_height: u32, -} - -impl Ride { - pub fn new(name: String, upside_down: bool, tickets: u32, min_height: u32) -> Ride { - Ride { - name, - upside_down, - tickets, - min_height, - } - } -} - -pub fn get_rides() -> Vec { - vec![ - Ride::new("Tilt-a-Whirl".to_string(), false, 3, 48), - Ride::new("Scrambler".to_string(), false, 3, 48), - Ride::new("Ferris Wheel".to_string(), false, 5, 55), - Ride::new("Zero Gravity".to_string(), true, 5, 60), - ] -} diff --git a/basics/repository-layout/anchor/programs/carnival/tests/test_carnival.rs b/basics/repository-layout/anchor/programs/carnival/tests/test_carnival.rs deleted file mode 100644 index 19ffcd977..000000000 --- a/basics/repository-layout/anchor/programs/carnival/tests/test_carnival.rs +++ /dev/null @@ -1,168 +0,0 @@ -use { - anchor_lang::{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}, - anchor_v2_testing::{litesvm::LiteSVM, Signer}, - solana_kite::{create_wallet, send_transaction_from_instructions}, -}; - -fn setup() -> (LiteSVM, anchor_v2_testing::Keypair) { - let program_id = carnival::id(); - let mut svm = anchor_v2_testing::svm(); - let bytes = include_bytes!("../../../target/deploy/carnival.so"); - svm.add_program(program_id, bytes).unwrap(); - let payer = create_wallet(&mut svm, 10_000_000_000).unwrap(); - (svm, payer) -} - -fn go_on_ride_ix( - payer: &anchor_v2_testing::Keypair, - name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::GoOnRide { - name: name.to_string(), - height, - ticket_count, - ride_name: ride_name.to_string(), - } - .data(), - accounts, - ) -} - -fn play_game_ix( - payer: &anchor_v2_testing::Keypair, - name: &str, - ticket_count: u32, - game_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::PlayGame { - name: name.to_string(), - ticket_count, - game_name: game_name.to_string(), - } - .data(), - accounts, - ) -} - -fn eat_food_ix( - payer: &anchor_v2_testing::Keypair, - name: &str, - ticket_count: u32, - food_stand_name: &str, -) -> Instruction { - let accounts = carnival::accounts::CarnivalAccountConstraints { - payer: payer.pubkey(), - } - .to_account_metas(None); - Instruction::new_with_bytes( - carnival::id(), - &carnival::instruction::EatFood { - name: name.to_string(), - ticket_count, - food_stand_name: food_stand_name.to_string(), - } - .data(), - accounts, - ) -} - -// Riders that have enough tickets and meet the height requirement succeed. -#[test] -fn test_go_on_ride_succeeds() { - let (mut svm, payer) = setup(); - // Scrambler costs 3 tickets and requires 48" min height; Alice has 15 - // tickets and is 56" tall. - let ix = go_on_ride_ix(&payer, "Alice", 56, 15, "Scrambler"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -// Insufficient tickets must produce a real error (NotEnoughTickets), not a -// silent Ok(). -#[test] -fn test_go_on_ride_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Tilt-a-Whirl costs 3 tickets; Bob only has 1. - let ix = go_on_ride_ix(&payer, "Bob", 49, 1, "Tilt-a-Whirl"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "ride must reject insufficient tickets"); -} - -// A rider below the minimum height must be rejected with RiderTooShort. -#[test] -fn test_go_on_ride_rejects_too_short() { - let (mut svm, payer) = setup(); - // Ferris Wheel requires 55" min height; Jimmy is 36" tall but has plenty - // of tickets. - let ix = go_on_ride_ix(&payer, "Jimmy", 36, 15, "Ferris Wheel"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "ride must reject riders below min height"); -} - -// Unknown attraction name routes to ProgramError::InvalidInstructionData. -#[test] -fn test_go_on_ride_rejects_unknown_ride() { - let (mut svm, payer) = setup(); - let ix = go_on_ride_ix(&payer, "Alice", 60, 100, "Quantum Coaster"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "unknown ride must be rejected"); -} - -#[test] -fn test_play_game_succeeds() { - let (mut svm, payer) = setup(); - // Ring Toss costs 3 tickets; Alice has 15. - let ix = play_game_ix(&payer, "Alice", 15, "Ring Toss"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -#[test] -fn test_play_game_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Ring Toss costs 3 tickets; Mary only has 1. - let ix = play_game_ix(&payer, "Mary", 1, "Ring Toss"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!(result.is_err(), "game must reject insufficient tickets"); -} - -#[test] -fn test_eat_food_succeeds() { - let (mut svm, payer) = setup(); - // Dough Boy's costs 1 ticket; Bob has 6. - let ix = eat_food_ix(&payer, "Bob", 6, "Dough Boy's"); - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None) - .unwrap(); -} - -#[test] -fn test_eat_food_rejects_insufficient_tickets() { - let (mut svm, payer) = setup(); - // Larry's Pizza costs 3 tickets; Mary has 1. - let ix = eat_food_ix(&payer, "Mary", 1, "Larry's Pizza"); - let result = - send_transaction_from_instructions(&mut svm, vec![ix], &[&payer], &payer.pubkey(), None); - assert!( - result.is_err(), - "food stand must reject insufficient tickets" - ); -} diff --git a/basics/repository-layout/native/CHANGELOG.md b/basics/repository-layout/native/CHANGELOG.md deleted file mode 100644 index 4f1163881..000000000 --- a/basics/repository-layout/native/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## 2026-07-07 - -Added this changelog. Changes prior to this date were tracked in git history only. diff --git a/basics/repository-layout/native/program/Cargo.toml b/basics/repository-layout/native/program/Cargo.toml deleted file mode 100644 index 232bf4574..000000000 --- a/basics/repository-layout/native/program/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "repository-layout-program" -version = "0.1.0" -edition = "2021" - -[dependencies] -borsh = "1.6.1" -borsh-derive = "0.9.1" -solana-program = "4.0" - -[lib] -crate-type = ["cdylib", "lib"] - -[features] -custom-heap = [] -custom-panic = [] - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] } - -[dev-dependencies] -litesvm = "0.18.1" -solana-instruction = "3.0.0" -solana-keypair = "3.0.1" -solana-native-token = "3.0.0" -solana-pubkey = "4.3" -solana-transaction = "4.2" diff --git a/basics/repository-layout/native/program/src/error.rs b/basics/repository-layout/native/program/src/error.rs deleted file mode 100644 index 9db2fad45..000000000 --- a/basics/repository-layout/native/program/src/error.rs +++ /dev/null @@ -1 +0,0 @@ -// For any custom errors diff --git a/basics/repository-layout/native/program/src/instructions/eat_food.rs b/basics/repository-layout/native/program/src/instructions/eat_food.rs deleted file mode 100644 index b5b0cc2e5..000000000 --- a/basics/repository-layout/native/program/src/instructions/eat_food.rs +++ /dev/null @@ -1,36 +0,0 @@ -use solana_program::{entrypoint::ProgramResult, msg, program_error::ProgramError}; - -use crate::state::food; - -// InstructionData Data - -pub struct EatFoodInstructionData { - pub eater_name: String, - pub eater_ticket_count: u32, - pub food_stand: String, -} - -pub fn eat_food(ix: EatFoodInstructionData) -> ProgramResult { - let food_stands_list = food::get_food_stands(); - - for food_stand in food_stands_list.iter() { - if ix.food_stand.eq(&food_stand.name) { - msg!("Welcome to {}! What can I get you?", food_stand.name); - - if ix.eater_ticket_count < food_stand.tickets { - msg!( - " Sorry {}, our {} is {} tickets!", - ix.eater_name, - food_stand.food_type, - food_stand.tickets - ); - } else { - msg!(" Enjoy your {}!", food_stand.food_type); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/native/program/src/instructions/get_on_ride.rs b/basics/repository-layout/native/program/src/instructions/get_on_ride.rs deleted file mode 100644 index 638d5d294..000000000 --- a/basics/repository-layout/native/program/src/instructions/get_on_ride.rs +++ /dev/null @@ -1,52 +0,0 @@ -use solana_program::{entrypoint::ProgramResult, msg, program_error::ProgramError}; - -use crate::state::ride; - -// InstructionData Data - -pub struct GetOnRideInstructionData { - pub rider_name: String, - pub rider_height: u32, - pub rider_ticket_count: u32, - pub ride: String, -} - -pub fn get_on_ride(ix: GetOnRideInstructionData) -> ProgramResult { - let rides_list = ride::get_rides(); - - for ride in rides_list.iter() { - if ix.ride.eq(&ride.name) { - msg!("You're about to ride the {}!", ride.name); - - if ix.rider_ticket_count < ride.tickets { - msg!( - " Sorry {}, you need {} tickets to ride the {}!", - ix.rider_name, - ride.tickets, - ride.name - ); - return Ok(()); - }; - - if ix.rider_height < ride.min_height { - msg!( - " Sorry {}, you need to be {}\" tall to ride the {}!", - ix.rider_name, - ride.min_height, - ride.name - ); - return Ok(()); - }; - - msg!(" Welcome aboard the {}!", ride.name); - - if ride.upside_down { - msg!(" Btw, this ride goes upside down. Hold on tight!"); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/native/program/src/instructions/mod.rs b/basics/repository-layout/native/program/src/instructions/mod.rs deleted file mode 100644 index ee6ea6d1e..000000000 --- a/basics/repository-layout/native/program/src/instructions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod eat_food; -pub mod get_on_ride; -pub mod play_game; diff --git a/basics/repository-layout/native/program/src/instructions/play_game.rs b/basics/repository-layout/native/program/src/instructions/play_game.rs deleted file mode 100644 index d78712bed..000000000 --- a/basics/repository-layout/native/program/src/instructions/play_game.rs +++ /dev/null @@ -1,41 +0,0 @@ -use solana_program::{entrypoint::ProgramResult, msg, program_error::ProgramError}; - -use crate::state::game; - -// InstructionData Data - -pub struct PlayGameInstructionData { - pub gamer_name: String, - pub gamer_ticket_count: u32, - pub game: String, -} - -pub fn play_game(ix: PlayGameInstructionData) -> ProgramResult { - let games_list = game::get_games(); - - for game in games_list.iter() { - if ix.game.eq(&game.name) { - msg!("You're about to play {}!", game.name); - - if ix.gamer_ticket_count < game.tickets { - msg!( - " Sorry {}, you need {} tickets to play {}!", - ix.gamer_name, - game.tickets, - game.name - ); - } else { - msg!(" Let's see what you got!"); - msg!( - " You get {} attempts and the prize is a {}!", - game.tries, - game.prize - ); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/native/program/src/lib.rs b/basics/repository-layout/native/program/src/lib.rs deleted file mode 100644 index f01f0620e..000000000 --- a/basics/repository-layout/native/program/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -// For setting up modules & configs - -pub mod error; -pub mod instructions; -pub mod processor; -pub mod state; diff --git a/basics/repository-layout/native/program/src/processor.rs b/basics/repository-layout/native/program/src/processor.rs deleted file mode 100644 index ae36137b4..000000000 --- a/basics/repository-layout/native/program/src/processor.rs +++ /dev/null @@ -1,50 +0,0 @@ -use borsh::{BorshDeserialize, BorshSerialize}; -use solana_program::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, -}; - -use crate::instructions::{eat_food, get_on_ride, play_game}; - -// For processing everything at the entrypoint - -entrypoint!(process_instruction); - -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub struct CarnivalInstructionData { - pub name: String, - pub height: u32, - pub ticket_count: u32, - pub attraction: String, - pub attraction_name: String, -} - -pub fn process_instruction( - _program_id: &Pubkey, - _accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - let ix_data_object = CarnivalInstructionData::try_from_slice(instruction_data)?; - - msg!("Welcome to the carnival, {}!", ix_data_object.name); - - match ix_data_object.attraction.as_str() { - "ride" => get_on_ride::get_on_ride(get_on_ride::GetOnRideInstructionData { - rider_name: ix_data_object.name, - rider_height: ix_data_object.height, - rider_ticket_count: ix_data_object.ticket_count, - ride: ix_data_object.attraction_name, - }), - "game" => play_game::play_game(play_game::PlayGameInstructionData { - gamer_name: ix_data_object.name, - gamer_ticket_count: ix_data_object.ticket_count, - game: ix_data_object.attraction_name, - }), - "food" => eat_food::eat_food(eat_food::EatFoodInstructionData { - eater_name: ix_data_object.name, - eater_ticket_count: ix_data_object.ticket_count, - food_stand: ix_data_object.attraction_name, - }), - _ => Err(ProgramError::InvalidInstructionData), - } -} diff --git a/basics/repository-layout/native/program/src/state/food.rs b/basics/repository-layout/native/program/src/state/food.rs deleted file mode 100644 index 62b5e04d6..000000000 --- a/basics/repository-layout/native/program/src/state/food.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Objects - -pub struct FoodStand { - pub name: String, - pub food_type: String, - pub tickets: u32, -} - -impl FoodStand { - pub fn new(name: String, food_type: String, tickets: u32) -> FoodStand { - FoodStand { - name, - food_type, - tickets, - } - } -} - -pub fn get_food_stands() -> Vec { - vec![ - FoodStand::new("Larry's Pizza".to_string(), "pizza".to_string(), 3), - FoodStand::new("Taco Shack".to_string(), "taco".to_string(), 2), - FoodStand::new("Dough Boy's".to_string(), "fried dough".to_string(), 1), - ] -} diff --git a/basics/repository-layout/native/program/src/state/game.rs b/basics/repository-layout/native/program/src/state/game.rs deleted file mode 100644 index 185547230..000000000 --- a/basics/repository-layout/native/program/src/state/game.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Objects - -pub struct Game { - pub name: String, - pub tickets: u32, - pub tries: u32, - pub prize: String, -} - -const DEFAULT_TICKETS_TO_PLAY: u32 = 3; - -impl Game { - pub fn new(name: String, tries: u32, prize: String) -> Game { - Game { - name, - tickets: DEFAULT_TICKETS_TO_PLAY, - tries, - prize, - } - } -} - -pub fn get_games() -> Vec { - vec![ - Game::new("Ring Toss".to_string(), 5, "teddy bear".to_string()), - Game::new("I Got It!".to_string(), 12, "goldfish".to_string()), - Game::new("Ladder Climb".to_string(), 1, "popcorn bucket".to_string()), - ] -} diff --git a/basics/repository-layout/native/program/src/state/mod.rs b/basics/repository-layout/native/program/src/state/mod.rs deleted file mode 100644 index 12a92a2cf..000000000 --- a/basics/repository-layout/native/program/src/state/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod food; -pub mod game; -pub mod ride; diff --git a/basics/repository-layout/native/program/src/state/ride.rs b/basics/repository-layout/native/program/src/state/ride.rs deleted file mode 100644 index a3af23969..000000000 --- a/basics/repository-layout/native/program/src/state/ride.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Objects - -pub struct Ride { - pub name: String, - pub upside_down: bool, - pub tickets: u32, - pub min_height: u32, -} - -impl Ride { - pub fn new(name: String, upside_down: bool, tickets: u32, min_height: u32) -> Ride { - Ride { - name, - upside_down, - tickets, - min_height, - } - } -} - -pub fn get_rides() -> Vec { - vec![ - Ride::new("Tilt-a-Whirl".to_string(), false, 3, 48), - Ride::new("Scrambler".to_string(), false, 3, 48), - Ride::new("Ferris Wheel".to_string(), false, 5, 55), - Ride::new("Zero Gravity".to_string(), true, 5, 60), - ] -} diff --git a/basics/repository-layout/native/program/tests/test.rs b/basics/repository-layout/native/program/tests/test.rs deleted file mode 100644 index e75b5d8e7..000000000 --- a/basics/repository-layout/native/program/tests/test.rs +++ /dev/null @@ -1,97 +0,0 @@ -use litesvm::LiteSVM; - -use repository_layout_program::processor::CarnivalInstructionData; -use solana_instruction::{AccountMeta, Instruction}; -use solana_keypair::{Keypair, Signer}; -use solana_program::native_token::LAMPORTS_PER_SOL; -use solana_pubkey::Pubkey; -use solana_transaction::Transaction; - -#[test] -fn test_repo_layout() { - let mut svm = LiteSVM::new(); - - let program_id = Pubkey::new_unique(); - // The .so is built into the workspace target/deploy by - // `cargo build-sbf --manifest-path=./program/Cargo.toml` (run from the project - // root). Rebuild after every program change: the binary is embedded at - // test-compile time, so a stale .so silently tests old code. - let program_bytes = include_bytes!("../../../../../target/deploy/repository_layout_program.so"); - - svm.add_program(program_id, program_bytes).unwrap(); - - let payer = Keypair::new(); - svm.airdrop(&payer.pubkey(), LAMPORTS_PER_SOL * 10).unwrap(); - - let data = borsh::to_vec(&CarnivalInstructionData { - name: "Jimmy".to_string(), - height: 36, - ticket_count: 15, - attraction: "ride".to_string(), - attraction_name: "Scrambler".to_string(), - }) - .unwrap(); - - let ix = Instruction { - program_id, - accounts: vec![AccountMeta::new(payer.pubkey(), true)], - data, - }; - - let tx = Transaction::new_signed_with_payer( - &[ix], - Some(&payer.pubkey()), - &[&payer], - svm.latest_blockhash(), - ); - - assert!(svm.send_transaction(tx).is_ok()); - - let data = borsh::to_vec(&CarnivalInstructionData { - name: "Jimmy".to_string(), - height: 36, - ticket_count: 15, - attraction: "game".to_string(), - attraction_name: "I Got It!".to_string(), - }) - .unwrap(); - - let ix = Instruction { - program_id, - accounts: vec![AccountMeta::new(payer.pubkey(), true)], - data, - }; - - let tx = Transaction::new_signed_with_payer( - &[ix], - Some(&payer.pubkey()), - &[&payer], - svm.latest_blockhash(), - ); - - assert!(svm.send_transaction(tx).is_ok()); - - let data = borsh::to_vec(&CarnivalInstructionData { - name: "Jimmy".to_string(), - height: 36, - ticket_count: 15, - attraction: "food".to_string(), - attraction_name: "Taco Shack".to_string(), - }) - .unwrap(); - - let ix = Instruction { - program_id, - accounts: vec![AccountMeta::new(payer.pubkey(), true)], - data, - }; - - let tx = Transaction::new_signed_with_payer( - &[ix], - Some(&payer.pubkey()), - &[&payer], - svm.latest_blockhash(), - ); - - assert!(svm.send_transaction(tx).is_ok()); -} diff --git a/basics/repository-layout/pinocchio/CHANGELOG.md b/basics/repository-layout/pinocchio/CHANGELOG.md deleted file mode 100644 index 4f1163881..000000000 --- a/basics/repository-layout/pinocchio/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - -## 2026-07-07 - -Added this changelog. Changes prior to this date were tracked in git history only. diff --git a/basics/repository-layout/pinocchio/README.md b/basics/repository-layout/pinocchio/README.md deleted file mode 100644 index df9728ab9..000000000 --- a/basics/repository-layout/pinocchio/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Recommended Program Layout: Solana Pinocchio - -The recommended multi-file layout for a Solana [program](https://solana.com/docs/terminology#program), written using the [Pinocchio](https://github.com/anza-xyz/pinocchio) framework with only the Solana toolchain. - -The `src` folder splits responsibilities the same way the `native` and `anchor` examples do: - -- `lib.rs` - module declarations and the program entrypoint -- `processor.rs` - decodes instruction data and dispatches to the right instruction -- `instructions/` - one file per instruction (`get_on_ride`, `play_game`, `eat_food`) -- `state/` - the program's data objects (`ride`, `game`, `food`) -- `error.rs` - custom errors - -## Setup - -1. Build the [program](https://solana.com/docs/terminology#program): `cargo build-sbf --manifest-path=./program/Cargo.toml` -2. Run the Rust + LiteSVM tests: `cargo test --manifest-path=./program/Cargo.toml` - -Rebuild the program after every change before re-running the tests: the tests embed the `.so` at compile time, so a stale binary silently tests old code. - -## Credits - -Ported from the [Pinocchio repository-layout example](https://github.com/solana-developers/program-examples/pull/582) contributed by [@MarkFeder](https://github.com/MarkFeder) to solana-developers/program-examples. diff --git a/basics/repository-layout/pinocchio/program/Cargo.toml b/basics/repository-layout/pinocchio/program/Cargo.toml deleted file mode 100644 index f926f1ef7..000000000 --- a/basics/repository-layout/pinocchio/program/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "repository-layout-pinocchio-program" -version = "0.1.0" -edition = "2021" - -[dependencies] -pinocchio.workspace = true -pinocchio-log.workspace = true - -[lib] -crate-type = ["cdylib", "lib"] - -[features] -custom-heap = [] -custom-panic = [] - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] } - -[dev-dependencies] -litesvm = "0.18.1" -solana-instruction = "3.0.0" -solana-keypair = "3.0.1" -solana-pubkey = "4.3" -solana-transaction = "4.2" -solana-native-token = "3.0.0" diff --git a/basics/repository-layout/pinocchio/program/src/error.rs b/basics/repository-layout/pinocchio/program/src/error.rs deleted file mode 100644 index 9db2fad45..000000000 --- a/basics/repository-layout/pinocchio/program/src/error.rs +++ /dev/null @@ -1 +0,0 @@ -// For any custom errors diff --git a/basics/repository-layout/pinocchio/program/src/instructions/eat_food.rs b/basics/repository-layout/pinocchio/program/src/instructions/eat_food.rs deleted file mode 100644 index f435e8281..000000000 --- a/basics/repository-layout/pinocchio/program/src/instructions/eat_food.rs +++ /dev/null @@ -1,35 +0,0 @@ -use pinocchio::{error::ProgramError, ProgramResult}; -use pinocchio_log::log; - -use crate::state::food; - -// InstructionData Data - -pub struct EatFoodInstructionData<'a> { - pub eater_name: &'a str, - pub eater_ticket_count: u32, - pub food_stand: &'a str, -} - -pub fn eat_food(ix: EatFoodInstructionData) -> ProgramResult { - for food_stand in food::FOOD_STANDS.iter() { - if ix.food_stand == food_stand.name { - log!("Welcome to {}! What can I get you?", food_stand.name); - - if ix.eater_ticket_count < food_stand.tickets { - log!( - " Sorry {}, our {} is {} tickets!", - ix.eater_name, - food_stand.food_type, - food_stand.tickets - ); - } else { - log!(" Enjoy your {}!", food_stand.food_type); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/pinocchio/program/src/instructions/get_on_ride.rs b/basics/repository-layout/pinocchio/program/src/instructions/get_on_ride.rs deleted file mode 100644 index a8946eabf..000000000 --- a/basics/repository-layout/pinocchio/program/src/instructions/get_on_ride.rs +++ /dev/null @@ -1,51 +0,0 @@ -use pinocchio::{error::ProgramError, ProgramResult}; -use pinocchio_log::log; - -use crate::state::ride; - -// InstructionData Data - -pub struct GetOnRideInstructionData<'a> { - pub rider_name: &'a str, - pub rider_height: u32, - pub rider_ticket_count: u32, - pub ride: &'a str, -} - -pub fn get_on_ride(ix: GetOnRideInstructionData) -> ProgramResult { - for ride in ride::RIDES.iter() { - if ix.ride == ride.name { - log!("You're about to ride the {}!", ride.name); - - if ix.rider_ticket_count < ride.tickets { - log!( - " Sorry {}, you need {} tickets to ride the {}!", - ix.rider_name, - ride.tickets, - ride.name - ); - return Ok(()); - }; - - if ix.rider_height < ride.min_height { - log!( - " Sorry {}, you need to be {} tall to ride the {}!", - ix.rider_name, - ride.min_height, - ride.name - ); - return Ok(()); - }; - - log!(" Welcome aboard the {}!", ride.name); - - if ride.upside_down { - log!(" Btw, this ride goes upside down. Hold on tight!"); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/pinocchio/program/src/instructions/mod.rs b/basics/repository-layout/pinocchio/program/src/instructions/mod.rs deleted file mode 100644 index ee6ea6d1e..000000000 --- a/basics/repository-layout/pinocchio/program/src/instructions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod eat_food; -pub mod get_on_ride; -pub mod play_game; diff --git a/basics/repository-layout/pinocchio/program/src/instructions/play_game.rs b/basics/repository-layout/pinocchio/program/src/instructions/play_game.rs deleted file mode 100644 index 132fb09f6..000000000 --- a/basics/repository-layout/pinocchio/program/src/instructions/play_game.rs +++ /dev/null @@ -1,40 +0,0 @@ -use pinocchio::{error::ProgramError, ProgramResult}; -use pinocchio_log::log; - -use crate::state::game; - -// InstructionData Data - -pub struct PlayGameInstructionData<'a> { - pub gamer_name: &'a str, - pub gamer_ticket_count: u32, - pub game: &'a str, -} - -pub fn play_game(ix: PlayGameInstructionData) -> ProgramResult { - for game in game::GAMES.iter() { - if ix.game == game.name { - log!("You're about to play {}!", game.name); - - if ix.gamer_ticket_count < game.tickets { - log!( - " Sorry {}, you need {} tickets to play {}!", - ix.gamer_name, - game.tickets, - game.name - ); - } else { - log!(" Let's see what you got!"); - log!( - " You get {} attempts and the prize is a {}!", - game.tries, - game.prize - ); - }; - - return Ok(()); - } - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/pinocchio/program/src/lib.rs b/basics/repository-layout/pinocchio/program/src/lib.rs deleted file mode 100644 index adf936c09..000000000 --- a/basics/repository-layout/pinocchio/program/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_std] - -// For setting up modules & configs - -pub mod error; -pub mod instructions; -pub mod processor; -pub mod state; - -use pinocchio::{entrypoint, nostd_panic_handler}; - -entrypoint!(processor::process_instruction); -nostd_panic_handler!(); diff --git a/basics/repository-layout/pinocchio/program/src/processor.rs b/basics/repository-layout/pinocchio/program/src/processor.rs deleted file mode 100644 index 2529de207..000000000 --- a/basics/repository-layout/pinocchio/program/src/processor.rs +++ /dev/null @@ -1,76 +0,0 @@ -use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult}; -use pinocchio_log::log; - -use crate::instructions::{eat_food, get_on_ride, play_game}; - -// For processing everything at the entrypoint -// -// Instruction data layout (matches the native borsh layout): -// - name: u32 LE length + utf-8 bytes -// - height: u32 LE -// - ticket_count: u32 LE -// - attraction: u32 LE length + utf-8 bytes ("ride" | "game" | "food") -// - attraction_name: u32 LE length + utf-8 bytes - -pub fn process_instruction( - _program_id: &Address, - _accounts: &[AccountView], - instruction_data: &[u8], -) -> ProgramResult { - let mut cursor = 0; - let name = read_str(instruction_data, &mut cursor)?; - let height = read_u32(instruction_data, &mut cursor)?; - let ticket_count = read_u32(instruction_data, &mut cursor)?; - let attraction = read_str(instruction_data, &mut cursor)?; - let attraction_name = read_str(instruction_data, &mut cursor)?; - - log!("Welcome to the carnival, {}!", name); - - match attraction { - "ride" => get_on_ride::get_on_ride(get_on_ride::GetOnRideInstructionData { - rider_name: name, - rider_height: height, - rider_ticket_count: ticket_count, - ride: attraction_name, - }), - "game" => play_game::play_game(play_game::PlayGameInstructionData { - gamer_name: name, - gamer_ticket_count: ticket_count, - game: attraction_name, - }), - "food" => eat_food::eat_food(eat_food::EatFoodInstructionData { - eater_name: name, - eater_ticket_count: ticket_count, - food_stand: attraction_name, - }), - _ => Err(ProgramError::InvalidInstructionData), - } -} - -fn read_u32(data: &[u8], cursor: &mut usize) -> Result { - let end = cursor - .checked_add(4) - .ok_or(ProgramError::InvalidInstructionData)?; - if end > data.len() { - return Err(ProgramError::InvalidInstructionData); - } - let bytes: [u8; 4] = data[*cursor..end] - .try_into() - .map_err(|_| ProgramError::InvalidInstructionData)?; - *cursor = end; - Ok(u32::from_le_bytes(bytes)) -} - -fn read_str<'a>(data: &'a [u8], cursor: &mut usize) -> Result<&'a str, ProgramError> { - let len = read_u32(data, cursor)? as usize; - let end = cursor - .checked_add(len) - .ok_or(ProgramError::InvalidInstructionData)?; - if end > data.len() { - return Err(ProgramError::InvalidInstructionData); - } - let s = core::str::from_utf8(&data[*cursor..end]) - .map_err(|_| ProgramError::InvalidInstructionData)?; - *cursor = end; - Ok(s) -} diff --git a/basics/repository-layout/pinocchio/program/src/state/food.rs b/basics/repository-layout/pinocchio/program/src/state/food.rs deleted file mode 100644 index ecf1313f7..000000000 --- a/basics/repository-layout/pinocchio/program/src/state/food.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Objects - -pub struct FoodStand { - pub name: &'static str, - pub food_type: &'static str, - pub tickets: u32, -} - -pub const FOOD_STANDS: &[FoodStand] = &[ - FoodStand { - name: "Larry's Pizza", - food_type: "pizza", - tickets: 3, - }, - FoodStand { - name: "Taco Shack", - food_type: "taco", - tickets: 2, - }, - FoodStand { - name: "Dough Boy's", - food_type: "fried dough", - tickets: 1, - }, -]; diff --git a/basics/repository-layout/pinocchio/program/src/state/game.rs b/basics/repository-layout/pinocchio/program/src/state/game.rs deleted file mode 100644 index 2f1e6423f..000000000 --- a/basics/repository-layout/pinocchio/program/src/state/game.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Objects - -pub struct Game { - pub name: &'static str, - pub tickets: u32, - pub tries: u32, - pub prize: &'static str, -} - -const DEFAULT_TICKETS_TO_PLAY: u32 = 3; - -pub const GAMES: &[Game] = &[ - Game { - name: "Ring Toss", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 5, - prize: "teddy bear", - }, - Game { - name: "I Got It!", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 12, - prize: "goldfish", - }, - Game { - name: "Ladder Climb", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 1, - prize: "popcorn bucket", - }, -]; diff --git a/basics/repository-layout/pinocchio/program/src/state/mod.rs b/basics/repository-layout/pinocchio/program/src/state/mod.rs deleted file mode 100644 index 12a92a2cf..000000000 --- a/basics/repository-layout/pinocchio/program/src/state/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod food; -pub mod game; -pub mod ride; diff --git a/basics/repository-layout/pinocchio/program/src/state/ride.rs b/basics/repository-layout/pinocchio/program/src/state/ride.rs deleted file mode 100644 index 2746d80e1..000000000 --- a/basics/repository-layout/pinocchio/program/src/state/ride.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Objects - -pub struct Ride { - pub name: &'static str, - pub upside_down: bool, - pub tickets: u32, - pub min_height: u32, -} - -pub const RIDES: &[Ride] = &[ - Ride { - name: "Tilt-a-Whirl", - upside_down: false, - tickets: 3, - min_height: 48, - }, - Ride { - name: "Scrambler", - upside_down: false, - tickets: 3, - min_height: 48, - }, - Ride { - name: "Ferris Wheel", - upside_down: false, - tickets: 5, - min_height: 55, - }, - Ride { - name: "Zero Gravity", - upside_down: true, - tickets: 5, - min_height: 60, - }, -]; diff --git a/basics/repository-layout/pinocchio/program/tests/test.rs b/basics/repository-layout/pinocchio/program/tests/test.rs deleted file mode 100644 index bf10fd808..000000000 --- a/basics/repository-layout/pinocchio/program/tests/test.rs +++ /dev/null @@ -1,122 +0,0 @@ -use litesvm::LiteSVM; -use solana_instruction::{AccountMeta, Instruction}; -use solana_keypair::{Keypair, Signer}; -use solana_native_token::LAMPORTS_PER_SOL; -use solana_pubkey::Pubkey; -use solana_transaction::Transaction; - -// The .so is built into the workspace target/deploy by -// `cargo build-sbf --manifest-path=./program/Cargo.toml` (run from the project -// root). Rebuild after every program change: the binary is embedded at -// test-compile time, so a stale .so silently tests old code. -const PROGRAM_SO: &[u8] = - include_bytes!("../../../../../target/deploy/repository_layout_pinocchio_program.so"); - -// Builds the carnival instruction data in the wire format the program decodes: -// name (str), height (u32), ticket_count (u32), attraction (str), attraction_name (str) -// where each str is a u32 LE length followed by its utf-8 bytes. -fn carnival_ix_data( - name: &str, - height: u32, - ticket_count: u32, - attraction: &str, - attraction_name: &str, -) -> Vec { - fn push_str(buf: &mut Vec, s: &str) { - buf.extend_from_slice(&(s.len() as u32).to_le_bytes()); - buf.extend_from_slice(s.as_bytes()); - } - - let mut data = Vec::new(); - push_str(&mut data, name); - data.extend_from_slice(&height.to_le_bytes()); - data.extend_from_slice(&ticket_count.to_le_bytes()); - push_str(&mut data, attraction); - push_str(&mut data, attraction_name); - data -} - -fn setup() -> (LiteSVM, Pubkey, Keypair) { - let program_id = Pubkey::new_unique(); - - let mut svm = LiteSVM::new(); - svm.add_program(program_id, PROGRAM_SO).unwrap(); - - let payer = Keypair::new(); - svm.airdrop(&payer.pubkey(), LAMPORTS_PER_SOL * 10).unwrap(); - - (svm, program_id, payer) -} - -// The program ignores accounts entirely, so a single signer is all we need. -fn send_carnival(svm: &mut LiteSVM, program_id: Pubkey, payer: &Keypair, data: Vec) -> bool { - let ix = Instruction { - program_id, - accounts: vec![AccountMeta::new(payer.pubkey(), true)], - data, - }; - let tx = Transaction::new_signed_with_payer( - &[ix], - Some(&payer.pubkey()), - &[payer], - svm.latest_blockhash(), - ); - svm.send_transaction(tx).is_ok() -} - -#[test] -fn test_go_on_a_ride() { - let (mut svm, program_id, payer) = setup(); - assert!(send_carnival( - &mut svm, - program_id, - &payer, - carnival_ix_data("Alice", 56, 15, "ride", "Scrambler") - )); -} - -#[test] -fn test_play_a_game() { - let (mut svm, program_id, payer) = setup(); - assert!(send_carnival( - &mut svm, - program_id, - &payer, - carnival_ix_data("Bob", 49, 6, "game", "Ring Toss") - )); -} - -#[test] -fn test_eat_some_food() { - let (mut svm, program_id, payer) = setup(); - assert!(send_carnival( - &mut svm, - program_id, - &payer, - carnival_ix_data("Mary", 52, 3, "food", "Taco Shack") - )); -} - -#[test] -fn test_unknown_attraction_name_fails() { - let (mut svm, program_id, payer) = setup(); - // "ride" is a valid attraction type, but there is no ride by this name, so the - // program falls through its lookup table and returns InvalidInstructionData. - assert!(!send_carnival( - &mut svm, - program_id, - &payer, - carnival_ix_data("Jimmy", 40, 5, "ride", "Roller Coaster") - )); -} - -#[test] -fn test_unknown_attraction_type_fails() { - let (mut svm, program_id, payer) = setup(); - assert!(!send_carnival( - &mut svm, - program_id, - &payer, - carnival_ix_data("Jimmy", 40, 5, "spaceship", "Apollo") - )); -} diff --git a/basics/repository-layout/quasar/.gitignore b/basics/repository-layout/quasar/.gitignore deleted file mode 100644 index 262226bcb..000000000 --- a/basics/repository-layout/quasar/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Build artifacts -/target - -# Dependencies -node_modules - -# Environment -.env -.env.* - -# OS -.DS_Store diff --git a/basics/repository-layout/quasar/CHANGELOG.md b/basics/repository-layout/quasar/CHANGELOG.md deleted file mode 100644 index fd1f535cc..000000000 --- a/basics/repository-layout/quasar/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# Changelog - -## [2026-07-22] - -### Changed - -- Migrated to Quasar 0.1.0 (`0.1.0-release` branch, rev `be60fca`): Quasar.toml - rewritten to the 0.1.0 schema, `idl-build` feature and `lib` crate-type added, - and tests rewritten from the direct QuasarSVM harness to `quasar-test` - (`#[quasar_test]` fixtures, `crate::cpi` instruction builders, `Outcome` - assertions). The `quasar-svm` git dev-dependency is gone; the hand-built - wire-format instruction encoding in tests was replaced by the generated - `GoOnRide`/`PlayGame`/`EatFood` instruction builders. - -## 2026-07-07 - -Added this changelog. Changes prior to this date were tracked in git history only. diff --git a/basics/repository-layout/quasar/Cargo.toml b/basics/repository-layout/quasar/Cargo.toml deleted file mode 100644 index 7767f26fc..000000000 --- a/basics/repository-layout/quasar/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "quasar-carnival" -version = "0.1.0" -edition = "2021" - -# Standalone workspace - not part of the root program-examples workspace. -# Quasar uses a different resolver and dependency tree. -[workspace] - -[lints.rust.unexpected_cfgs] -level = "warn" -check-cfg = [ - 'cfg(target_os, values("solana"))', -] - -[lib] -# "lib" is required alongside "cdylib": the 0.1.0 IDL build compiles the -# crate as a host library. -crate-type = ["cdylib", "lib"] - -[features] -alloc = [] -client = [] -debug = [] -idl-build = ["quasar-lang/idl-build"] - -[dependencies] -# Quasar 0.1.0-release, pinned by rev: crates.io still hosts 0.0.0 -# placeholders for quasar-lang/quasar-cli, so the release line installs from -# git. Keep this rev in lockstep with the CLI rev installed by -# .github/workflows/quasar.yml. -quasar-lang = { git = "https://github.com/blueshift-gg/quasar", rev = "be60fca" } -# Pinned: quasar-lang asks for zeropod "0.3.3" and wincode 0.4, but zeropod -# 0.3.4 moved to wincode 0.5, so a fresh resolve (no lockfile is committed) -# splits the graph across two wincode versions and every Pod* trait bound -# fails. Unpin when quasar-lang's pinned rev accepts zeropod 0.3.4+. -zeropod = "=0.3.3" -solana-instruction = { version = "3.2.0" } - -[dev-dependencies] -quasar-test = { git = "https://github.com/blueshift-gg/quasar", rev = "be60fca" } diff --git a/basics/repository-layout/quasar/Quasar.toml b/basics/repository-layout/quasar/Quasar.toml deleted file mode 100644 index 2cd6f148a..000000000 --- a/basics/repository-layout/quasar/Quasar.toml +++ /dev/null @@ -1,9 +0,0 @@ -[project] -name = "quasar-carnival" - -[testing] -command = { program = "cargo", args = ["test"] } - -[clients] -path = "target/client" -targets = ["rust"] diff --git a/basics/repository-layout/quasar/README.md b/basics/repository-layout/quasar/README.md deleted file mode 100644 index af19e1874..000000000 --- a/basics/repository-layout/quasar/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Repository Layout (Quasar) - -Organize a program across modules (state, handlers, errors). - -See also: [Repository Layout overview](../README.md) and the [repository catalog](../../../README.md). - -## Major concepts - -- Multi-file layout -- Separation of concerns - -## Setup - -From `basics/repository-layout/quasar/`: - -```bash -quasar build -``` - -Prerequisites: [Quasar](https://quasar-lang.com/docs) CLI and [Agave](https://docs.anza.xyz/) toolchain (see `Quasar.toml`). - -## Testing - -In-process tests via **Quasar SVM** (`quasar-svm` in `Quasar.toml`): - -```bash -cargo test -``` - -Tests invoke instruction handlers and assert onchain state. No local validator. - -## Usage - -Read `src/` and `Quasar.toml`. Compare with the [Anchor](../anchor/) variant in the same example where present. diff --git a/basics/repository-layout/quasar/src/instructions/carnival_context.rs b/basics/repository-layout/quasar/src/instructions/carnival_context.rs deleted file mode 100644 index c990a8c79..000000000 --- a/basics/repository-layout/quasar/src/instructions/carnival_context.rs +++ /dev/null @@ -1,42 +0,0 @@ -use quasar_lang::prelude::*; - -use super::{eat_food, get_on_ride, play_game}; - -/// Minimal accounts context - a signer submits the transaction. -/// The instructions just process instruction data (no onchain state). -#[derive(Accounts)] -pub struct CarnivalAccountConstraints { - #[allow(dead_code)] - pub payer: Signer, -} - -#[inline(always)] -pub fn handle_go_on_ride( - _accounts: &mut CarnivalAccountConstraints, - name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, -) -> Result<(), ProgramError> { - get_on_ride::get_on_ride(name, height, ticket_count, ride_name) -} - -#[inline(always)] -pub fn handle_play_game( - _accounts: &mut CarnivalAccountConstraints, - name: &str, - ticket_count: u32, - game_name: &str, -) -> Result<(), ProgramError> { - play_game::play_game(name, ticket_count, game_name) -} - -#[inline(always)] -pub fn handle_eat_food( - _accounts: &mut CarnivalAccountConstraints, - name: &str, - ticket_count: u32, - food_stand_name: &str, -) -> Result<(), ProgramError> { - eat_food::eat_food(name, ticket_count, food_stand_name) -} diff --git a/basics/repository-layout/quasar/src/instructions/eat_food.rs b/basics/repository-layout/quasar/src/instructions/eat_food.rs deleted file mode 100644 index 5fd0628a6..000000000 --- a/basics/repository-layout/quasar/src/instructions/eat_food.rs +++ /dev/null @@ -1,26 +0,0 @@ -use quasar_lang::prelude::*; - -use crate::state::food; - -/// Validate food stand ticket requirements and log the result. -pub fn eat_food(_name: &str, ticket_count: u32, food_stand_name: &str) -> Result<(), ProgramError> { - let stands = food::get_food_stands(); - - let mut i = 0; - while i < stands.len() { - if food::food_stand_name_matches(&stands[i], food_stand_name) { - log("Welcome to the food stand!"); - - if ticket_count < stands[i].tickets { - log("Sorry, you don't have enough tickets for this food!"); - } else { - log("Enjoy your food!"); - } - - return Ok(()); - } - i += 1; - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/quasar/src/instructions/get_on_ride.rs b/basics/repository-layout/quasar/src/instructions/get_on_ride.rs deleted file mode 100644 index a817e63ce..000000000 --- a/basics/repository-layout/quasar/src/instructions/get_on_ride.rs +++ /dev/null @@ -1,43 +0,0 @@ -use quasar_lang::prelude::*; - -use crate::state::ride; - -/// Validate rider requirements and log the result. -/// Quasar's `log()` takes &str - no format! in no_std - so we use static -/// messages matching the Anchor version's logic without string interpolation. -pub fn get_on_ride( - _name: &str, - height: u32, - ticket_count: u32, - ride_name: &str, -) -> Result<(), ProgramError> { - let rides = ride::get_rides(); - - let mut i = 0; - while i < rides.len() { - if ride::ride_name_matches(&rides[i], ride_name) { - log("You're about to go on a ride!"); - - if ticket_count < rides[i].tickets { - log("Sorry, you don't have enough tickets for this ride!"); - return Ok(()); - } - - if height < rides[i].min_height { - log("Sorry, you're not tall enough for this ride!"); - return Ok(()); - } - - log("Welcome aboard the ride!"); - - if rides[i].upside_down { - log("This ride goes upside down. Hold on tight!"); - } - - return Ok(()); - } - i += 1; - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/quasar/src/instructions/mod.rs b/basics/repository-layout/quasar/src/instructions/mod.rs deleted file mode 100644 index 8ec137d67..000000000 --- a/basics/repository-layout/quasar/src/instructions/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod carnival_context; -pub mod eat_food; -pub mod get_on_ride; -pub mod play_game; - -pub use carnival_context::*; diff --git a/basics/repository-layout/quasar/src/instructions/play_game.rs b/basics/repository-layout/quasar/src/instructions/play_game.rs deleted file mode 100644 index aec74f533..000000000 --- a/basics/repository-layout/quasar/src/instructions/play_game.rs +++ /dev/null @@ -1,27 +0,0 @@ -use quasar_lang::prelude::*; - -use crate::state::game; - -/// Validate game ticket requirements and log the result. -pub fn play_game(_name: &str, ticket_count: u32, game_name: &str) -> Result<(), ProgramError> { - let games = game::get_games(); - - let mut i = 0; - while i < games.len() { - if game::game_name_matches(&games[i], game_name) { - log("You're about to play a game!"); - - if ticket_count < games[i].tickets { - log("Sorry, you don't have enough tickets for this game!"); - } else { - log("Let's see what you got!"); - log("Good luck winning the prize!"); - } - - return Ok(()); - } - i += 1; - } - - Err(ProgramError::InvalidInstructionData) -} diff --git a/basics/repository-layout/quasar/src/lib.rs b/basics/repository-layout/quasar/src/lib.rs deleted file mode 100644 index 30a7cee71..000000000 --- a/basics/repository-layout/quasar/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![cfg_attr(not(test), no_std)] - -use quasar_lang::prelude::*; - -pub mod instructions; -use instructions::*; -pub mod state; -#[cfg(test)] -mod tests; - -declare_id!("8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP"); - -#[program] -mod quasar_carnival { - use super::*; - - /// Ride a carnival ride. Validates height and ticket requirements. - #[instruction(discriminator = 0)] - pub fn go_on_ride( - ctx: Ctx, - height: u32, - ticket_count: u32, - name: String<50>, - ride_name: String<50>, - ) -> Result<(), ProgramError> { - instructions::handle_go_on_ride(&mut ctx.accounts, name, height, ticket_count, ride_name) - } - - /// Play a carnival game. Validates ticket requirements. - #[instruction(discriminator = 1)] - pub fn play_game( - ctx: Ctx, - ticket_count: u32, - name: String<50>, - game_name: String<50>, - ) -> Result<(), ProgramError> { - instructions::handle_play_game(&mut ctx.accounts, name, ticket_count, game_name) - } - - /// Eat at a carnival food stand. Validates ticket requirements. - #[instruction(discriminator = 2)] - pub fn eat_food( - ctx: Ctx, - ticket_count: u32, - name: String<50>, - food_stand_name: String<50>, - ) -> Result<(), ProgramError> { - instructions::handle_eat_food(&mut ctx.accounts, name, ticket_count, food_stand_name) - } -} diff --git a/basics/repository-layout/quasar/src/state/food.rs b/basics/repository-layout/quasar/src/state/food.rs deleted file mode 100644 index a9f92591e..000000000 --- a/basics/repository-layout/quasar/src/state/food.rs +++ /dev/null @@ -1,31 +0,0 @@ -/// A carnival food stand. -pub struct FoodStand { - pub name: &'static str, - pub food_type: &'static str, - pub tickets: u32, -} - -pub fn food_stand_name_matches(stand: &FoodStand, other: &str) -> bool { - stand.name.as_bytes() == other.as_bytes() -} - -/// Static list of food stands. -pub fn get_food_stands() -> &'static [FoodStand] { - &[ - FoodStand { - name: "Larry's Pizza", - food_type: "pizza", - tickets: 3, - }, - FoodStand { - name: "Taco Shack", - food_type: "taco", - tickets: 2, - }, - FoodStand { - name: "Dough Boy's", - food_type: "fried dough", - tickets: 1, - }, - ] -} diff --git a/basics/repository-layout/quasar/src/state/game.rs b/basics/repository-layout/quasar/src/state/game.rs deleted file mode 100644 index b088c1570..000000000 --- a/basics/repository-layout/quasar/src/state/game.rs +++ /dev/null @@ -1,37 +0,0 @@ -/// A carnival game with ticket cost, attempts, and prize. -pub struct Game { - pub name: &'static str, - pub tickets: u32, - pub tries: u32, - pub prize: &'static str, -} - -const DEFAULT_TICKETS_TO_PLAY: u32 = 3; - -pub fn game_name_matches(game: &Game, other: &str) -> bool { - game.name.as_bytes() == other.as_bytes() -} - -/// Static list of carnival games. -pub fn get_games() -> &'static [Game] { - &[ - Game { - name: "Ring Toss", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 5, - prize: "teddy bear", - }, - Game { - name: "I Got It!", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 12, - prize: "goldfish", - }, - Game { - name: "Ladder Climb", - tickets: DEFAULT_TICKETS_TO_PLAY, - tries: 1, - prize: "popcorn bucket", - }, - ] -} diff --git a/basics/repository-layout/quasar/src/state/mod.rs b/basics/repository-layout/quasar/src/state/mod.rs deleted file mode 100644 index 12a92a2cf..000000000 --- a/basics/repository-layout/quasar/src/state/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod food; -pub mod game; -pub mod ride; diff --git a/basics/repository-layout/quasar/src/state/ride.rs b/basics/repository-layout/quasar/src/state/ride.rs deleted file mode 100644 index 644bb40c3..000000000 --- a/basics/repository-layout/quasar/src/state/ride.rs +++ /dev/null @@ -1,43 +0,0 @@ -/// A carnival ride with requirements. -/// Uses &'static str instead of String for no_std compatibility. -pub struct Ride { - pub name: &'static str, - pub upside_down: bool, - pub tickets: u32, - pub min_height: u32, -} - -/// Check if a ride name matches (byte comparison, no alloc). -pub fn ride_name_matches(ride: &Ride, other: &str) -> bool { - ride.name.as_bytes() == other.as_bytes() -} - -/// Static list of carnival rides. -pub fn get_rides() -> &'static [Ride] { - &[ - Ride { - name: "Tilt-a-Whirl", - upside_down: false, - tickets: 3, - min_height: 48, - }, - Ride { - name: "Scrambler", - upside_down: false, - tickets: 3, - min_height: 48, - }, - Ride { - name: "Ferris Wheel", - upside_down: false, - tickets: 5, - min_height: 55, - }, - Ride { - name: "Zero Gravity", - upside_down: true, - tickets: 5, - min_height: 60, - }, - ] -} diff --git a/basics/repository-layout/quasar/src/tests.rs b/basics/repository-layout/quasar/src/tests.rs deleted file mode 100644 index 04c7127a5..000000000 --- a/basics/repository-layout/quasar/src/tests.rs +++ /dev/null @@ -1,152 +0,0 @@ -use { - crate::cpi::{EatFoodInstruction, GoOnRideInstruction, PlayGameInstruction}, - quasar_test::prelude::*, -}; - -// Deterministic addresses keep tests independent of discovery order. -const USER: Pubkey = Pubkey::new_from_array([1; 32]); - -fn go_on_ride(name: &str, height: u32, ticket_count: u32, ride_name: &str) -> GoOnRideInstruction { - GoOnRideInstruction { - payer: USER, - height, - ticket_count, - name: name.to_string().into(), - ride_name: ride_name.to_string().into(), - } -} - -fn play_game(name: &str, ticket_count: u32, game_name: &str) -> PlayGameInstruction { - PlayGameInstruction { - payer: USER, - ticket_count, - name: name.to_string().into(), - game_name: game_name.to_string().into(), - } -} - -fn eat_food(name: &str, ticket_count: u32, food_stand_name: &str) -> EatFoodInstruction { - EatFoodInstruction { - payer: USER, - ticket_count, - name: name.to_string().into(), - food_stand_name: food_stand_name.to_string().into(), - } -} - -#[quasar_test] -fn tall_rider_with_tickets_boards_the_ride(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(go_on_ride("Alice", 60, 5, "Ferris Wheel")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("about to go on a ride"), - "should announce ride" - ); - assert!(logs.contains("Welcome aboard"), "should welcome aboard"); -} - -#[quasar_test] -fn short_rider_is_turned_away(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(go_on_ride("Bob", 40, 5, "Ferris Wheel")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("not tall enough"), - "should reject short rider" - ); -} - -#[quasar_test] -fn rider_without_enough_tickets_is_turned_away(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(go_on_ride("Charlie", 60, 1, "Zero Gravity")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("enough tickets"), - "should reject insufficient tickets" - ); -} - -#[quasar_test] -fn upside_down_ride_warns_the_rider(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(go_on_ride("Dave", 65, 5, "Zero Gravity")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("upside down"), - "should warn about upside down" - ); -} - -#[quasar_test] -fn player_with_tickets_plays_the_game(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(play_game("Alice", 5, "Ring Toss")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!(logs.contains("about to play"), "should announce game"); - assert!(logs.contains("what you got"), "should encourage player"); -} - -#[quasar_test] -fn player_without_enough_tickets_is_turned_away(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(play_game("Bob", 1, "Ring Toss")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("enough tickets"), - "should reject insufficient tickets" - ); -} - -#[quasar_test] -fn visitor_with_tickets_eats_at_the_stand(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(eat_food("Alice", 3, "Larry's Pizza")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!(logs.contains("food stand"), "should welcome to food stand"); - assert!(logs.contains("Enjoy"), "should say enjoy"); -} - -#[quasar_test] -fn visitor_without_enough_tickets_cannot_eat(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - let outcome = test.send(eat_food("Bob", 0, "Larry's Pizza")); - outcome.succeeds(); - - let logs = outcome.logs().join("\n"); - assert!( - logs.contains("enough tickets"), - "should reject insufficient tickets" - ); -} - -#[quasar_test] -fn unknown_ride_name_is_rejected(test: &mut Test) { - test.add(Wallet::new().at(USER)); - - test.send(go_on_ride("Eve", 60, 5, "Nonexistent Ride")) - .fails(ProgramError::InvalidInstructionData); -} diff --git a/scripts/generate-quasar-readmes.mjs b/scripts/generate-quasar-readmes.mjs index fbe1160dc..aae62dca0 100644 --- a/scripts/generate-quasar-readmes.mjs +++ b/scripts/generate-quasar-readmes.mjs @@ -80,11 +80,6 @@ const examples = { purpose: "Compute account size and minimum rent-exempt [lamports](https://solana.com/docs/terminology#lamport).", concepts: ["Rent-exempt balance", "Space planning"], }, - "basics/repository-layout/quasar": { - title: "Repository Layout", - purpose: "Organize a program across modules (state, handlers, errors).", - concepts: ["Multi-file layout", "Separation of concerns"], - }, "basics/transfer-sol/quasar": { title: "Transfer SOL", purpose: "Transfer native SOL via the System Program.",