EOS博彩合約設計

集中博彩遊戲合約設計
1、功能接口
1. 質押deposit數據結構

由用戶發起,用戶將我的帳戶中token質押給平臺,從而能夠進入平臺去參與平臺活動。ui

2. 贖回withdrawurl

由用戶發起,在用戶結束平臺活動須要離開時,發起贖回曾質押給平臺的token到我的帳戶。設計

3. 開啓一期下注openbetcode

由平臺發起,平臺啓動一期下注,玩家能夠參與下注。token

4. 結束一期下注 closurebet接口

由平臺發起,平臺關閉一期下注,因此玩家的下注被鎖定。遊戲

5. 下注offerbetci

由用戶發起,用戶參與平臺開啓的下注,須要在一期下注開啓以後執行。rem

6. 取消下注canneloffer

由用戶發起,用戶取消曾參與的下注,須要在該期下注結束以前執行。

7. 開獎reveal

由平臺發起,平臺在一期下注上進行結果操做。

2、數據存儲
1. 質押贖回帳戶表account_index

我的帳戶token質押給平臺和從平臺贖回token須要一個帳戶表來管理我的帳戶token信息,帳戶表數據結構以下:

a. 我的帳戶名稱

b. 資產額

2. 下注期數記錄表g_bet_index

遊戲從第1期開始,隨後每開啓一期遊戲,期數自動加1,遊戲期數記錄表記錄了總的下注期數,同時也記錄了當前正在進行或者要開啓的下一期下注的期數,下注期數記錄表結構以下:

a. 記錄id

b. 當前正在進行的下注期數

c. 當前正在進行的下注期名稱

d. 當前下注開啓關閉狀態

e. 當前下注結算狀態

3. 下注記錄表bet_index

在一期下注開啓時間窗口,平臺用戶能夠自由下注以及取消下注,下注記錄表則記錄了用戶的下注狀況,下注記錄表數據結構以下:

a. 記錄id

b. 下注期數

c. 我的帳戶名稱

d. 下注資產額

e. 下注信息

3、接口實現設計
a. 質押贖回

 

1. 用戶發起質押操做,參數包括質押資產額

2. 從user帳戶轉帳token到dice帳戶,dice.xxx合約將調用eosio.token的transfer action執行轉帳操做,將user我的帳戶中token轉帳到合約帳戶dice

3. 修改質押贖回帳戶表,記錄user我的用戶的質押信息,添加新記錄或者修改記錄

4. 用戶發起贖回操做

5. 從dice帳戶轉帳token到user我的帳戶,dice.xxx合約將調用eosio.token的transfer action執行轉帳

6. 修改質押贖回帳戶表,修改user我的用戶的質押信息,修改記錄或者刪除記錄

b. 開啓下注、結束下注以及開獎

 

1. 平臺發起一次openbet

2. 檢查新一期下注是否合法,檢查是否有正在進行的下注,若是沒有則當前期數+1,同時下注開啓

3. 平臺發起一次closurebet

4. 檢查關閉的下注是否合法,檢查是否有正在進行的下注,若是有,則關閉下注

5. 平臺發起一次開獎

6. 計算改期開獎結果,計算池中各用戶的下注信息並給出各個用戶的開獎結果

7. 開獎結果兌現,將開獎結果兌現到各用戶的質押上

c. 下注及取消下注

 

1. 用戶發起下注,參數包括下注資產額、下注信息

2. 檢查該期下注是否在開放窗口期,若是不在開放窗口期則不能下注

3. 修改用戶質押資產額

4. 修改用戶下注資產額以及下注信息

5. 用戶發起取消下注

6. 檢查該期下注是否在開放窗口期,若是不在開放窗口期則不能取消下注

7. 修改用戶下注資產額及下注信息

8. 修改用戶質押資產額

4、部署合約
1. 建立合約帳戶

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 create account eosio dice.xxx EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

2. 部署合約

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 set contract dice.xxx /home/kingnet/tangy/eos/mycontracts/dice.xxx

5、平臺接口使用
 

平臺接口須要合約帳戶

1. 開啓下注

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx openbet '{}' -p dice.xxx

2. 關閉下注

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx closurebet '{}' -p dice.xxx

3. 開獎

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx reveal '{}' -p dice.xxx

6、用戶接口使用
 

1. 質押

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx deposit '{"from":"alice", "quantity":"100.0000 EOS"}' -p alice

2. 贖回

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx withdraw '{"to":"alice", "quantity":"100.0000 EOS"}' -p alice

如下用戶接口須要在下注開啓窗口才能被執行

3. 用戶下注

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx offerbet '{"bet":"10.0000 EOS", "player":"alice","info":0}' -p alice

4. 取消下注

cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action dice.xxx canceloffer '{"player":"alice"}' -p alice

7、合約代碼

 
/**

  • @file
  • @copyright defined in eos/LICENSE.txt
    */

    include

    include

    include

    include <eosiolib/eosio.hpp>

    include <eosiolib/asset.hpp>

    include <eosiolib/contract.hpp>

    include <eosiolib/crypto.h>

using eosio::key256;
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::asset;
using eosio::permission_level;
using eosio::action;
using eosio::print;
using eosio::name;

class dice : public eosio::contract {
public:
dice(account_name self) : eosio::contract(self),
bets(_self, _self),
gbet(_self, _self),
accounts(_self, _self) {
}

//@abi action
  void offerbet(const asset& bet, account_name player, const uint64_t betinfo) {

     eosio_assert( bet.symbol == S(4,EOS) , "only EOS token allowed" );
     eosio_assert( bet.is_valid(), "invalid bet" );
     eosio_assert( bet.amount > 0, "must bet positive quantity" );

     //
     require_auth( player );

     auto cur_player_itr = accounts.find( player );
     eosio_assert(cur_player_itr != accounts.end(), "unknown account");
     eosio_assert(cur_player_itr->balance >= bet, "insufficient balance");

     auto cur_gbet_itr = gbet.begin();
     eosio_assert(cur_gbet_itr != gbet.end(), "bet is not build");
     eosio_assert(cur_gbet_itr->open == 1, "bet is not open");
     eosio_assert(cur_gbet_itr->reveal == 0, "bet is revealed");

     uint64_t betid = cur_gbet_itr->betid;
     uint64_t exist_bet = false;
     auto bybetid_index = bets.template get_index<N(bybetid)>();
     auto cur_bets_itr = bybetid_index.find(betid);
     while(cur_bets_itr != bybetid_index.end() && cur_bets_itr->betid == betid) {
         if(cur_bets_itr->owner == player) {
             exist_bet = true;
             break;
         }
         cur_bets_itr ++;
     }

     eosio_assert(exist_bet == false, "bet is done");

     // Store new offer
     auto new_bet_itr = bets.emplace(_self, [&](auto& rbet){
        rbet.id          = bets.available_primary_key();
        rbet.balance     = bet;
        rbet.owner       = player;
        rbet.betid       = betid;
        rbet.info        = betinfo;
     });

     // Update player's accounts
     accounts.modify( cur_player_itr, 0, [&](auto& acnt) {
        eosio_assert( acnt.balance >= bet, "insufficient balance" );
        acnt.balance -= bet;
     });
  }

  //@abi action
  void canceloffer(account_name player) {
      //
      require_auth( player );

      auto cur_gbet_itr = gbet.begin();
      eosio_assert(cur_gbet_itr != gbet.end(), "bet is not build");
      eosio_assert(cur_gbet_itr->open == 1, "bet is closed");
      eosio_assert(cur_gbet_itr->reveal == 0, "bet is revealed");

      uint64_t betid = cur_gbet_itr->betid;
      uint64_t exist_bet = false;
      auto bybetid_index = bets.template get_index<N(bybetid)>();
      auto cur_bets_itr = bybetid_index.find(betid);
      while(cur_bets_itr != bybetid_index.end() && cur_bets_itr->betid == betid) {
         if(cur_bets_itr->owner == player) {
             exist_bet = true;
             break;
         }
        cur_bets_itr ++;
      }
      eosio_assert(exist_bet == true, "no bet");

      // Update player's accounts
      auto cur_player_itr = accounts.find( player );
      if( cur_player_itr == accounts.end() ) {
        cur_player_itr = accounts.emplace(_self, [&](auto& acnt){
          acnt.owner = player;
        });
      }

      accounts.modify( cur_player_itr, 0, [&](auto& acnt) {
         acnt.balance += cur_bets_itr->balance;
      });

      bybetid_index.erase(cur_bets_itr);
      //bets.erase(cur_bet_itr);
  }

  //@abi action
  void openbet() {
      //
      require_auth(_self);

      // Create global bet counter if not exists
      auto cur_gbet_itr = gbet.begin();
      if( cur_gbet_itr == gbet.end() ) {
          cur_gbet_itr = gbet.emplace(_self, [&](auto& g_bet){
             g_bet.id = gbet.available_primary_key();
             g_bet.betid = 0;
             g_bet.open = 0;
             g_bet.reveal = 1;
         });
      }

      eosio_assert(cur_gbet_itr != gbet.end(), "bet is not build");
      eosio_assert(cur_gbet_itr->open == 0, "bet is opened");
      eosio_assert(cur_gbet_itr->reveal == 1, "bet is not reveal");

      // Increment global bet counter
      gbet.modify(cur_gbet_itr, 0, [&](auto& g_bet){
          g_bet.betid++;
          g_bet.open = 1;
          g_bet.reveal = 0;
      });
  }

  //@abi action
  void closurebet() {
      //
      require_auth(_self);

      auto cur_gbet_itr = gbet.begin();
      eosio_assert(cur_gbet_itr != gbet.end(), "bet is not build");
      eosio_assert(cur_gbet_itr->open == 1, "bet is closure");
      eosio_assert(cur_gbet_itr->reveal == 0, "reveal is done");

      // udpate global bet status
      gbet.modify(cur_gbet_itr, 0, [&](auto& g_bet){
          g_bet.open = 0;
      });
  }

  //@abi action
  void reveal() {
      //
      require_auth(_self);


      auto cur_gbet_itr = gbet.begin();
      eosio_assert(cur_gbet_itr != gbet.end(), "bet is not build");
      eosio_assert(cur_gbet_itr->open == 0, "bet is open");
      eosio_assert(cur_gbet_itr->reveal == 0, "reveal is done");

      //
      uint64_t bet_result = 0;
      uint64_t betid = cur_gbet_itr->betid;

      //
      {
          uint32_t t_now = now();
          checksum256 hash;
          sha256((char*)(&t_now), sizeof(uint32_t), &hash);
          bet_result = (hash.hash[15]) % 2;
      }

      //
      asset total_balance;
      asset win_balance;
      auto bybetid_index = bets.template get_index<N(bybetid)>();
      auto cur_bets_itr = bybetid_index.find(betid);
      while(cur_bets_itr != bybetid_index.end() && cur_bets_itr->betid == betid) {
          total_balance += cur_bets_itr->balance;
          if(cur_bets_itr->info == bet_result)
              win_balance += cur_bets_itr->balance;
          cur_bets_itr++;
      }

      if(win_balance.amount == 0) {
         bybetid_index = bets.template get_index<N(bybetid)>();
         cur_bets_itr = bybetid_index.find(betid);

         while(cur_bets_itr != bybetid_index.end() && cur_bets_itr->betid == betid) {

            auto use_balance = cur_bets_itr->balance;
            auto cur_player_itr = accounts.find( cur_bets_itr->owner );
            if( cur_player_itr == accounts.end() ) {
               cur_player_itr = accounts.emplace(_self, [&](auto& acnt){
               acnt.owner = cur_bets_itr->owner;
               });
            }
            accounts.modify( cur_player_itr, 0, [&]( auto& acnt ) {
              acnt.balance += use_balance;
            });

            cur_bets_itr ++;
         }
      } else {
          bybetid_index = bets.template get_index<N(bybetid)>();
          cur_bets_itr = bybetid_index.find(betid);
          while(cur_bets_itr != bybetid_index.end() && cur_bets_itr->betid == betid) {
              if(cur_bets_itr->info == bet_result) {
                  //auto use_balance = (cur_bets_itr->balance / win_balance) * total_balance;
                  auto use_balance = (cur_bets_itr->balance * total_balance.amount) / win_balance.amount;

                  auto cur_player_itr = accounts.find( cur_bets_itr->owner );
                  if( cur_player_itr == accounts.end() ) {
                      cur_player_itr = accounts.emplace(_self, [&](auto& acnt){
                        acnt.owner = cur_bets_itr->owner;
                     });
                  }
                  accounts.modify( cur_player_itr, 0, [&]( auto& acnt ) {
                     acnt.balance += use_balance;
                  });
              }
              cur_bets_itr ++;
          }
      }

      // udpate global bet status
      gbet.modify(cur_gbet_itr, 0, [&](auto& g_bet){
         g_bet.reveal = 1;
      });
  }

  //@abi action
  void deposit( const account_name from, const asset& quantity ) {
     
     require_auth( from );

     eosio_assert( quantity.is_valid(), "invalid quantity" );
     eosio_assert( quantity.amount > 0, "must deposit positive quantity" );

     auto itr = accounts.find(from);
     if( itr == accounts.end() ) {
        itr = accounts.emplace(_self, [&](auto& acnt){
           acnt.owner = from;
        });
     }

     action(
        permission_level{ from, N(active) },
        N(eosio.token), N(transfer),
        std::make_tuple(from, _self, quantity, std::string(""))
     ).send();

     accounts.modify( itr, 0, [&]( auto& acnt ) {
        acnt.balance += quantity;
     });
  }

  //@abi action
  void withdraw( const account_name to, const asset& quantity ) {
     require_auth( to );

     eosio_assert( quantity.is_valid(), "invalid quantity" );
     eosio_assert( quantity.amount > 0, "must withdraw positive quantity" );

     auto itr = accounts.find( to );
     eosio_assert(itr != accounts.end(), "unknown account");

     accounts.modify( itr, 0, [&]( auto& acnt ) {
        eosio_assert( acnt.balance >= quantity, "insufficient balance" );
        acnt.balance -= quantity;
     });

     action(
        permission_level{ _self, N(active) },
        N(eosio.token), N(transfer),
        std::make_tuple(_self, to, quantity, std::string(""))
     ).send();

     if( itr->is_empty() ) {
        accounts.erase(itr);
     }
  }

  //@abi action
  void reset() {
      //
     require_auth( _self );

     auto cur_gbet_itr = gbet.begin();
     while(cur_gbet_itr != gbet.end()) {
         cur_gbet_itr = gbet.erase(cur_gbet_itr);
     }

     auto cur_bet_itr = bets.begin();
     while(cur_bet_itr != bets.end()) {
         cur_bet_itr = bets.erase(cur_bet_itr);
     }
  }

private:
//@abi table bet i64
struct bet {
uint64_t id;
account_name owner;
asset balance;
uint64_t betid;
uint64_t info;

uint64_t primary_key()const { return id; }
     uint64_t by_betid()const { return betid; }
     account_name by_owner() const { return owner; }

     EOSLIB_SERIALIZE( bet, (id)(owner)(balance)(betid)(info) )
  };

  typedef eosio::multi_index< N(bet), bet,
     indexed_by< N(bybetid), const_mem_fun<bet, uint64_t, &bet::by_betid > >,
     indexed_by< N(byowner), const_mem_fun<bet, account_name, &bet::by_owner > > > bet_index;

  //@abi table gbet i64
  struct gbet {
     uint64_t id;
     uint64_t betid;
     uint64_t betname;
     uint64_t open = 0;
     uint64_t reveal = 0;

     uint64_t primary_key()const { return id; }

     EOSLIB_SERIALIZE( gbet, (id)(betid)(betname)(open)(reveal) )
  };

  typedef eosio::multi_index< N(gbet), gbet> gbet_index;

  //@abi table account i64
  struct account {
     account( account_name o = account_name() ):owner(o){
     }

     account_name owner;
     asset        balance;

     bool is_empty()const { return !( balance.amount); }
     uint64_t primary_key()const { return owner; }

     EOSLIB_SERIALIZE( account, (owner)(balance) )
  };

  typedef eosio::multi_index< N(account), account> account_index;

  bet_index         bets;
  gbet_index        gbet;
  account_index     accounts;

};

EOSIO_ABI( dice, (offerbet)(canceloffer)(openbet)(closurebet)(reveal)(deposit)(withdraw)(reset) )
 

 

8、合約ABI
 

 

{

  "____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-03-29T02:09:11",   "types": [],   "structs": [{       "name": "bet",       "base": "",       "fields": [{           "name": "id",           "type": "uint64"         },{           "name": "owner",           "type": "account_name"         },{           "name": "balance",           "type": "asset"         },{           "name": "betid",           "type": "uint64"         },{           "name": "info",           "type": "uint64"         }       ]     },{       "name": "gbet",       "base": "",       "fields": [{           "name": "id",           "type": "uint64"         },{           "name": "betid",           "type": "uint64"         },{           "name": "betname",           "type": "uint64"         },{           "name": "open",           "type": "uint64"         },{           "name": "reveal",           "type": "uint64"         }       ]     },{       "name": "account",       "base": "",       "fields": [{           "name": "owner",           "type": "account_name"         },{           "name": "balance",           "type": "asset"         }       ]     },{       "name": "offerbet",       "base": "",       "fields": [{           "name": "bet",           "type": "asset"         },{           "name": "player",           "type": "account_name"         },{           "name": "info",           "type": "uint64"         }       ]     },{       "name": "canceloffer",       "base": "",       "fields": [{           "name": "player",           "type": "account_name"         }       ]     },{       "name": "openbet",       "base": "",       "fields": [       ]     },{       "name": "closurebet",       "base": "",       "fields": [       ]     },{       "name": "reveal",       "base": "",       "fields": [       ]     },{       "name": "deposit",       "base": "",       "fields": [{           "name": "from",           "type": "account_name"         },{           "name": "quantity",           "type": "asset"         }       ]     },{       "name": "withdraw",       "base": "",       "fields": [{           "name": "to",           "type": "account_name"         },{           "name": "quantity",           "type": "asset"         }       ]     },{       "name": "reset",       "base": "",       "fields": [       ]     }   ],   "actions": [{       "name": "offerbet",       "type": "offerbet",       "ricardian_contract": ""     },{       "name": "canceloffer",       "type": "canceloffer",       "ricardian_contract": ""     },{       "name": "openbet",       "type": "openbet",       "ricardian_contract": ""     },{       "name": "closurebet",       "type": "closurebet",       "ricardian_contract": ""     },{       "name": "reveal",       "type": "reveal",       "ricardian_contract": ""     },{       "name": "deposit",       "type": "deposit",       "ricardian_contract": ""     },{       "name": "withdraw",       "type": "withdraw",       "ricardian_contract": ""     },{       "name": "reset",       "type": "reset",       "ricardian_contract": ""     }   ],   "tables": [{       "name": "bet",       "index_type": "i64",       "key_names": [         "id"       ],       "key_types": [         "uint64"       ],       "type": "bet"     },{       "name": "gbet",       "index_type": "i64",       "key_names": [         "id"       ],       "key_types": [         "uint64"       ],       "type": "gbet"     },{       "name": "account",       "index_type": "i64",       "key_names": [         "owner"       ],       "key_types": [         "account_name"       ],       "type": "account"     }   ],   "ricardian_clauses": [] }

相關文章
相關標籤/搜索