1. 程式人生 > 實用技巧 >60、solidity程式語言瞭解、實踐-1——2020年07月12日12:01:12

60、solidity程式語言瞭解、實踐-1——2020年07月12日12:01:12

2020年07月12日12:04:35

2019年09月26日10:01:43

solidity程式語言瞭解、實踐-1

1.pragma

版本標識指令,用來啟動編譯器檢查。

使用規範:

pragma solidity ^0.5.2;

這樣的話,原始檔不允許低於0.5.2版本的編譯器編譯,也不允許高於0.6.0版本的編譯器編譯。(第二個條件因使用^被新增)

2.合約結構

2.1 介紹

每個合約可以包括狀態變數,函式,事件Event,結構體,列舉型別,合約可以從其他合約繼承。

2.2 狀態變數

指永久地儲存在合約中的值。

合約內——方法外

pragma solidity >=0.4.0 <0.7.0;

contract TinyStorage {
    uint storedXlbData; // 狀態變數
    // ...
}

2.3 函式

函式是合約中程式碼的可執行單元。

pragma solidity >=0.4.0 <0.7.0;

contract TinyAuction {
    function Mybid() public payable { // 定義函式
        // ...
    }
}

函式接受引數和返回值。

2.4 函式修飾器 modifier

修飾符可以用來以宣告的方式來修改函式語義。

pragma solidity >=0.4.22 <0.7.0;

contract MyPurchase {
    address public seller;

    modifier onlySeller() { // 修飾器
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    function abort() public onlySeller { // 修飾器用法
        // ...
    }
}

2.4 事件Event

事件是能方便地呼叫以太坊虛擬機器日誌功能的介面。

pragma solidity >=0.4.21 <0.7.0;
contract TinyAuction {
    event HighestBidIncreased(address bidder, uint amount); // 事件

    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // 觸發事件
    }
}

事件編寫:

pragma solidity  >=0.4.21 <0.7.0;

contract ClientReceipt {
    event Deposit(
        address indexed _from,
        bytes32 indexed _id,
        uint _value
    );

    function deposit(bytes32 _id) public payable {
        // 事件使用 emit 觸發事件。
        // 我們可以過濾對 `Deposit` 的呼叫,從而用 Javascript API 來查明對這個函式的任何呼叫(甚至是深度巢狀呼叫)。
        emit Deposit(msg.sender, _id, msg.value);
    }
}

使用 JavaScript API 呼叫事件的用法如下:

var abi = /* abi 由編譯器產生 */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...xlb67" /* 地址 */);

var event = clientReceipt.Deposit();

// 監聽變化
event.watch(function(error, result) {
    // 結果包含 非索引引數 以及 主題 topic
    if (!error)
        console.log(result);
});

// 或者通過傳入回撥函式,立即開始聽監
var event = clientReceipt.Deposit(function(error, result) {
    if (!error)
        console.log(result);
});

上面的輸出如下所示(有刪減):

{
   "returnValues": {
       "_from": "0x1111…FFFFCCCC",
       "_id": "0x50…sd5adb20",
       "_value": "0x420042"
   },
   "raw": {
       "data": "0x7f…91385",
       "topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
   }
}

2.5 結構體

結構體是可以將幾個變數分組的自定義型別。

pragma solidity >=0.4.0 <0.7.0;

contract TinyBallot {
    struct Voter { // 結構體
        uint weight;
        bool voted;
        address delegate;
        uint vote;
    }
}

2.6 列舉型別

列舉可用來建立一定數量的“常量值”構成的自定義型別。

pragma solidity >=0.4.0 <0.7.0;

contract Upchain {
    enum State { Created, Locked, InValid } // 列舉
}

3.型別

“undefined”或“null”值的概念在Solidity中不存在,但是新宣告的變數總是有一個預設值,具體的預設值跟型別相關。

3.1 值型別

3.1.1 bool :true或者false
3.1.2 整形

int / uint :分別表示有符號和無符號的不同位數的整型變數。 支援關鍵字 uint8uint256 (無符號,從 8 位到 256 位)以及 int8int256,以 8 位為步長遞增。uintint 分別是 uint256int256 的別名。

3.2 地址型別

地址型別有兩種形式,他們大致相同:

  • address:儲存一個20位元組的值(以太坊地址的大小)。
  • ddress payable :可支付地址,與 address 相同,不過有成員函式 transfersend

這種區別背後的思想是 address payable 可以接受以太幣的地址,而一個普通的 address 則不能。

型別轉換:

允許從 address payableaddress 的隱式轉換,而從 addressaddress payable 的轉換是不可以的( 執行這種轉換的唯一方法是使用中間型別,先轉換為 uint160

大部分情況下你不需要關心 addressaddress payable 之間的區別,並且到處都使用 address 。 例如,如果你在使用取款模式, 你可以(也應該)儲存地址為 address 型別, 因為可以在 msg.sender 物件上呼叫 transfer 函式, 因為 msg.senderaddress payable

addressaddress payable 的區別是在 0.5.0 版本引入的,同樣從這個版本開始,合約型別不再繼承自地址型別,不過如果合約有可支付的回退( payable fallback )函式,合約型別仍然可以顯示轉換為 addressaddress payable

3.2.1 balance tranfer

可以使用 balance 屬性來查詢一個地址的餘額, 也可以使用 transfer 函式向一個可支付地址(payable address)傳送 Ether (以 wei 為單位)

address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

如果當前合約的餘額不夠多,則 transfer 函式會執行失敗,或者如果以太轉移被接收帳戶拒絕, transfer 函式同樣會失敗而進行回退。

3.2.2 send

sendtransfer 的低階版本。如果執行失敗,當前的合約不會因為異常而終止,但 send 會返回 false

3.2.3 calldelegatecallstaticcall

為了與不符合應用二進位制介面Application Binary Interface(ABI) 的合約互動,或者要更直接地控制編碼,提供了函式 calldelegatecallstaticcall 。 它們都帶有一個bytes memory引數和返回執行成功狀態(bool)和資料(bytes memory)。

所有三個函式 calldelegatecallstaticcall 都是非常低階的函式,應該只把它們當作 最後一招 來使用,因為它們破壞了 Solidity 的型別安全性。

3.3 合約型別

每一個contract定義都有他自己的型別。

您可以隱式地將合約轉換為從他們繼承的合約。 合約可以顯式轉換為 address 型別。

只有當合約具有可支付回退函式時,才能顯式和 address payable 型別相互轉換 轉換仍然使用 address(x) 執行,而不是使用 address payable(x)

在版本0.5.0之前,合約直接從地址型別派生的, 並且 addressaddress payable 之間沒有區別。

3.4 字串字面常量及型別

字串字面常量是指由雙引號或單引號引起來的字串("foo" 或者 'bar')。 不像在 C 語言中那樣帶有結束符;"foo" 相當於 3 個位元組而不是 4 個。 和整數字面常量一樣,字串字面常量的型別也可以發生改變,但它們可以隱式地轉換成 bytes1,……,bytes32,如果合適的話,還可以轉換成 bytes 以及 string

3.5 對映

對映型別在宣告時的形式為 mapping(_KeyType => _ValueType)。 其中 _KeyType 可以是任何基本型別,即可以是任何的內建型別加上bytes and string。 而使用者定義的型別或複雜的型別如:合約型別、列舉、對映、結構體、即除bytes and string 之外的陣列型別 是不可以作為 _KeyType 的型別的。

_ValueType 可以是包括對映型別在內的任何型別。

對映可以視作 雜湊表 ,它們在實際的初始化過程中建立每個可能的 key, 並將其對映到位元組形式全是零的值:一個型別的 預設值。然而下面是對映與雜湊表不同的地方: 在對映中,實際上並不儲存 key,而是儲存它的 keccak256 雜湊值,從而便於查詢實際的值。

正因為如此,對映是沒有長度的,也沒有 key 的集合或 value 的集合的概念。

pragma solidity >=0.4.0 <0.7.0;

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingLBC {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(this);
    }
}