Ethereum Advent Calendar 2018 20日目、@satomin_devです。
Solidity v0.5.0 Breaking Changesの変更で、remix時に影響出そうな箇所を見ていきます。
※すべてをやると長くなるので、一般的なコントラクトをコンパイルしてエラーを見ていきます。
Solidity v0.4.25 で正常に機能していたコントラクト
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;
}
}
この状態でコンパイルした結果がこちら。
問題なくコンパイルできています。
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では、address
とaddress payable
は区別されるようになりました。
ということで、
struct Project {
string script;
uint value;
address payable recipient;
bool complete;
uint approvalCount;
mapping(address => bool) approvals;
}
recipient
のタイプをaddress payable
に変更します。
それに加えて、先程のcreateProject
functionも変更します。
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);
}
これでコンパイルがエラーなくいけます。
Solidity v0.5.0 Breaking Changesを一つ一つ見ていくのは時間がかかるので、一旦一般的なコントラクトコードにおいてでてきそうなエラーを対処してみました。
<補足> 他の変更点
・keccak256()
に渡せる引数がbytes
1つに変更。
・keccak256()
のエイリアスsha3()
使用不可に。
・.call()
, .delegatecall()
, .staticcall()
もbytes
1つに変更。
The functions .call(), .delegatecall(), staticcall(), keccak256(), sha256() and ripemd160() now accept only a single bytes argument
・.transfer()
, .send()
はaddress payable
にする。
・msg.sender
はaddress 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 で正常に機能したコントラクト
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;
}
}