[Near Protocol] Analysis of Near Development Demo-Gamble Game Near
smart contract
Hello everyone, Near Protocol is a relatively eye-catching public chain project in recent years, and its sharding technology has also made great improvements and breakthroughs in its performance and stability. I have recently studied the basic concepts of Near-related development. Based on the concept of the game of dice, I have developed a Demo of Near contract + Dapp. Now I will share and discuss with you, hoping to make progress.
The source code is available on Github, here is the smart contract link:
https://github.com/Yang94J/Gamble_Game_Near
Introduction
Contract Name: Gamble
Contract function: Allow users to bet by sending Near, and simulate dice tossing activities through random numbers. When 6 is tossed, they can get 6 times the return.
Implementation language: Rust
Deployment Network: Near Testnet
Near-sdk documentation: https://www.near-sdk.io
Environment construction
- Prerequisite : Rust should already be installed on the system
Install wasm32 (compile):
rustup target add wasm32-unknown-unknown
Initialize the project (compile):
cargo new Gamble_Game_Near
- Modify the directory on the generated project, and the final structure is as follows:
Where lib.rs is the contract file, Cargo.toml describes the dependencies (you can understand it as pom.xml in a Maven project), and Cargo.lock contains the exact information of the dependencies.
<h3>Code Analysis</h3>
The following are the important parts of Cargo.toml
[lib]
crate-type = ["cdylib","rlib"]
[dependencies]
uint = {version = "0.9.0", default-features = false}
near-sdk = "4.0.0-pre.7"
near-contract-standards = "4.0.0-pre.7"
The crate-type is specified as the C dynamic link library and the Rust link library, depending on the near-sdk version 4.0.0-pre.7
lib.rs first imports related functions and interfaces from near-sdk:
use near_sdk::{
borsh::{self, BorshDeserialize, BorshSerialize},
near_bindgen, Balance,PanicOnDefault,
env, require, log, Promise,
};
At the same time, the Gamble class is declared as a smart contract class by #[near_bindgen]
and #[derive(BorshDeserialize, BorshSerialize,PanicOnDefault)]
. #[near_bindgen]
macro is used to generate the code required by the Near contract and expose the interface. #[derive(BorshDeserialize, BorshSerialize,PanicOnDefault)]
declares serialization, deserialization, and requires the implementation of the constructor. The Gamble class contains gamble_min_price
and gamble_max_price
attributes, which are the minimum and maximum bets.
pub struct Gamble {
// Minimum price that should be transfered to the contract, revert otherwise
gamble_min_price : Balance,
// Maximum price that should be transfered to the contract, revert otherwise
gamble_max_price : Balance,
}
The related methods of the Gamble class will be implemented later in this article (note the difference between &self and &mut self, similar to the difference between solidity view and change) as follows:
- pub fn new() -> Self : used to initialize the contract, set
gamble_min_price
andgamble_max_price
properties.
#[init]
pub fn new() -> Self {
let account_balance = env::account_balance();
let gamble_max_price = account_balance / (5 * FACTOR);
log!("we have {} uints in total, be sure not to exceed the max gamble price limit {} to get {}X \n", account_balance, gamble_max_price, FACTOR);
Self{
gamble_max_price : gamble_max_price,
gamble_min_price : 0,
}
}
- pub fn get_minimal_gamble_price(&self) -> u128 : get the minimum bet amount ( yoctoNEAR )
// Get the Minimum amount of near to be transfered(Used for dapp, but usually won't as it's 0 all the time)
pub fn get_minimal_gamble_price(&self) -> u128 {
self.gamble_min_price
}
- pub fn get_maximum_gamble_price(&self) -> u128: get the maximum number of bets ( yoctoNEAR )
// Get the Minimum amount of near to be transfered(Used for dapp)
pub fn get_maximum_gamble_price(&self) -> u128 {
self.gamble_max_price
}
- pub fn get_balance(&self) -> u128: get the contract balance (yoctoNEAR)
// Get contract balance U128
pub fn get_balance(&self) -> u128 {
env::account_balance()
}
- pub fn update_price(&mut self) : update bet max and min
// Update price everytime the account balance changes
// Only contract call
fn update_price(&mut self){
let account_balance = env::account_balance();
self.gamble_max_price = account_balance / (5 * FACTOR);
log!("we have {} uints in total, be sure not to exceed the max gamble price limit {} to get {}X \n", account_balance, self.gamble_max_price, FACTOR);
}
- pub fn sponsor(&mut self): sponsorship function, that is, the user sends near ( yoctoNEAR ) to the contract account, and finally updates the upper and lower betting limits
// The user could sponsor the contract(maybe only the owner will...)
#[payable]
pub fn sponsor(&mut self){
let sponsor_id = env::signer_account_id();
let deposit = env::attached_deposit();
log!("sponsor {} has add {} to the game to increase balance, thank you ~ \n", sponsor_id, deposit);
self.update_price();
}
- pub fn gamble(&mut self) -> u8: Throwing function, returns the number of points thrown (1-6), and finally updates the upper and lower betting limits
// The user could transfer near to get a chance to gamble
// return the dice throwed by the user (Randomly generated)
#[payable]
pub fn gamble(&mut self) -> u8{
let gambler_id = env::signer_account_id();
let deposit = env::attached_deposit();
require!(deposit>=self.gamble_min_price,"The gamble price must exceed gamble_min_price");
require!(deposit<=self.gamble_max_price,"The gamble price must not exceed gamble_max_price");
let num = self.rand_dice();
if num == FACTOR as u8 {
let amount = (deposit as f32 ) *(FACTOR as f32) * TAX;
let amount_u128 = amount as u128;
log!("Congratuations to {}, he has won the gamble, the prize is {} \n",gambler_id,deposit);
Promise::new(gambler_id).transfer(amount_u128);
}
self.update_price();
return num;
}
- pub fn rand_dice(&self) -> u8 : Based on random_seed(), generate random numbers and map to 1-6 by taking the remainder
// Generate random number from 1 to 6
pub fn rand_dice(&self) -> u8 {
*env::random_seed().get(0).unwrap()%6+1
}
The follow-up is the unit test part. The test background is set by fn get_context(input: Vec<u8>) -> VMContext
fn rand_test()
, the random number generation is tested by fn gamble_test()
, and the variable condition after contract initialization is tested by ---6c33e8efb487d30f7d7060e6c0697cc8--- .
Contract deployment
- Please install Near-cli before this
- Compile and generate
cargo build --target wasm32-unknown-unknown --release
- Deploy (generate a sub-account first and then deploy)
near create-account gamble_game1.XXX.testnet --masterAccount XXX.testnet --initialBalance 100
near deploy --wasmFile target/wasm32-unknown-unknown/release/gamble_game_near.wasm --accountId gamble_game1.XXX.testnet
- test
near call gamble_game1.XXX.testnet new --accountId gamble_game1.XXX.testnet
near view gamble_game1.XXX.testnet get_balance
near view gamble_game1.XXX.testnet get_maximum_gamble_price
near call gamble_game1.XXX.testnet sponsor --deposit 1 --accountId XXX.testnet
near call gamble_game1.XXX.testnet gamble --deposit 1 --accountId XXX.testnet
At this point, the smart contract is partially completed, and the follow-up will bring
[Near Protocol] Analysis of Near Development Demo-Gamble Game Near (2): Introduction to the Dapp part.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。