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;
    }
  
  
}