基于以太坊的次高價盲拍solidity智能合約(一)

1.功能描述

該合約主要功能描述如下:
用戶A發(fā)起盲拍項目,即提出拍賣申請。

競標(biāo)原則

1.所有人都可以對任何一個盲拍項目發(fā)出拍賣申請。
2.競拍用戶每次出價都需要給出一個對外可見的價格(顯式價格),同時給出一個自己心里認(rèn)為可以接受的價格(隱式價格)。顯式價格在競拍階段其他人是可以看見的,但是隱式價格別人看不見。因為這個隱式價格被該用戶利用自己定的密碼加密。即每個人競標(biāo)時輸入兩個數(shù)字和一個密碼,隱式價格和密碼直接在remix編輯器中以參數(shù)的形式輸入,顯式價格以value的形式將以太坊輸入合約內(nèi)。也就是說如果我想買這個東西準(zhǔn)備花10eth,但是我想放出假話我要用12eth來買,我就要先將12eth打入這個合約內(nèi)。
3.用戶可以重復(fù)對一個商品進(jìn)行多次競拍(隱式價格可以改,但是你需要記住你最高的一次競拍的密碼和隱式價格),如果由于誤操作導(dǎo)致隱式價格和密碼兩次都輸入一樣,那么第一次輸入合約的以太坊將無法返還。這點需要注意!
4.顯式價格必須大于隱式價格,否則在揭標(biāo)的時候會被認(rèn)作無效標(biāo)。無效競標(biāo)可以提交,用于迷惑競爭者,揭標(biāo)時系統(tǒng)會自動退款。
5.競標(biāo)成功的人最終會以合約內(nèi)所有揭標(biāo)后的次高價購買到商品(買家花費的錢比心理預(yù)期少,賣家至少會得到起拍價)。

揭標(biāo)原則

1.只能在競標(biāo)仲裁之間進(jìn)行
2.競標(biāo)者在揭標(biāo)的相關(guān)函數(shù)中輸入每個發(fā)起過的競標(biāo)的隱式價格和密碼。這樣每一次發(fā)起的競標(biāo)會由合約進(jìn)行判斷處理,如果不符合獲勝條件會自動返回給競標(biāo)發(fā)起人。(如果兩次競標(biāo)同一個隱式價格和密碼,只能對最后一次的標(biāo)進(jìn)行揭標(biāo),前面標(biāo)的錢將丟失在合約中無法退回)
3.揭標(biāo)的時候會一直動態(tài)更新當(dāng)前產(chǎn)品的最高價,次高價和出價人。這時候的所有價格都是隱式價格。由于這個時候已經(jīng)無法競標(biāo),所以公開隱式價格已經(jīng)無所謂了,同時也可以讓競標(biāo)成功者知道次高價為多少。
4.競標(biāo)失敗的人會得到系統(tǒng)發(fā)回來的退款。
5.最高價的人得到顯式價格與隱式價格的差額。剩余在合約內(nèi)的前為隱式價格,到仲裁投票后會將與次高位出價的差額退回。
6.如果參與競標(biāo)但是未及時揭標(biāo),無法自動退款。所以揭標(biāo)是必須要做的!

仲裁原則

1.仲裁者不允許是買家或競標(biāo)者,會有require()進(jìn)行判斷。

  1. 創(chuàng)建一個合約,將次高價金額、買家、賣家、仲裁人信息轉(zhuǎn)到第該合約內(nèi)。
  2. 仲裁結(jié)束后,將差額(最高價-次高價的)退還給競標(biāo)獲勝的買家。

仲裁投票原則

1.只有賣家,競標(biāo)獲勝者和仲裁者可以參與投票。每個人只能投票一次,投票有兩個選擇,一個是支持付款給賣家,一個是付款給買家。這個過程是為了防止賣家不發(fā)貨,或者買家拿到東西不愿意付錢設(shè)置的。2.兩個選擇得到票數(shù)先到達(dá)兩票時,合約會執(zhí)行對應(yīng)的付款行為。

我所使用的solidity編譯器版本為0.5.0版本

代碼實現(xiàn)

1.定義競標(biāo)商品結(jié)構(gòu)

pragma solidity ^0.5.0;

contract Auction {
    uint productIndex;
    struct Product{
        uint id;
        string name;
        string category;
        //圖片的鏈接
        string imageLink;
        //商品描述信息的鏈接
        string descLink;
        uint startPrice;
        uint revealStartTime;
        uint arbitrateStartTime;
        //商品的狀態(tài):競標(biāo)中,賣出成功,賣出失敗
        ProductStatus status;
        //新的,或者二手的
        ProductCondition condition;
        //最高價格
        uint highestBid;
        //次高價格
        uint secondHighestBid;
        //最高出價者
        address payable highestBidder;
        //所有的競標(biāo)人數(shù)
        uint totalBids;
    }
    enum ProductStatus {OPEN,SOLD,UNSOLD}
    enum ProductCondition {USED,NEW}
}

2.賣家添加競拍商品至商城

在合約中需要添加兩個mapping結(jié)構(gòu),用于賣家-商品-商品id之間相互查找

 mapping(address=>mapping(uint=>Product)) stores;
 mapping(uint=>address payable) public productIdToOwmer;

添加函數(shù)

function addProductToStore(
   string memory _name,
   string memory _category,
   string memory _imageLink,
   string memory _descLink,
   uint _startPrice,
   uint _revealStartTime,
   uint _arbitrateStartTime,
   ProductCondition _condition) public{
       productIndex++;
       Product memory pro =  Product({
           id:productIndex,
           name:_name,
           category:_category,
           imageLink:_imageLink,
           descLink:_descLink,
           startPrice:_startPrice,
           revealStartTime:_revealStartTime,
           arbitrateStartTime:_arbitrateStartTime,
           status:ProductStatus.OPEN,
           condition:_condition,
           highestBid:0,
           secondHighestBid:0,
           highestBidder:address(0),
           totalBids:0
       });
       stores[msg.sender][productIndex]=pro;
       productIdToOwmer[productIndex]=msg.sender;
   }

3.競標(biāo)

定義標(biāo)結(jié)構(gòu)

struct Bid {
        //對應(yīng)拍賣品的ID
        uint productId;
        //該標(biāo)中的顯式價格
        uint price2Show;
        //是否揭標(biāo)標(biāo)志位,為了保證每個標(biāo)只能被揭一次
        bool isRevealed;
        //本次投標(biāo)的投標(biāo)人地址
        address bidder;
    }

由于每個人都可以對任何產(chǎn)品發(fā)起投標(biāo),所以我們需要為每個商品添加一個管理競標(biāo)的數(shù)據(jù)結(jié)構(gòu)。
設(shè)計思路:由于不想對心里的隱式價格保密,同時還希望能找到自己能找到該標(biāo),別人找不到,設(shè)計數(shù)據(jù)結(jié)構(gòu)如下:

mapping(address=>mapping(bytes32=>Bid)) bids;

address是競標(biāo)人地址,byte32是隱式價格和密碼字符串packed后生成的哈希。這樣,這有自己能找到該標(biāo)。
注意:如果多次競標(biāo)時,輸入與上次相同的隱式價格和密碼,所對應(yīng)的hash一樣。這樣導(dǎo)致key被覆蓋,上一次的標(biāo)就找不到了。這也是前面所提的,為什么輸入相同的隱式價格和密碼字符串時,上一次的競標(biāo)將丟失,里面的以太坊也將丟失在合約內(nèi)無法退出的原因。
將該數(shù)據(jù)結(jié)構(gòu)加入Product結(jié)構(gòu)體

struct Product{
    ...
    mapping(address=>mapping(bytes32=>Bid)) bids;
    ...
    }

競標(biāo)函數(shù)

 function bid(uint _productIndex,uint _idealPrice,string memory _password) public payable{
        //對隱式價格和密碼打包并求哈希    
        bytes memory bidInfo = abi.encodePacked(_idealPrice,_password);
        bytes32 bytesInfo = keccak256(bidInfo);
        //通過商品編號找到該拍賣商品的實例
        address owner = productIdToOwmer[_productIndex];
        Product storage product = stores[owner][_productIndex];
        //確保只能在揭標(biāo)前發(fā)起競標(biāo)
         require(now<product.revealStartTime);
        product.totalBids++;
        //添加標(biāo)進(jìn)管理數(shù)據(jù)結(jié)構(gòu)
        Bid memory b = Bid(_productIndex, msg.value, false, msg.sender);
        product.bids[msg.sender][bytesInfo]=b;
    }

此時可以寫一些輔助函數(shù)來進(jìn)行代碼階段性測試

    function getBidById(uint _productIndex,uint _idealPrice,string memory _password) public view returns(uint,uint,bool,address){
        address owner = productIdToOwmer[_productIndex];
        bytes memory bidInfo = abi.encodePacked(_idealPrice,_password);
        bytes32 bidBytes = keccak256(bidInfo);
        Product storage pro = stores[owner][_productIndex];
        Bid memory b = pro.bids[msg.sender][bidBytes];
        return (b.productId,b.price2Show,b.isRevealed,b.bidder);
    }

    function getBalance() public view returns(uint){
        return address(this).balance;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容