Solidity


起因是Xenc師傅給我截了張圖,我日 居然看不懂 ,一搜才知道,之前學的版本有些老了.. 這次學下新一點的記錄下

HelloWorld

pragma solidity ^0.6.0;     // version

contract One{
    // 狀態變量
    string name;
    
    function setName() public{  // 必須要加權限修飾符了
        name = "Muxue";
    }
    
    // view修飾的函數 表示訪問狀態變量, 但是不會修改, 不會消耗任何資源
    function getName() public view returns(string memory){  // 返回字符串類型也需要加memory了
        return name;
    }
    
    // pure修飾的函數 不不修飾也不讀取狀態變量 不會消耗任何資源
    function pureTest() public pure returns(string memory){
        return "test";
    }

}

變量 常量 標識符 關鍵字

變量

type name = value;

標識符

需要命名的地方都是標識符
標識符命名規則

1. 字符, 數字, _, $組成
2. 不能以數字開頭
3. 區分大小寫

數據類型

基本類型

  1. 布爾(bool)
  2. 整形(int / uint)
  3. 地址(address)
  4. 定長字節數組
  5. 定長浮點(fixed / ufixed)
  6. 枚舉類型(enum)
  7. 函數類型(function)

引用類型

  1. 數組(array) {不定長字節數組, 字符串}
  2. 結構體(struct)
  3. 映射(mapping)

區別

值類型一般都是值拷貝傳遞,但引用類型 有些是地址傳遞

數組

全局數組

全局的是storage
Array
幾種聲明 初始化方式

pragma solidity ^0.6.0;

contract ArrayTest{
    uint[4] arr1;    // 固定數組的聲明
    uint[] arr2;    //  動態長度數組的聲明
    
    // 聲明並初始化
    uint[2] arr3 = [1,2];
    uint[] arr4 = [1,4,12,4,2];
    
    // 使用new
    uint[] public arr5 =  new uint[](3);
} 

length和push pop的講解

length: 長度 修改length 可改變動態長度數組的長度,但是0.6.0以上的版本都不能使用length修改數組長度
push:固定數組不能用 動態數組storage可以用 memory不能用
pop: 把push進來的數據 再頂出去

pragma solidity ^0.6.0;

contract ArrayTest{
    uint[] arr4 = [1,4,12,4,2];
    
    // 使用new
    uint[] public arr5 =  new uint[](3);
	
    function push() public{
        arr5.push(1);
    }
    
    // 嘗試使用修改length
    function changeLength() public{
        // arr4.length = 12; // browser/Array/1.sol:19:9: TypeError: Member "length" is read-only and cannot be used to resize arrays. arr4.length = 12; // ^---------^
    }
    
    
    function Get() view public returns(uint[] memory){
        return arr5;
    }
    
    function pop() public{
        arr5.pop();     // 把push進來的再推出去
    }
    
}

局部數組

局部的是memory
注意的一點是 新版本的 局部的都要加memory,包括返回數組 返回值那里

pragma solidity ^0.6.0;

contract ArrayTest{
    
    function test() public returns(uint[] memory){
        uint[] memory arr8 = new uint[](3);    // 使用new方法創建局部數組
        arr8[0] = 1;
        return arr8;   
    }
    
}

數據存儲位置

storage:是存在區塊鏈 上的,全局變量

哪些數組類型可以使用storage
- 數組
- 結構體
- 映射

memory:是存在內存上的,函數內的 局部的變量
:值類型的局部變量存儲在這里
calldata:當函數為外部函數(external),如果此函數的參數(非返回參數),則參數要求必須用calldata,(教程里是這樣說的,但是我用memory也沒報錯)
image

對於存儲型的數組(storage), 可以放任意的元素類型
對於內存性的數組(memory), 元素不可以是映射類型mapping

storage和memory的相互轉換

pragma solidity ^0.6.0;

contract testFunc{
        
    uint[] public a1Storage = [1,2,3];
    
    function Geta1() public view returns(uint[] memory){
        return a1Storage;
    }
    // sotrage -> memory    值傳遞 不會修改storage的值
    function storageTomemory() public view{
        uint[] memory b = a1Storage;   // storage 賦值給 memory
        b[0] = 100;
    }
    
    // memory -> storage    值傳遞
    function memoryTostorage() public{
        uint8[4] memory c = [2,4,1,5];
        a1Storage = c;
        c[0] = 255;
    }
    
    // sotrage -> storage    引用傳遞 修改一個 原數據也會修改數據
    function storageTostorage() public{
        uint[] storage m = a1Storage;
        m[1] = 123;
    }
    
    // memory -> memory      引用傳遞
    function memoryTomemory() public view returns(uint8[3] memory){
        uint8[3] memory a = [1,2,3];
        uint8[3] memory b = a;
        b[1] = 12;
        return a;
    }
    
}

簡單來說兩個相同的都是引用傳遞,不同的都是值傳遞

字節數組

也分為

  • 變長字節數組
  • 定長字節數組
    bytes : 變成字節數組
    bytes+num:后面跟數字的是定長字節數組
pragma solidity ^0.6.0;
contract Test{
    
    bytes b1;   // 變長字節數組 bytes里是以16進制存儲值的
    bytes b2 = "abc\x22\x25";   // \x后面就代表是十六進制的值
    bytes b3 = "ce\u8bd5";
    
    function Get() public view returns( bytes memory){
        return b3;
    }
    
    bytes4 bt = 0x74657374;     // 定長字節數組
    
}

定長字節數組轉變長字節數組

pragma solidity ^0.6.0;
contract Test{
    
    bytes4 bt = 0x74657374;     // 定長字節數組
    
    // 把定長字節數組轉換成變長字節數組
    function To() public view returns(bytes memory){
        bytes memory temp = new bytes(bt.length);
        for(uint i=0;i<bt.length;i++){
            temp[i] = bt[i];
        }
        return temp;
    }
}

string

string沒啥好說了吧 我擦..
字節轉字符串

pragma solidity ^0.6.0;
contract Test{
    
    bytes public name = "慕雪";
    function EchoName() public view returns(string memory){
        return string(name);
    }
    
}

Solidity交易和內置對象

提到交易不得不提Gas了哈,也就是燃料也叫手續費
Gas又分為兩個:

  • gas price 每個gas的價格
  • gas limit 限制gas的最大值

需要給礦工預付的gas = gas price * gas limit

消息:不修改合約狀態,如帶有pure,view等關鍵字的

賬戶和地址

分為外部賬戶合約賬戶, 外部賬戶地址, 外部合約地址

也就是外部賬戶和外部合約都各有地址

外部賬戶:我們錢包的一個用戶 給你一個地址 就是一個外部賬戶
image
合約賬戶:我們部署一個合約 都會給我們一個地址
image

地址
address

  • 20個字節,160位
  • 值類型

address payable
0.5.0以后出的
只能使用它來進行轉賬

屬性

  • balance

函數
專門用來做交易的

  • transfer
  • send

不會提示錯誤

demo code

pragma solidity ^0.6.0;

contract Test{
    function echoAddress() public view returns(uint){
        address _address = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
        return uint(_address);
    }
    
    function getBalance(address _account) public view returns(uint){
        return _account.balance;
        // return address(this).balance; 返回當前合約賬戶余額
        // this是指當前合約賬戶
    }
    
    function getThis() public view returns(address){
        return address(this);
    }
    
    function testTransfer() public payable{
        address(this).transfer(msg.value);  // 前面是要給哪個地址轉賬的地址
    }
    
    function testTransfer2() public payable{
        address payable _address = address(this);   // 轉賬必須加payable
        _address.transfer(1 ether);     // 雖然這里寫1了 但是我們還是需要在value那里設置1 ether
    }
	
	function testSend() public payable{
        address payable _address = address(this);
        _address.send(1 ether);
    }
    
    fallback() payable external{    // 回退函數
        
    }
    receive() payable external{
        
    }
}

transfersend的區別:

transfer假如value和設置的值不同 會報錯。而send不會,但會返回一個false,但是錯誤了 還是會扣錢

Solidity單位和全局變量

具體可以看
https://solidity-cn.readthedocs.io/zh/develop/units-and-global-variables.html?highlight=全局#id3

區塊鏈和交易屬性

pragma solidity ^0.6.0;

contract One{
    /*
        block.blockhash(uint blockNumber) returns (bytes32):指定區塊的區塊哈希——僅可用於最新的 256 個區塊且不包括當前區塊;而 blocks 從 0.4.22 版本開始已經不推薦使用,
        由 blockhash(uint blockNumber) 代替
        block.number (uint): 當前區塊號
        msg.data (bytes): 完整的 calldata
        msg.gas (uint): 剩余 gas - 自 0.4.21 版本開始已經不推薦使用,由 gesleft() 代替
        msg.sig (bytes4): calldata 的前 4 字節(也就是函數標識符)
        msg.value (uint): 隨消息發送的 wei 的數量
        tx.gasprice (uint): 交易的 gas 價格
        tx.origin (address): 交易發起者(完全的調用鏈)
    */
    
    function getBlockHash() public view returns(bytes32){
        return blockhash(block.number-1);     // block.number 當前區塊號,得-1 要不然獲取不到hash
    }
    
    // block.coinbase (address): 挖出當前區塊的礦工地址
    function getBlockCoinbase() public view returns(address){
        return block.coinbase;
    }
    
    //  block.difficulty (uint): 當前區塊難度
    function getBlockDifficulty() public view returns(uint){
        return block.difficulty;
    }
    
    // block.gaslimit (uint): 當前區塊 gas 限額
    function getBlockLimit() public view returns(uint){
        return block.gaslimit;
    }
    
    //  block.timestamp (uint): 自 unix epoch 起始當前區塊以秒計的時間戳
    function getBlockTimestamp() public view returns(uint){
        return block.timestamp;
    }
    
    // gasleft() returns (uint256):剩余的 gas
    function getGasleft() public view returns(uint){
        return gasleft();
    }
    
    // now (uint): 目前區塊時間戳(block.timestamp)
    function getTime() public view returns(uint){
        return now;
    }
    
    // msg.sender (address): 消息發送者(當前調用)
    function GetMsgsender() public view returns(address){
        return msg.sender;
    }
    
}

image

錯誤處理

最常用的應該就是require了吧

pragma solidity ^0.6.0;

contract Two{
    uint public money;
    address master;
    constructor() public{       // 構造函數
        master = msg.sender;
    }
    
    function changeMoney() public {
        require(msg.sender ==  master); // 如果不是 就直接報錯了 不會往下面走
        money += 5000;
    }
}

函數

也就是function,已經非常熟悉了

可見性

  • public:內部 外部都可調用,狀態變量和函數都可以用
  • private:私有的 只有內部可以調用,狀態變量和函數都可以用
  • internal:只有內部可以調用,狀態變量和函數都可以用
  • external:只有外部可以調用,只有函數可以用
    image
    image

可以明顯的看出 被privateinternal修飾的函數 外部不可以調用

external修飾的內部不可以調用
如要調用可以加一個this.

function test3() public view returns(uint) {
	return this.testExternal();
}

調用方式

  • external:外部的 也就是外部點擊函數名稱那種方式
  • internal:內部的 就是一個合約方法調用另一個方法唄

可變性 可修改性

  • pure:不用狀態變量的時候
  • view:需要用到狀態變量
  • payable:需要付款的時候
  • constant:和view一樣的,0.5.0以后舍棄了

返回值

pragma solidity ^0.6.0;

contract Two{
    // 返回多個參數
    function test1() public pure returns(uint a,uint b){
        return (2,4);
    }
    
    // 寫參數名字
    function test2() public pure returns(uint sum){
        sum = 1+2;  // 不需要加類型 因為上面我們定義了
        // 不用reutrn 要是return,以return的值為結果
    }
}

get

public修飾的變量,系統默認寫了一個同名的函數,用來獲取值,0.5.0以上的版本 不支持重寫了
image

修改器 modifier

作用:可以修改函數的行為,控制函數的邏輯,代碼重用

pragma solidity ^0.6.0;

contract TestModifier{
    
    uint public a;
    
    // create modifier
    modifier myModifier(){
        a=1;
        _;  // 函數會到這
        a=10;
    }
    
    // call modifier
    function callModifier() public  myModifier{
        a=9;       // 最終結果為10
    }
    
}

帶參數的modifier

來個demo看看modifier的強大之處

pragma solidity ^0.6.0;

contract Two{
    uint public level;
    string public name;
    string public sex;
    
    modifier levelRequire(uint _level) {
        require(level>=_level);
        _;
    }
    
    function setLevel(uint _num) public{
        level = _num;
    }
    
    function setName() public levelRequire(5){  // 相當於level需要大於或者等於五級才可以調用此方法
        name = "Muxue";		// 如果modifier的_;后面還有 會在當前代碼后面執行
    }
    
    function setSex() public  levelRequire(7){
        sex = "male";
    }
    
}

多重modifier的執行順序

這個是有點東西的

contract Three{
    uint public a = 1;
    
    modifier Test1{
        a = 2;  // 執行順序1
        _;      // 把Test2帶進來
        a = 3;  // 執行順序5    也就是最后一個
    }
    
    modifier Test2{
        a = 4;  // 執行順序2
        _;      // 把test函數帶進來
        a = 5;  // 執行順序4
    }
    
    function test() public Test1 Test2{
        a = 6;  // 執行順序3
        // a的結果為3
    }
}

如果不信的 可以debug一步步的調着看

image

合約

構造函數

  • 合約創建時自動調用,且只調用一次。每個合約只能有一個構造函數
  • 構造函數一般是為狀態變量初始化
  • constructor 關鍵字
  • 分為有參和無參
pragma solidity ^0.6.0;

contract TestNewContract{
    uint age;
    constructor(uint _age) public{
        age = _age;
    }    
}

封裝性

  1. 函數合約

  2. 修飾符(訪問修飾符)

    • public, private , internal, external

      • 狀態變量:publicprivate, internal
      • 函數:publicprivate, internal, external

繼承

  • is 關鍵字
pragma solidity ^0.6.0;

contract Father{
    
    address owner;
    string name;
    uint money = 100000000000000000000000;
    
    constructor() public{
        owner = msg.sender;
        name = "Muxue";
    }
    
    function changeName(string memory _name ) public{
        require(msg.sender == owner);
        name = _name;
    }
    
}

contract Son is Father{
    function getMoney() public view returns(uint){
        return money;   // 繼承的父親的
    }
}

父合約構造函數的傳參

pragma solidity ^0.6.0;

contract Father{
    uint private age;
    constructor(uint _age) public{
        age = _age;
    }
}
    
contract Son is Father(46){ // 繼承式
    
}
    // 這兩種方法不能同時使用
contract Son2 is Father{
    constructor() Father(46) public{    // 修改風格式
        
    }
}

多態

重寫父類方法和狀態變量

得在父類方法上加上virtual關鍵字,子類重寫的方法加上override方法

pragma solidity ^0.6.0;

contract A{
    function F() public pure virtual returns(string memory){ //  僅當函數被標記為 virtual 或在接口中定義 時,才可以覆蓋  
        return "A";
    }
}

contract B is A{
    // 函數重寫
    function F() public pure override returns(string memory){      // override 代表可重寫    
        return "B";
    }
    // 函數重載
    function F(string memory _test) public pure returns(string memory){
            return _test;
        }
    
}

父類:virtual,子類:override
重寫要保證,函數名,參數,返回值相同

重寫狀態變量
0.6.0后 不可再重寫狀態變量

super

pragma solidity ^0.6.0;

contract A{
    function F() public pure virtual returns(string memory){ //  僅當函數被標記為 virtual 或在接口中定義 時,才可以覆蓋  
        return "A";
    }
    
    function eat() public pure virtual returns(string memory){
        return "rou";
    }
}

contract B is A{
    // 函數重寫
    function F() public pure override returns(string memory){      // override 代表可重寫    
        return "B";
    }
        // 函數重載
    function F(string memory _test) public pure returns(string memory){
            return _test;
    }
    
    function eat() public pure override returns(string memory){
        return "yu";
    }
       
    function test() public view returns(string memory){
        // return eat();   // 調用的是自己重寫的那個方法
        return super.eat(); // 調用父類eat方法
    }         
    
}

想要調用父類的,也可以用 父類名.方法名
super是內部調用

多重繼承 重寫父類方法

pragma solidity ^0.6.0;

contract Father{
    function getMoney() public pure virtual returns(uint ){
        return 10000;
    }
}

contract Mother{
    function getMoney() public pure virtual returns(uint ){
        return 8000;
    }
}

contract Son is Father,Mother{
    function getMoney() public pure override(Father,Mother) returns(uint){		// 處理重名函數
        return 18000;
    }
}

這個在我編譯時,一直在報錯,但是我還不知道哪錯了,我用0.8.4編譯一次之后,沒問題。最后再換回0.6的版本,照樣可以編譯成功

抽象合約

抽象合約

  • 關鍵字:abstract
  • 抽象合約中,可以有抽象函數,非抽象函數
  • 抽象函數 不需要實現函數體
  • 抽象合約不能實例化

作用:起到約束,約束繼承的抽象合約的子合約,必須重寫抽象函數

pragma solidity ^0.6.0;

abstract contract Father{   // 抽象合約		關鍵字 abstract
    function eat() public pure virtual; // 抽象函數
}

contract Son is Father{
    // 必須重寫函數
    function eat() public pure override{
        
    }
}

接口

  • 關鍵字interface
  • 接口中所有的函數都是抽象函數,所有可以省略virtual關鍵字
  • 接口函數的修飾符必須使用external
  • 其余限制:不能繼承其他合約或接口,不能定義構造函數,不能定義狀態變量,不能定義結構體
interface Father{   // 接口
    function eat() external pure;    // 沒有{}
}

contract Son is Father{
    function eat() public pure override{    // 重寫eat方法
        
    }
}

作用

  • 代碼重要性
  • 將多個合約重復的代碼提取到一個庫中
  • 不需要繼承 節省gas

關鍵字及特性

libray

  • 庫中的函數不能修改狀態變量
  • 庫不可以被銷毀
  • 不能定義狀態變量
  • 不可以繼承其他元素,也不能被繼承
  • 庫不能接受以太幣

使用

有兩種方式
庫名.方法名

pragma solidity ^0.6.0;

library Search{ // 庫定義
        function indexOf(uint[] storage _data,uint _value) public view returns(uint){
        for(uint i=0;i<_data.length;i++){
            if(_data[i] == _value){
                return i;
            }
        }
        return uint(-1);
    }
}

contract TestLibray{    // 第一種
    uint[] data;
    
    constructor() public{
        data.push(1);
        data.push(2);
        data.push(3);
        data.push(4);
    }
    
    // 調用庫函數
    function indexof(uint _value) public view returns(uint){
        return Search.indexOf(data,_value);
    }
}

using 庫名 for 狀態變量

pragma solidity ^0.6.0;

library Search{ // 庫定義
        function indexOf(uint[] storage self,uint _value) public view returns(uint){    // self也就是傳過來的那個類型
        for(uint i=0;i<self.length;i++){
            if(self[i] == _value){
                return i;
            }
        }
        return uint(-1);
    }
}

contract TestUsingFor{

    using Search for uint[];    // 把search這個庫綁定為uint[]類型
    uint[] data;
    
    constructor() public{
        data.push(1);
        data.push(2);
        data.push(3);
        data.push(4);
    }
    
    // 調用庫函數
    function indexof(uint _value) public view returns(uint){
        return data.indexOf(_value);    // 會自動把data傳給第一個參數
    }
}

合約銷毀

合約生命周期

  1. 合約創建(new、sdk)
  2. 合約操作、使用(調用函數實現功能)
  3. 合約銷毀(區塊鏈上 關於合約的存儲和代碼都會被刪除)
pragma solidity ^0.6.0;

library Search{ // 庫定義
        function indexOf(uint[] storage self,uint _value) public view returns(uint){    // self也就是傳過來的那個類型
        for(uint i=0;i<self.length;i++){
            if(self[i] == _value){
                return i;
            }
        }
        return uint(-1);
    }
}

contract TestUsingFor{

    using Search for uint[];    // 把search這個庫綁定為uint[]類型
    uint[] data;
    address owner;
    
    constructor() public{
        owner = msg.sender;
        data.push(1);
        data.push(2);
        data.push(3);
        data.push(4);
    }
    
    modifier onlyOwner() {
        require(owner == msg.sender);
        _;
    }
    
    // 調用庫函數
    function indexof(uint _value) public view returns(uint){
        return data.indexOf(_value);    // 會自動把data傳給第一個參數
    }
    
    function kill() public onlyOwner{
        selfdestruct(msg.sender);   // 銷毀合約
    }
    
}

映射

引用數據類型

關鍵字及特性

  1. mapping
  2. 存儲一對數據,以key-value形式的
  3. key的數據類型是有要求的:動態數組,枚舉,structmapping都不可以;value基本上都可以
  4. mapping不能作為參數使用
pragma solidity ^0.6.0;

contract TestMapping{
    mapping(uint=>string) public uintMapping; // key:uint value:string
    mapping(address=>uint) public addressMapping;
    mapping(string=>mapping(uint=>address)) public stringMapping;
    
    constructor() public{
        uintMapping[1] = "test";
        addressMapping[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4] = 1;
        addressMapping[0x17F6AD8Ef982297579C203069C1DbfFE4348c372] = 2;
        stringMapping["Muxue"][1] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; // 復雜的是[key][key] = value;
        
    }
}

結構體

引用數據類型

關鍵字及特性

  1. 關鍵字struct
  2. 大部分類型都可以,但是不可以包含自己本身
  3. 結構體作為函數的返回值類型使用時
    • 結構體中有mapping類型,函數只能使用internal,private
    • 結構體中沒有mapping類型,需要添加pragma experimental ABIEncoderV2;
contract TestStruct{
    struct People{
        uint id;
        string name;
        mapping(string=>uint) grade;
        // People test;
    }
    
    People public pp;
    
    constructor() public{
        // People memory ppt = People(1,"Muxue");  // mapping類型不能直接賦值,因為是storage的
        People memory ppt = People({name:"Muxue",id:1});
        pp = ppt;
        pp.grade["level"] = 1;
    }
    
}

枚舉

  1. 作用:增強代碼的可讀性
  2. 基本數據類型
  3. 關鍵字:enum
  4. 書寫時不能有;
  5. 不能有""
  6. 不能有中文
  7. 結果可以轉為uint
contract TestEnum{
    enum Sex{Man,Woman}
    
    function useEnum() public pure returns(Sex){
        return Sex.Man;
    }
    
    function useenum() public pure returns(uint){
        return uint(Sex.Woman);
    }
}

事件與日志

  1. 合約中不能直接訪問日志中的內容,可以通過sdk的方式進行交互 獲取
  2. 日志通過事件來實現
  3. Solidity中,事件是操作觸發行為,日志是觸發事件后將數據記錄在區塊鏈中

事件

事件可以用來做操作記錄,存儲為日志。主要就是用來記錄日志的
關鍵字:

  • event:創建事件
  • emit:觸發事件
pragma solidity ^0.6.0;

contract TestEvent{
    event LogEvent(string _name,uint _age); // 創建一個事件
    
    function emitEvent() public{    // 不能加 pure or view
        emit LogEvent("Muxue",17);  // 觸發事件
    }
}

image

事件的主題

  1. 將事件索引化
  2. 一個沒主題的事件,無法搜索到
  3. 一個事件,最多有4個主題
    • 事件簽名
    • 參數簽名
      經過Keccak-256算法加密

簡單來說:主題就是為了講事件索引化,可查詢到這個事件

pragma solidity ^0.6.0;

contract TestEvent{
    event LogEvent(string indexed _name,uint indexed _age); // 創建一個事件
    
    function emitEvent() public{    // 不能加 pure or view
        emit LogEvent("Muxue",17);  // 觸發事件
    }
}

image

image

異常

  1. 程序編譯或運行中發生的錯誤 即異常
  2. 發生運行時異常,會將之前修改的狀態全部還原(0.6.0版本可以選擇)
  3. solidity異常
    • 0.4.10之前,throw 條件不滿足,中斷運行,恢復修改的狀態,耗光gas
    • 0.4.10之后,throw廢棄,require() ,assert(), revert()代替原來的throw
    • 0.6.0版本,增加了try catch
  4. 功能介紹
    條件檢查
    - require():還原狀態 返回gas
    - assert():還原狀態 耗光gas
    引發異常
    - throw:已經廢棄
    - revert():與thorw的區別,允許返回錯誤原因,可以退回gas
    捕獲/處理異常
    - try..catch:只適合於外部調用,
pragma solidity ^0.6.0;

contract TestException{
    uint public data = 100;
    function testThrow(uint _i) public pure{
        //if(_u<10) throw;
    }
    
    function testRequire(uint _i) public{
        data = 200;     // 假如下面引發異常了,會把修改的狀態還原回去
        require(_i>10,"_i < 10");
    }
    
    function testAssert(uint _i) public{
        data = 200;     // 假如下面引發異常了,會把修改的狀態還原回去
        assert(_i>10);
    }
    
    function testRevert(uint _i) public returns(uint){
        data = 200;     // 假如下面引發異常了,會把修改的狀態還原回去
        if(_i<10){
        revert("_i < 10");
        }
        return 12;
    }
    
    event successEvent();
    event failEvent();
    function testTry(uint _i) external returns(uint){
        try this.testRevert( _i ) returns(uint _value){ // 某個函數可能會出現異常, 必須外部調用所以加this. 返回值需要定義一個變量
            // 如果沒有異常走這個代碼塊
            emit successEvent();
            return _value;
        }catch{
             // 如果有異常走這個代碼塊
             emit failEvent();
        }
    }
}

Solidity匯編

https://learnblockchain.cn/article/675

概念

solidity匯編語言,采用自己獨特的語言風格,使編寫的代碼可讀性更高,且能夠直接與EVM交互,而且可以減少gas的消耗】

分類

  • 內聯匯編
  • 獨立匯編

內聯匯編的作用

  1. 可以直接嵌入到solidity的源碼中使用
  2. 代碼的可讀性更高
  3. 直接訪問棧
  4. 可以節省gas

字面量

  • 字符常量:10進制 16進制
  • 字符串:"",字符串字面量最多可以包含32個字符

作用域

{} 離開大括號就不能用了

特點

函數式操作碼

pragma solidity ^0.6.0;

contract Test{
    
    function TestSum(uint _n) public pure returns(uint _sum){
        for(uint i=0;i<_n;i++){
            _sum += i;
        }
    }
    
    function TestAssembly(uint _n) public pure returns(uint){
        assembly{
            let sum := 0        // let定義變量 := 賦值
            for{let i:=0} lt(i,_n) {i := add(i,1)}
            {
                sum := add(sum,i)
            }
            mstore(0x0,sum)
            return(0x0,32)
        }
    }
}

匯編局部變量

訪問外部變量

pragma solidity ^0.6.0;

contract Test2{
    uint outerV;
    
    function outerValue() public pure{
        uint innerV;
        assembly{
            let y := innerV     // 只能訪問局部變量
            
            let x := outerV     // 不能訪問狀態變量
        }
    }
}

標簽
標簽已經廢棄掉了

流程控制
solidity沒有switch,但匯編中有

  • 判斷:
    ①. if 沒有else
    ②. switch,做多選擇
  • 循環
    只有for
pragma solidity ^0.6.0;

contract Test3{
    function testIf(uint _v) public pure returns(bool flag){
        assembly{
            flag := true
            if lt(_v,5){
                flag := false
            }
        }
    }
    
    function testSwitch(uint _n) public pure returns(uint _num){
        assembly{
            let z:=_n
            switch z
                case 1{
                    _num := 1
                }
                case 2{
                    _num := 2
                }
                case 3{
                    _num := 3
                }
                default{
                    _num := 100
                }
        }
    }
}

函數

pragma solidity ^0.6.0;

contract Test4{
    function test() public pure{
        assembly{
            function sum(a,b) -> res{
                
            } 
            // 調用
            let xx := sum(1,4)
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM