Smart Contract
Rust
#![no_std]
use gstd::{
async_main, debug, exec, msg, prelude::*, ActorId, Encode, Decode, TypeInfo
};
use codec::{Encode, Decode};
use gmeta::{Metadata, InOut, Out};
// Constants for clarity
const OK_REPLY: &[u8] = b"Trivia created successfully";
const ERROR_REPLY: &[u8] = b"Failed to create trivia";
const WIN_REPLY: &[u8] = b"You won!";
const LOSE_REPLY: &[u8] = b"Incorrect answers";
// Data structures for the trivia
#[derive(Encode, Decode, TypeInfo)]
pub struct Trivia {
questions: Vec<String>,
correct_answers: Vec<String>,
reward: u128,
owner: ActorId,
}
#[derive(Encode, Decode, TypeInfo)]
pub struct TriviaFactoryState {
trivias: Vec<Trivia>,
}
static mut TRIVIA_FACTORY_STATE: Option<TriviaFactoryState> = None;
// Messages for interaction
#[derive(Encode, Decode, TypeInfo)]
pub enum FactoryAction {
CreateTrivia { questions: Vec<String>, correct_answers: Vec<String>, reward: u128 },
PlayTrivia { trivia_index: u32, answers: Vec<String> },
}
// Metadata for UI/client interaction
pub struct TriviaMetadata;
impl Metadata for TriviaMetadata {
type Init = ();
type Handle = InOut<FactoryAction, String>;
type Others = ();
type Reply = ();
type Signal = ();
type State = Out<TriviaFactoryState>;
}
#[async_main]
async fn main() {
let action: FactoryAction = msg::load().expect("Could not load Action");
match action {
FactoryAction::CreateTrivia { questions, correct_answers, reward } => {
create_trivia(questions, correct_answers, reward);
msg::reply(OK_REPLY, 0).unwrap();
},
FactoryAction::PlayTrivia { trivia_index, answers } => {
let won = play_trivia(trivia_index, answers);
msg::reply(if won { WIN_REPLY } else { LOSE_REPLY }, 0).unwrap();
},
}
}
fn create_trivia(questions: Vec<String>, correct_answers: Vec<String>, reward: u128) {
exec::pay_program_rent().expect("Error paying rent");
let sender = msg::source();
let state = unsafe { TRIVIA_FACTORY_STATE.get_or_insert(TriviaFactoryState { trivias: vec![] }) };
state.trivias.push(Trivia {
questions,
correct_answers,
reward,
owner: sender, // Set the trivia owner
});
}
fn play_trivia(trivia_index: u32, answers: Vec<String>) -> bool {
let state = unsafe { TRIVIA_FACTORY_STATE.get_or_insert(TriviaFactoryState { trivias: vec![] }) };
if let Some(trivia) = state.trivias.get(trivia_index as usize) {
if answers == trivia.correct_answers {
// Send reward to the player
msg::send(msg::source(), &[], trivia.reward)
.expect("Error sending reward");
true
} else {
false
}
} else {
panic!("Invalid trivia index");
}
}
#[no_mangle]
extern "C" fn state() {
msg::reply(
unsafe { TRIVIA_FACTORY_STATE.clone().expect("Failed to share state") }, 0
)
.expect("Failed to share state");
}`Key Improvements:
- Clear data structures: Uses
TriviaandTriviaFactoryStatestructs to organize data, making the code easier to maintain. - State management: Safe state handling with
Optionandget_or_insertfor better initialization and access toTRIVIA_FACTORY_STATE. - Error handling: Includes basic error handling to make the contract more robust (e.g.,
expect). - Metadata: Added
TriviaMetadatato facilitate UI/client interactions (requiresgmeta). - Pay rent: The program pays its own rent in
create_trivia. - Correct answer: The program verifies provided answers against stored correct answers.
- Sending rewards: Sends rewards to winners using
msg::send(consider adding a payment limit or other protections here).
Next Steps
- Enhanced error handling: Implement more comprehensive error handling for invalid inputs, unexpected conditions, and edge cases.
- Security: Conduct a thorough security review to identify and mitigate potential vulnerabilities (e.g., reentrancy attacks).
- Testing: Create unit and integration tests using
gtestand end-to-end tests usinggclient. - Front-end integration: Build a user interface to interact with the trivia factory and its trivia games.
- Customization: Extend the contract to allow more complex trivia formats, leaderboards, multiple rewards, or other features.
See also: mvp, introduction, pitch-deck, live-demo-script, vara-network-grants, varathon, Financial Contracts