1.imageHash
就是將punk全部圖像合在一塊兒的那張圖punks.png進行hash獲得一個值,並將該值存儲到鏈上,用處就是你能夠經過將圖像hash而後跟該值對比看圖像對不對。這就是它的用處,在代碼中它沒用。即該圖punks.png,在https://github.com/larvalabs/cryptopunks/tree/master/test能獲得:html
2.函數簡單介紹
CryptoPunksMarket
建立10000個token,該合同的擁有者即該合約的部署者owner = msg.sender
setInitialOwner\setInitialOwners
將token初始無償送給一些用戶的函數,只有合同擁有者才能調用這兩個函數(if (msg.sender != owner) throw;)當變量allPunksAssigned爲false時這兩個函數才能調用,這時候其餘的函數都不能調用。該兩個函數是在合同剛部署時使用的,由於當allPunksAssigned被設爲true後,這兩個函數將永遠不能被調用。即便一個token剛剛送給了別的用戶,合同擁有者仍是能經過函數setInitialOwner將其拿回,轉送給另外一個用戶
allInitialOwnersAssigned
將變量allPunksAssigned設爲true,這樣,上面的兩個無償贈送的函數就不能使用了,只能使用下面的其餘函數
getPunk
用戶仍是能夠經過這份函數來無償得到token,不一樣之處在於:
首先不須要是合同擁有者也能調用這個函數,可是這有那些無主的token才能被get
transferPunk
把本身擁有的token轉贈給別的用戶
punkNoLongerForSale
把以前標價打算要賣出去的token取消了,就是不打算sell了
offerPunkForSale
把本身的token標一個最低價想要賣出
offerPunkForSaleToAddress
把本身的token標一個最低價想要賣給一個指定的用戶
buyPunk
購買那些標價賣出的token
withdraw
將本身臨時帳戶中的錢取出來
enterBidForPunk
進入到某個token的競標市場,只有出價比以前最高價高時該競標纔回成功,成爲最高價
acceptBidForPunk
sell方接受競標
withdrawBidForPunk
競標方放棄競標
3.整個代碼的詳細介紹git
pragma solidity ^0.4.8; contract CryptoPunksMarket { // 圖片的hash值,用於驗證圖片文件的正確性 string public imageHash = "ac39af4793119ee46bbff351d8cb6b5f23da60222126add4268e261199a2921b"; //該合約的擁有者聲明 address owner; //該token的一些定義信息 string public standard = 'CryptoPunks'; string public name; string public symbol; uint8 public decimals; //token的總量聲明 uint256 public totalSupply; //下一個被分配的token的索引,從0開始 uint public nextPunkIndexToAssign = 0; //用於上鎖函數 bool public allPunksAssigned = false; //剩餘的可以進行分配的token數量 uint public punksRemainingToAssign = 0; //將token索引映射到擁有者的地址,punkIndexToAddress[0]即獲得索引爲0的token的擁有者地址 mapping (uint => address) public punkIndexToAddress; //用戶所擁有的token的數量 mapping (address => uint256) public balanceOf; //聲明賣token的結構體 struct Offer { bool isForSale;//true即token正在賣出 uint punkIndex; address seller;//賣方,及其主人 uint minValue; // in ether,賣出最低價 address onlySellTo; //用於設置賣給誰,不設則賣給誰均可以 } //聲明競標token的結構體 struct Bid { bool hasBid;//是否正在競標 uint punkIndex; address bidder; uint value; } //經過token索引來查看該token的賣出信息,也可能沒有 mapping (uint => Offer) public punksOfferedForSale; //經過token索引來查看該token的最高價競標信息,也可能沒有 mapping (uint => Bid) public punkBids; //用戶的臨時帳戶,存放其賣token的回報或者競標失敗退回的競標額 mapping (address => uint) public pendingWithdrawals; //用於事件監聽 event Assign(address indexed to, uint256 punkIndex); event Transfer(address indexed from, address indexed to, uint256 value); event PunkTransfer(address indexed from, address indexed to, uint256 punkIndex); event PunkOffered(uint indexed punkIndex, uint minValue, address indexed toAddress); event PunkBidEntered(uint indexed punkIndex, uint value, address indexed fromAddress); event PunkBidWithdrawn(uint indexed punkIndex, uint value, address indexed fromAddress); event PunkBought(uint indexed punkIndex, uint value, address indexed fromAddress, address indexed toAddress); event PunkNoLongerForSale(uint indexed punkIndex); //構造函數,初始化該token function CryptoPunksMarket() payable { owner = msg.sender; totalSupply = 10000; //token總數爲10000 punksRemainingToAssign = totalSupply; //餘量爲10000 name = "CRYPTOPUNKS"; // Set the name for display purposes symbol = "Ͼ"; // Set the symbol for display purposes decimals = 0; // Amount of decimals for display purposes } //合約擁有者經過調用該函數將token送給某些用戶 function setInitialOwner(address to, uint punkIndex) { if (msg.sender != owner) throw; //函數調用者必須是合約擁有者 if (allPunksAssigned) throw; //allPunksAssigned要爲false if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (punkIndexToAddress[punkIndex] != to) { //token的主人不能是要送給的用戶 if (punkIndexToAddress[punkIndex] != 0x0) {//送出去的token能夠收回再送給別的用戶 balanceOf[punkIndexToAddress[punkIndex]]--; } else { punksRemainingToAssign—; //該token以前沒被贈送過,可分配token數減1 } punkIndexToAddress[punkIndex] = to; //更換token擁有者爲to balanceOf[to]++; //to用戶的token擁有總數加1 Assign(to, punkIndex); //事件event Assign,監聽到該事件則說明該函數調用成功 } } //同時將多個token送給多個用戶 function setInitialOwners(address[] addresses, uint[] indices) { if (msg.sender != owner) throw; uint n = addresses.length; for (uint i = 0; i < n; i++) { setInitialOwner(addresses[i], indices[i]); } } //將變量allPunksAssigned設置爲true,用以中止上面兩個函數的使用,開始下面函數的使用 function allInitialOwnersAssigned() { if (msg.sender != owner) throw; allPunksAssigned = true; } //任何用戶均可以經過該函數來得到無主的token function getPunk(uint punkIndex) { if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punksRemainingToAssign == 0) throw; //剩下可以分配的token數要大於0 if (punkIndexToAddress[punkIndex] != 0x0) throw; //只能獲得無主token if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 punkIndexToAddress[punkIndex] = msg.sender; balanceOf[msg.sender]++; punksRemainingToAssign--; Assign(msg.sender, punkIndex); //事件event Assign,監聽到該事件則說明該函數調用成功 } // 轉贈token function transferPunk(address to, uint punkIndex) { if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] != msg.sender) throw; //函數調用者必須是token的主人 if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (punksOfferedForSale[punkIndex].isForSale) { //若是以前想要sell這個token,則先取消sell punkNoLongerForSale(punkIndex); } punkIndexToAddress[punkIndex] = to; balanceOf[msg.sender]--; balanceOf[to]++; Transfer(msg.sender, to, 1); //事件event Transfer,監聽到該事件則說明該函數調用成功 PunkTransfer(msg.sender, to, punkIndex); //事件 // Check for the case where there is a bid from the new owner and refund it. // Any other bid can stay in place. //查看被贈方是否以前有對該token進行投標,有則取消,並將其投標的金額放進其臨時帳戶中 Bid bid = punkBids[punkIndex]; if (bid.bidder == to) { // Kill bid and refund value pendingWithdrawals[to] += bid.value; punkBids[punkIndex] = Bid(false, punkIndex, 0x0, 0);//清空投標信息,並設置狀態爲false } } //取消以前設置的token賣出信息 function punkNoLongerForSale(uint punkIndex) { if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] != msg.sender) throw; //函數調用者必須是token的主人 if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 //清空賣出offer信息,並設置狀態爲false punksOfferedForSale[punkIndex] = Offer(false, punkIndex, msg.sender, 0, 0x0); PunkNoLongerForSale(punkIndex); } //想將某token賣出,將相應信息寫入offer中,用以告知你們 function offerPunkForSale(uint punkIndex, uint minSalePriceInWei) { if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] != msg.sender) throw; //函數調用者必須是token的主人 if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 //將相應的信息寫入結構體offer中 punksOfferedForSale[punkIndex] = Offer(true, punkIndex, msg.sender, minSalePriceInWei, 0x0); PunkOffered(punkIndex, minSalePriceInWei, 0x0);//事件PunkOffered,用以告知函數調用成功 } //聲明某token想要賣給某個特定的用戶 function offerPunkForSaleToAddress(uint punkIndex, uint minSalePriceInWei, address toAddress) { if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] != msg.sender) throw; //函數調用者必須是token的主人 if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 //將相應的信息寫入結構體offer中 punksOfferedForSale[punkIndex] = Offer(true, punkIndex, msg.sender, minSalePriceInWei, toAddress); PunkOffered(punkIndex, minSalePriceInWei, toAddress);//事件PunkOffered,用以告知函數調用成功 } //購買在offer中設置爲賣出的token function buyPunk(uint punkIndex) payable { if (!allPunksAssigned) throw; //allPunksAssigned爲true Offer offer = punksOfferedForSale[punkIndex]; //token的賣出信息 if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (!offer.isForSale) throw; // token的賣出狀態要爲true //token指定的賣出用戶要是函數調用者或者沒有指定賣出用戶 if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) throw; if (msg.value < offer.minValue) throw; // 你的出價必須大於或等於offer中標出的價格 if (offer.seller != punkIndexToAddress[punkIndex]) throw; //offer中代表的賣方必須仍是token的主人 address seller = offer.seller; punkIndexToAddress[punkIndex] = msg.sender; balanceOf[seller]--; balanceOf[msg.sender]++; Transfer(seller, msg.sender, 1); //事件Transfer,說明買賣成功 punkNoLongerForSale(punkIndex); //清空賣出offer信息 pendingWithdrawals[seller] += msg.value; //將賣出的錢msg.value寫入seller的臨時帳戶 PunkBought(punkIndex, msg.value, seller, msg.sender); //事件 //若是該函數調用者以前有投標過該token,則取消,並將投標錢放入臨時帳戶中 Bid bid = punkBids[punkIndex]; if (bid.bidder == msg.sender) { // Kill bid and refund value pendingWithdrawals[msg.sender] += bid.value; punkBids[punkIndex] = Bid(false, punkIndex, 0x0, 0);//清空投標信息 } } //將臨時帳戶中的錢拿出,其實就是要求合約地址send回錢 function withdraw() { if (!allPunksAssigned) throw; //allPunksAssigned爲true uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; //清空臨時帳戶 msg.sender.transfer(amount); //合約地址send回錢amount給msg.sender,即合約調用者 } //進入某個token的投標市場 function enterBidForPunk(uint punkIndex) payable { if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] == 0x0) throw; //該token要有主 if (punkIndexToAddress[punkIndex] == msg.sender) throw; //該token的主人不是函數調用者 if (msg.value == 0) throw; //投標價格必定要大於0 Bid existing = punkBids[punkIndex]; //以前的最高價的投標信息 if (msg.value <= existing.value) throw; //出的投標價高於以前的最高價時,該投標才成功 if (existing.value > 0) {//以前投標的人的投標價會返回到它的臨時帳戶中 // Refund the failing bid pendingWithdrawals[existing.bidder] += existing.value; } punkBids[punkIndex] = Bid(true, punkIndex, msg.sender, msg.value); //覆蓋投標信息 PunkBidEntered(punkIndex, msg.value, msg.sender); //事件 } //接受目前出最高投標價的投標者的投標 function acceptBidForPunk(uint punkIndex, uint minPrice) { if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] != msg.sender) throw; //只有token主人才能接受投標 address seller = msg.sender; //賣方地址 Bid bid = punkBids[punkIndex]; //投標信息 if (bid.value == 0) throw; //投標價等於0說明沒人投標 if (bid.value < minPrice) throw; //投標價要大於接受投標的最小价格 punkIndexToAddress[punkIndex] = bid.bidder; balanceOf[seller]--; balanceOf[bid.bidder]++; Transfer(seller, bid.bidder, 1); //成功後,不管是賣出offer仍是投標bid信息都要被清空,offer中的主人會換成投標者 punksOfferedForSale[punkIndex] = Offer(false, punkIndex, bid.bidder, 0, 0x0); uint amount = bid.value; punkBids[punkIndex] = Bid(false, punkIndex, 0x0, 0); pendingWithdrawals[seller] += amount; //seller賺到的錢放入臨時帳戶 PunkBought(punkIndex, bid.value, seller, bid.bidder);//事件 } //取消對某個token的投標 function withdrawBidForPunk(uint punkIndex) { if (punkIndex >= 10000) throw; //punkIndex即token的索引要小於token總數10000 if (!allPunksAssigned) throw; //allPunksAssigned爲true if (punkIndexToAddress[punkIndex] == 0x0) throw; //該token要有主 if (punkIndexToAddress[punkIndex] == msg.sender) throw; //該token的主人不是函數調用者 Bid bid = punkBids[punkIndex]; //投標信息 if (bid.bidder != msg.sender) throw; //投標者即函數調用者 PunkBidWithdrawn(punkIndex, bid.value, msg.sender); //事件 uint amount = bid.value; punkBids[punkIndex] = Bid(false, punkIndex, 0x0, 0); //清空投標信息 // Refund the bid money msg.sender.transfer(amount); //直接將投標價send給投標者,不放入臨時帳戶 } }
注:
1.爲何要設置臨時帳戶:
由於當用戶調用定義爲payable的函數時,它經過msg.value 傳入的金額其實已經從他的帳戶中轉到了該合約地址當中,就是調用函數的交易的from爲函數調用者,to爲合約地址。當其後面想要取消該函數的調用時,好比以前投標bid了某token,後面想取消時,函數就會講bid結構體中記錄的你的投標價格放入你的臨時帳戶中,語句msg.sender.transfer(金額數)就是合約地址將該金額轉回給你了
對裏面使用到的solidity的介紹,看這裏solidity學習-cryptoPunks爲實例
github