智能合約開發之簡易球星卡交易系統

個人自制編寫了一個簡易球星卡智能合約,暫不包括前端後端

此合約是在以太坊上部署一個集卡類遊戲,玩家可以購買卡片獲得自己喜愛的稀有的球星卡,玩家也可以在交易所賣出或者買入自己中意的卡片。

功能包括:
  • getplayer 查看當前用戶擁有的卡片
  • getStarcard 查看指定卡片向合約購買的價格(ether)
  • getCreatesale 查看制定交易下標返回詳細數值
  • getallCrea 返回當前所有的交易

  • Starcardbuy 輸入指定卡片號碼與轉賬ether向合約購買卡片

  • Createsale 輸入指定卡片號碼與指定數額創建一個交易
  • Starcardbuytran 輸入指定交易下標與轉賬ether競拍交易
  • open 輸入指定下標,交易發起者可調用此合約結束競拍,最高者獲得指定卡片
代碼如下:
pragma solidity ^0.4.20;

contract Starcard {
    address Boss;
    uint[20] StarID;
    uint[20] cardID;
    mapping (address => uint[20]) balances;
    struct tran{
        address Seller;
        uint8 ofcard;
        uint price;
        uint auction;
        uint time;
        address HighestBidder;
        uint HighestPrice;
        bool status1;
    }
     tran[] Tran;
    function Starcard() public{
        Boss = msg.sender;
        for(uint8 i=0;i<20;i++){
            StarID[i]=20-i;
        }
    }
    function getStarcard(uint i) public view returns(uint,bool){
        if(i<20){
            return (StarID[i],true);
        }
        else
            return (0,false);
    }
    function Starcardbuy(uint i)payable public returns(uint[20],bool){
        if(StarID[i]==msg.value/(10**18)){
           cardID=balances[msg.sender];
           cardID[i] = cardID[i]+1;
           balances[msg.sender] = cardID;
           return(balances[msg.sender],true);
        }
        else{
            msg.sender.transfer(msg.value);
            return(balances[msg.sender],false);
        }
    }
    function getplayer() public view returns(uint[20]){
        return balances[msg.sender];
    }
    function Createsale(uint8 thecard,uint theprice) public returns(uint,bool){
        cardID = balances[msg.sender];
        tran memory tra;
        if(cardID[thecard] != 0){
            tra.Seller = msg.sender;
            tra.ofcard = thecard;
            tra.price = theprice;
            tra.auction = tra.price*6/10;
            tra.status1 = true;
            tra.time = block.number;
            Tran.push(tra);
            cardID[thecard] = cardID[thecard]-1;
            balances[msg.sender] = cardID;
            return (Tran.length-1,true);
        }
        else
            return (0,false);
    }
    function Starcardbuytran(uint _number)payable public returns(string,bool){
        if(msg.sender != Tran[_number].Seller){
            if(Tran[_number].price == msg.value/(10**18)){
                if(Tran[_number].HighestBidder != 0){
                    Tran[_number].HighestBidder.transfer(Tran[_number].HighestPrice*(10**18));
                }
                Tran[_number].Seller.transfer(msg.value*9/10);
                cardID=balances[msg.sender];
                cardID[Tran[_number].ofcard] += 1;
                balances[msg.sender] = cardID;
                delete Tran[_number];
                return ("price",true);
            }
            else if(msg.value/(10**18) >= Tran[_number].auction && msg.value/(10**18) > Tran[_number].HighestPrice){
                if(Tran[_number].HighestBidder != 0){
                    Tran[_number].HighestBidder.transfer(Tran[_number].HighestPrice*(10**18));
                }
                Tran[_number].HighestBidder = msg.sender;
                Tran[_number].HighestPrice = msg.value/(10**18);
                return ("HighestBidder",true);
            }
            else {
                msg.sender.transfer(msg.value);
                return ("Low",false);
            }
        }
        else
            return ("FAQ",false);
    }
    function open(uint ofnumber)public returns(string,bool){
        if(Tran[ofnumber].Seller == msg.sender){
            if(Tran[ofnumber].time + 6 <= block.number){
                if(Tran[ofnumber].HighestPrice != 0){
                    msg.sender.transfer(Tran[ofnumber].HighestPrice*(10**18));
                    cardID = balances[Tran[ofnumber].HighestBidder];
                    cardID[Tran[ofnumber].ofcard] += 1;
                    balances[Tran[ofnumber].HighestBidder] = cardID;
                    delete Tran[ofnumber];
                    return ("OK",true);
                }
                else
                    return ("No HighestBidder",false);
            }
             else
                 return("No time",false);
        }
        else
            return ("you no Seller",false);
    }
    function() external payable {
        if(msg.value != 0)
            msg.sender.transfer(msg.value);
    }
    function getCreatesale(uint thnumbe) public view returns(uint8,uint,uint){
        return (Tran[thnumbe].ofcard,Tran[thnumbe].price,Tran[thnumbe].HighestPrice);
    }
    function getallCrea() public view returns(uint,uint8,uint,uint){
        for(uint dubi;dubi <= Tran.length-1;dubi++){
            if(Tran[dubi].Seller != 0){
                return (dubi,Tran[dubi].ofcard,Tran[dubi].price,Tran[dubi].HighestPrice);
            }
        }
    }
}

案例:向合約轉賬並讀取合約餘額

pragma solidity ^0.4.20;
//每一份合約前都要加這一句來定義使用的solidity的版本
contract SendMoney{
//定義一個名稱爲**的合約
    function () public payable{
    }
    //一個沒有名稱的方法它會向合約轉賬
    function getBalance() constant public returns(uint){
        return address(this).balance;
    }
    /*定義了一個查看當前合約餘額的方法
      這裏getBalance()爲方法名 constant爲標明本方法並不會產生數據,public爲此方法可見returns(uint)標明此方法會返回一個uint類型的值.

        !return address(this).balance
        address(this).balance它的值爲當前合約擁有多少餘額,return會返回它*/
}

這個案例很簡單又讓人清晰看清solidity的語法,solidity向合約轉賬的方式具體有兩種(詳細的話三種),一種向合約轉賬與一種部署合約時轉賬,下面會有例子講道如何部署合約時轉賬。

address(this).balance //返回合約餘額

案例:將錢從合約中轉出

    pragma solidity ^0.4.20;
    contract GetMoney{
        address owner;
        function GetMoney() public{  //GetMoney構造函數
            owner = msg.sender; //構造時調用的函數 owner合約發起人
        }
        function () public payable{
        }
        function getBalance() constant public returns(uint){
            return address(this).balance;
        }
        function showMeTheMoney() payable public returns(bool) { //有關於錢的方法定義都要+ payable
            address who = msg.sender; //who記錄了是誰要錢
            if(getBalance() >= 1 ether) //調用getBalance方法判斷餘額>=1
            {
                who.transfer(1 ether); //向要錢的人轉出 1 個以太幣
                return true;
            }
            return false;
        }
        function kill() public{
            if (msg.sender == owner){
                selfdestruct(owner);
            }
        }
        //調用kill方法會驗證msg.sender調用者是不是owner(合約發起人)是的話殺死此合約並將合約剩餘的以太幣發送到owner(合約發起人),注意selfdestruct中可以填入其他賬戶。
    }

 ```
 這個案例比上一個案例多加了構造函數與幾個方法,理解上一個案例這個案例將很好理解。
 ```
    msg.sender //返回當前賬戶
    payable //涉及錢的變動就要加此聲明
    *.transfer(*) //想特定的賬戶轉入特定的ether
    selfdestruct(owner);//死此合約並將合約剩餘的以太幣發送到owner(合約發起人),注意selfdestruct中可以填入其他賬戶。

案例:發紅包

pragma solidity ^0.4.20;
// the Tutao put the money in contract,
// others can get Money from the redPacket
contract redPacket {
    address tuhao;
    int pieces;
    function redPacket(int _pieces) public payable {
        tuhao = msg.sender;
        pieces = _pieces;
    }
    function getBalance() public view returns (uint) { //view與constant標識基本相同,表示並不會產生數據修改
      //  return tuhao.balance; //返回tuhao的餘額 !!此條語句爲錯誤的並且會影響下面程序導致無法正常分紅包!

        return address(this).balance;//此條語句纔是正確語句,返回合約餘額
    }   
    function showMeTheMoney() payable public returns (bool) {
        address who = msg.sender; //who記錄了誰想分錢
        if(pieces > 0) //pieces爲紅包數
        {
            pieces --; //每次調用一次紅包數-1
            uint hash = uint(block.blockhash(block.number-1));
            //!這裏hash記錄了當前區塊的hash值block.blockhash(block.number)的返回值爲當前區塊的hash(哈希值)
            uint percent = hash % 100;//因爲hash值是隨機的 所以將hash值進行取餘
            if(percent < 20) percent = 20; //分到的當前紅包總金額比例不會小於20
            if(percent > 80) percent = 80;
            uint balance = getBalance(); //當前餘額,因之前返回的tuhao的餘額  所以無法正常運行程序!
            who.transfer(balance * percent / 100);
            return true;
        }
        return false;
    }
    function kill() public {
        if(msg.sender == tuhao) {
            selfdestruct(tuhao);
        }
    }
}
    block.blockhash(block.number)
    /*此函數返回了當前鏈的hash值,但hash值爲bytes32類型的數值,所以用uint類型進行強制轉換,但最終並沒有給hash變量進行賦值。
    原因猜測有可能javascript vm並沒有真正產生一個區塊的hash值。猜測二爲可能bytes32強轉uint類型是出現了問題,隨後安裝了ganache進行測試第一種猜測。*/

案例:賭大小

pragma solidity ^0.4.20;
contract Bet{
    address owner;
    struct Player {//結構體
        address addr;
        uint money;
    }
    Player[] inBig;     //定義變量
    Player[] inSmall;
    uint blockNumber;
    uint totalBig;
    uint totalSmall;
    function Bet() public{ //構造函數
        owner = msg.sender;
        blockNumber = block.number;
        totalSmall = 0;
        totalBig = 0;
    }
    function getBalance() public view returns (uint) { //查看合約餘額
        return address(this).balance;
    }
    function getBlockNumber() public view returns (uint, uint) {//查看現在距離新生成的區塊的距離(下注時間)
        return (blockNumber, block.number);
    }
    function chipin(bool big) payable public returns (bool) {
        //投錢方法,當投的錢是0的時踢出,不是0的時候可以爲獎池增加值。
        Player memory player = Player(msg.sender, msg.value);
        if(player.money == 0)
            return false;
        if (big) {
            inBig.push(player);
            totalBig += player.money;
        }
        else {
            inSmall.push(player);
            totalSmall += player.money;
        }
        return true;
    }
    function open() payable public returns (bool) {
    //開盤
        if(block.number < 2 + blockNumber) {
            return false; // 如果調用此函數時並沒有超過下注時間就會退出
        }
        if(totalSmall == 0 || totalBig == 0) {
            return false;
        }
        uint hash = uint(block.blockhash(block.number-1));
        uint points = hash % 19; //使hash值隨機到1-18
        uint count;
        uint i;
        Player memory player;
        if(points < 9) { // small win
            count = inSmall.length;
            for(i=0; i<count; ++i) {
                player = inSmall[i];
                player.addr.transfer(totalBig * player.money / totalSmall +     player.money
            }   
        }
        else { // big win
            count = inBig.length;
            for(i=0; i<count; ++i) {
                player = inBig[i];
                player.addr.transfer(totalSmall * player.money / totalBig + player.money
            }
        }
        return true;
    }
    function kill() public {
        if(msg.sender == owner) {
            selfdestruct(owner);
        }
    }
}

這個案例的賭博規則有些簡陋,但它的確實現了一個賭大小的合約,並且可以通過返回錢的函數進行抽成,但還是有很多的漏洞,例如kill合約創建者在開沒開盤的情況下就能捲了錢跑了等小漏洞外還是讓我學習了很多。


發佈了44 篇原創文章 · 獲贊 7 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章