SOL01:Solidity語言基礎


??Solidity是傳說中編寫智能合約的腳本語言,運行在EVM中;用以解決區(qū)塊鏈中的任務執(zhí)行。一個目前看起來還非常稚嫩的語言。這里做一個結構性介紹。并據此展開詳細的說明。


Solidity語言特點

  • Solidity 是一門面向合約的、為實現(xiàn)智能合約而創(chuàng)建的高級編程語言。
  • 這門語言受到了 C++,Python 和 Javascript 語言的影響,設計的目的是能在以太坊虛擬機(EVM)上運行。
  • Solidity 是靜態(tài)類型語言,支持繼承、庫和復雜的用戶定義類型等特性。
  • 使用Solidity 語言,可以為各種應用創(chuàng)建合約
    • 投票;
    • 眾籌;
    • 秘密競價(盲拍);
    • 多重簽名的錢包;
    • 以及其他應用;

Slidity語言結構

源代碼文件

  1. 源代碼文件與其他語言一樣,使用文本文件,擴展名使用sol。

  2. 文件的的組織操作系統(tǒng)的文件一樣,使用目錄組織;

    • 文件之間使用import引用,引用可以指定目錄名。這個與ES6語法類似。
    • import "filename";
      • 此語句將從 “filename” 中導入所有的全局符號到當前全局作用域中。
    • import * as symbolName from "filename";
      • 創(chuàng)建一個新的全局符號 symbolName,其成員均來自 "filename" 中全局符號。
    • import {symbol1 as alias, symbol2} from "filename";
      • 創(chuàng)建新的全局符號 alias 和 symbol2,分別從 "filename" 引用 symbol1 和 symbol2 。
    • import "filename" as symbolName;
      • 條語句等同于 import * as symbolName from "filename";。

目錄

  • 支持"."與".."
    • ".":當前目錄
    • "..":上級目錄
  • 在編譯器支持目錄重映射:
    • import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
    • 編譯:solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

文件結構

    • 版本申明
    • import
  1. 合約

    • contract 合同名 {}
  2. 注釋

    • 與javascript一樣,行注釋與塊注釋。
    • //
    • /**/

例子

  1. other.sol
pragma solidity ^0.6.1;

contract Other {
    uint value;
}

  1. solidity.sol
pragma solidity ^0.6.1;

import "./other.sol";
// 行注釋
contract MySol is Other {
    /**
    塊注釋
    */
    uint age;
}

  1. 編譯
    • solcjs --abi solidity.sol other.sol
多文件編譯

合約contract

  • 在 Solidity 中,合約類似于面向對象編程語言中的類。 每個合約中可以包含

    1. 狀態(tài)變量;
    2. 函數(shù);
    3. 函數(shù)修飾器;
    4. 事件;
    5. 結構類型;
    6. 枚舉類型 ;
  • 合約可以繼承的

合約定義


    contract 合約名 [is] 父合約{
        // 1. 狀態(tài)變量;
        // 2. 函數(shù);
        // 3. 事件;
        // 4. 結構類型;
        // 5. 枚舉類型;
    }

狀態(tài)變量


    contract MyContract {
        uint varState; // 狀態(tài)變量
        // ...
    }
  • 狀態(tài)變量:
    • 類型 存儲名;
    • 類型見后面說明;

函數(shù)與函數(shù)修飾

  • 合約中可執(zhí)行單元

contract Purchase {
    address public seller;

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

    function abort() public onlySeller { // 函數(shù)與修飾器使用
        // ...
    }
}

事件

  • 事件是能方便地調用以太坊虛擬機日志功能的接口。
    contract SimpleAuction {
        
        event HighestBidIncreased(address bidder, uint amount); // 事件

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

結構類型

  • 結構是可以將幾個變量分組的自定義類型
    • 用戶自定義復合類型
    contract Ballot {
        struct Voter { // 結構
            uint weight;
            bool voted;
            address delegate;
            uint vote;
        }
        // .....
    }

枚舉類型

  • 舉可用來創(chuàng)建由一定數(shù)量的“常量值”構成的自定義類型.
    contract Purchase {
        enum State { Created, Locked, Inactive } // 枚舉
    }

合同繼承

  • 使用is關鍵字實現(xiàn)。
    pragma solidity ^0.4.16;

    contract owned {
        function owned() { owner = msg.sender; }
        address owner;
    }

    // 使用 is 從另一個合約派生。派生合約可以訪問所有非私有成員,包括內部函數(shù)和狀態(tài)變量,
    // 但無法通過 this 來外部訪問。
    contract mortal is owned {
        function kill() {
            if (msg.sender == owner) selfdestruct(owner);
        }
    }
  • 主要:
    • 繼承包含抽象類與接口的定義。

抽象合約

  • 語法沒有什么差異,主要在函數(shù)的實現(xiàn)上:
    • 包含抽象函數(shù) // 沒有實現(xiàn)函數(shù)體的函數(shù)就是抽象函數(shù)
    • 抽象合約:
      • 主要是只有抽象函數(shù)的合約就是接口合約。
      • 實現(xiàn)函數(shù)與抽象函數(shù)混雜的就是抽象合約。
    contract Feline {    // 可以包含實現(xiàn)的就是抽象合約,這里只有一個抽象函數(shù),實際也是接口合約。
        function utterance() public returns (bytes32);   
    }
    contract Cat is Feline {
        function utterance() public returns (bytes32) { return "miaow"; }
    }

  • 使用libarary定義庫,定義好的庫可以在合約中使用,下面是官方的例子:
    • 庫的語法與合約差不多,合約可以使用庫中數(shù)據與函數(shù)。
pragma solidity ^0.4.16;

library Set {
  // 我們定義了一個新的結構體數(shù)據類型,用于在調用合約中保存數(shù)據。
  struct Data { mapping(uint => bool) flags; }

  // 注意第一個參數(shù)是“storage reference”類型,因此在調用中參數(shù)傳遞的只是它的存儲地址而不是內容。
  // 這是庫函數(shù)的一個特性。如果該函數(shù)可以被視為對象的方法,則習慣稱第一個參數(shù)為 `self` 。
  function insert(Data storage self, uint value)
      public
      returns (bool)
  {
      if (self.flags[value])
          return false; // 已經存在
      self.flags[value] = true;
      return true;
  }

  function remove(Data storage self, uint value)
      public
      returns (bool)
  {
      if (!self.flags[value])
          return false; // 不存在
      self.flags[value] = false;
      return true;
  }

  function contains(Data storage self, uint value)
      public
      view
      returns (bool)
  {
      return self.flags[value];
  }
}

contract C {
    Set.Data knownValues;

    function register(uint value) public {
        // 不需要庫的特定實例就可以調用庫函數(shù),
        // 因為當前合約就是“instance”。
        require(Set.insert(knownValues, value));
    }
    // 如果我們愿意,我們也可以在這個合約中直接訪問 knownValues.flags。
}

數(shù)據類型與數(shù)據

數(shù)據的定義

  • 語法:

    • 類型 變量名 = 初始值
    • 變量名遵循Javascript的命名規(guī)則。
  • 刪除變量

    • delete 變量名;
  • 常量:

    • constant:
    • int constant a = 2000;

數(shù)據類型與字面值

布爾類型與布爾值

  1. 類型關鍵字:bool
  2. 布爾值:truefalse

整數(shù)類型與整數(shù)值

  1. 類型關鍵值:
    • int / uint (有符號與無符號)
    • 整數(shù)也分字節(jié)大?。簡挝皇俏?,8位一個字節(jié),根據對齊規(guī)則,必須是8的倍數(shù)。
      • int8/uint8
      • 。。。
      • int256/uint256 = int/uint
  2. 整數(shù)值:
    • 只支持10與16進制
      • 普通法表示:122
        • 不能前綴0。
        • 16進制使用hex前綴轉換:hex"001122FF" 或者 0x前綴。
      • 科學記數(shù)法表示:1e10
        • 指數(shù)必須是整數(shù),不支持小數(shù)。

小數(shù)類型與小數(shù)值

  1. 小數(shù)類型關鍵字:fixed / ufixed

    • 有符號與無符號小數(shù)
    • 小數(shù)還可以自帶精度表示:
      • ufixedMxN / fixedMxN (M表示表示該類型占用的位數(shù),N表示可用的小數(shù)位數(shù))
        • M也必須是8的倍數(shù),最大256。
  2. 小數(shù)值:

    • 使用小數(shù)點表示小數(shù)。 與整數(shù)一樣,分成普通表示與科學表示。
  3. 例子:

    • ufixed32x2 score = 12.45;
  4. 注意:

    • Solidity 還沒有完全支持定長浮點型??梢月暶鞫ㄩL浮點型的變量,但不能給它們賦值或把它們賦值給其他變量。。

地址類型與地址值

  • 地址類型存儲一個 20 字節(jié)的值(以太坊地址的大?。?。
  • 地址類型也有成員變量,并作為所有合約的基礎。
  1. 地址類型關鍵字:address
  2. 地址值表示:0x開頭的16進制表示。
  3. 地址變量包含多個成員(成員屬性與成員函數(shù)),用來訪問地址相關信息:
    • balance :balance 屬性來查詢一個地址的余額
    • send/transfer :transfer 函數(shù)向一個地址發(fā)送 以太幣Ether (以 wei 為單位):
  • 備注:地址的所有成員:
    1. <address>.balance (uint256):

      • 以Wei為單位的地址類型的余額。
    2. <address>.transfer(uint256 amount):

      • 向地址類型發(fā)送數(shù)量為amount的Wei,失敗時拋出異常,發(fā)送 2300 gas 的礦工費,不可調節(jié)。
    3. <address>.send(uint256 amount) returns (bool):

      • 向地址類型 發(fā)送數(shù)量為 amount 的 Wei,失敗時返回 false,發(fā)送 2300 gas 的礦工費用,不可調節(jié)。
    4. <address>.call(...) returns (bool):

      • 發(fā)出低級函數(shù) CALL,失敗時返回 false,發(fā)送所有可用 gas,可調節(jié)。
    5. <address>.callcode(...) returns (bool)

      • 發(fā)出低級函數(shù) CALLCODE,失敗時返回 false,發(fā)送所有可用 gas,可調節(jié)。
    6. <address>.delegatecall(...) returns (bool):

      • 發(fā)出低級函數(shù) DELEGATECALL,失敗時返回 false,發(fā)送所有可用 gas,可調節(jié)。

數(shù)組類型與值表示

  1. 數(shù)組關鍵字:類型[]
  2. 數(shù)組的創(chuàng)建:new
  3. 例子:

    pragma solidity ^0.4.16;

    contract C {
        function f(uint len) public pure {
            uint[] memory a = new uint[](7);
            a[6] = 8;
        }
    }
  1. 兩個特殊的數(shù)組:
    • bytes 與 string 等加以 int8[]或者 byte[]
  2. 例子:
    pragma solidity ^0.4.16;

    contract C {
        function f(uint len) public pure {
            uint[]  a = new uint[](7);
            bytes  b = new bytes(len);   // string b= new string(len)
            // 這里我們有 a.length == 7 以及 b.length == len
            a[6] = 8;
        }
    }
  1. 數(shù)組字面值

    • [1, 2, 3, 4]
  2. 數(shù)組變量賦值的注意事項:長度一致

    • 下面例子無法賦值:
// 這段代碼并不能編譯。

pragma solidity ^0.4.0;

contract C {
    function f() public {
        // 這一行引發(fā)了一個類型錯誤,因為 unint[3] memory
        // 不能轉換成 uint[] memory。
        uint[] x = [uint(1), 3, 4];
    }
}
  1. 數(shù)組的成員

    • length屬性:獲取數(shù)組長度,還可以通過這個成員屬性修改數(shù)組的長度(只對存儲有效,為位置在內存的變量無效,參考后面存儲位置的說明)
    • push函數(shù):用來向數(shù)組末尾添加數(shù)據
    • 這length對字符串數(shù)組無效。
  2. 多維數(shù)組:

    • bool[2][3] m_pairsOfFlags;

字符串類型與值表示

  1. 字符串也是數(shù)組,其字面值表示:

    • "foo":3字節(jié)字符數(shù)組。
    • 字符串數(shù)組與bytes數(shù)組可以隱式轉換。
  2. 字符串支持轉移字符

    • 字符串字面常數(shù)支持轉義字符,例如 \n,\xNN 和 \uNNNN。\xNN 表示一個 16 進制值,最終轉換成合適的字節(jié), 而 \uNNNN 表示 Unicode 編碼值,最終會轉換為 UTF-8 的序列。

枚舉類型

  1. 定義枚舉類型

    • enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
  2. 使用枚舉類型

    • ActionChoices defaultChoice = ActionChoices.GoStraight;
pragma solidity ^0.4.16;

contract test {
    enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choice;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;

    function setGoStraight() public {
        choice = ActionChoices.GoStraight;
    }

結構體

  • 結構體是定義新的類型,語法如下:

    struct Funder {
        address addr;
        uint amount;
    }


  • 結構體不能包含自己。

存儲位置

  • 所有的復雜類型,即數(shù)組和結構類型,都有一個額外屬性,“數(shù)據位置”,

    1. 說明數(shù)據是保存在 內存memory 中還是 存儲storage 中。
    2. 根據上下文不同,大多數(shù)時候數(shù)據有默認的位置,但也可以通過在類型名后增加關鍵字 storage 或 memory 進行修改。
    3. 函數(shù)參數(shù)(包括返回的參數(shù))的數(shù)據位置默認是 memory, 局部變量的數(shù)據位置默認是 storage,狀態(tài)變量的數(shù)據位置強制是 storage 。
  • 第三種數(shù)據位置calldata

    1. 這是一塊只讀的,且不會永久存儲的位置,用來存儲函數(shù)參數(shù)。
    2. 外部函數(shù)的參數(shù)(非返回參數(shù))的數(shù)據位置被強制指定為 calldata ,效果跟 memory 差不多。
  • 例子:

pragma solidity ^0.4.0;

contract C {
    uint[] x; // x 的數(shù)據存儲位置是 storage

    // memoryArray 的數(shù)據存儲位置是 memory
    function f(uint[] memoryArray) public {
        x = memoryArray; // 將整個數(shù)組拷貝到 storage 中,可行
        var y = x;  // 分配一個指針(其中 y 的數(shù)據存儲位置是 storage),可行
        y[7]; // 返回第 8 個元素,可行
        y.length = 2; // 通過 y 修改 x,可行
        delete x; // 清除數(shù)組,同時修改 y,可行
        // 下面的就不可行了;需要在 storage 中創(chuàng)建新的未命名的臨時數(shù)組, /
        // 但 storage 是“靜態(tài)”分配的:
        // y = memoryArray;
        // 下面這一行也不可行,因為這會“重置”指針,
        // 但并沒有可以讓它指向的合適的存儲位置。
        // delete y;

        g(x); // 調用 g 函數(shù),同時移交對 x 的引用
        h(x); // 調用 h 函數(shù),同時在 memory 中創(chuàng)建一個獨立的臨時拷貝
    }

    function g(uint[] storage storageArray) internal {}
    function h(uint[] memoryArray) public {}
}

映射(key - value)

  • 映射類型在聲明語法為

    • mapping(_KeyType => _ValueType)。
    • 其中 _KeyType 可以是除了映射、變長數(shù)組、合約、枚舉以及結構體以外的幾乎所有類型。
    • _ValueType 可以是包括映射類型在內的任何類型。
  • 映射可以視作哈希表

    • 它們在實際的初始化過程中創(chuàng)建每個可能的 key, 并將其映射到字節(jié)形式全是零的值:一個類型的 默認值。
    • 然而下面是映射與哈希表不同的地方:
      • 在映射中,實際上并不存儲 key,而是存儲它的keccak256哈希值,從而便于查詢實際的值。

類型轉換

  • 類型轉換一般分成顯式轉換與隱式轉換:
    • 顯式轉換 : 類型(值)

運算符與表達式

布爾運算

  1. ! (邏輯非)
  2. && (邏輯與, "and" )
  3. || (邏輯或, "or" )
  4. == (等于)
  5. != (不等于)

整數(shù)運算

  1. 比較運算符:
    • <= , < , == , != , >= , > (返回布爾值)
  2. 位運算符:
    • & , | , ^ (異或), ~ (位取反)
  3. 算數(shù)運算符:
    • + , - , 一元運算 - , 一元運算 + , * , / , % (取余) , ** (冪), << (左移位) , >> (右移位)

小數(shù)運算

  1. 比較運算符:
    • <=, <, ==, !=, >=, > (返回值是布爾型)
  2. 算術運算符:
    • +, -, 一元運算 -, 一元運算 +, *, /, % (取余數(shù))

地址運算

  • 比較運算:
    • <=, <, ==, !=, >= 和 >

數(shù)組運算

  1. 比較運算符:
    • <=, <, ==, !=, >=, > (返回布爾型)
  2. 位運算符:
    • &, |, ^ (按位異或), ~ (按位取反), << (左移位), >> (右移位)
  3. 索引訪問:
    • 如果 x 是 bytesI 類型,那么 x[k] (其中 0 <= k < I)返回第 k 個字節(jié)(只讀)。
  4. .length
    • 表示這個字節(jié)數(shù)組的長度(對定長只讀).

流程控制

  1. JavaScript 中的大部分控制結構在 Solidity 中都是可用的,除了 switch 和 goto。

    • if,else,
    • while,
    • do,
    • for,
    • break,continue,return,
    • ? :
  2. 注意:

    • 用于表示條件的括號不可以被省略,單語句體兩邊的花括號可以被省略。
    • 與C和JavaScript不同, Solidity 中非布爾類型數(shù)值不能轉換為布爾類型,因此 if (1) { ... } 的寫法在 Solidity 中 無效 。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容