Smart Contract

Smart Contracts 是在 Ethereum 中最常被大家提到的應用。Smart Contract 是儲存在 Block Chain 中的程式這篇簡單的記錄一下自己摸索 smart contracts 的過程。

透過 Solidity 來定義合約內容以及執行的動作,當執行任何動作時都需要付出手續費,

進行方式

若是要測試 smart contract,可以透過 testrpc 搭配 truffle。testrpc 是基於 node.js 開發的 Ethereum client 端,將整個模擬的 block chain 儲存在記憶體中,並且產生 10 個已經擁有 ether 的交易帳號。交易時,會自動挖礦處理。而 truffle 是能夠處理合約的 compile 以及 deploy。

這篇文章主要專注在藉由 Solidity 來定義 Contract,就先去掉麻煩的區塊鏈建立,這邊直接使用官方推薦的 Remix - Solidity IDE 來做測試。

舉個例子,假設一位叫 Vincent 的工程師有個 idea 想要做一個覺得很酷炫的 project,但剛好礙於所需要的資金不足,需要一些贊助資金來幫助他完成一些計畫,如果該計畫成功了,那麼會將收入的 5% 依據權重分給不同的贊助者。那麼我們就能針對 Vincent 想要募集資金的這個需求來擬定一份名稱為 OkSponsor 的 smart contract。

以下列了這份合約會用到的幾個 functions 以及其功能簡略描述。

  • sponsor:執行贊助
  • getPot:取得目前贊助總額度
  • refund:退款
  • 執行drawdown:轉移合約中所有的 ether 給被贊助者
  • futureSponsor:該合約的 constructor。constructor 的命名需與 contract 名稱相同

以下是程式範例:

pragma solidity ^0.4.0;
contract OkSponsor {

    address public owner;
    address public benefactor;
    bool public completed;
    bool public refunded;
    uint public numSponsors;
    
    struct SponsorInfo {
        uint amount;
        address eth_address;
    }

    mapping (uint => SponsorInfo) public sponsors;

    function OkSponsor(address _benefactor) {
        owner = msg.sender;
        numSponsors = 0;
        completed = false;
        refunded = false;
        benefactor = _benefactor;
    }
    
    function sponsor() payable {
        if (completed || refunded ) revert();
        sponsors[numSponsors] = SponsorInfo(msg.value, msg.sender);

        numSponsors++;
    }
    
    function getPot() constant returns (uint) {
        return this.balance;
    }
    
    function drawdown() {
        if (msg.sender != owner || completed || refunded) revert();
        benefactor.send(this.balance);
        completed = true;
    }
}

將上述程式放到 Remix - Solidity IDE ,就能執行並且測試這份 smart contract。

Remix - Ethereum IDE

在這份 smart contract 建立時,需要先輸入 benefactor 的 address,也就是被贊助者的 account address。在 create 旁的欄位輸入。

建立合約

在每份 contract 中可包含State VariablesFunctionsFunction ModifiersEventsStructs 以及 Enum

程式本身邏輯算簡單,就只挑幾個重點出來介紹一下

mapping

在上面的程式中,有一行透過 mapping 定義了 sponsors 這個 variable

mapping (uint => SponsorInfo) public sponsors;

這是在 Solidity 中的 key-value 結構。定義方式為 mapping(_KeyType => _KeyValue)。key 的類型了 mapping 本身之為,所以的類型都能使用。而 value 則沒有任何限制。

msg 是一個 global variable

* msg.data (bytes): complete calldata
* msg.gas (uint): remaining gas
* msg.sender (address): sender of the message (current call)
* msg.value (uint): number of wei sent with the message

modifier

在 Solidity 0.4.0 之後,多了一個新的名稱為 payable 的 modifier 。如果 function 有使用到 msg.value 也就是有進行到貨幣的操作時,但卻沒有加 payable 這個 modifier 就會造成 error。

getPots()

以下是 getPots 的執行結果,回傳的值為 75000000000000000,單位為 wei,也就是 75 Ether。

getPots 執行結果

另外還能看到 Execution cost 為 619 gas 這個數值,在 smart contract 中執行每個 function 都會消耗 gas,能看做是每次交易的手續費,一份合約或者一次交易的gas是固定的(根據合約大小和 function 的複雜度),而一個 gas 的價格能夠在 Ethereum Network Status 看到,目前 gas price 為 20 gwei。

若是發生了某個交易還未執行結束,而 gas 已經提前消耗完,那麼這個 contract 就會終止並且 rollback 回先前的狀態。

未完成的部分

上面提到的例子提到 Vincent 會把成功後的收入 5% 發給 sponsors,但是要如何在合約中寫驗證收入的第三方資料來源,這是目前自己還沒有搞懂的問題。


References