Ethereum/Solidityのテスト＆デプロイ

Ethereum界隈が楽しそうなので、Solidityの開発環境を整えた。
mochaとganache-cliを使ったローカルテストと、truffle-hdwallet-providerとinfura.ioを使ったデプロイ。

利用するライブラリのバージョンなどは後述するpackage.json参照。作業環境のディレクトリ構成は以下の通り。


```
.
├── compile.js
├── contracts
│   └── Calc.sol
├── deploy.js
├── package.json
└── test
    └── Calc.test.js
```

 

# ①アカウント等を準備

Metamaskをインストール。
KovanやRopstenの Test NetのfaucetからEtherをゲット。
Hi-EtherのSlackにも参加させて頂きました。

 

# ②コントラクトを実装

まずはSolidityで簡単なコントラクトを実装。remix便利だ。

``` solidity:contracts/Calc.sol
pragma solidity ^0.4.17;

contract Calc{
    int32 public num;

    function Calc(int32 initialNum) public{
        num = initialNum;
    }

    function setNum(int32 newNum) public{
        num = newNum;
    }

    function sqNum() public returns (int32){
        return num * num;
    }
} 
```

 

# ③ローカルテスト

環境はmacOS version10.12.3、npm 5.6.0、node v9.6.1で、package.jsonは以下の通り。

```package.json
{
  "name": "calc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ganache-cli": "^6.0.3",
    "mocha": "^5.0.1",
    "solc": "^0.4.20",
    "truffle-hdwallet-provider": "0.0.3",
    "web3": "^1.0.0-beta.26"
  }
}
```

Solidityコンパイルは以下の通り行う。

```Javascript:compile.js
const path = require('path');
const fs = require('fs');
const solc = require('solc');

const calcPath = path.resolve(__dirname, 'contracts', 'Calc.sol');
const source = fs.readFileSync(calcPath, 'utf8');

module.exports = solc.compile(source, 1).contracts[':Calc'];
```

プロバイダーはganacheを使ってテストプログラムを下記のように作成。

```Javascript:test/Calc.test.js
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const provider = ganache.provider();
const web3 = new Web3(provider);
const {interface, bytecode } = require('../compile');

let accounts;
let calc;

beforeEach(async () => {
    accounts = await web3.eth.getAccounts();
    calc = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({data: bytecode, arguments: [7]})
        .send({from: accounts[0], gas: '1000000'});
    calc.setProvider(provider);
});

describe('Calc', () => {
    it('deploys a contract', () => {
        assert.ok(calc.options.address);
    });
    it('has a default number', async () => {
        const num = await calc.methods.num().call();
        assert.equal(num, 7);
    });
    it('can change the number', async () => {
        await calc.methods.setNum(3).send({from: accounts[0]});
        const num = await calc.methods.num().call();
        assert.equal(num, 3);
    });
    it('can square the number', async () => {
        const squaredNum = await calc.methods.sqNum().call();
        assert.equal(squaredNum, 49);
    });
});
```

npm run testでテスト完了！



 

# ④デプロイ

infura.ioのAPI とtruffle-hdwallet-providerを使ってデプロイ。

```Javascript:deploy.js
const HDWalletProvider = require('truffle-hdwallet-provider');
const Web3 = require('web3');
const {interface, bytecode } = require('./compile');

const provider = new HDWalletProvider(
    'xxx xxx xxx  xxx xxx xxx  xxx xxx xxx  xxx xxx xxx',
    'https://kovan.infura.io/xxxxxxxxxxxxxxxxxxxx'
);
const web3 = new Web3(provider);

const deploy = async () => {
    const accounts = await web3.eth.getAccounts();
    console.log("account: ", accounts[0]);

    const result = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({ data: bytecode, arguments: [7]})
        .send({ gas: '1000000', from: accounts[0]});
    console.log("address: ", result.options.address);
};
deploy();
```

node deploy.jsでデプロイしたら、トランザクションアドレスを表示するので、あとはremixなどから実際に動かしたりなどご自由に楽しむ感じで。

# 参考資料

Stephen Grider, “Ethereum and Solidity: The Complete Developer's Guide”