[Near Protocol] Near開發Demo淺析-Gamble Game Near(一):智慧合約

young發表於2022-05-15

[Near Protocol] Near開發Demo淺析-Gamble Game Near

智慧合約

大家好,Near Protocol是近年來比較引人注目的公鏈專案,其分片技術也使得其效能和穩定性出現了很大的提升和突破。我最近對Near相關開發的基礎概念進行了學習,以擲色子游戲的概念為基礎,開發了Near 合約+Dapp的Demo,現在和大家分享討論,希望都能有所進益。

原始碼在Github上可以找到,以下是智慧合約連結:

https://github.com/Yang94J/Gamble_Game_Near


簡介

合約名稱:Gamble
合約功能:允許使用者通過傳送Near進行投注,通過隨機數模擬擲色子活動,當擲到6時,可以獲得6倍返還。
實現語言:Rust
部署網路: Near Testnet
Near-sdk文件:https://www.near-sdk.io


環境搭建

  • Prerequisite : 系統中應當已經安裝好Rust
  • 安裝wasm32(編譯):

          rustup target add wasm32-unknown-unknown
  • 初始化專案(編譯):

          cargo new Gamble_Game_Near
  • 在生成的工程上進行目錄的修改,最終結構如下:
    工程目錄示意圖
    其中lib.rs是合約檔案,而Cargo.toml描述依賴(你可以理解為類似於Maven專案中的pom.xml),而Cargo.lock則包含依賴項的確切資訊。

<h3>程式碼解析</h3>

Cargo.toml

以下是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"

其中crate-type指定為C動態連結庫及Rust連結庫,依賴使用4.0.0-pre.7版本的near-sdk

lib.rs

lib.rs首先從near-sdk中匯入相關函式及介面:

use near_sdk::{
    borsh::{self, BorshDeserialize, BorshSerialize},
    near_bindgen, Balance,PanicOnDefault,
    env, require, log,  Promise, 
};

同時通過#[near_bindgen]#[derive(BorshDeserialize, BorshSerialize,PanicOnDefault)]來宣告Gamble類為智慧合約類。#[near_bindgen]巨集用以生成Near合約所需的程式碼並對外暴露介面。#[derive(BorshDeserialize, BorshSerialize,PanicOnDefault)]則宣告序列化、反序列化,且要求實現建構函式。Gamble類中包含gamble_min_pricegamble_max_price屬性,為投注的最小值和最大值。

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,

}

本文後續實現Gamble類的相關方法,(注意&self 和 &mut self的區別,類似於solidity view和change的區別)如下:

  • pub fn new() -> Self :用以初始化合約,設定gamble_min_pricegamble_max_price屬性。
    #[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 :獲取最小投注數量( 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:獲取最大投注數量( 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:獲取合約餘額( yoctoNEAR)
    // Get contract balance U128
    pub fn get_balance(&self) -> u128 {
        env::account_balance()
    }  
  • pub fn update_price(&mut self) :更新投注最大值和最小值
    // 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):贊助函式,即使用者向合約賬號傳送near( yoctoNEAR),最後更新投注上限和下限
    // 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:投擲函式,返回投擲出的點數(1-6),最後更新投注上限和下限
    // 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 :基於random_seed(),生成隨機數並通過取餘對映到1-6
    // Generate random number from 1 to 6
    pub fn rand_dice(&self) -> u8 {
        *env::random_seed().get(0).unwrap()%6+1
    }

後續則是單元測試部分,通過fn get_context(input: Vec<u8>) -> VMContext設定了測試背景,由fn rand_test()測試了隨機數生成,fn gamble_test()則測試了合約初始化後的變數情況。

合約部署

  • 在此之前請先安裝Near-cli
  • 編譯生成
        cargo build --target wasm32-unknown-unknown --release
  • 部署(先生成子賬號再部署)
        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
  • 測試
        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



至此,智慧合約部分完成,後續會帶來
[Near Protocol] Near開發Demo淺析-Gamble Game Near(二):Dapp部分的介紹。

相關文章