【智能合约学习笔记】solidity v0.5.0重大改变

参考资料:http://www.qubi8.com/archives/101373.html

显式声明

函数可见性

  • 函数可见性必须显式声明. 之前, 函数如果不显式声明,将默认 public可见性.

  • public: constructor构造函数必须声明为 public可见性,否则编译报错.

  • external: 回退函数(fallback function), 接口(interface)的函数必须声明为 external可见性,否则编译报错.

存储位置

  • 结构体(struct),数组(array),映射(mapping)类型的变量必须显式声明存储位置( storage, memeory, calldata),包括函数参数和返回值变量都必须显式声明.

  • external 的函数参数需显式声明为 calldata.

合约与地址

  • contract合约类型不再包括 address类型的成员函数,必须显式转换成 address地址类型才能使用 send(), transfer(), balance等与之相关的成员函数/变量成员.

  • address地址类型细分为 address和 address payable,只有 address payable可以使用 transfer(), send()函数.

  • address payable类型可以直接转换为 address类型, 反之不能.

  • 但是 address x可以通过 address(uint160(x)),强制转换成 address payable类型.

  • 如果 contract A不具有 payable的回退函数, 那么 address(A)是 address类型.

  • 如果 contract A具有 payable的回退函数, 那么 address(A)是 address payable类型.

  • msg.sender属于 address payable类型.

转换与填充(PADDING)

UINTY与 BYTESX

  • 因为填充(padding)的时候, bytesX是右填充(低比特位补0),而 uintY是左填充(高比特位补0),二者直接转换可能会导致不符合预期的结果,所以现在当 bytesX和 uintY长度大小不一致(即X*8 != Y)时,不能直接转换,必须先转换到相同长度,再转换到相同类型.

  • 10进制数值不能直接转换成 bytesX类型, 必须先转换到与 bytesX相同长度的 uintY,再转换到 bytesX类型

  • 16进制数值如果长度与 bytesX不相等,也不能直接转换成 byteX类型

ABI

  • 字面值必须显式转换成类型才能使用 abi.encodePacked()

  • ABI编码器在构造外部函数入参和 abi.encode()会恰当地处理 bytes和 string类型的填充(padding),若不需要进行填充,请使用 abi.encodePacked()

  • ABI解码器在解析函数入参和 abi.decode()时,如果发现 calldata太短或超长,将直接抛出异常,而不是像之前自动填充(补0)和截断处理,从而有效地遏制了短地址攻击.

  • .call()族函数( .call(), .delegatecall(), .staticcall())和 哈希函数( keccak256(),sha256(), ripemd160())只接受一个参数 bytes,且不进行填充(padding)处理.

  • .call()空参数必须写成 .call("")

  • .call(sig,a,b,c)必须写成 .call(abi.encodeWithSignature(sig,a,b,c)),其他类推

  • keccak256(a,b,c)必须写成 keccak256(abi.encodePacked(a,b,c)),其他类推

  • 另外, .call()族函数之前只返回函数执行成功是否的 bool, 现在还返回函数执行的返回值,即 (bool,bytes memory). 所以之前 boolresult=.call(sig,a,b,c)现在必须写成 (boolresult,bytes memory data)=.call(sig,a,b,c).

不允许的写法

在之前版本的solidity编译,以下不允许的写法只会导致 warnings报警,现在将直接 errors报错.

  • 不允许声明0长度的定长数组类型.

  • 不允许声明0结构体成员的结构体类型.

  • 不允许声明未初始化的 storage变量.

  • 不允许定义具有命名返回值的函数类型.

  • 不允许定义非编译期常量的 constant常量. 如 uintconstant time=now;是不允许的.

  • 不允许 0X(X大写)做16进制前缀,只能用 0x.

  • 不允许16进制数和单位名称组合使用. 如 value=0xffether必须写成 value=0xff*1ether.

  • 不允许小数点后不跟数字的数值写法. 如 value=255.0ether不能写成 value=255.ether.

  • 不允许使用一元运算符 +. 例如 value=1ether不能写成 value=+1ether.

  • 不允许布尔表达式使用算术运算.

  • 不允许具有一个或多个返回值的函数使用空返回语句.

  • 不允许未实现的函数使用修饰符(modifier).

  • 不允许 msg.value用在非 payable函数里以及此函数的修饰符(modifier)里.

废弃的关键字/函数

  • years时间单位已弃用,因为闰年计算容易导致各种问题.

  • var已弃用,请用 uintY精确声明变量长度.

  • constant函数修饰符已弃用,不能用作修饰函数状态可变性, 请使用 view关键字.

  • throw关键字已弃用,请使用 revert(), require(), assert()抛出异常.

  • .callcode()已弃用,请使用 .delegatecall(). 但是注意,在内联汇编仍可使用.

  • suicide()已弃用, 请使用 selfdestruct().

  • sha3()已弃用,请使用 keccak256().

构造函数

  • 构造函数必须用 constructor关键字定义. 之前,并未强制要求,既可以用合约同名函数定义构造函数,也可以用 constructor关键字定义.

  • 不允许调用没有括号的基类构造函数.

  • 不允许在同一继承层次结构中多次指定基类构造函数参数.

  • 不允许调用带参数但具有错误参数计数的构造函数.如果只想在不提供参数的情况下指定继承关系,请不要提供括号.

其他

  • do...while循环里的 continue不再跳转到循环体内,而是跳转到 while处判断循环条件,若条件为假,就退出循环.这一修改更符合一般编程语言的设计风格.

  • 实现了C99风格的作用域:

  • 变量必须先声明,后使用.之前,是可以先使用,后声明,现在会导致编译报错.

  • 只能在相同或嵌套作用域使用.比如 if(){...}, do{...}while();, for{...}块内声明的变量,不能用在块外.

  • 变量和结构体的循环依赖递归限制在256层.

  • pure和 view函数在EVM内部采用 staticcall操作码实现(EVM版本>=拜占庭),而非之前的 call操作码,这使得状态不可更改(state changes disallowed)在虚拟机层面得到保证.

  • 示例代码

其中 // Error…注释掉的代码在solidity版本<0.5.0均可以编译通过,但是在>=0.5.0均得换成 // Right …的代码才能编译通过.

  1. pragma solidity >0.4.99 <0.6;

  2. contract A {

  3. function () external payable {}

  4. }    

  5. contract B {

  6.    function () external {}

  7. }

  8. interface ERC20 {

  9.    // function transferFrom(address _from, address _to, uint256 _value) public; // Error, `interface` must declare with `external`

  10.    function transfer(address _to, uint256 _value) external;            // Right

  11. }

  12. contract F

  13. {

  14.    address payable to_pay;

  15.    address to;

  16.    address owner;

  17.    // uint constant time = now;            // Error, value should be compile time constant.

  18.    string constant time = “johnwick.io”;   // Right

  19.    modifier onlyOwner {

  20.        require (msg.sender == owner);

  21.        _;

  22.    }

  23.    // function () {            // Error, fallback function MUST be `external`

  24.    function () external  {     // Right

  25.    }

  26.    // function F() {           // Error, consturctor function should use `constructor` keyword

  27.    // constructor() {          // Error, `constructor` should be `public`

  28.     constructor () public {    // Right

  29.    }

  30.    function payable_or_not() public {

  31.        bool result;

  32.        /* `address payable` VS `address` */

  33.        // to_pay = new A();             // Error, `contract` should explicitly convert to `address`

  34.        to_pay = address(new A());  // Right, fallback function of contract A is `payable`

  35.        to = address(new A());      // Right    

  36.        to_pay.transfer(1);         // Right, `transfer()` is member of `address payable`

  37.        result = to_pay.send(1);    // Right, `send()` is member of `address payable`

  38.        to = address(new B());          // Right

  39.        // to_pay = address(new B());   // Error, fallback function of contract B is not `payable`.

  40.        // to.transfer(1 ether);        // Error, `transfer()` is not member of `address`

  41.        // result = to.send(1 ether);   // Error,  `send()` is not member of `address`

  42.        bool success;

  43.        bytes memory data;

  44.        (success, data)= to.call.gas(0).value(1 ether)(“”);   // However, you can use `call(“”)` to send ether

  45.        address john;

  46.        john = to_pay;          // Right, `address payable` can directly convert to `address`

  47.        address payable wick;

  48.        // wick = to;                   // Error, `address` can’t directly convert to `address payable`

  49.        wick = address(uint160(to));    // Right,  `address` can forcibly convert to `address payable`

  50.        wick.transfer(1 ether);         // `wick` is `address payable` now    

  51.    }

  52.    // struct dummy {}              // Error, empty struct is not allowed.

  53.    struct yummy {string food;}     // Right

  54.    // `external` function input parameter should explicitly declare `calldata` location.

  55.    function transfer(address _to, uint _value, bytes calldata _data)  pure external returns (bool success){

  56.        // return;            // Error, empty return statements for functions with one or more return values are now disallowed.

  57.    }

  58.    // `public` function input parameter should explicitly declare `memory` location.

  59.    function try_some(bytes memory _data) public view returns(bool) {

  60.        if(to == to_pay)

  61.        {

  62.        // throw;                   // Error, `throw` is deprecated.

  63.           revert();                // Right, you should use `assert(),require(),revert()` to raise an exception.

  64.        }

  65.        bytes32 _hash;

  66.        // _hash = sha3(_data);     // Error, `sha3()` is deprecated.

  67.        _hash = keccak256(_data);   // Right

  68.        uint256 _time;

  69.        // _time = 1 years;      // Error, `years` is deprecated, due to the leap year problem.

  70.        _time = 366 days;        // Right

  71.        // var secs = _time * 3600;    // Error, `var` is deprecated.

  72.        uint secs; secs = _time * 0x3600;     // Right

  73.        int256 _value;

  74.        // _value = 0xff wei;    // Error, hex number with unit is not allowed now.

  75.        _value = 0xff*1 wei;    // Right

  76.        // _value = 0Xff*1 wei;  // Error, hex number prefix `0X` is not allowed now.

  77.        // _value = 1. ether;      // Error, dot followed without numbers is not allowed now.

  78.        _value = 1.0 ether;         // Right

  79.       // _value = +1;            // Error

  80.        _value = -1;

  81.        // bytes storage not_initial_bytes;   // Error, `storage` without initialization is not allowed now.

  82.        // uint[0] zero_array;              // Error, fixed-size array of zero length is not allowed now.

  83.        bytes32 b32;

  84.        bytes20 b20;

  85.        bytes1  b1;

  86.        uint256 u256;

  87.        uint160 u160;

  88.        // b32 = bytes32(u160);              // Error, can’t directly convert `uintX` to `bytesY` of different size.

  89.        b32 = bytes32(uint256(u160));       // Right, convert to the same size, then convert to the same type.

  90.        u160 = uint160(bytes20(b32));       // Right

  91.        b32 = bytes32(u256);                // Right, both are the same size, then convert to the same type.

  92.        // b1 = 255;                        // Error

  93.        b1 = bytes1(uint8(255+360));        // Right, decimal number like `uintX` should convert to the same size, then the same type.

  94.        // b16 = 0xff;                      // Error, hex number and `bytesX` are different size.

  95.        b20 = bytes20(uint160(0xff));       // Right, same size, then same type.

  96.        b1 = 0xff;                          // Right, hex number and `bytesX` are the same size

  97.    }

  98.    function im_not_payable() public returns (uint256 _value){

  99.     //   _value = msg.value;                 // Error, `msg.value` MUST be used in `payable` function.

  100.    }

  101.    function im_payable() public payable returns (uint256 _value){

  102.        _value = msg.value;                 // Right, `msg.value` CAN be used in `payable` function.

  103.    }

  104.    // function not_impletement() public onlyOwner; // Error, function without implementation can’t use `modifier`

  105.    function kill(address payable _to) public {

  106.        // suicide(_to);        // Error, `suicide` is deprecated.

  107.        selfdestruct(_to);      // Right

  108.    }

  109.    function scope_() view public {

  110.        uint count = 0 ;

  111.        for (uint i=1 ; i<100; i++){

  112.            count += i;

  113.        }

  114.        // i = now;             // Error, not C99-Style scope

  115.        uint i; i = now;         // Right, but this is error in solidity < 0.5.0 due to variable redefinition.

  116.    }

  117.    event ShowCount(uint);

  118.    function inf_loop() public {

  119.        uint count = 0;

  120.        do {

  121.            if (count!=0) {

  122.                continue;

  123.            }

  124.            count = 1;              // <0.5.0, `continue` jump to this loop body, which results in infinite loop !!!

  125.        }while((count++)<10);       // >=0.5.0 `continue` jump to this condition check.  

  126.        emit ShowCount(count);

  127.    }

  128. }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Solidity是一种智能合约语言,它是以太坊平台上的主要编程语言之一。以下是Solidity的教程: 1. 智能合约的基础结构 Solidity智能合约由两个部分组成:状态变量和函数。状态变量是合约内部存储的数据,而函数是合约内部的代码,用于处理状态变量或执行其他操作。 2. Solidity的数据类型 Solidity支持各种数据类型,包括布尔值、整型、地址、字符串、数组、结构体等。例如: - 布尔值:bool - 整型:int、uint - 地址:address - 字符串:string - 数组:array - 结构体:struct 3. Solidity的函数 函数是Solidity合约中最重要的部分之一。函数可以接受参数,也可以返回值。例如: ``` function add(uint a, uint b) public returns (uint) { return a + b; } ``` 这个函数接受两个整数作为参数,并返回它们的和。 4. Solidity的控制流 Solidity支持各种控制流结构,包括if、for、while、do-while等。例如: ``` function isEven(uint x) public returns (bool) { if (x % 2 == 0) { return true; } else { return false; } } ``` 这个函数接受一个整数作为参数,并返回它是否为偶数。 5. Solidity的事件 事件是Solidity合约中的一种通知机制,它可以向外部应用程序发送消息。例如: ``` event Transfer(address indexed _from, address indexed _to, uint _value); ``` 这个事件表示在合约中发生了一次转账操作,它包含了发送方地址、接收方地址和转账金额。 6. Solidity的继承 Solidity支持继承,一个合约可以从另一个合约中继承状态变量和函数。例如: ``` contract A { uint public x = 1; } contract B is A { uint public y = 2; } ``` 在这个例子中,合约B从合约A中继承了状态变量x,并且定义了自己的状态变量y。 以上就是Solidity的基础教程,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值