前言:本文主要介绍合约实现竞标逻辑以及合约中使用到的语法解释
一、竞标拍卖原理规则(也叫第二价格密封拍卖):
见: 维克瑞拍卖_百度百科
二、关系图:
三、合约代码
pragma solidity ^0.4.18;
contract EbayStore{
uint public productIndex;
function EbayStore() public{
productIndex =0;
}
enum ProductStatus{
Open,Sold,Unsold
}
enum ProductCondition{
New,Used
}
struct Bid{
address bidder;
uint productId;
uint value;
bool revealed; //是否公告
}
//存储所有商品,mapping类似数组,根据前面的条件可以获得最后一个参数的数据
mapping(address=>mapping(uint=>Product)) stores;
//存储商品ID与所有者的mapping
mapping(uint=>address) productIdInStore;
struct Product{
uint id;
string name;
string category;//分类
string imageLink;
string descLink;
uint auctionStartTime; //开始竞标时间
uint auctionEndTime; //结束竞标时间
uint startPrice;//拍卖价格
address highestBidder;//赢家的钱包地址
uint highestBid; //竞标的价格
uint secondHighestBid; //第二高出价
uint totalBids; //总共多少人参与竞标
ProductStatus status;
ProductCondition condition;
mapping(address => mapping(bytes32 => Bid)) bids;
}
function addProductToStore(string _name, string _category, string _imageLink, string _descLink,
uint _auctionStartTime, uint _auctionEndTime, uint _startPrice, uint _productCondition) public{
require(_auctionStartTime < _auctionEndTime);
productIndex ++;
Product memory product = Product(productIndex, _name, _category, _imageLink, _descLink, _auctionStartTime, _auctionEndTime, _startPrice, 0, 0, 0, 0, ProductStatus.Open, ProductCondition(_productCondition));
stores[msg.sender][productIndex] = product;
productIdInStore[productIndex] = msg.sender;
}
function getProduct(uint _productId) view public returns(uint,string,string,string,string,uint,uint,uint,ProductStatus,ProductCondition){
Product memory product = stores[productIdInStore[_productId]][_productId];
return (product.id, product.name, product.category, product.imageLink, product.descLink, product.auctionStartTime, product.auctionEndTime, product.startPrice, product.status, product.condition);
}
//出价竞标
function bid(uint _productId,bytes32 _bid) payable public returns(bool) {
Product storage product = stores[productIdInStore[_productId]][_productId];
require(now >= product.auctionStartTime);
require(now >= product.auctionEndTime);
require(msg.value > product.startPrice); //mask的值需要大于初始竞标价
product.bids[msg.sender][_bid] = Bid(msg.sender,_productId,msg.value,false);
product.totalBids++;
return true;
}
//公示_amount:实际的价格
function revealBid(uint _productId,string _amount,string _secret){
Product storage product = stores[productIdInStore[_productId]][_productId];//从mapping中获取product对象
//条件判断
require(now > product.auctionEndTime);
bytes32 sealedBid = sha3(_amount,_secret);//将实际出价+密钥加密成哈希
Bid memory bidInfo = product.bids[msg.sender][sealedBid];//取出投标者的投标对象
require(bidInfo.bidder > 0);
require(bidInfo.revealed == false); //之前没有公示
uint refund;//退款金额
uint amount = stringToUint(_amount); //好奇为什么不直接传uint的amount,导致这里需要转换一次
//mask金额 小于实际报价,则直接全额退款
if(bidInfo.value < amount ) {
refund = bidInfo.value;
}else {
//如果是第一次出价
if(product.highestBidder == 0){
product.highestBidder = msg.sender;
product.highestBid = amount;
product.secondHighestBid = product.startPrice;
refund = bidInfo.value - amount;
}else{
if(amount > product.highestBid){
product.secondHighestBid = product.highestBid;
//退款,往原最高金额的账户转账之前的最高金额
product.highestBidder.transfer(product.highestBid);
product.highestBid = amount;
product.highestBidder = bidInfo.bidder;//?与源码不一致,按道理bidInfo.address==msg.sender
//当前投标者还有退款金额
refund = bidInfo.value - amount;
}else{
refund = amount;
}
}
}
if(refund > 0){
msg.sender.transfer(refund);
product.bids[msg.sender][sealedBid].revealed = true;
}
}
//工具包
function highestBidderInfo(uint _productId) view public returns(address,uint,uint){
Product memory product = stores[productIdInStore[_productId]][_productId];
return (product.highestBidder,product.highestBid,product.secondHighestBid);
}
function totalBids(uint _productId) public returns(uint){
Product memory product = stores[productIdInStore[_productId]][_productId];
return product.totalBids;
}
//根据ascii转uint规则
function stringToUint(string _str) public returns(uint){
bytes memory b = bytes(_str);
uint result = 0;
for(uint i=0;i=48 && b[i] <= 57){
result = result*10 + (uint(b[i]) - 48);
}
}
return result;
}
}
然后再重新编译compile,部署migrate(注意删除原有编译的合约文件)
四、遇到的问题
如果是手动一个个敲出来的代码,那么应该会遇到一下一些问题。
1、更改sol合约代码之后,compile后重新migrate,会出现以下错误
Error: Attempting to run transaction which calls a contract function, but recipient address 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 is not a contract address
解决办法:
将build的合约删除。(删除目录:build/contracts),再重新compile、migrate即可。
2、struct会有默认的构造方法嘛? contract呢
如在addProductToStore函数中的第三行
Product memory product = Product(productIndex,......);product初始化
struct在初始化时按照顺序struct(param1,param2...)初始化,但是忽略mapping。如
struct A{
uint i;
string j;
mapping map;
string m;
}
初始化可以是:
A memory a=A(1,"str","str");
3、memory 与 storage 的作用
memory :值传递(默认)
storage: 指针传递
详见:https://blog.csdn.net/liyuechun520/article/details/78408588
4、msg.sender是默认有值吗,如addProduct
msg.sender总是存放着当前函数的外部调用者的地址。
5、提示 CONNECTION ERROR: Couldn't connect to node http://127.0.0.1:9545/
很奇怪,我的truffle.js中明明已经是8545端口了,但这里却报错9545
我的做法:
重新vi编辑了一下truffle.js文件,保存,然后重新truffle develop
在控制端输入web3.accounts就正常了。
目前还是出现该问题了,之前的做法不生效,先留着
6、view、constant、pure修饰的区别
constant:用于返回状态变量时
pure:用于返回写死的常量时,如下图的“HelloWorld”
view:以上情况之外,如:msg.sender