Truffleのテストが実行できないエラーに対応する

ブロックチェーンアプリケーション開発の教科書のERC20準拠のトークン作成でエラーに遭遇したのでメモ。

## 結論
solidityのバージョンエラーとopenzeppelinのコードに変更があったことによるエラーでした。
以下のように修正することでテストが実行できました。

```
pragma solidity ^0.5.0;
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

contract DappsToken is ERC20 {
  string public name = "DappsToken";
  string public symbol = "DTKN";
  uint public decimals = 18;

  constructor(uint initialSupply) public {
    _mint(msg.sender, initialSupply);
  }

}

```

## エラー対応①

テストを実行すると以下のようなエラーが出た。

```
truffle(develop)> test

Compiling your contracts...
===========================
> Compiling ./contracts/DappsToken.sol
> Compiling ./contracts/Migrations.sol
> Compiling openzeppelin-solidity/contracts/ownership/Ownable.sol

SyntaxError: Source file requires different compiler version (current compiler is 0.5.0+commit.1d4f565a.Emscripten.clang - note that nightly builds are considered to be strictly less than the released version
pragma solidity ^0.4.23;
^----------------------^

DeclarationError: Identifier not found or not unique.
contract DappsToken is StandardToken {
                       ^-----------^

Error: Truffle is currently using solc 0.5.0, but one or more of your contracts specify "pragma solidity ^0.4.23".
Please update your truffle config or pragma statement(s).
(See https://truffleframework.com/docs/truffle/reference/configuration#compiler-configuration for information on
configuring Truffle to use a specific solc compiler version.)

Compilation failed. See above.
```

エラー内容としては、以下の2点。
- truffleの対応バージョンが0.5.0からなのでsolidityのバージョンが古い
- `StandardToken`が見つからない


### truffleの対応バージョンが0.5.0からなのでsolidityのバージョンが古い
バージョンを修正。

```
-pragma solidity ^0.4.23;
+pragma solidity ^0.5.0;
```

### `StandardToken`が見つからない
`openzeppelin-solidity/contracts/token/StandardToken.sol'`が正しそうだが、最新のopenzeppelinのソースコードにはStandardToken.solというファイルは無く、代わりにERC20.solというファイルが出来ていた。こちらを使うのが正しそう。

```
-import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
+import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
 
-contract DappsToken is StandardToken {
+contract DappsToken is ERC20 {
```


## エラー対応②

上記の修正を行いもう一度テストするとまたエラー。

```
Compiling your contracts...
===========================
> Compiling ./contracts/DappsToken.sol
> Compiling ./contracts/Migrations.sol
> Compiling openzeppelin-solidity/contracts/math/SafeMath.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/IERC20.sol

DeclarationError: Undeclared identifier. Did you mean "totalSupply"?
    totalSupply_ = initialSupply;
    ^----------^

DeclarationError: Undeclared identifier. Did you mean "balanceOf"?
    balances[msg.sender] = initialSupply;
    ^------^

Compilation failed. See above.
```

### totalSupplyとbalancesが無い

`openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol`にinternalのmapping, 変数として定義されていたため、継承先でも触ることが出来たが、最新版ではprivateで定義されているため参照出来ない。

[openzeppelin-solidity/BasicToken.sol at v1.12.0 · OpenZeppelin/openzeppelin-solidity · GitHub](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v1.12.0/contracts/token/ERC20/BasicToken.sol)

```
mapping(address => uint256) internal balances;

uint256 internal totalSupply_;
```

[openzeppelin-solidity/ERC20.sol at master · OpenZeppelin/openzeppelin-solidity · GitHub](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/ERC20.sol)

```
mapping (address => uint256) private _balances;

mapping (address => mapping (address => uint256)) private _allowed;

uint256 private _totalSupply;
```

代わりに`mint`という関数で同様の処理を行うことができそう。

```
/**
 * @dev Internal function that mints an amount of the token and assigns it to
 * an account. This encapsulates the modification of balances such that the
 * proper events are emitted.
 * @param account The account that will receive the created tokens.
 * @param value The amount that will be created.
 */
function _mint(address account, uint256 value) internal {
    require(account != address(0), "ERC20: mint to the zero address");

    _totalSupply = _totalSupply.add(value);
    _balances[account] = _balances[account].add(value);
    emit Transfer(address(0), account, value);
}
```

下記のように修正。

```
-    totalSupply_ = initialSupply;
-    balances[msg.sender] = initialSupply;
+    _mint(msg.sender, initialSupply);
```


無事に実行できた。

```
truffle(develop)> test

Compiling your contracts...
===========================
> Compiling ./contracts/DappsToken.sol
> Compiling ./contracts/Migrations.sol
> Compiling openzeppelin-solidity/contracts/math/SafeMath.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
> Compiling openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
> Artifacts written to /var/folders/05/8v72f98537x82ygl33y3_xzw0000gp/T/test-11942-93269-g4qlu1.bbje4
> Compiled successfully using:
   - solc: 0.5.0+commit.1d4f565a.Emscripten.clang



  0 passing (0ms)
```


最終的には下記のように変更することでテストが実行できた。

```
pragma solidity ^0.5.0;
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

contract DappsToken is ERC20 {
  string public name = "DappsToken";
  string public symbol = "DTKN";
  uint public decimals = 18;

  constructor(uint initialSupply) public {
    _mint(msg.sender, initialSupply);
  }

}

```

## 参考

[OpenZeppelinを活用してセキュアのコントラクトを書く - Qiita](https://qiita.com/blueplanet/items/c372ff82efc9f82268de)
[GitHub - OpenZeppelin/openzeppelin-solidity: OpenZeppelin is a library for secure smart contract development](https://github.com/OpenZeppelin/openzeppelin-solidity)
[ブロックチェーンアプリケーション開発の教科書 \| 加嵜 長門, 篠原 航, 丸山 弘詩 |本 | 通販 | Amazon](https://www.amazon.co.jp/%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%8A%A0%E5%B5%9C-%E9%95%B7%E9%96%80/dp/4839965137)