2、學習基礎智能合約

第一講 介紹

一、 智能合約是什麼?

智能合約並不是現實中常見的合同,而是存在區塊鏈上,能夠被觸發執行的一段程序代碼,這些代碼實現了某種預約的規則,是存在於區塊鏈執行環境中的「自治代理」。智能合約須要被觸發,代碼纔會執行,不被觸發他的狀態將會始終保持一個狀態,而且部署後的智能合約將不可被修改。智能合約語言的語法和js腳本語言很像,所以有過js開發經驗的小夥伴們學習起來會很快。程序員

二、 編程工具的介紹。

咱們都知道「預先善其事、必先利其器」的道理,現實中織布是須要織布機才能完成織布,一樣的咱們的智能合約學習也是要有編程工具的使用的。咱們本套課程都將以 remix 爲編程工具進行講解課程。remix 就是咱們手裏的織布機,能織出什麼布就看咱們這些使用 remix 的程序員了。
地址爲(http://remix.ethereum.org/)能夠直接在瀏覽器中開發,很方便,只要有一個 google chrome 谷歌瀏覽器就能夠開發了。chrome

remix 有幾個主要的經常使用面板,分別是文件面板、編譯器、運行器、以及佔用最大一部分的文本編輯器組成。編程

文件面板:咱們能夠在這個面板進行建立文件、選擇文件等管理文件的操做。
編譯器:咱們能夠將sol文件進行編譯,編譯不經過將不能執行合約,而且能夠獲得code.json以及abi.json。咱們能夠將他們在支持sol語言的公鏈上運行。
運行器:能夠將sol智能合約部署在eth鏈上,而且能對合約的方法進行調用等操做。
文本編輯器:咱們的代碼就是寫在這個位置的,後面基本大部分時間你將面對的是這個頁面。json

三、 個人第一個智能合約程序

下面的智能合約是咱們第一個合約程序,所以咱們命名爲 「FirstContract.sol」 文件名數組

pragma solidity ^0.6.0;

// first contract
contract FirstContract {
    // first method
    function firstMethod() public pure returns (string memory) {
        return 'I am first method !!!';
    }
}

上面代碼不少小夥伴應該不是很懂什麼意思,不懂不要緊,我來給你們一行一行講解。
pragma solidity ^0.6.0;
這一行是說明使用solidity 0.6.0版本寫的,能夠運行在0.6.0到0.7.0之間的版本上。
contract FirstContract {
這一句是定義一個叫 FirstContract 名稱的合約。
function firstMethod() public pure returns (string memory){
這一行是定義一個方法叫作 firstMethod, 該方法有返回值,類型是 string 類型的。瀏覽器

return 'I am first method !!!';

這一行是這個方法將會返回 「I am first method !!!」。app

看起來可能仍是會有小夥伴們有不明白的地方,可是咱們先只教這麼多,關於什麼是string,string 就是字符串的意思,字符串你就能夠看成是任意的abcde這些字母等還有標點符號寫在了單引號或者雙引號中。這就是字符串最通俗易懂的解釋了,小夥伴們,你們能夠動手試試自定義一些字符串讓他返回。編輯器

第二講 智能合約結構

在solidity中,合約有點相似面嚮對象語言中的類,每一個合約中包含狀態變量、函數、函數變量、函數修飾器、事件、結構、和枚舉類的聲明,合約也能夠繼承其餘的合約。你們可能對類和類中的結構的概念沒有什麼瞭解,我簡單給你們舉個例子。一個類能夠比做是汽車,汽車裏面的油就是變量,而後油門、剎車等就是函數,咱們踩油門至關於調用類中的函數,汽車動起來,油減小,至關於變量值改變了。函數

咱們來根據上面的描述寫一個汽車的合約。先使用remix 建立一個CarContract1.sol文件,而後設定一個CarContract1名字的合約。汽車有了,還要有一個油箱,設定一個變量_gasoline,做爲油箱。而後咱們再給汽車加一個油門,寫一個startUp函數做爲油門。如今有了油箱可是不知道有多少油,再加gatGasoline函數做爲一個儀表盤。我們只有油箱沒有油汽車也跑不了,在加一個加油的接口,給汽車加油,使用addGasoline函數進行加油。下面就是咱們完整的小汽車的代碼。工具

CarContract1.sol

pragma solidity ^0.6.0;

contract CarContract1 {
    uint256 _gasoline;
    
    function startUp() public {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline = _gasoline + gasoline;
    }
    
}

一、 狀態變量

小汽車合約中的_gasoline就是咱們定義的狀態變量,類型是 uint256 類型。 該變量是存儲在鏈上的,也就是說他的數據是被保存起來的,每次改動都會記錄下來。所以咱們在進行調用 addGasoline 函數時,會給這個小汽車加油成功,_gasoline 的值會變化,一樣的咱們調用 startUp 函數時,_gasoline 的值也會變化。

二、 函數

在CarContract1小汽車中,startUp()、getGasoline()、addGasoline(uint256 gasoline) 都是函數。這些函數有的是沒有參數的,又叫無參函數,好比:startUp()、getGasoline()。有的是有參數的,就叫有參函數,好比:addGasoline(uint256 gasoline)。這些函數,有的有返回值,有的沒有返回值,根據具體場景來定,通常call操做都是有返回值的,call操做不會改變合約狀態。只有send操做,纔會進行改變合約的狀態。

三、 函數變量
咱們都知道加不一樣的型號汽油會有不同的效果,咱們來給汽車換下不一樣的型號汽油,在汽車上咱們放置一個桶名字是_bucket,用來裝另外一個型號的汽油。若是咱們本身的兩個容器裏面有一個是空的,咱們能夠直接進行轉換汽油。可是咱們本身的兩個容器中都有油的時候,兩個容器很明顯不能進行交換汽油,這個時候咱們須要向隔壁的老李借一個桶 __tempBucket,這樣三個容器就能進行轉換油箱裏面的汽油和桶裏面的汽油進行對換了,換完之後把桶還回去。

咱們進行在進行造一個新的小汽車名字是CarContract2,增長一個桶,設定變量爲_bucket,做爲桶。還須要記錄當前汽車的油的型號。設定變量 _gasolineType 爲當前油類型,默認是 1類型。設定一個函數 changeGasolineType,進行交換汽油類型,在設定一個函數進行查看當前汽車的類型 getGasolineType 。至此咱們小汽車升級成功。

CarContract2.sol

pragma solidity ^0.6.0;

contract CarContract2 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    function startUp() public {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasolineType() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
}

上面的小汽車2代正式出爐,我來給你們講下作了那些升級,首先咱們的changeGasolineType內部定義了 __tempBucket 變量,該變量就是函數變量,是臨時建立的而且不會被記錄在鏈上的變量,也就是咱們用完就還給隔壁老李了,還回去的時候桶是空的。

四、 函數修飾器

咱們的小汽車仍是很簡單,咱們在給他加一點東西,規定小汽車要想啓動必須關閉車門。

下面咱們再一次修改咱們的小汽車,加一個_doorStatus狀態變量做爲咱們的車門狀態。再加連個函數getDoorStatus()、changeDoorStatus(),用來控制開門/關門而且查看門的狀態。而且加入一個whenDoorClose()做爲咱們的判斷器。

pragma solidity ^0.6.0;

contract CarContract3 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    bool _doorStatus;
    
    modifier whenDoorClose() {
        require(!_doorStatus, "door is not close");
        _;
        
    }
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
}

上面咱們的3代小汽車已經完成了,whenDoorClose() 就是咱們定義的函數修飾器 使用modifier 來定義的。

五、 事件

每次都到沒有油了纔去加油,咱們加一個功能,當行駛時油量低於5的時候咱們要進行預警。

咱們加入一個 gasolineAlarm 事件,該事件有一個參數,當前的油量。這樣咱們在啓動的函數中加入這個事件的調用,判斷本次使用後的油量是否小於等於5,是的話進行調用該事件

pragma solidity ^0.6.0;

contract CarContract4 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    
    bool _doorStatus;
    
    modifier whenDoorClose() {
        require(!_doorStatus, "door is not close");
        _;
        
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
}

咱們已經更新到第四代小汽車了,四代小汽車的gasolineAlarm 就是咱們定義的事件,事件是會在虛擬機上記錄一條日誌的,我麼能夠經過查詢日誌的方式獲得事件內容。

六、 結構

咱們的汽車感受成熟了,這個時候咱們要給咱們的汽車打上一些特性,好比顏色,好比車輪數,好比車門數等等。

咱們在小汽車裏面加入CarInfo結構體,裏面能夠定義color顏色,wheelNum 車輪數等等,而後咱們加入設置和獲取的函數:setCarInfo()、getCarInfo(), 這樣咱們的小汽車就有了一些參數了。

pragma solidity ^0.6.0;

contract CarContract5 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    bool _doorStatus;
    
    struct CarInfo {
        string color;
        uint8 wheelNum;
    }
    
    CarInfo _carInfo;
    
    modifier whenDoorClose() {
        require(!_doorStatus, "door is not close");
        _;
        
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
    
    function setCarInfo(string memory color, uint8 wheelNum) public {
        _carInfo.color = color;
        _carInfo.wheelNum = wheelNum;
        
        //_carInfo = CarInfo(color, wheelNum);

    }
    
    function getCarInfo() public view returns(string memory color, int wheelNum) {
        color = _carInfo.color;
        wheelNum = _carInfo.wheelNum;
    }
}

咱們的5代小汽車加入了CarInfo就是結構體,結構體中不能進行設置初值,咱們能把一類的屬性等進行分類的放在結構體中,能夠充當咱們的數據模型。

七、 枚舉類

咱們的小汽車想要開門,須要打開車鎖,車鎖是一種狀態,開/關。

咱們加入枚舉類DoorSwitch,定義兩個狀態open,close 。在定義whenDoorSwitch函數修飾器,進行判斷。

pragma solidity ^0.6.0;

contract CarContract6 {
    uint256 _gasoline;
    uint256 _bucket;
    int _gasolineType = 1;
    bool _doorStatus;
    
    enum DoorSwitch{ open, close }
    
    DoorSwitch _doorSwitch;
    
    struct CarInfo {
        string color;
        uint8 wheelNum;
    }
    
    CarInfo _carInfo;
    
    modifier whenDoorClose() {
        require(!_doorStatus, "door is not close");
        _;
        
    }
    
    modifier whenDoorSwitch() {
        if (!_doorStatus) {
            require(_doorSwitch == DoorSwitch.open, "door switch is close");
        }
        _;
    }
    
    event gasolineAlarm(uint256 gasoline);
    
    function startUp() public whenDoorClose {
        require(_gasoline >= 1, "gasoline is not haved");
        _gasoline = _gasoline - 1;
        if (_gasoline <= 5) {
            emit gasolineAlarm(_gasoline);
        }
    }
    
    function getGasoline() public view returns(uint256 gasoline) {
        return _gasoline;
    }
    
    function addGasoline(uint256 gasoline) public {
        _gasoline += gasoline;
    }
    
    function changeGasoline() public {
        require(_gasoline != 0 || _bucket != 0, "can not change");
        
        if (_gasoline == 0) {
            _gasoline = _bucket;
            _bucket = 0;
        } else if (_bucket == 0) {
            _bucket = _gasoline;
            _gasoline = 0;
        } else {
            uint256 __tempBucket = _gasoline;
            _gasoline = _bucket;
            _bucket = __tempBucket;
        }
        
        _gasolineType = -1 * _gasolineType;
    }
    
    function getGasolineType() public view returns(int gasolineType) {
        return _gasolineType;
    }
    
    
    function getDoorStatus() public view returns(bool doorStatus) {
        return _doorStatus;
    }
    
    function changeDoorStatus() public {
        _doorStatus = ! _doorStatus;
    }
    
    function setCarInfo(string memory color, uint8 wheelNum) public {
        _carInfo.color = color;
        _carInfo.wheelNum = wheelNum;
        
        //_carInfo = CarInfo(color, wheelNum);
    }
    
    function getCarInfo() public view returns(string memory color, int wheelNum) {
        color = _carInfo.color;
        wheelNum = _carInfo.wheelNum;
    }
    
    function setDoorSwitch(DoorSwitch doorSwitch) public {
        _doorSwitch = doorSwitch; 
    }
}

咱們已經更新到6代小汽車了,在6代小汽車中咱們加入了DoorSwitch車門的開關,使用的就是枚舉定義的,在實際項目中枚舉定義的話,通常使用在狀態和類型的定義上,方便進行管理。

到此咱們的小汽車已經完成了,經歷了6代的更新,相信你們對於本節課程有空了必定的瞭解了。智能合約包含的狀態變量、函數、函數變量、函數修飾器、事件、結構、枚舉類都已經在製做和升級小汽車中使用了。

第三講 數據類型

在solidity中有專門的數據類型,什麼是數據類型呢,咱們能夠認爲數字是類型,是不是類型,地址是類型等。在solidity中存在如下經常使用的類型,uint/int、bool、 address、 mapping、bytes、string、fixed/ufixed 經常使用的類型。

pragma solidity ^0.6.0;


contract Test {
    uint v1 = 1;
    int v2 = -2;
    bool v3 = true;
    address v4 = 0x8a5fa31F2bf83812ECd8E5Ef1878dD12bBaDb40C;
    mapping(uint => uint) v5;
    bytes v6 = "0x123"; 
    string v7 = "asd";
    fixed v8 = 1.3;
    ufixed v9 = 1.2;
}

一、 uint/int類型

uint/int 類型都是整型,也就是都是整數。一、二、三、4相似的數沒有小數點。區別是uint是沒有符號的。看代碼中的 v1 就是無符號整數,v2就是有符號整數。默認是0

二、bool

bool 中文是布爾類型,表示是/否的類型。v3就是bool類型,只有兩個值 true/false,默認是false

三、address

address是地址類型,存儲地址的,帳戶地址,合約地址等,均可以。

四、mapping

映射類型示例中的代碼不是很全,我來解釋下,實際上他是一個key-value模型,也就是一個key對應一個value。好比咱們說小明,咱們就是到小明這我的。就是這個道理。

五、bytes

定長數組,就是說他的長度是固定的,不能改變,設定是多少就是多少。

六、string

字符串類型,能夠放字符串,長度是255位二級制數。

七、fixed/ufixed

浮點類型,就是帶有小數點的,1.二、1.四、1.9這類數值。也是分爲有無符號項。

第四講 控制語句

在solidity中可使用如下控制結構,有 if,else,while,for,break,continue,return,? : 咱們來以此介紹這些結構。

一、if else語句

你們確定好奇什麼是if else語句。他就是咱們說的若是 否者。也就是說若是我怎麼樣否者我怎麼樣。注意else只能與if一同使用,你們一塊兒來看下面代碼:

pragma solidity ^0.6.0;


contract Test {
    uint256 temp;
    
    function test1(bool doorSwitch) public {
        if (doorSwitch) {
            temp = 1;
        }
    }
    
    function test2(bool doorSwitch) public {
        if (doorSwitch) {
            temp = 1;
        }
        temp = 2;
    }
    
    function test3(bool doorSwitch) public {
        if (doorSwitch) {
            temp = 1;
        } else {
            temp = 2;
        }
    }
    
    function getTemp() public view returns(uint256){
        return temp;
    }
}

上面代碼中咱們定義了三個測試方法,以及一個獲取temp值的方法。第一個測試方法表示若是咱們傳進去的doorSwitch是true,門是開的,那麼temp就等於1,否者doorSwitch等於false的話temp值不變。第二個測試方法表示若是咱們傳進去的doorSwitch是true,門是開的,那麼temp就先等於1,而後等於2,否者doorSwitch等於false的話temp直接等於2。第三個測試方法表示若是咱們傳進去的doorSwitch是true,門是開的,那麼temp就等於1,否者doorSwitch等於false的話temp等於2。

二、while語句

while 語句是循環語句,表示知足條件就一直循環,通常我麼會和break來使用,當達到某種狀況進行跳出循環,可讓循環體自動結束循環。

pragma solidity ^0.6.0;


contract Test {
    function test1() public {
        int a = 1;
        while(true) {
            if (a == 5) {
                break;
            }
            a++;
        }
    }
    
    function test2() public {
        int a = 1;
        while(a != 5) {
            
            a++;
        }
    }
}

上面兩個函數內部都有一個循環,實際上兩個循環的效果是同樣的,有一些細微的差異,須要根據具體業務場景分析了,不建議使用while 一不當心死循環就很差玩了。

三、for循環

for循環概念直接上代碼你們好理解些。

pragma solidity ^0.6.0;


contract Test {
    function test1() public {
        for(uint8 i = 0; i < 10; i ++) {
            ...
        }
    }
}

上面就是一個經常使用的for循環使用方式,循環10次 「...」 的內容。

四、continue 語句

continue語句是跳過本次循環進入下一次循環,來看代碼:

pragma solidity ^0.6.0;


contract Test {
    function test1() public {
        for(uint8 i = 0; i < 10; i ++) {
            if (i == 3) {
                continue;
            }
            
            ...
        }
    }
}

上面的代碼是若是i = 3後跳過此次循環。

五、return 語句

return 是返回的命令當遇到return時就是表示結束了。看代碼:

pragma solidity ^0.6.0;


contract Test {
    function test1() public {
        for(uint8 i = 0; i < 10; i ++) {
            if (i == 3) {
                return;
            }
            
            ...
        }
    }
}

上面的代碼能夠看出,運行到i = 3時,程序直接結束。

六、 結束

到此咱們的控制語句基本學習結束。這些概念每一個語言都差很少,因此很重要。多練小夥伴!

結束

學習完上面的課程你們對於智能合約基本已經學習好了,可是仍是缺少使用,只是知道有啥東西,何時用不知道,這個時候須要你們進行鍛鍊了,多寫一些小東西練習。

相關文章
相關標籤/搜索