AdventCalendar
Blockchain
Ethereum
solidity
SmartContract
EthereumDay 20

Solidity v0.5.0 Breaking Changesでの変更点をコントラクトべースで見てみる


Ethereum Advent Calendar 2018 20日目、@satomin_devです。

Solidity v0.5.0 Breaking Changesの変更で、remix時に影響出そうな箇所を見ていきます。

※すべてをやると長くなるので、一般的なコントラクトをコンパイルしてエラーを見ていきます。


Solidity v0.4.25 で正常に機能していたコントラクト


Crowdfunding.sol

pragma solidity >=0.4.0 <0.6.0;

contract Crowdfunding {
struct Project {
string script;
uint value;
address recipient;
bool complete;
uint approvalCount;
mapping(address => bool) approvals;
}

Project[] public projects;
address public manager;
uint public minimumValue;
mapping(address => bool) contributers;
uint public approversCount;

modifier onlyowner() {
require(msg.sender == manager);
_;
}

constructor(uint minimum) public {
manager = msg.sender;
minimumValue = minimum;
}

function contribute() public payable {
require(msg.value > minimumValue);
contributers[msg.sender] = true;
approversCount++;
}

function createProject(string script, uint value, address recipient) public onlyowner {
Project memory newProject = Project({
script: script,
value: value,
recipient: recipient,
complete: false,
approvalCount: 0
});
projects.push(newProject);
}

function contributeRequest(uint index) public {
Project storage project = projects[index];

require(contributers[msg.sender]);
require(!project.approvals[msg.sender]);

project.approvals[msg.sender] = true;
project.approvalCount++;
}

function finalizeProject(uint index) public payable onlyowner {
Project storage project = projects[index];

require(project.approvalCount > (approversCount / 2));
require(!project.complete);

project.recipient.transfer(project.value);
project.complete = true;
}

}


この状態でコンパイルした結果がこちら。

Remix - Solidity IDE 2018-12-13 18-58-28.png

問題なくコンパイルできています。


Solidity v0.5.0 でコンパイルしていきます

browser/Crowdfunding.sol:35:28: TypeError: Data location must be "memory" for parameter in function, but none was given.


//問題の箇所はこちらです
function createProject(string script, uint value, address recipient) public onlyowner {
Project memory newProject = Project({
script: script,
value: value,
recipient: recipient,
complete: false,
approvalCount: 0
});
projects.push(newProject);
}

memoryじゃなきゃいけないのに、なにもないぞ」と怒られています。

Solidity v0.5.0 Breaking Changes によると


Explicit data location for all variables of struct, array or mapping types is now mandatory. This is also applied to function parameters and return variables.


ということで、このように変えます。


function createProject(string memory script, uint value, address recipient) public onlyowner {
Project memory newProject = Project({
script: script,
value: value,
recipient: recipient,
complete: false,
approvalCount: 0
});
projects.push(newProject);
}

これでOKです。

次のエラーはこちら。

browser/Crowdfunding.sol:62:9: TypeError: Member "transfer" not found or not visible after argument-dependent lookup in address.

recipientのaddressがpayableじゃない」と怒られています。


The address type was split into address and address payable


Solidity v0.5.0では、addressaddress payableは区別されるようになりました。

ということで、


struct Project {
string script;
uint value;
address payable recipient;
bool complete;
uint approvalCount;
mapping(address => bool) approvals;
}

recipientのタイプをaddress payableに変更します。

それに加えて、先程のcreateProjectfunctionも変更します。


function createProject(string memory script, uint value, address payable recipient) public onlyowner {
Project memory newProject = Project({
script: script,
value: value,
recipient: recipient,
complete: false,
approvalCount: 0
});
projects.push(newProject);
}

これでコンパイルがエラーなくいけます。

Remix - Solidity IDE 2018-12-13 18-57-37.png

Solidity v0.5.0 Breaking Changesを一つ一つ見ていくのは時間がかかるので、一旦一般的なコントラクトコードにおいてでてきそうなエラーを対処してみました。

<補足> 他の変更点

keccak256()に渡せる引数がbytes1つに変更。

keccak256()のエイリアスsha3()使用不可に。

.call(), .delegatecall(), .staticcall()bytes1つに変更。


The functions .call(), .delegatecall(), staticcall(), keccak256(), sha256() and ripemd160() now accept only a single bytes argument


.transfer(), .send()address payableにする。

msg.senderaddress payable


The address type was split into address and address payable, where only address payable provides the transfer function. An address payable can be directly converted to an address, but the other way around is not allowed. Converting address to address payable is possible via conversion through uint160. If c is a contract, address(c) results in address payable only if c has a payable fallback function. If you use the withdraw pattern, you most likely do not have to change your code because transfer is only used on msg.sender instead of stored addresses and msg.sender is an address payable.



詳細|Solidity v0.5.0

Solidity v0.5.0 Breaking Changes


Solidity v0.5.0 で正常に機能したコントラクト


Crowdfunding.sol

pragma solidity >=0.4.0 <0.6.0;

contract Crowdfunding {
struct Project {
string script;
uint value;
address payable recipient;
bool complete;
uint approvalCount;
mapping(address => bool) approvals;
}

Project[] public projects;
address public manager;
uint public minimumValue;
mapping(address => bool) contributers;
uint public approversCount;

modifier onlyowner() {
require(msg.sender == manager);
_;
}

constructor(uint minimum) public {
manager = msg.sender;
minimumValue = minimum;
}

function contribute() public payable {
require(msg.value > minimumValue);
contributers[msg.sender] = true;
approversCount++;
}

function createProject(string memory script, uint value, address payable recipient) public onlyowner {
Project memory newProject = Project({
script: script,
value: value,
recipient: recipient,
complete: false,
approvalCount: 0
});
projects.push(newProject);
}

function contributeRequest(uint index) public {
Project storage project = projects[index];

require(contributers[msg.sender]);
require(!project.approvals[msg.sender]);

project.approvals[msg.sender] = true;
project.approvalCount++;
}

function finalizeProject(uint index) public payable onlyowner {
Project storage project = projects[index];

require(project.approvalCount > (approversCount / 2));
require(!project.complete);

project.recipient.transfer(project.value);
project.complete = true;
}

}