天天看點

智能合約開發入門第一步:正确認識并了解合約代碼的含義

作者:識鍊科技

Shenzhen Shilian Information Technology Co., Ltd.

深圳市識鍊資訊科技有限公司

智能合約開發入門第一步:正确認識并了解合約代碼的含義

智能合約開發第一步,在于要認識與了解合約代碼的含義。接下來讓我們先看一下最基本的例子。現在就算你都不了解也不要緊,後面我們會有更深入的講解。

1、存儲合約示例

把一個資料儲存到鍊上

// SPDX-License-Identifier: GPL-3.0

pragma solidity>=0.4.16<0.9.0;

contractSimpleStorage{

uintstoredData;

functionset(uintx)public{

storedData=x;

}

functionget()public view returns(uint){

returnstoredData;

}

}

第一行是說明源代碼是根據GPL 3.0版本授權的。預設情況下,在釋出源代碼時加入機器可讀許可證說明是很重要的,

下一行是告訴編譯器源代碼所适用的Solidity版本為>=0.4.16 及 pragma是告知編譯器如何處理源代碼的通用指令(例如,pragma once)。

Solidity中智能合約的含義就是一組代碼(它的功能)和資料(它的狀态)的集合,并且它們是位于以太坊區塊鍊的一個特定位址上的。uintstoredData;這一行代碼聲明了一個名為``storedData``的狀态變量,其類型為uint(256位無符号整數)。 你也可以認為它是資料庫裡的一個插槽,并且可以通過調用管理資料庫代碼的函數進行查詢和更改。在這個例子中,上述的合約定義了``set``和``get`` 函數,可以用來修改或檢索變量的值。

要通路目前合約的成員(如:狀态變量),通常不需要像添加this.這樣的字首,你隻需要通過名字就可以直接通路它。 與其他一些語言不同的是,省略它不僅僅是一個風格問題,因為它是一種完全不同的通路成員的方式,這一塊後面會詳細介紹。

該合約能完成的事情并不多(由于以太坊建構的基礎架構的原因):它能允許任何人在合約中存儲一個單獨的數字,并且這個數字可以被世界上任何人通路,且沒有可行的辦法阻止你釋出這個數字。當然,任何人都可以再次調用set,傳入不同的值,覆寫你的數字,但是這個數字仍會被存儲在區塊鍊的曆史記錄中。随後,我們會看到怎樣施加通路限制,以確定隻有你才能改變這個數字。

2、貨币合約(Subcurrency)示例

下面的合約實作了一個最簡單的加密貨币。這裡,币确實可以無中生有地産生,但是隻有建立合約的人才能做到(實作一個不同的發行計劃也不難)。而且,任何人都可以給其他人轉币,不需要注冊使用者名和密碼 —— 所需要的隻是以太坊密鑰對。

// SPDX-License-Identifier: GPL-3.0

pragma solidity^0.8.4;

contractCoin{

// 關鍵字“public”讓這些變量可以從外部讀取

addresspublic minter;

mapping(address=>uint)publicbalances;

// 輕用戶端可以通過事件針對變化作出高效的反應

eventSent(addressfrom,addressto,uintamount);

// 這是構造函數,隻有當合約建立時運作

constructor(){

minter =msg.sender;

}

functionmint(addressreceiver,uintamount)public{

require(msg.sender== minter);

balances[receiver]+=amount;

}

// Errors allow you to provide information about

// why an operation failed. They are returned

// to the caller of the function.

error InsufficientBalance(uintrequested,uintavailable);

functionsend(addressreceiver,uintamount)public{

if(amount > balances[msg.sender])

revert InsufficientBalance({

requested: amount,

available: balances[msg.sender]

});

balances[msg.sender]-= amount;

balances[receiver]+=amount;

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

}

}

這個合約引入了一些新的概念,讓我們逐一解讀。

addresspublicminter;這一行聲明了一個可以被公開通路的address類型的狀态變量。address類型是一個160位的值,且不允許任何算數操作。這種類型适合存儲合約位址或外部人員的密鑰對。關鍵字public自動生成一個函數,允許你在這個合約之外通路這個狀态變量的目前值。如果沒有這個關鍵字,其他的合約沒有辦法通路這個變量。由編譯器生成的函數的代碼大緻如下所示(暫時忽略 external 和 view):

functionminter()external view returns(address){return minter;}

當然,加一個和上面完全一樣的函數是行不通的,因為我們會有同名的一個函數和一個變量,這裡,主要是希望你能明白——編譯器已經幫你實作了。

下一行,mapping(address=>uint)publicbalances;也建立一個公共狀态變量,但它是一個更複雜的資料類型。 該類型将address映射為無符号整數。 Mappings 可以看作是一個哈希表它會執行虛拟初始化,以使所有可能存在的鍵都映射到一個位元組表示為全零的值。 但是,這種類比并不太恰當,因為它既不能獲得映射的所有鍵的清單,也不能獲得所有值的清單。 是以,要麼記住你添加到mapping中的資料(使用清單或更進階的資料類型會更好),要麼在不需要鍵清單或值清單的上下文中使用它,就如本例。 而由public關鍵字建立的getter函數getter function則是更複雜一些的情況, 它大緻如下所示:

functionbalances(addressaccount)external view returns(uint){

return balances[account];

}

正如你所看到的,你可以通過該函數輕松地查詢到賬戶的餘額。

eventSent(addressfrom,addressto,uintamount);這行聲明了一個所謂的“事件(event)”,它會在send函數的最後一行被發出。使用者界面(當然也包括伺服器應用程式)可以監聽區塊鍊上正在發送的事件,而不會花費太多成本。一旦它被發出,監聽該事件的listener都将收到通知。而所有的事件都包含了from,to和amount三個參數,可友善追蹤交易。 為了監聽這個事件,你可以使用如下JavaScript代碼(假設 Coin 是已經通過web3.js 建立好的合約對象):

Coin.Sent().watch({},'',function(error,result){

if(!error){

console.log("Coin transfer: "+result.args.amount+

" coins were sent from "+result.args.from+

" to "+result.args.to+".");

console.log("Balances now:\n"+

"Sender: "+Coin.balances.call(result.args.from)+

"Receiver: "+Coin.balances.call(result.args.to));

}

})

這裡請注意自動生成的balances函數是如何從使用者界面調用的。

特殊函數constructor是僅在建立合約期間運作的構造函數,不能在建立之後調用。 在 Coin 合約中,構造函數永久存儲建立合約的人的位址:msg(類似的還有tx和block) 是一個特殊的全局變量, 參考特殊變量和函數,這些變量允許我們通路區塊鍊的屬性。msg.sender始終記錄目前(外部)函數調用是來自于哪一個位址。

最後,真正被使用者或其他合約所調用的,以完成本合約功能的方法是mint和send。

mint函數用來新發行一定數量的币到一個位址。require用來檢查某些條件,如果不滿足這些條件就會回推所有的狀态變化。 在這個例子中,require(msg.sender==minter);確定隻有合約的建立者可以調用mint。 一般來說,建立者可以随心所欲地鑄造代币,但在某些時候,這将導緻一種叫做 “溢出” 的現象。

請注意,由于預設的算術檢查模式,如果表達式balances[receiver]+=amount;溢出交易将被還原。 即當任意精度算術中的balances[receiver]+amount大于uint(2**256-1)。同樣在在函數send中的balances[receiver]+=amount;這對語句來說也是如此。

Errors用來向調用者描述錯誤資訊。Error與revert 語句一起使用。revert語句無條件地中止執行并回退所有的變化,類似于require函數,它也同樣允許你提供一個錯誤的名稱和額外的資料,這些額外資料将提供給調用者(并最終提供給前端應用程式或區塊資料總管),這樣就可以更容易地調試或應對失敗。

任何人(已經擁有一些代币)都可以使用send函數來向其他人發送代币。如果發送者沒有足夠的代币可以發送,if條件為真revert将觸發失敗,并通過InsufficientBalance向發送者提供錯誤細節。

如果mint被合約建立者外的其他人調用則什麼也不會發生。 另一方面,send函數可被任何人用于向他人發送币 (當然,前提是發送者擁有這些币)。記住,如果你使用合約發送币給一個位址,當你在區塊鍊浏覽器上檢視該位址時是看不到任何相關資訊的。因為,實際上你發送币和更改餘額的資訊僅僅存儲在特定合約的資料存儲器中。通過使用事件,你可以非常簡單地為你的新币建立一個“區塊鍊浏覽器”來追蹤交易和餘額。

國内領先的Web3.0倡導者

繼續閱讀