Skip to content

Solidity8基本语法

HelloWorld

JavaScript
// SPDX-License-Identifier:MIT

// 版权声明

pragma solidity 0.8.7;

// ^代表该版本之上与该版本之内

contract HelloWorld {

    // public 的变量会自带一个getter方法

    string public myString = "hello world";

}

常用类型

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract ValueTypes {

    bool public b = true;

    uint public u = 123;

    // uint = uint256 0-->2**256-1

    //        uint8   0-->2**8-1

    //        uint16  0-->2**16-1

    // 表示负数

    int public i = -123;

    // int = int256   -2**255-->2**255-1

    //       int127   -2**127-->2**127-1

    int public minInt = type(int).min;

    int public maxInt = type(int).max;

    // 最特殊的类型-->代表地址

    address public addr = 0xF9F4eD85E440BfD7A8bADE33454C32dB9E66b283;

    // 比16位的地址长一些

    bytes32 public b32 = 0x89c58ced8a9078bdef2bb60f22e58eeff7dbfed6c2dff3e7c508b629295926fa;

}

函数

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract FunctionIntro {

    // external代表外部函数,只能在外部读取的函数

    // pure代表纯函数的概念,意思是这个函数不能够读,也不能够写状态变量,只能够拥有自己的局部变量

    // returns规定一下返回的类型

    function add(uint x, uint y) external pure returns (uint) {

        return x+y;

    }

    function sub(uint x, uint y) external pure returns (uint) {

        return x-y;

    }

}

状态变量

状态变量是保存在链上的。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract StateVariables {

    uint public myUint = 123;

    function foo() external {

        uint notStateVariable = 456;

        // 这个变量只有在调用的时候才会在虚拟机的内存中产生

    }

}
  • 状态变量改变了之后依然会保存在链上
  • 而局部变量改变了之后同样也不会保存在链上

全局变量

全局变量指的是不用定义就能够使用的变量,这些变量往往记录了链上的信息和账户的信息。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract GlobalVariables {

    // view和pure类似都是只读方法,但是view可以读取变量的值;

    function globalVars() external view returns (address, uint, uint) {

        // 展示的账户的内容,指的是调用这个函数的地址是什么(有可能是一个人,也有可能是另一个合约)

        address sender = msg.sender;

        uint timestamp = block.timestamp;  // 指当前区块链的时间戳

        uint blockNum = block.number;  // 区块编号

        return (sender, timestamp, blockNum);

    }

}

变量的默认值

状态变量和局部变量在你没有给他赋值的情况下都是以默认值存在。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract DefaultValues {

    bool public b;  // false

    uint public u;  // 0

    int public i;   // 0

    address public a; // 0x很多0

    bytes32 public b32; //0x很多0

}

常量

如果不变的值,那么就尽量定义为常量,因为它可以节省你的gas费。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Constants {

    // 加上constant关键字,同时习惯于变量名为大写

    address public constant MY_ADDRESS = 0xF9F4eD85E440BfD7A8bADE33454C32dB9E66b283;

    uint public constant MY_UINT = 123;

}

ifElse

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract ifElse {

    function example(uint _x) external pure returns (uint) {

        if(_x < 10){

            return 1;

        }else if(_x < 20){

            return 2;

        }else {

            return 3;

        }

    }

  

    function ternary(uint _x) external pure returns (uint) {

        return _x < 10 ? 1 : 2;

    }

}

循环

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Loop {

    function loops() external pure {

        for (uint i = 0; i < 10 ; i++){

            // continue

            // break

        }

        while(true){

            // ode

        }

    }

}

报错控制

  • require
  • revert
  • assert 这三种方法都具有gas费的退还,和状态变量回滚的特性 8.0之后还可以自定义错误
JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Errors {

    function testRequire(uint _i) public pure {

        require(_i <= 10, "i > 10");  // 只有满足这个条件才可以继续运行,如果不为真,则会报出后面的报错信息

    }

    function testRevert(uint _i) public pure {

        // revert是不能够包含表达式的

        if(_i > 10){

            revert("i > 10")

        }

    }

    uint public num = 123;

  

    function testAssert() public view {

        assert(num == 123);  // 不含报错信息,只能进行断言判断的作用

    }

    // 自定义错误,可以节约gas费

    error MyError(address caller, uint i);

  

    function testCustomError(uint _i) public view {

        if(_i > 10) {

            revert MyError(msg.sender, _i);

        }

    }

}

函数修改器

能够使复用的代码简化的语法

  • Basic
  • inputs
  • sandwich
JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract FunctionModifier {

    bool public paused;

    uint public count;

  

    function setPause(bool _paused) external {

        paused = _paused;

    }

  

    function inc() external {

        require(!paused, "paused");  // 1

        count += 1;

    }

    function dec() external {

        require(!paused, "paused");  // 2 这两处可以复用

        count -= 1;

    }

}

修改之后:

基本使用

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract FunctionModifier {

    bool public paused;

    uint public count;

  

    function setPause(bool _paused) external {

        paused = _paused;

    }

    modifier whenNotPaused() {

        require(!paused, "paused");

        _;  // 这个下划线代表其他不同的代码所在的位置

    }

  

    function inc() external whenNotPaused{  // 然后这里再添加上函数名就可以了

        count += 1;

    }

    function dec() external whenNotPaused{

        count -= 1;

    }

}

带参数的修改器

JavaScript
    modifier cap(uint _x){

        require(_x < 100, "x >= 100");

        _;

    }

    function incBy(uint _x) external whenNotPaused cap(_x) {

        count += _x;

    }

Sandwich

JavaScript
    modifier sandwih() {

        // code here

        count += 10;

        _;

        // more code here

        count *= 2;

    }

构造函数

仅能在合约被部署的时候调用一次

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Constructor {

    address public owner;

    uint public x;

  

    constructor(uint _x) {

        owner = msg.sender;

        x = _x;

    }

}

例子--Ownable合约

设计一个有管理员权限的智能合约;

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Ownable {

    address public owner;  // 管理员账户地址;

  

    constructor(){

        owner = mg.sender;  // 将合约的部署者传进去

    }

  

    modifier onlyOwner(){

        require(msg.sender == owner, "not owner");  

        // 函数的调用者只有等于之前记录的owner才可以继续调用;

        _;

    }

    // 当前Owner才可以操作

    function setOwner(address _newOwner) external onlyOwner {

        // 不能传给零地址,不然就锁死了

        require(_newOwner != address(0), "invaild address")

        owner = _newOwner;

    }

  

    function onlyOwnerCanCallThisFunc() external onlyOwner{

        // some code

    }

    function anyOneCanCall() external {

        // code

    }

  

}

函数返回值

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract FunctionOutputs {

    function returnMany() public pure returns (uint, bool) {

        return (1, true);

    }

    function named() public pure returns (uint x, bool b) {

        return (1, true);

    }

  

    function assigned() public pure returns (uint x, bool b) {

        // 这里可以隐式返回

        x = 1;

        b = true;

    }

    // 获取函数调用的返回值

    function destructingAssignments() public pure {

        (uint x, bool b) = returnMany();

        // (, bool b) = returnMany();

    }

}

数组

  • 动态数组和固定长度的数组
  • 数组的初始化
  • 数组的操作
  • 在内存中创建数组
  • 通过函数来返回数组
JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Array {

    uint[] public nums = [1,2,3];  // 动长数组

    uint[3] public numsFixed = [4,5,6];  // 固定数组

  

    function examples() external {

        nums.push(4);  // [1,2,3,4]

        uint x = nums[1];

        nums[1] = 777;  // [1,2,777,4]

        delete nums[1]; // [1,0,777,4] delete不能减少数组的长度

        nums.pop(); //[1,0,777]

        uint len = nums.length;

  

        // 在内存中创建数组

        uint[] memory a = new uint[](5);  // 在内存中你是不能创建动态数组的,所以这里还要定义一下它的长度

        // 这里不能使用pop,push等修改数组长度的方法,只能通过索引操作

        // 总结:在内存中局部变量只能够定义定长数组,而动态数组只能够存在于状态变量中

        a[0] = 123;

    }

    // 返回

    function returnArray() external view returns (uint[] memory){

        return nums;

    }

}

通过移动位置达到真正地删除数组元素的效果。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract ArrayShift {

    uint[] public arr;

  

    function example() public {

        arr = [1,2,3];

        delete arr[1]; // [1,0,3]

    }

  

    // [1,2,3] -- remove(1) --> [1,3,3]  --> [1,3];

    function remove(uint _index) public {

        require(_index < arr.length, "index out of bound");

  

        for(uint i = _index; i < arr.length-1; i++){

            arr[i] = arr[i+1];

        }

        arr.pop();

    }

}

上述方法是比较消耗gas的,因为数组要向左移动,是一个循环; 通过替换位置达到真正地删除数组元素的效果。

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

// 就是将最后一个元素覆盖对应要删除的元素的位置,这样可以减少操作,但是没有保证数组的顺序;

contract ArrayReplaceLast {

    uint[] public arr;

  

    // [1,2,3,4] -- remove(1) --> [1,4,3]

    function remove(uint _index) public {

        arr[_index] = arr[arr.length - 1];

        arr.pop();

    }

}

映射

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Mapping {

    mapping(address => uint) public balances;

    // 嵌套

    mapping(address => mapping(address => bool)) public isFriend;

  

    function examples() external {

        balances[msg.sender] = 123;

        uint bal = balances[msg.sender];

        uint bal2 = balances[address(1)];  // 没有的返回默认值,uint的默认值为0

  

        balances[msg.sender] += 456;  //123+456=579;

  

        delete balances[msg.sender];  // 0

  

        isFriend[msg.sender][address(this)] = true;

    }

}

映射迭代

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract IterableMapping {

    mapping(address => uint) public balances;  // 地址==>y余额

    mapping(address => bool) public inserted;  // 某一个地址是否存在于映射中

    address[] public keys;  // 记录所有存在的地址

  

    function set(address _key, uint _val) external {

        balances[_key] = _val;

  

        if(!inserted[_key]) {

            inserted[_key] = true;

            keys.push(_key);

        }

    }

  

    function getSize() external view returns (uint) {

        return keys.length;

    }

    function first() external view returns (uint) {

        return balances[keys[0]];

    }

    function last() external view returns (uint) {

        return balances[keys[keys.length-1]];

    }

    function get(uint _i) external view returns (uint) {

        return balances[keys[_i]];

    }

}

结构体

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Structs {

    struct Car {

        string model;

        uint year;

        address owner;

    }

  

    Car public car;

    Car[] public cars;

    mapping(address => Car[]) public carsByOwner;

  

    function examples() external {

        Car memory toyota = Car("Toyota", 1990, msg.sender);

        Car memory lambo = Car({year: 1980, model: "Lamborghini", owner: msg.sender});

        Car memory tesla;  // 这个会使用各个数据类型的默认值

        // 赋值

        tesla.model = "Tesla";

        tesla.year = 2010;

        tesla.owner = msg.sender;

  

        cars.push(toyota);

        cars.push(lambo);

        cars.push(tesla);

        // 推入到数组中,它就从内存中的局部变量值变成了状态变量了

        cars.push(Car("Ferrri", 2020, msg.sender));

  

        // 获取结构体的值

        Car memory _car = cars[0];

        // _car.model;

        // 而如果我们定义在存储中,那么我们就可以修改结构体中的值了

        Car storage _car = cars[0];

        _car.year = 1999;  // 这hi后,合约中的状态变量也随之被修改了

  

        // 删除

        delete _car.owner;  // 恢复到默认值

        delete cars[1];

    }

}

枚举

使一个变量具有多个状态

JavaScript
// SPDX-License-Identifier:MIT

  

pragma solidity 0.8.7;

  

contract Enum {

    enum Status {

        None,

        Pending,

        Shipped,

        Completed,

        Rejected,

        Ccanceled,

    }

  

    Status public status;

  

    struct Order {

        address buyer;

        Status status;

    }

  

    Order[] public orders;

  

    // 操作

    function get() view returns (Status) {

        return status;

    }

    function set(Status _status) external {

        status = _status;

    }

  

    function ship() external {

        status = Status.Shipped;

    }

  

    function reset() external {

        delete status;

    }

  
  

}