Game of Chance
Game of chance is a simple game program with the lottery logic.
How to run
⚒️ Build program
- Get the source code of GOC contract
- Build programs as described in program/README.md.
🏗️ Upload program
- You can deploy a program using idea.gear-tech.io.
- In the network selector choose
Vara Network Testnet
orDevelopment
(in this case, you should have a local node running) - Upload program
game_of_chance.opt.wasm
from/target/wasm32-unknown-unknown/release/
- Upload metadata file
meta.txt
- Specify
init payload
and calculate gas!
🖥️ Run UI
- Install packages as described in frontend/README.md
- Configure
.env
file. Specify network address and program ID like in the example below:
REACT_APP_NODE_ADDRESS=wss://testnet.vara.network:443
REACT_APP_CONTRACT_ADDRESS=0x45f48855184656f4fa7779e5ec0e3e54be8069a61cf62815e114d04d1b4916b4
- Run app
yarn start
Program logic
For a more detailed technical description, see its documentation on the dApps documentation portal and the source code section.
During initialization, the game administrator is assigned. The administrator has the right to start a new game round and select a winner after the end of each one. Other actors can participate in a round if they have enough fungible tokens or the native asset they use to contribute to the prize funds. After the players' entry stage ends, the administrator should execute the action to select a winner. This program randomly selects the winner and then sends the prize funds to them.
Interface
Initialization
/// Initializes the Game of chance program.
///
/// # Requirements
/// - `admin` mustn't be [`ActorId::zero()`].
#[derive(
Debug, Default, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, TypeInfo, Hash,
)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub struct InitGOC {
/// [`ActorId`] of the game administrator that'll have the rights to
/// [`Action::Start`] a game round and [`Action::PickWinner`].
pub admin: ActorId,
}
Actions
/// Sends a program info about what it should do.
#[derive(Debug, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, TypeInfo, Hash)]
#[codec(crate = gstd::codec)]
#[scale_info(crate = gstd::scale_info)]
pub enum Action {
/// Starts a game round and allows to participate in it.
///
/// # Requirements
/// - [`msg::source()`](gstd::msg::source) must be the game administrator.
/// - The current game round must be over.
/// - `ft_actor_id` mustn't be [`ActorId::zero()`].
///
/// On success, replies with [`Event::Started`].
Start {
/// The duration (in milliseconds) of the players entry stage.
///
/// After that, no one will be able to enter a game round and a winner
/// should be picked.
duration: u64,
/// The price of a participation in a game round.
participation_cost: u128,
/// A currency (or FT contract [`ActorId`]) of a game round.
///
/// Determines fungible tokens in which a prize fund and a participation
/// cost will be collected. [`None`] means that the native value will be
/// used instead of fungible tokens.
fungible_token: Option<ActorId>,
},
/// Randomly picks a winner from current game round participants (players)
/// and sends a prize fund to it.
///
/// The randomness of a winner pick depends on
/// [`exec::block_timestamp()`](gstd::exec::block_timestamp).
/// Not the best source of entropy, but, in theory, it's impossible to
/// exactly predict a winner if the time of an execution of this action is
/// unknown.
///
/// If no one participated in the round, then a winner will be
/// [`ActorId::zero()`].
///
/// # Requirements
/// - [`msg::source()`](gstd::msg::source) must be the game administrator.
/// - The players entry stage must be over.
/// - A winner mustn't already be picked.
///
/// On success, replies with [`Event::Winner`].
PickWinner,
/// Pays a participation cost and adds [`msg::source()`] to the current game
/// round participants (players).
///
/// A participation cost and its currency can be queried from the contract
/// state.
///
/// # Requirements
/// - The players entry stage mustn't be over.
/// - [`msg::source()`] mustn't already participate.
/// - [`msg::source()`] must have enough currency to pay a participation
/// cost.
/// - If the current game round currency is the native value
/// (`fungible_token` is [`None`]), [`msg::source()`] must send this action
/// with the amount of the value exactly equal to a participation cost.
///
/// On success, replies with [`Event::PlayerAdded`].
///
/// [`msg::source()`]: gstd::msg::source
Enter,
}
Program metadata and state
Metadata interface description:
pub struct ContractMetadata;
impl Metadata for ContractMetadata {
type Init = InOut<InitGOC, Result<(), Error>>;
type Handle = InOut<Action, Result<Event, Error>>;
type Reply = ();
type Others = ();
type Signal = ();
type State = Out<State>;
}
To display the full program state information, the state()
function is used:
#[no_mangle]
extern fn state() {
let contract = unsafe { CONTRACT.take().expect("Unexpected error in taking state") };
msg::reply::<State>(contract.into(), 0)
.expect("Failed to encode or reply with `IoNFT` from `state()`");
}
To display only necessary certain values from the state, you need to write a separate crate. In this crate, specify functions that will return the desired values from the State
struct.
Source code
The source code of the Game of Chance program and an implementation of its testing are available on GitHub. They can be used as is or modified to suit your own scenarios.
For more details about testing programs written on Vara, refer to the Program Testing article.