1. 程式人生 > >solidity高階語法

solidity高階語法

高階語法

1. 自動推導 var

強烈不建議使用!!

為了方便,並不總是需要明確指定一個變數的型別,編譯器會通過第一個向這個物件賦予的值的型別來進行推斷

uint24 x = 0x123;
var y = x;
  • 由var引發的血案…

    需要特別注意:由於型別推斷是根據第一個變數進行的賦值。所以下面的程式碼將是一個無限迴圈,因為一個uint8的i的將小於2000。

for (var i = 0; i < 2000; i++)
{
    //uint8 -> 255
	//無限迴圈
}
pragma solidity ^0.4.25;

contract Test{
function Hi() view returns (uint, uint){ uint count = 0; var i = 0; for (; i < 257; i++) { count++; if(count >= 260){ break; } } return (count, i); } } /* 結果: 0: uint256: 260 1: uint256: 3 分析:當 迴圈到 i =255 時,count = 256,再往後 i 又從0開始了! i , count 255 , 256 0 , 257 1 , 258 2 , 259 3 , 260 */

2.全域性函式/變數

2.1 區塊和交易的屬性

函式 含義
block.blockhash(uint blockNumber) 雜湊值(byte32)
block.coinbase (address) 當前塊礦工的地址。
block.difficulty (uint)當前塊的難度
block.gaslimit (uint)當前塊的gaslimit
block.number (uint)當前區塊的塊號
block.timestamp (uint)當前塊的時間戳
msg.data (bytes)完整的呼叫資料(calldata)
msg.gas (uint)當前還剩的gas
msg.sender (address)當前呼叫發起人的地址
msg.sig (bytes4)呼叫資料的前四個位元組(函式識別符號)
msg.value (uint)這個訊息所附帶的貨幣量,單位為wei
now (uint)當前塊的時間戳 等同於block.timestamp
tx.gasprice (uint) 交易的gas價格
tx.origin (address)交易的傳送者(完整的呼叫鏈)
  • 示例:
pragma solidity ^0.4.25;

contract Test {
    bytes32 public blockhash;
    address public coinbase;
    uint public difficulty;
    uint public gaslimit;
    uint public blockNum;
    uint public timestamp;
    bytes public calldata;
    uint public gas;
    address public sender;
    bytes4 public sig;
    uint public msgValue;
    uint public now;
    uint public gasPrice;
    address public txOrigin;
    
    function letgo (){
        //給定區塊號的雜湊值,只支援最近256個區塊,且不包含當前區塊
        blockhash = block.blockhash(block.number - 1);
        coinbase = block.coinbase ;//當前塊礦工的地址。
        difficulty = block.difficulty;//當前塊的難度。
        gaslimit = block.gaslimit;// (uint)當前塊的gaslimit。
        blockNum = block.number;// (uint)當前區塊的塊號。
        timestamp = block.timestamp;// (uint)當前塊的時間戳。
        calldata = msg.data;// (bytes)完整的呼叫資料(calldata)。
        gas = msg.gas;// (uint)當前還剩的gas。
        sender = msg.sender; // (address)當前呼叫發起人的地址。
        sig = msg.sig;// (bytes4)呼叫資料的前四個位元組(函式識別符號)。
        msgValue = msg.value;// (uint)這個訊息所附帶的貨幣量,單位為wei。
        now = now;// (uint)當前塊的時間戳,等同於block.timestamp
        gasPrice = tx.gasprice;// (uint) 交易的gas價格。
        txOrigin = tx.origin;// (address)交易的傳送者(完整的呼叫鏈)  
    }
}

2.2 貨幣單位

  • 一個字面量的數字,可以使用字尾weifinneyszaboether來在不同面額中轉換。
  • 不含任何字尾的預設單位是wei。如1 ether == 1000 finney的結果是true
pragma solidity ^0.4.25;

contract EthUnit{
    uint  a = 1 ether;
    uint  b = 10 ** 18 wei;
    uint  c = 1000 finney;
    uint  d = 1000000 szabo;
    
    function f1() constant public returns (bool){
        return a == b;
    }
    
    function f2() constant public returns (bool){
        return a == c;
    }
    
    function f3() constant public returns (bool){
        return a == d;
    }
    
    function f4() constant public returns (bool){
        return 1 ether == 100 wei;
    }
}

2.3 時間單位

  • seconds,minutes,hours,days,weeks,years均可做為字尾,預設是seconds為單位。
  • 1 = 1 seconds
  • 1 minutes = 60 seconds
  • 1 hours = 60 minutes
  • 1 days = 24 hours
  • 1 weeks = 7 days
  • 1 years = 365 days
pragma solidity ^0.4.25;

contract TimeUnit{

    function f1() pure public returns (bool) {
        return 1 == 1 seconds;
    }
    
    function f2() pure public returns (bool) {
        return 1 minutes == 60 seconds;
    }
    
    function f3() pure public returns (bool) {
        return 1 hours == 60 minutes;
    }
    
    function f4() pure public returns (bool) {
        return 1 days == 24 hours;
    }
    
    function f5() pure public returns (bool) {
        return 1 weeks == 7 days;
    }
    
    function f6() pure public returns (bool) {
        return 1 years == 365 days;
    }
}

3.事件(Event)

  • 可以用來記錄日誌
pragma solidity ^0.4.0;

contract ClientReceipt {
    //定義,注意,需要加分號,相當於一句語句,與struct和enum不同。
    event Deposit(
        address indexed _from,
        uint indexed _id,
        uint _value
    );

    function deposit(uint _id) {
        //使用
        Deposit(msg.sender, _id, msg.value);
    }
}

在這裡插入圖片描述

4.訪問函式

  • 編譯器為自動為所有的public的狀態變數建立訪問函式。下面的合約例子中,編譯器會生成一個名叫data的無參,返回值是uint的型別的值data。狀態變數的初始化可以在定義時完成。
pragma solidity ^0.4.25;

contract C{
    uint public data = 10;
}

contract D{
    C c = new C();
    
    function getDataUsingAccessor() returns (uint){
        return c.data();
    }
}
  • 訪問函式有外部(external)可見性。如果通過內部(internal)的方式訪問,比如直接訪問,你可以直接把它當一個變數進行使用,但如果使用外部(external)的方式來訪問,如通過this.,那麼它必須通過函式的方式來呼叫。
pragma solidity ^0.4.25;

contract viewTest{
    uint public c = 10;
    
    function accessInternal() internal view returns (uint){
        return c;
    }
    //合約內部也能夠呼叫用external修飾的函式, 但是要使用外部呼叫方式this	
    function accessExternal() view external returns (uint){
        return this.c();
    }
}

5.錯誤處理

傳統方法:採用 throw 和 if … throw 模式==(已過時)==,例如合約中有一些功能,只能被授權為擁有者的地址才能呼叫

if(msg.sender != owner) { 
	throw; 
}

等價於如下任意一種形式:

if(msg.sender != owner) { 
	revert(); 
} 
assert(msg.sender == owner); 
require(msg.sender == owner);

示例:

pragma solidity ^0.4.21;

contract HasAnOwner {
    address public owner;
    uint public a ;
    
    constructor() public {
        owner = msg.sender;
    }
    
    function useSuperPowers()  public { 
        require(msg.sender == owner);
        a = 11111;
        /*
        if (msg.sender != owner){
            throw;
        }
        */
        
        a = 10;
       // do something only the owner should be allowed to do
    }
}

6.修飾器(modifier)

修改器(Modifiers)可以用來輕易的改變一個函式的行為。比如用於在函式執行前檢查某種前置條件。修改器是一種合約屬性,可被繼承,同時還可被派生的合約重寫(override)。下面我們來看一段示例程式碼:

pragma solidity ^0.4.21;

contract HasAnOwner {
    address public owner;
    uint public a ;
    
    constructor() public {
        owner = msg.sender;
    }
    
    modifier ownerOnly(address addr) {
        require(addr == owner);
        //程式碼修飾器所修飾函式的程式碼
        _;
    }
    
    function useSuperPowers() ownerOnly(msg.sender) public { 
    	//require(addr == owner);
        a = 10;
       // do something only the owner should be allowed to do
    }
}

7.元組 (tuple)

元組是一個數據集合,類似於字典但是無法修改資料,使用圓括號包括多種資料型別。

pragma solidity ^0.4.25;

contract Test {
    
    struct Student {
        string name;
        uint age;
        uint score;
        string sex;
    }
    
    //兩種賦值方式
    Student public stu1 = Student("lily", 18, 90, "girl");
    Student public stu2 = Student({name:"Jim", age:20, score:80, sex:"boy"});

    Student[] public Students;
    
    function assign() public {
        Students.push(stu1);
        Students.push(stu2);
        
        stu1.name = "Lily";
    }
    
    //1. 返回一個Student結構
    function getLily() public view returns(string, uint, uint, string) {
        Student memory lily = Students[0];
        return (lily.name, lily.age, lily.score, lily.sex);
    }
}

8. 內建數學函式

ripemd160

keccak256

addmod

ecrecover

9.繼承

  • 繼承關鍵字:is,語法:contract Son is Father{ }
  • 合約 繼承 另一個合約 來 達到複用的目的
    • 繼承 父合約的所有成員變數(state var - 狀態變數)
    • 繼承 父合約的所有方法
  • 繼承的本質:程式碼複製,將 父合約 程式碼複製到 子合約
pragma solidity ^0.4.25;
//父合約
contract Father{
    string public fatherName = "James";
}
//子合約
contract Son is Father{
    string public sonName = "Jun";
    
    function testFather() public{
        fatherName = "James01";
        sonName = "Jun01";
    }
}
  • super和this關鍵字
    • super 在合約中指向父合約
    • this 在合約中指向本合約
pragma solidity ^0.4.25;
//父合約
contract Father{
    string public fatherName = "James";
    function printName() public view returns(string){
        return fatherName;
    }
}
//子合約
contract Son is Father{
    string public sonName = "Jun";
    
    function testFather() public{
        this.sonName = "Jun01";
        super.fatherName = "James01";
        super.printName();
    }
}
  • 注意:父子同名的成員都會存在,只是訪問方式有變化
pragma solidity ^0.4.25;

contract Father{
    string public fatherName = "James";
    function outName() public view returns(string){
        return fatherName;
    }
}

contract Son is Father{
    string public fatherName = "James2";
    string public sonName = "Jun";
    
    function outName() public view returns(string){
        return super.outName();
    }
}
  • 訪問 父合約所有的非私有成員 (包括 internal 的函式 和 成員變數)
contract Father{
  uint public stateVar;//成員變數
  //public 方法
  function aPublicFun() public pure returns(string){
      return "publicFunc invoked";
  }
  function aInternalFun() internal pure returns(string){
      return "internalFunc invoked";
  }
  function aExternalFun() external pure returns(string){
      return "externalFunc invoked";
  }
  function aPrivateFun() private pure returns(string){
      return "privateFunc invoked";
  }
}
// 子合約 繼承 父合約
contract Son is Father{
  function testFather() public{
    //不能訪問`private`
    //aPrivateFun();

    //訪問父類的`public`方法
    aPublicFun();

    //訪問父類的狀態變數
    stateVar = 10;

    //訪問父類的`internal`方法
    aInternalFun();
    //訪問父類的'external'方法,需要通過this關鍵字 
    this.aExternalFun();
  }
    
  // 呼叫某合約訪問許可權是external的方法
  function testExternal(Father fObj)public view returns(string){
      return fObj.aExternalFun();
  }
}

10.繼承中同名成員

問題:如果父合約 與 子合約 中 有 相同名字 的 成員變數 或 方法的話,會怎麼樣?
答:不管同名與否,父合約所有成員都會被 “繼承” 到 子合約中。只是子合約呼叫時有些區別。

  • 訪問就近原則:子合約中 呼叫 同名成員 都是子合約中的,如:
pragma solidity ^0.4.25;

contract Father{
    string public fatherName = "James";
}

contract Son is Father{
    string public fatherName = "James2";
    
    function outName() public view returns(string){
        return fatherName; // James2
    }
}
  • 父合約與子合約
  • 最遠繼承原則:在繼承鏈中,由於繼承實現是程式碼複製。如果出現函式名重寫,最終使用的是繼承鏈上哪個合約定義的程式碼呢?實際執行時,依據的是 最遠繼承原則(most derived)
pragma solidity ^0.4.0;

contract Base1{
  function data() public view returns(uint){
    return 1;
  }
}

contract Base2{
  function data() public view  returns(uint){
    return 2;
  }
}

contract MostDerived1 is Base1, Base2{ // extends Base2 (most derived)
  function call() public view returns(uint){
    return data(); // Base2.data()
  }
}

contract MostDerived2 is Base2, Base1{ // extends Base1 (most derived)
  function call() public view returns(uint){
    return data(); // Base1.data()
  }
}
  • 可以指定某個父合約
pragma solidity ^0.4.0;
contract owned {
    function owned() { owner = msg.sender; }
    address owner;
}

contract mortal is owned {
    event mortalCalled(string);
    function kill() {
        mortalCalled("mortalCalled");
        if (msg.sender == owner) selfdestruct(owner);
    }
}

contract SpecifyBase is mortal {
    event SpecifyBase(string);
    function kill() {
      SpecifyBase("do own cleanup");
      mortal.kill();
    }
}

11.外部呼叫

外部呼叫,後面專案會用到

pragma solidity ^0.4.24;


contract C1 {
    uint public num = 10;
    function setValue(uint a) public {
            num = a;
    }
}

//手動賦值
contract C2 {
    C1 c1;
    function call (address addr) public {
        c1 = C1(addr);
        c1.setValue(100);
        
        //addr.setValue(1000); 不允許
    }
}

//合約自動呼叫
contract C3 {
    C1 c1;
    
    function C3() {
        address  c1Addr = new C1();
        c1 = C1(c1Addr);  
        
        //或者 直接使用C1來承接亦可
        //c1 = new C1();
    }
    
    function getValue() public constant returns(uint) {
        return c1.num();
    }
    
    //注意constant 的坑!!!!,
    //函式加上constant之後,執行修改狀態變數,但是修改不會生效,好坑!!!
    function setValue(uint num) public {
        c1.setValue(num);
    }
}

官方示例

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() public payable returns (uint ret) {
        return 42; 
    }
}

contract Consumer {
    InfoFeed feed;
    function setFeed(address addr) public { 
        feed = InfoFeed(addr); 
    }
    
    function callFeed() public { 
        feed.info.value(10).gas(800)(); 
    }
}