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

<br/>

Ethereum Advent Calendar 2018 20日目、[@satomin_dev](https://twitter.com/satomin_dev)です。



Solidity v0.5.0 Breaking Changesの変更で、remix時に影響出そうな箇所を見ていきます。
※すべてをやると長くなるので、一般的なコントラクトをコンパイルしてエラーを見ていきます。

# Solidity v0.4.25 で正常に機能していたコントラクト
```javascript: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;
    } 
    
}
```

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

<img width="501" alt="Remix - Solidity IDE 2018-12-13 18-58-28.png" src="https://qiita-image-store.s3.amazonaws.com/0/244700/36c164ab-9a1e-3541-3d37-ab5ee40163f6.png">

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



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

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

```javascript

//問題の箇所はこちらです
    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](https://solidity.readthedocs.io/en/v0.5.0/050-breaking-changes.html)  によると

>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.


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

```javascript

    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`は区別されるようになりました。

ということで、

```javascript

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

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

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

```javascript

    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);
    }
```

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

<img width="496" alt="Remix - Solidity IDE 2018-12-13 18-57-37.png" src="https://qiita-image-store.s3.amazonaws.com/0/244700/3bac395b-ea25-f356-328e-5b1954481e51.png">

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](https://solidity.readthedocs.io/en/v0.5.0/050-breaking-changes.html)


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

```javascript: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;
    } 
    
}

```