1

[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>

Cargo.toml

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

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 and gamble_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.


young
77 声望2 粉丝