2019-01-09 02:00:00
热度: 29505
作者: 隔壁老王(Old Wang NExt Door)
前言
随着solidity 0.5.0 nightly build版本的稳步推进,正式版也将在不久的将来与开发者见面。作为一个大版本更新,新版引入了很多特性,也废弃了很多关键字,比如
.call()
不仅可以获知远程调用执行成功与否,还将获得远程调用执行的返回值address
地址类型细分成 address
和 address payable
uintY
和 bytesX
不能直接转换external
可见性constructor
关键字定义throw
关键字弃用, 函数状态可变性修饰符必须用 view
,不能混用 constant
和 view
下面我们将对这些改变一一予以介绍,最后给出一个示例代码,对比展示新旧版solidity代码写法的区别,供大家参考。
显式声明
函数可见性
public
可见性。public
: constructor
构造函数必须声明为 public
可见性,否则编译报错。external
: 回退函数(fallback function), 接口(interface)的函数必须声明为 external
可见性,否则编译报错。存储位置
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
bytesX
是右填充(低比特位补0),而 uintY
是左填充(高比特位补0),二者直接转换可能会导致不符合预期的结果,所以现在当 bytesX
和 uintY
长度大小不一致(即X*8 != Y)时,不能直接转换,必须先转换到相同长度,再转换到相同类型.bytesX
类型, 必须先转换到与 bytesX
相同长度的 uintY
,再转换到 bytesX
类型bytesX
不相等,也不能直接转换成 byteX
类型ABI
abi.encodePacked()
abi.encode()
会恰当地处理 bytes
和 string
类型的填充(padding),若不需要进行填充,请使用 abi.encodePacked()
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
报错.
storage
变量.constant
常量. 如 uintconstant time=now;
是不允许的.0X
(X大写)做16进制前缀,只能用 0x
.value=0xffether
必须写成 value=0xff*1ether
.value=255.0ether
不能写成 value=255.ether
.+
. 例如 value=1ether
不能写成 value=+1ether
.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
处判断循环条件,若条件为假,就退出循环.这一修改更符合一般编程语言的设计风格.if(){...}
, do{...}while();
, for{...}
块内声明的变量,不能用在块外.pure
和 view
函数在EVM内部采用 staticcall
操作码实现(EVM版本>=拜占庭),而非之前的 call
操作码,这使得状态不可更改(state changes disallowed)在虚拟机层面得到保证.示例代码
其中 // Error...
注释掉的代码在solidity版本<0.5.0均可以编译通过,但是在>=0.5.0均得换成 // Right ...
的代码才能编译通过.
pragma solidity >0.4.99 <0.6;
contract A {
function () external payable {}
}
contract B {
function () external {}
}
interface ERC20 {
// function transferFrom(address _from, address _to, uint256 _value) public; // Error, `interface` must declare with `external`
function transfer(address _to, uint256 _value) external; // Right
}
contract F
{
address payable to_pay;
address to;
address owner;
// uint constant time = now; // Error, value should be compile time constant.
string constant time = "johnwick.io"; // Right
modifier onlyOwner {
require (msg.sender == owner);
_;
}
// function () { // Error, fallback function MUST be `external`
function () external { // Right
}
// function F() { // Error, consturctor function should use `constructor` keyword
// constructor() { // Error, `constructor` should be `public`
constructor () public { // Right
}
function payable_or_not() public {
bool result;
/* `address payable` VS `address` */
// to_pay = new A(); // Error, `contract` should explicitly convert to `address`
to_pay = address(new A()); // Right, fallback function of contract A is `payable`
to = address(new A()); // Right
to_pay.transfer(1); // Right, `transfer()` is member of `address payable`
result = to_pay.send(1); // Right, `send()` is member of `address payable`
to = address(new B()); // Right
// to_pay = address(new B()); // Error, fallback function of contract B is not `payable`.
// to.transfer(1 ether); // Error, `transfer()` is not member of `address`
// result = to.send(1 ether); // Error, `send()` is not member of `address`
bool success;
bytes memory data;
(success, data)= to.call.gas(0).value(1 ether)(""); // However, you can use `call("")` to send ether
address john;
john = to_pay; // Right, `address payable` can directly convert to `address`
address payable wick;
// wick = to; // Error, `address` can't directly convert to `address payable`
wick = address(uint160(to)); // Right, `address` can forcibly convert to `address payable`
wick.transfer(1 ether); // `wick` is `address payable` now
}
// struct dummy {} // Error, empty struct is not allowed.
struct yummy {string food;} // Right
// `external` function input parameter should explicitly declare `calldata` location.
function transfer(address _to, uint _value, bytes calldata _data) pure external returns (bool success){
// return; // Error, empty return statements for functions with one or more return values are now disallowed.
}
// `public` function input parameter should explicitly declare `memory` location.
function try_some(bytes memory _data) public view returns(bool) {
if(to == to_pay)
{
// throw; // Error, `throw` is deprecated.
revert(); // Right, you should use `assert(),require(),revert()` to raise an exception.
}
bytes32 _hash;
// _hash = sha3(_data); // Error, `sha3()` is deprecated.
_hash = keccak256(_data); // Right
uint256 _time;
// _time = 1 years; // Error, `years` is deprecated, due to the leap year problem.
_time = 366 days; // Right
// var secs = _time * 3600; // Error, `var` is deprecated.
uint secs; secs = _time * 0x3600; // Right
int256 _value;
// _value = 0xff wei; // Error, hex number with unit is not allowed now.
_value = 0xff*1 wei; // Right
// _value = 0Xff*1 wei; // Error, hex number prefix `0X` is not allowed now.
// _value = 1. ether; // Error, dot followed without numbers is not allowed now.
_value = 1.0 ether; // Right
// _value = +1; // Error
_value = -1;
// bytes storage not_initial_bytes; // Error, `storage` without initialization is not allowed now.
// uint[0] zero_array; // Error, fixed-size array of zero length is not allowed now.
bytes32 b32;
bytes20 b20;
bytes1 b1;
uint256 u256;
uint160 u160;
// b32 = bytes32(u160); // Error, can't directly convert `uintX` to `bytesY` of different size.
b32 = bytes32(uint256(u160)); // Right, convert to the same size, then convert to the same type.
u160 = uint160(bytes20(b32)); // Right
b32 = bytes32(u256); // Right, both are the same size, then convert to the same type.
// b1 = 255; // Error
b1 = bytes1(uint8(255+360)); // Right, decimal number like `uintX` should convert to the same size, then the same type.
// b16 = 0xff; // Error, hex number and `bytesX` are different size.
b20 = bytes20(uint160(0xff)); // Right, same size, then same type.
b1 = 0xff; // Right, hex number and `bytesX` are the same size
}
function im_not_payable() public returns (uint256 _value){
// _value = msg.value; // Error, `msg.value` MUST be used in `payable` function.
}
function im_payable() public payable returns (uint256 _value){
_value = msg.value; // Right, `msg.value` CAN be used in `payable` function.
}
// function not_impletement() public onlyOwner; // Error, function without implementation can't use `modifier`
function kill(address payable _to) public {
// suicide(_to); // Error, `suicide` is deprecated.
selfdestruct(_to); // Right
}
function scope_() view public {
uint count = 0 ;
for (uint i=1 ; i<100; i++){
count += i;
}
// i = now; // Error, not C99-Style scope
uint i; i = now; // Right, but this is error in solidity < 0.5.0 due to variable redefinition.
}
event ShowCount(uint);
function inf_loop() public {
uint count = 0;
do {
if (count!=0) {
continue;
}
count = 1; // <0.5.0, `continue` jump to this loop body, which results in infinite loop !!!
}while((count++)<10); // >=0.5.0 `continue` jump to this condition check.
emit ShowCount(count);
}
}
文章声明:本文为火星财经专栏作者作品,不代表火星财经观点,版权归作者所有,如需转载,请提前联系作者或注明出处。