Solidity 8.0 初阶-基础语法

类型和值

// 常用变量
bool public b = true;
uint public u = 123; // uint  = uint256 0~2**256 -1
                    // uint 8 = 0~ 2**8 -1
int public i = -123;
int public minInt = type(int).min;
int public maxInt = type(int).max;
address public addr = 0x0eF3E974d17C21e42B40909445B134480Df1Aa6F;
bytes32 public b32 = 0x0909445B13440eF3E974d17C21e42B40909445B134480Df1Aa6F3234532345;

函数介绍

contract HelloWord {
    // external 外部函数,只能在外部读取,能出现在 abi 中
    // pure     不读也不写(只能有局部变量)
    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;
    }
}

变量

  • 局部变量 函数内变量
  • 全局变量
  • 状态变量(链上变量)
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract HelloWord {
    // 状态变量(链上变量)
    uint public myUint = 123;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract HelloWord {
    // 常见的全局变量
    
    // view 可以读取全局变量和状态变量,  pure 只能读取局部变量
    function globalCars() external view returns(address, uint, uint) {
        address sender = msg.sender;
        uint timestamp = block.timestamp; // 区块时间戳(只读的)
        uint blockNum = block.number; // 区块
        return (sender, timestamp, blockNum);
    }
}

只读函数

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;


// view 和 pure 区别 
// 如果要改写变量,这不能填写 view或者pure
contract HelloWord {
    uint public num;

    // 因为该函数读取了,状态变量(链上变量)只能用 view 来定义
    function viewFunc() external view returns (uint){
        return num;
    }

    // 只读取局部变量用 pure
    function pureFunc(uint x, uint y) external pure returns (uint) {
        return x + y;
    }
  
    // 对链上变量进行改写,就能用 view 来修饰了
    function inc() external {
        num -= 1;
    }
}

常量

:::info
constant 可以修饰变量,让其变为常量,不能修改其值,节约 gas 费用
:::

// constant 修饰变量位常量,且命名规范位大写
address public constant MY_ADDRESS = 0x0eF3E974d17C21e42B40909445B134480Df1Aa6F;
uint public constant MY_UINT = 123;

返回值

contract FunctionOutputs {
    // 返回方法1
    function retrunMany() public pure returns(uint, bool) {
        return (1, true);
    }

    // 返回方法2
    function assigned() public pure returns (uint x, bool b){
        x = 1;
        b = true;
    }

    // 接收返回值
    function destructing() public pure {
        (uint x, bool b) = retrunMany(); // 接受2个返回值
        (, bool _b) = retrunMany(); // 只要第二个返回值
    }
}

判断

普通判断与三元运算

if(_x<10){
    return 1;
}else if(_x<20){
    return 2;
}
// 三元运算
return _x < 10 ? 1:2;

循环

function loops() external pure{
    for (uint i=0; i<10; i++){
        if (i==3){
            continue;  // break;
        }
    }

    uint j = 0;
    while (j < 10){
        j++;
    }
}

报错

  • 8.0 版本之前 通常用 require, revert, assert
  • 8.0后用 error 定义
// 报错控制
contract Error {
    // require, revert, assert 都需要 gas 费
    function testRequire(uint _i) public pure{
        require(_i <= 10, "Test: i > 10");
    }
    
    function testRevert(uint _i) public pure {
        if(_i > 10) {  // 大于 10 就进行报错
            revert("i > 10");
        }
    }

    // assert 断言
    uint public num = 123;
    function testAssert() public view {
        assert(num == 123);
    }

    // 8.0+可以自定义错误
    error MyError(address caller, uint i);  // 定义在顶部可以给当前文件使用
    function testError(uint _i) public view{
        if (_i > 10){
            revert MyError(msg.sender, _i);
        }
    }
}

数组

contract Array {
    uint[] public nums = [1,2,3,4]; // 动态数组
    uint[3] public numsFixed = [1,2,3]; // 定长数组

    // 数组
    function examples() external {
        nums.push(9);  // 末尾推入一个新的值
        uint x = nums[1]; // 访问指定下标,从0开始
        nums[2] = 777; // 修改
        delete nums[1];  // 删除下标位置元素,值改为默认值 uint 默认 0 (总长度不变) 想要实现真正删除,只能直接写remove例子在下面
        nums.pop(); //弹出最后一个元素(会删除)
        uint len  = nums.length; // 获取长度

        // 内存中创建数组
        // 只能定义定长数组,且不能 pop, push
        uint[] memory a = new uint[](5);
    }

    // 通过函数返回数组的全部类容
    function returnArray() external view returns(uint[] memory){
        return nums;
    }

    // [1,2,3] -- remove(1) -- [1,3] 删除元素
    uint[] public arr = [1,2,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();
    }
}

类型-映射 (json)

映射就相当于 json

// 映射(json)
contract Mapping{
    // 声明映射: 地址映射余额
    mapping(address => uint) public balances;  // 查找地址,余额就会返回
    // 嵌套映射, 查找2个地址是否是朋友关系
    mapping(address => mapping(address => bool)) public isFirend;
    
    function examples() external{
        balances[msg.sender] = 123;   // 设置映射值
        uint bal = balances[msg.sender];  // 获取映射值
        delete balances[msg.sender];  // 删除后,等于默认值 0

        // 设置嵌套映射
        isFirend[msg.sender][address(this)] = true; // {address1: {address2: true}}
    }
}

获取迭代映射数据

contract IterableMapping {
    mapping(address => uint) public balances;   // 地址对应余额
    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]];
    } 
}

类型-结构体(对象)

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

// 结构体
contract Structs{
    struct Car {  // 汽车对象结构体
        string model;
        uint year;
        address owner;
    }

    Car public car;  // 定义一个变量
    Car[] public cars;  // 定义一个数组
    mapping(address => Car[]) public carsByOwner;  // 定义一个 映射 json,一个地址可能有多个汽车

    function examples() external {
        // 生成结构体方法1
        Car memory toyota = Car("Toyota", 1990, msg.sender);
        // 方法2
        Car memory lambo = Car({model:"Lamborghini", year: 1980, owner:msg.sender});
        // 方法3
        Car memory tesla; // 默认值
        tesla.year = 2020;
        tesla.model = "Tesla";
        tesla.owner = msg.sender;

        // 推入结构体数组 2 种方法
        cars.push(toyota);
        cars.push(lambo);
        cars.push(tesla);

        cars.push(Car("Ferrari", 1943, msg.sender));

        // 获取局部变量值
        Car memory _car = cars[0];
        _car.model;

        // 结构体可以定义在内存中 memory
        // 也可以定义在 存储中 (就可以进行修改和删除操作)
        Car storage _car2 = cars[0];
        _car2.year = 1888; // 存储中才能进行修改
        delete _car2.owner; // 删除(清空为默认值)

        delete cars[1]; // 删除,都是清空为默认值
    }
}

类型-枚举

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

// 枚举类型
contract Enum {
    enum Status {  // 例子:枚举 当前操作的状态
        None,   // 空
        Pending,// 处理中
        Shipped,// 装载中
        Completed, // 已装载
        Rejected,  // 已拒绝
        Canceled   // 已取消
    }

    Status public status;

    struct Order {  // 结构体
        address buyer;
        Status status;
    }

    Order[] public orders;

    // 操作枚举类型

    function get() public view returns (Status){  // 读取
        return status;
    }

    function set(Status _status) external {
        status = _status;
        // status= Status.Shipped;  // 也可以直接设置为指定值
    }

    function reset() external{  // 状态变量恢复到默认值 (第一个值)
        delete status;
    }
}

Hash 运算

  • abi.encode() 采用 abi 的编码形式补0 【推荐使用】
  • abi.encodePacked() 只是把字符串变成了16进制
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract HashFunc {
    // 建议使用 encode 标准模式打包
    function hash(string memory text, uint num, address addr) external pure returns(bytes32){
        return keccak256(abi.encodePacked(text, num, addr));
    }
  
    // 举例两种打包模式的区别
    function encode(string memory text, string memory text2) external pure returns(bytes memory){
        return abi.encode(text, text2);
    }
    function encodePacked(string memory text, string memory text2) external pure returns(bytes memory){
        return abi.encodePacked(text, text2);
    }
}
// 测试: hellow,222,0x5B38Da6a701c568545dCfcB03FcB875f56beddC4