Solidity 教程笔记

/ / 3090浏览

Solidity 教程笔记

数据类型

  1. bool : 布尔类型

  2. int : 整型, 包括负数

  3. uint : 正整数

  4. uint256 : 256位正整数

  5. address : 地址类型, 该类型存储一个20字节的值, 如

    address public _address = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
    

    Tips: 地址作为所有合约的基础, 有普通的地址和可以转账ETH的地址(payable)。payable的地址拥有balance和transfer()两个成员,方便查询ETH余额以及转账。如:

    address payable public _address1 = payable(_address); // payable address,可以转账、查余额
    
  6. byte, bytes8, bytes32 : 定长字节数组, 定长bytes可以存一些数据,消耗gas比较少.

  7. enum 枚举, 主要用于 uint 分配名称, 使用名称代替从0开始的uint. (不常用)

函数(Function)

  1. 函数组成:
function <function name> (<parameter types>) {internal|external|public|private} [pure|view|payable] [returns (<return types>)]
  1. 函数关键字解释:

    • function : 声明函数的关键字

    • internal, external, public, private : 函数可见性说明符, 默认为public, 合约(constract)之外的函数默认具有internal可见性

      public: 内外部均可见

      private: 只能从合约内部访问, 继承合约不可访问.

      external : 只能从合约外部访问, 例如 Constract.func(), 也可以使用 this.func()

      internal : 只能从合约外部访问, 继承的合约也可以使用(也可用于修饰状态变量)

    • pure, view, payale : 决定函数权限/功能的关键字.

      payable : 可支付的, 被该类型修饰的函数可以给合约转入ETH, 同时拥有函数所有功能, 能改变链上的状态变量

      pure : 被该关键字修饰的方法无法查看或更改链上的状态变量

      view : 被该关键字修饰的方法可以查看链上的状态, 但无法更改值.

      Tips: 以下语句视为修改链上状态, 而修改链上状态则需要支付GAS

      1. 写入状态变量
      2. 释放事件
      3. 创建其他合同
      4. 使用selfdestruct
      5. 通过调用发送以太币
      6. 调用任何未被标记的prueview的函数
      7. 使用低级调用(low-level calls)
      8. 使用某些操作码的内联汇编
    • returnreturns

      returns : 加在函数名后面, 用于声明返回的变量类型及变量名

      return : 用于函数主体中, 指定返回变量

      如下:

      // 返回多个变量
      function returnMultiple() public pure returns (uint256, bool, uint256[3] memory) {    // 最后这个 memory是指返回值存储在内存中
        return(1, true, [uint256(1), 2, 5]);
      }
      
    • 数据位置(solidity中引用类型[array, struct, mapping ]这类变量所占空间大, 赋值传递时直接传递地址, 因此使用时必须声明数据存储的位置, 返回值也一样需要)

      solidity数据存储位置有三类:storagememorycalldata

      1. storage : 合约里的变量默认都是storage, 存储在链上, 消耗GAS

        uint[] public x = [1, 2, 3];
        
            // 合约变量赋值给本地storeage时会创建引用, 指向合约变量, 修改xStoreage会影响x
            function fStrage() public {
                uint[] storage xStorage = x;
                xStorage[0] = 100;  // 执行后 x[0] = 100;
            }
        

      2. memory : 函数里的参数和临时变量存储在内存中, 不上链, 但仍需使用GAS

        uint[] public x = [1, 2, 3];
        
        // 合约变量赋值给本地memory变量中时, 会创建变量副本, 更改本地变量值, 不会更改合约变量的值(使用view也是这个道理)
            function fMemory() public view{
                uint[] memory xStorage = x;
                xStorage[0] = 101;    // 执行后 x[0] = 1;
            }
        

        Tips: memory赋值给memory 会创建引用, 修改新变量会影响原变量

      3. calldatamemory 类似, 存储在内存中, 但是calldata变量不能修改(immutable), 一般用于函数的参数, 也需要使用少量的GAS, 例如:

        function fCalldata(uint[] calldata _x) public pure returns(uint[] calldata){
          //参数为calldata数组,不能被修改
          // _x[0] = 0 //这样修改会报错
          return(_x);
        }
        
    • 构造函数:

      • 构造函数(constructor)是一种特殊的函数,每个合约可以定义一个,并在部署合约的时候自动运行一次. 例如:

        address owner; // 定义owner变量
        // 构造函数
        constructor() {
            owner = msg.sender; // 在部署合约的时候,将owner设置为部署者的地址
        }
        

        Tips: 构造函数在不同的solidity版本中的语法并不一致,在Solidity 0.4.22之前,构造函数不使用 constructor 而是使用与合约名同名的函数作为构造函数而使用

引用类型

引用类型包含两种 arraystruct

  1. array : 数组分为固定长度和可变长度两种

    • 固定长度:

      uint[8] arrayA;
      bytes1[5] array2;
      address[100] array3;
      
    • 可变长度数:

      uint[] array4;
      bytes1[] array5;
      address[] array6;
      bytes array7;    // bytes 比较特殊, 是数组但是不用加[]
      

      Tips 数组有以下成员:

      length : 数组包含一个长度值, memory 数组长度在创建后是固定的;
      push : 动态数组bytes拥有push()成员, 可以追加一个0元素;
      push(x): 同上, 可以追加一个x元素;
      pop(): 动态数组bytes拥有pop()成员, 从最后弹出一个元素;

      • 创建数组的规则:
    • 对于memory 修饰的动态数组, 可以使用new 关键字创建, 但是必须声明长度, 且长度不能改变, 如下:

      uint[] memory array8 = new uint[](5)

      Tips: 如果创建的是动态数组,你需要一个一个元素的赋值

      uint[] memory x = new uint[](2);
      x[0] = 1;
      x[1] = 2;
      
  1. struct 结构体:

    • 构建结构体:

      struct Student {
       uint256 id;
       string name;
      }
      
    • 初始化方法:

    // 结构体
    struct Student {
     uint256 id;
     string name;
    }
    
    Student public student;
    
    // 以下为两种初始化方法
    // 1. 创建本地 storage 变量指针, 赋值初始化
    function initStruct1(uint256 id, string calldata name) external {
     Student storage stu = student;
     student.id = id;
     student.name = name;
    }
    
    // 2. 直接引用状态变量的指针
    function initStudent2(uint256 id, string calldata name) external {
     student.id = id;
     student.name = name;
    }
    

映射类型(Mapping)

声明映射的格式为mapping(_KeyType => _ValueType),其中_KeyType_ValueType分别是KeyValue的变量类型。例子:

 mapping(uint => address) public idToAddress; // id映射到地址
 mapping(address => address) public swapPair; // 币对的映射,地址到地址
  1. 映射规则:
    • mappingkey只能使用solidity默认类型, value不限制
    • mapping的存储位置必须是storage, 不能用于public函数的参数或返回结果中(新版本可能会变??)
    • 如果映射声明为public,那么solidity会自动给你创建一个getter函数,可以通过Key来查询对应的Value
    • 新增键值对的语法为map[key] = val
    • mapping不存储任何key的列表, 也没有length成员
    • mapping使用keccak256(key)当成offset存取value
    • 因为Etherenum会定义所有未使用的空间为0, 所以未赋值的键初始值都是0

变量的初始值

  1. 值类型的初始值
    • boolean : false
    • string:""
    • int : `0``
    • `uint : 0
    • enum: 枚举中的第一个元素
    • address:0x0000000000000000000000000000000000000000(或address(0))
    • function: [internal,external`] : 空白方程
    • 引用类型为其内部类型的初始值
    • delete 操作符(可以算作关键字)
  2. delete a 会让变量a 的值变为初始值.

语言特征

  1. 修饰器(modifier):

    • 定义: 类似于面向对象编程中的decorator,声明函数拥有的特性,并减少代码冗余, modifier的主要使用场景是运行函数前的检查,例如地址,变量,余额等

    • 编码Code

      // 定义modifier
      modifier onlyOwner {
          require(msg.sender == owner); // 检查调用者是否为owner地址
          _; // 如果是的话,继续运行函数主体;否则报错并revert交易
      }
      
      function changeOwner(address newOwner) external onlyOwner {
          owner = newOwner;    //只有owner地址的人运行这个函数才会改变owner地址
      }
      

      最常用的控制智能合约权限的方法

    • 事件(event)

      • solidity中的事件是EVM上日志的抽象表现, 两个特点: 响应式(可以RPC调用)和经济(存储消耗的GAS少, 大概2000, 十分之一变量的存储)

      • 编码Code, 以ERC20代币合约的Transfer事件为例:

        event Transfer(address indexed from, address indexed to, uint256 value);

        1. 事件声明由event关键字开头
        2. indexed关键字: 每个被indexed标记的便来可以理解为事件的索引, 在以太坊上作为一个独立的topic进行存储和索引, 每个事件最多带有三个indexed变量, 每个indexed变量的大小固定为256byte, 其中topic[0]是此事件的keccak256哈希,topic[1]topic[3]存储了带indexed变量的keccak256哈希, value不带indexed关键字, 会被存储在事件的data部分, data不能被直接检索, 但可以存储任意大小的数据, 如下: img

继承

抽象和接口

异常(errorrequireassert)

总结


基础 =.= 完