Browse Source

recommended layout native & anchor

jpcaulfi 3 years ago
parent
commit
bd00782cf3
32 changed files with 839 additions and 79 deletions
  1. 3 1
      program-basics/recommended-program-layout/README.md
  2. 14 0
      program-basics/recommended-program-layout/anchor/Anchor.toml
  3. 13 0
      program-basics/recommended-program-layout/anchor/Cargo.toml
  4. 14 0
      program-basics/recommended-program-layout/anchor/package.json
  5. 19 0
      program-basics/recommended-program-layout/anchor/programs/carnival/Cargo.toml
  6. 2 0
      program-basics/recommended-program-layout/anchor/programs/carnival/Xargo.toml
  7. 2 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/error.rs
  8. 37 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/eat_food.rs
  9. 49 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/get_on_ride.rs
  10. 4 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/mod.rs
  11. 38 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/play_game.rs
  12. 73 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/lib.rs
  13. 28 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/state/food.rs
  14. 32 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/state/game.rs
  15. 4 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/state/mod.rs
  16. 31 0
      program-basics/recommended-program-layout/anchor/programs/carnival/src/state/ride.rs
  17. 57 0
      program-basics/recommended-program-layout/anchor/tests/test.ts
  18. 10 0
      program-basics/recommended-program-layout/anchor/tsconfig.json
  19. 0 19
      program-basics/recommended-program-layout/native/program/src/entrypoint.rs
  20. 0 9
      program-basics/recommended-program-layout/native/program/src/instruction.rs
  21. 41 0
      program-basics/recommended-program-layout/native/program/src/instructions/eat_food.rs
  22. 53 0
      program-basics/recommended-program-layout/native/program/src/instructions/get_on_ride.rs
  23. 4 0
      program-basics/recommended-program-layout/native/program/src/instructions/mod.rs
  24. 42 0
      program-basics/recommended-program-layout/native/program/src/instructions/play_game.rs
  25. 2 3
      program-basics/recommended-program-layout/native/program/src/lib.rs
  26. 45 11
      program-basics/recommended-program-layout/native/program/src/processor.rs
  27. 0 2
      program-basics/recommended-program-layout/native/program/src/state.rs
  28. 28 0
      program-basics/recommended-program-layout/native/program/src/state/food.rs
  29. 32 0
      program-basics/recommended-program-layout/native/program/src/state/game.rs
  30. 4 0
      program-basics/recommended-program-layout/native/program/src/state/mod.rs
  31. 31 0
      program-basics/recommended-program-layout/native/program/src/state/ride.rs
  32. 127 34
      program-basics/recommended-program-layout/native/tests/test.ts

+ 3 - 1
program-basics/recommended-program-layout/README.md

@@ -2,4 +2,6 @@
 
 This is the typical layout for a Solana program as it grows in size and begins to require multiple Rust files. You'll notice a lot of the programs in the [Solana Program Library](https://github.com/solana-labs/solana-program-library) follow this format.
 
-> Note: You can structure your Rust `src` folder however you wish - provided you follow Cargo's repository structure standards. You don't have to follow this pattern, but it's here so you can recognize other programs, too.
+> Note: You can structure your Rust `src` folder however you wish - provided you follow Cargo's repository structure standards. You don't have to follow this pattern, but it's here so you can recognize other programs, too.
+
+You can see that the structure for a `native` repository is very similar to that of the `anchor` repository. The only difference is the inclusion of a `processor.rs` in the `native` setup - one of the many things Anchor abstracts away for you!

+ 14 - 0
program-basics/recommended-program-layout/anchor/Anchor.toml

@@ -0,0 +1,14 @@
+[features]
+seeds = false
+[programs.localnet]
+carnival = "8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP"
+
+[registry]
+url = "https://anchor.projectserum.com"
+
+[provider]
+cluster = "localnet"
+wallet = "~/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
program-basics/recommended-program-layout/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 14 - 0
program-basics/recommended-program-layout/anchor/package.json

@@ -0,0 +1,14 @@
+{
+    "dependencies": {
+        "@project-serum/anchor": "^0.24.2"
+    },
+    "devDependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "typescript": "^4.3.5"
+    }
+}

+ 19 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "carnival"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "carnival"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.24.2"

+ 2 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/Xargo.toml

@@ -0,0 +1,2 @@
+[target.bpfel-unknown-unknown.dependencies.std]
+features = []

+ 2 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/error.rs

@@ -0,0 +1,2 @@
+
+// For any custom errors

+ 37 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/eat_food.rs

@@ -0,0 +1,37 @@
+use anchor_lang::prelude::*;
+
+use crate::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);
+            } else {
+                msg!("  Enjoy your {}!", food_stand.food_type);
+            };
+
+            return Ok(())
+        }
+    };
+
+    Err(ProgramError::InvalidInstructionData.into())
+}

+ 49 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/get_on_ride.rs

@@ -0,0 +1,49 @@
+use anchor_lang::prelude::*;
+
+use crate::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);
+
+            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.into())
+}
+

+ 4 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/mod.rs

@@ -0,0 +1,4 @@
+
+pub mod eat_food;
+pub mod play_game;
+pub mod get_on_ride;

+ 38 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/instructions/play_game.rs

@@ -0,0 +1,38 @@
+use anchor_lang::prelude::*;
+
+use crate::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);
+            } 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.into())
+}

+ 73 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/lib.rs

@@ -0,0 +1,73 @@
+use anchor_lang::prelude::*;
+
+pub mod error;
+pub mod instructions;
+pub mod state;
+
+use crate::instructions::{
+    get_on_ride,
+    play_game,
+    eat_food,
+};
+
+
+// For setting up modules & configs
+
+
+declare_id!("8t94SEJh9jVjDwV7cbiuT6BvEsHo4YHP9x9a5rYH1NpP");
+
+
+#[program]
+pub mod carnival {
+    use super::*;
+
+    pub fn go_on_ride(
+        _ctx: Context<CarnivalContext>,
+        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(
+        _ctx: Context<CarnivalContext>,
+        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(
+        _ctx: Context<CarnivalContext>,
+        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 CarnivalContext<'info> {
+    #[account(mut)]
+    pub payer: Signer<'info>,
+}

+ 28 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/state/food.rs

@@ -0,0 +1,28 @@
+
+
+// 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<FoodStand> {
+    return 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),
+    ]
+}

+ 32 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/state/game.rs

@@ -0,0 +1,32 @@
+
+
+// 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<Game> {
+    return 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()),
+    ]
+}

+ 4 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/state/mod.rs

@@ -0,0 +1,4 @@
+
+pub mod food;
+pub mod game;
+pub mod ride;

+ 31 - 0
program-basics/recommended-program-layout/anchor/programs/carnival/src/state/ride.rs

@@ -0,0 +1,31 @@
+
+
+// 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<Ride> {
+    return 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),
+    ]
+}

+ 57 - 0
program-basics/recommended-program-layout/anchor/tests/test.ts

@@ -0,0 +1,57 @@
+import * as anchor from "@project-serum/anchor";
+import { Carnival } from "../target/types/carnival";
+
+
+
+describe("Carnival", () => {
+
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const wallet = provider.wallet as anchor.Wallet;
+  const program = anchor.workspace.Carnival as anchor.Program<Carnival>;
+
+  async function sendCarnivalInstructions(instructionsList: anchor.web3.TransactionInstruction[]) {
+    let tx = new anchor.web3.Transaction();
+    for (var ix of instructionsList) {
+        tx.add(ix);
+    };
+    await anchor.web3.sendAndConfirmTransaction(
+        provider.connection, 
+        tx,
+        [wallet.payer]
+    );
+}
+
+
+  it("Go on some rides!", async () => {
+
+    await sendCarnivalInstructions([
+        await program.methods.goOnRide("Jimmy", 36, 15, "Scrambler").instruction(),
+        await program.methods.goOnRide("Mary", 52, 1, "Ferris Wheel").instruction(),
+        await program.methods.goOnRide("Alice", 56, 15, "Scrambler").instruction(),
+        await program.methods.goOnRide("Bob", 49, 6, "Tilt-a-Whirl").instruction(),
+    ]);
+  });
+
+
+  it("Play some games!", async () => {
+
+    await sendCarnivalInstructions([
+        await program.methods.playGame("Jimmy", 15, "I Got It!").instruction(),
+        await program.methods.playGame("Mary", 1, "Ring Toss").instruction(),
+        await program.methods.playGame("Alice", 15, "Ladder Climb").instruction(),
+        await program.methods.playGame("Bob", 6, "Ring Toss").instruction(),
+    ]);
+  });
+
+
+  it("Eat some food!", async () => {
+
+    await sendCarnivalInstructions([
+        await program.methods.eatFood("Jimmy", 15, "Taco Shack").instruction(),
+        await program.methods.eatFood("Mary", 1, "Larry's Pizza").instruction(),
+        await program.methods.eatFood("Alice", 15, "Dough Boy's").instruction(),
+        await program.methods.eatFood("Bob", 6, "Dough Boy's").instruction(),
+    ]);
+  });
+});

+ 10 - 0
program-basics/recommended-program-layout/anchor/tsconfig.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "types": ["mocha", "chai"],
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2015"],
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true
+  }
+}

+ 0 - 19
program-basics/recommended-program-layout/native/program/src/entrypoint.rs

@@ -1,19 +0,0 @@
-use solana_program::{
-    account_info::AccountInfo, 
-    entrypoint, 
-    entrypoint::ProgramResult, 
-    pubkey::Pubkey,
-};
-
-
-entrypoint!(process_instruction);
-
-
-fn process_instruction(
-    _program_id: &Pubkey,
-    _accounts: &[AccountInfo],
-    instruction_data: &[u8],
-) -> ProgramResult {
-
-    crate::processor::process_instruction(_program_id, _accounts, instruction_data)
-}

+ 0 - 9
program-basics/recommended-program-layout/native/program/src/instruction.rs

@@ -1,9 +0,0 @@
-use borsh::{BorshDeserialize, BorshSerialize};
-
-// the various types of instruction data possible
-
-#[derive(BorshSerialize, BorshDeserialize, Debug)]
-pub struct InstructionData {
-    pub name: String,
-    pub height: u32,
-}

+ 41 - 0
program-basics/recommended-program-layout/native/program/src/instructions/eat_food.rs

@@ -0,0 +1,41 @@
+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)
+}

+ 53 - 0
program-basics/recommended-program-layout/native/program/src/instructions/get_on_ride.rs

@@ -0,0 +1,53 @@
+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)
+}
+

+ 4 - 0
program-basics/recommended-program-layout/native/program/src/instructions/mod.rs

@@ -0,0 +1,4 @@
+
+pub mod eat_food;
+pub mod play_game;
+pub mod get_on_ride;

+ 42 - 0
program-basics/recommended-program-layout/native/program/src/instructions/play_game.rs

@@ -0,0 +1,42 @@
+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)
+}

+ 2 - 3
program-basics/recommended-program-layout/native/program/src/lib.rs

@@ -1,8 +1,7 @@
 
-// For setting up modules & lib configs
+// For setting up modules & configs
 
-pub mod entrypoint;
 pub mod error;
-pub mod instruction;
+pub mod instructions;
 pub mod processor;
 pub mod state;

+ 45 - 11
program-basics/recommended-program-layout/native/program/src/processor.rs

@@ -1,14 +1,34 @@
+use borsh::{BorshDeserialize, BorshSerialize};
 use solana_program::{
     account_info::AccountInfo, 
+    entrypoint, 
     entrypoint::ProgramResult, 
     msg, 
+    program_error::ProgramError,
     pubkey::Pubkey,
 };
-use borsh::BorshDeserialize;
-use crate::instruction::InstructionData;
+
+use crate::instructions::{
+    get_on_ride,
+    play_game,
+    eat_food,
+};
+
+
+// For processing everything at the entrypoint
+
+
+entrypoint!(process_instruction);
 
 
-// For processing the instructions
+#[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(
@@ -17,14 +37,28 @@ pub fn process_instruction(
     instruction_data: &[u8],
 ) -> ProgramResult {
 
-    let instruction_data_object = InstructionData::try_from_slice(&instruction_data)?;
+    let ix_data_object = CarnivalInstructionData::try_from_slice(&instruction_data)?;
 
-    msg!("Welcome to the park, {}!", instruction_data_object.name);
-    if instruction_data_object.height > 5 {
-        msg!("You are tall enough to ride this ride. Congratulations.");
-    } else {
-        msg!("You are NOT tall enough to ride this ride. Sorry mate.");
-    };
+    msg!("Welcome to the carnival, {}!", ix_data_object.name);
+    
+    match ix_data_object.attraction.as_str() {
 
-    Ok(())
+        "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),
+    }
 }

+ 0 - 2
program-basics/recommended-program-layout/native/program/src/state.rs

@@ -1,2 +0,0 @@
-
-// For any object- or type-oriented logic (such as impl)

+ 28 - 0
program-basics/recommended-program-layout/native/program/src/state/food.rs

@@ -0,0 +1,28 @@
+
+
+// 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<FoodStand> {
+    return 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),
+    ]
+}

+ 32 - 0
program-basics/recommended-program-layout/native/program/src/state/game.rs

@@ -0,0 +1,32 @@
+
+
+// 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<Game> {
+    return 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()),
+    ]
+}

+ 4 - 0
program-basics/recommended-program-layout/native/program/src/state/mod.rs

@@ -0,0 +1,4 @@
+
+pub mod food;
+pub mod game;
+pub mod ride;

+ 31 - 0
program-basics/recommended-program-layout/native/program/src/state/ride.rs

@@ -0,0 +1,31 @@
+
+
+// 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<Ride> {
+    return 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),
+    ]
+}

+ 127 - 34
program-basics/recommended-program-layout/native/tests/test.ts

@@ -16,7 +16,7 @@ function createKeypairFromFile(path: string): Keypair {
 };
 
 
-describe("custom-instruction-data", () => {
+describe("Carnival", () => {
 
     const connection = new Connection(`http://localhost:8899`, 'confirmed');
     const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
@@ -29,55 +29,148 @@ describe("custom-instruction-data", () => {
             });
         };
     };
-
-    class InstructionData extends Assignable {};
-
-    const InstructionDataSchema = new Map([
+    
+    class CarnivalInstruction extends Assignable {
+        toBuffer() {
+            return Buffer.from(borsh.serialize(CarnivalInstructionSchema, this));
+        }
+    };
+    
+    const CarnivalInstructionSchema = new Map([
         [
-            InstructionData, {
+            CarnivalInstruction, {
                 kind: 'struct',
                 fields: [
                     ['name', 'string'],
                     ['height', 'u32'],
+                    ['ticket_count', 'u32'],
+                    ['attraction', 'string'],
+                    ['attraction_name', 'string'],
                 ]
             }
         ]
     ]);
+
+    async function sendCarnivalInstructions(instructionsList: CarnivalInstruction[]) {
+        let tx = new Transaction();
+        for (var ix of instructionsList) {
+            tx.add(new TransactionInstruction({
+                keys: [
+                    {pubkey: payer.publicKey, isSigner: true, isWritable: true}
+                ],
+                programId: program.publicKey,
+                data: ix.toBuffer(),
+            }));
+        };
+        await sendAndConfirmTransaction(
+            connection, 
+            tx,
+            [payer]
+        );
+    }
   
-    it("Go to the park!", async () => {
+    it("Go on some rides!", async () => {
 
-        const jimmy = new InstructionData({
-            name: "Jimmy",
-            height: 3
-        });
+        await sendCarnivalInstructions([
+            new CarnivalInstruction({
+                name: "Jimmy",
+                height: 36,
+                ticket_count: 15,
+                attraction: "ride",
+                attraction_name: "Scrambler",
+            }),
+            new CarnivalInstruction({
+                name: "Mary",
+                height: 52,
+                ticket_count: 1,
+                attraction: "ride",
+                attraction_name: "Ferris Wheel",
+            }),
+            new CarnivalInstruction({
+                name: "Alice",
+                height: 56,
+                ticket_count: 15,
+                attraction: "ride",
+                attraction_name: "Scrambler",
+            }),
+            new CarnivalInstruction({
+                name: "Bob",
+                height: 49,
+                ticket_count: 6,
+                attraction: "ride",
+                attraction_name: "Tilt-a-Whirl",
+            }),
+        ]);
+    });
 
-        const mary = new InstructionData({
-            name: "Mary",
-            height: 10
-        });
 
-        function toBuffer(obj: InstructionData): Buffer {
-            return Buffer.from(borsh.serialize(InstructionDataSchema, obj));
-        }
+    it("Play some games!", async () => {
 
-        let ix1 = new TransactionInstruction({
-            keys: [
-                {pubkey: payer.publicKey, isSigner: true, isWritable: true}
-            ],
-            programId: program.publicKey,
-            data: toBuffer(jimmy),
-        });
+        await sendCarnivalInstructions([
+            new CarnivalInstruction({
+                name: "Jimmy",
+                height: 36,
+                ticket_count: 15,
+                attraction: "game",
+                attraction_name: "I Got It!",
+            }),
+            new CarnivalInstruction({
+                name: "Mary",
+                height: 52,
+                ticket_count: 1,
+                attraction: "game",
+                attraction_name: "Ring Toss",
+            }),
+            new CarnivalInstruction({
+                name: "Alice",
+                height: 56,
+                ticket_count: 15,
+                attraction: "game",
+                attraction_name: "Ladder Climb",
+            }),
+            new CarnivalInstruction({
+                name: "Bob",
+                height: 49,
+                ticket_count: 6,
+                attraction: "game",
+                attraction_name: "Ring Toss",
+            }),
+        ]);
+    });
 
-        let ix2 = new TransactionInstruction({
-            ...ix1,
-            data: toBuffer(mary),
-        });
 
-        await sendAndConfirmTransaction(
-            connection, 
-            new Transaction().add(ix1).add(ix2),
-            [payer]
-        );
+    it("Eat some food!", async () => {
+
+        await sendCarnivalInstructions([
+            new CarnivalInstruction({
+                name: "Jimmy",
+                height: 36,
+                ticket_count: 15,
+                attraction: "food",
+                attraction_name: "Taco Shack",
+            }),
+            new CarnivalInstruction({
+                name: "Mary",
+                height: 52,
+                ticket_count: 1,
+                attraction: "food",
+                attraction_name: "Larry's Pizza",
+            }),
+            new CarnivalInstruction({
+                name: "Alice",
+                height: 56,
+                ticket_count: 15,
+                attraction: "food",
+                attraction_name: "Dough Boy's",
+            }),
+            new CarnivalInstruction({
+                name: "Bob",
+                height: 49,
+                ticket_count: 6,
+                attraction: "food",
+                attraction_name: "Dough Boy's",
+            }),
+        ]);
     });
   });