Datawhale區(qū)塊鏈編程task4

合約編寫(xiě)實(shí)戰(zhàn)實(shí)例

一生年、簡(jiǎn)單代幣合約

pragma solidity > 0.4.22;

contract Coin{

? ? //這里我們定義了一個(gè)address 作為key, uint做為value的hashTable balances; 我們還定義了一個(gè)address的變量minter;

? ? address public minter;

? ? mapping(address=>uint) balances;

? ? event Sent(address from, address to, uint amount);

? ? constructor(){

? ? ? ? //代表創(chuàng)建這個(gè)合約的賬戶地址惰许,被賦值給變量minter.

? ? ? ? minter = msg.sender;

? ? }

? ? //添加一個(gè)挖礦合約

? ? function mint(address receiver, uint amount) public{

? ? ? ? require(msg.sender == minter);

? ? ? ? balances[receiver] += amount;


? ? }

? ? function send(address receiver, uint amount) public{

? ? ? ? require(balances[msg.sender] >= amount);

? ? ? ? balances[msg.sender] -= amount;

? ? ? ? balances[receiver] += amount;

? ? ? ? emit Sent(msg.sender,receiver,amount);

? ? }


}

二至会、水龍頭合約

打開(kāi) faucet.sol,并寫(xiě)入如下代碼

pragma solidity ^0.7.0;

contract faucet {

? ? function withdraw (uint amount) public {

? ? ? ? require (amount <= 1e18);

? ? ? ? msg.sender.transfer (amount);

? ? }


? ? receive () external payable {}

}

通過(guò)這幾行代碼我們就實(shí)現(xiàn)了一個(gè)非常簡(jiǎn)單的水龍頭合約鹉究。首行代碼 pragma solidity ^0.7.0 是一個(gè)雜注瓣距,指定了我們的源文件使用的編譯器版本不能低于 0.7.0慨飘,也不能高于 0.8.0响疚。

contract faucet{...} 聲明了一個(gè)合約對(duì)象兼都,合約對(duì)象類(lèi)似面向?qū)ο笳Z(yǔ)言中的類(lèi),對(duì)象名必須跟文件名相同稽寒。

接下來(lái)通過(guò) function withdraw (uint amount) public {...} 創(chuàng)建了一個(gè)名為 withdraw 的函數(shù)扮碧,該函數(shù)接收一個(gè)無(wú)符號(hào)整數(shù)(uint)作為參數(shù),并且被聲明為 public 函數(shù)杏糙,意為可以被其他合約調(diào)用慎王。

withdraw 函數(shù)體中的 require 是 Solidity 的內(nèi)置函數(shù),用來(lái)檢測(cè)括號(hào)中的條件是否滿足宏侍。條件滿足則繼續(xù)執(zhí)行合約赖淤,條件不滿足則合約停止執(zhí)行,回撤所有執(zhí)行過(guò)的操作谅河,并拋出異常咱旱。在這里我們通過(guò) require (amount <= 1e18) 來(lái)檢測(cè)輸入的以太幣值是否小于等于1個(gè)以太。

接下來(lái)的這一行 msg.sender.transfer (amount) 就是實(shí)際的提款操作了绷耍。msg 是 Solidity 中內(nèi)置的對(duì)象吐限,所有合約都可以訪問(wèn),它代表觸發(fā)此合約的交易褂始。也就是說(shuō)當(dāng)我們調(diào)用 withdraw 函數(shù)的時(shí)候?qū)嶋H上觸發(fā)了一筆交易诸典,并用 msg 來(lái)表示它。sender 是交易 msg 的屬性崎苗,表示了交易的發(fā)件人地址狐粱。函數(shù) transfer 是一個(gè)內(nèi)置函數(shù),它接收一個(gè)參數(shù)作為以太幣的數(shù)量胆数,并將該數(shù)量的以太幣從合約賬戶發(fā)送到調(diào)用合約的用戶的地址中肌蜻。

最后一行是一個(gè)特殊的函數(shù) receive ,這是所謂的 fallback 或 default 函數(shù)必尼。當(dāng)合約中的其他函數(shù)無(wú)法處理發(fā)送到合約中的交易信息時(shí)蒋搜,就會(huì)執(zhí)行該函數(shù)。在這里胰伍,我們將該函數(shù)聲明為 external 和 payable 齿诞,external 意味著該函數(shù)可以接收來(lái)自外部賬戶的調(diào)用,payable 意味著該函數(shù)可以接收來(lái)自外部賬戶發(fā)送的以太幣骂租。

這樣,當(dāng)我們調(diào)用合約中的 withdraw 并提供一個(gè)參數(shù)時(shí)斑司,我們可以從這份合約中提出以太幣渗饮;當(dāng)我們向合約發(fā)送以太幣時(shí)但汞,就會(huì)調(diào)用 receive 函數(shù)往合約中捐贈(zèng)以太幣。

代碼編寫(xiě)完畢后互站,在 Remix 左側(cè)的功能欄中選擇第二項(xiàng)私蕾,并點(diǎn)擊 Compile faucet.sol 來(lái)編譯我們的 sol 文件。

三胡桃、投票合約的實(shí)現(xiàn)

以solidity文件中的contract/_Ballot.sol文件為例:

首先我們定義成員類(lèi)型踩叭,我們?yōu)槊總€(gè)投票者定義權(quán)重、是否已投票翠胰、

struct Voter {

? ? uint weight; // weight is accumulated by delegation

? ? bool voted;? // if true, that person already voted

? ? address delegate; // person delegated to

? ? uint vote;? // index of the voted proposal

}

然后我們定義提案類(lèi)型容贝,包含提案名和投票總數(shù):

struct Proposal {

? ? bytes32 name;? // short name (up to 32 bytes)

? ? uint voteCount; // number of accumulated votes

}

定義三個(gè)變量,主席是一個(gè)公開(kāi)的地址之景,建立投票者與地址的映射斤富,然后定義提案動(dòng)態(tài)數(shù)組:

address public chairperson;

mapping(address => Voter) public voters;

Proposal[] public proposals;

address public chairperson:投票發(fā)起人,類(lèi)型為 address锻狗。

mapping(address => Voter) public voters:所有投票人满力,類(lèi)型為 address 到 Voter 的映射。

Proposal[] public proposals:所有提案轻纪,類(lèi)型為動(dòng)態(tài)大小的 Proposal 數(shù)組油额。

3 個(gè)狀態(tài)變量都使用了 public 關(guān)鍵字,使得變量可以被外部訪問(wèn)(即通過(guò)消息調(diào)用)刻帚。事實(shí)上悔耘,編譯器會(huì)自動(dòng)為 public 的變量創(chuàng)建同名的 getter 函數(shù),供外部直接讀取我擂。

我們還需要為每個(gè)投票賦予初始權(quán)值衬以,并將主席的權(quán)重設(shè)置為1。我們一般使用constructor賦初值校摩,這與C++等語(yǔ)言類(lèi)似:

constructor(bytes32[] memory proposalNames) {

? ? chairperson = msg.sender;

? ? voters[chairperson].weight = 1;

? ? for (uint i = 0; i < proposalNames.length; i++) {

? ? ? ? proposals.push(Proposal({

? ? ? ? ? ? name: proposalNames[i],

? ? ? ? ? ? voteCount: 0

? ? ? ? }));

? ? }

}

所有提案的名稱通過(guò)參數(shù) bytes32[] proposalNames 傳入看峻,逐個(gè)記錄到狀態(tài)變量 proposals 中。同時(shí)用 msg.sender 獲取當(dāng)前調(diào)用消息的發(fā)送者的地址衙吩,記錄為投票發(fā)起人 chairperson互妓,該發(fā)起人投票權(quán)重設(shè)為 1。

接下來(lái)我們需要給每個(gè)投票者賦予權(quán)重:

function giveRightToVote(address voter) public {

? ? require(

? ? ? ? msg.sender == chairperson,

? ? ? ? "Only chairperson can give right to vote."

? ? );

? ? require(

? ? ? ? !voters[voter].voted,

? ? ? ? "The voter already voted."

? ? );

? ? require(voters[voter].weight == 0);

? ? voters[voter].weight = 1;

}

該函數(shù)給 address voter 賦予投票權(quán)坤塞,即將 voter 的投票權(quán)重設(shè)為 1冯勉,存入 voters 狀態(tài)變量。

上面這個(gè)函數(shù)只有投票發(fā)起人 chairperson 可以調(diào)用摹芙。這里用到了 require((msg.sender == chairperson) && !voters[voter].voted) 函數(shù)灼狰。如果 require 中表達(dá)式結(jié)果為 false,這次調(diào)用會(huì)中止浮禾,且回滾所有狀態(tài)和以太幣余額的改變到調(diào)用前交胚。但已消耗的 Gas 不會(huì)返還份汗。

下面一段是整段代碼的重點(diǎn),其作用是委托其他人代理投票蝴簇,基本思路是:

使用require判斷委托人是否已投票(若投過(guò)票再委托則重復(fù)投票)杯活,并判斷被委托對(duì)象是否是自己

當(dāng)判斷被委托人不是0地址(主席)時(shí),被委托人代理委托人的票熬词,【繞口警告】由于被委托人也可能委托了別人旁钧,因此這里需要一直循環(huán)直到找到最后沒(méi)有委托別人的被委托人為止!

委托人找到對(duì)應(yīng)的被委托人互拾,委托人已投票(避免重復(fù)投票)

判斷被委托人是否已投票歪今,若投了票則將被委托人投的提案票數(shù)加上委托人的權(quán)重,若未投票則令被委托人的權(quán)重加上委托人的權(quán)重(以后投票自然相當(dāng)于投兩票)

注:該函數(shù)使用了 while 循環(huán)摩幔,這里合約編寫(xiě)者需要十分謹(jǐn)慎彤委,防止調(diào)用者消耗過(guò)多 Gas,甚至出現(xiàn)死循環(huán)或衡。

function delegate(address to) public {

? ? Voter storage sender = voters[msg.sender];

? ? require(!sender.voted, "You already voted.");

? ? require(to != msg.sender, "Self-delegation is disallowed.");

? ? while (voters[to].delegate != address(0)) {

? ? to = voters[to].delegate;

? ? require(to != msg.sender, "Found loop in delegation.");

? ? }

? ? sender.voted = true;

? ? sender.delegate = to;

? ? Voter storage delegate_ = voters[to];

? ? if (delegate_.voted) {

? ? proposals[delegate_.vote].voteCount += sender.weight;

? ? } else {

? ? delegate_.weight += sender.weight;

? ? }

}

投票部分僅是幾個(gè)簡(jiǎn)單的條件判斷:

function vote(uint proposal) public {

? ? ? ? Voter storage sender = voters[msg.sender];

? ? ? ? require(sender.weight != 0, "Has no right to vote");

? ? ? ? require(!sender.voted, "Already voted.");

? ? ? ? sender.voted = true;

? ? ? ? sender.vote = proposal;

? ? ? ? proposals[proposal].voteCount += sender.weight;

? ? }

用 voters[msg.sender] 獲取投票人焦影,即此次調(diào)用的發(fā)起人。接下來(lái)檢查是否是重復(fù)投票封断,如果不是斯辰,進(jìn)行投票后相關(guān)狀態(tài)變量的更新。

接下來(lái)是計(jì)算獲勝提案:

function winningProposal() public view

? ? ? ? returns (uint winningProposal_)

{

? ? uint winningVoteCount = 0;

? ? for (uint p = 0; p < proposals.length; p++) {

? ? ? ? if (proposals[p].voteCount > winningVoteCount) {

? ? ? ? ? ? winningVoteCount = proposals[p].voteCount;

? ? ? ? ? ? winningProposal_ = p;

? ? ? ? }

? ? }

}

returns (uint winningProposal) 指定了函數(shù)的返回值類(lèi)型坡疼,constant 表示該函數(shù)不會(huì)改變合約狀態(tài)變量的值彬呻。

最后是查詢獲勝者名稱:

function winnerName() public view

? ? ? ? returns (bytes32 winnerName_)

{

? ? winnerName_ = proposals[winningProposal()].name;

}

這里采用內(nèi)部調(diào)用 winningProposal() 函數(shù)的方式獲得獲勝提案。如果需要采用外部調(diào)用柄瑰,則需要寫(xiě)為 this.winningProposal()闸氮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市教沾,隨后出現(xiàn)的幾起案子蒲跨,更是在濱河造成了極大的恐慌,老刑警劉巖授翻,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件或悲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堪唐,警方通過(guò)查閱死者的電腦和手機(jī)巡语,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淮菠,“玉大人男公,你說(shuō)我怎么就攤上這事《挡模” “怎么了理澎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵逞力,是天一觀的道長(zhǎng)曙寡。 經(jīng)常有香客問(wèn)我糠爬,道長(zhǎng),這世上最難降的妖魔是什么举庶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任执隧,我火速辦了婚禮,結(jié)果婚禮上户侥,老公的妹妹穿的比我還像新娘镀琉。我一直安慰自己,他們只是感情好蕊唐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布屋摔。 她就那樣靜靜地躺著,像睡著了一般替梨。 火紅的嫁衣襯著肌膚如雪钓试。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天副瀑,我揣著相機(jī)與錄音弓熏,去河邊找鬼。 笑死糠睡,一個(gè)胖子當(dāng)著我的面吹牛挽鞠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狈孔,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼信认,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了均抽?” 一聲冷哼從身側(cè)響起嫁赏,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎到忽,沒(méi)想到半個(gè)月后橄教,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喘漏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年护蝶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩迈。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡持灰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出负饲,到底是詐尸還是另有隱情堤魁,我是刑警寧澤喂链,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站妥泉,受9級(jí)特大地震影響椭微,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盲链,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一蝇率、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刽沾,春花似錦本慕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至布蔗,卻和暖如春藤违,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背何鸡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工纺弊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骡男。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓淆游,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親隔盛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子犹菱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容