Solidityプログラムをローカルで開発する

ローカルでの開発について

前節でInboxのコントラクトを作成しました。Remixで開発した内容をローカルで作成してwebで利用するには、ローカルでも開発できる環境にする必要があります。macの場合は「ターミナル」,windowsの場合は「コマンドプロンプト」または、「windows Power Shell(PS)」を開き進めていきます。 コマンドが異なる場合があるので、windowsはPSモードだと良いと思います。

Nodeのバージョン確認

nodejsのバージョンに注意してください。node -vv8.0.0 以上であれば良いはずです。まだnodejsをインストールしていない方は、https://nodejs.org/en/download からダウンロードとインストールしてください。

開発フォルダ作成

作業スペース(フォルダ)を作成しましょう。

mkdir ethereum-solidity で ethereum-solidityフォルダを作成した後、その中にmkdir Inbox でInboxフォルダを作成します。

フォルダ内で初期設定を行います。Inboxフォルダの中で npm init と入力しましょう。

するとパッケージの名前などを聞かれるので下記のように最初だけ「inbox」と入力して進めます。あとは、エンターを入力し続けて大丈夫です。

name: (Inbox) inbox
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 

入力が完了すると下記のように作成されたパッケージの内容が「package.json」にあることを知らせてくれます。

About to write to /作成場所までのパス/ethereum-solidity/Inbox/package.json:

{
  "name": "inbox",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

するとフォルダの中に「package.json」作成されています。

フォルダ構成を下記のようにします。段落下げは、フォルダの中にあるという表記です。

Inbox/
  contracts/
     Inbox.sol
  test/
     Inbox.test.sol
  package.json
  compile.js
  deploy.js

まずは、contractsフォルダを作成します。 mkdir contracts と入力しましょう。

次に、contractsフォルダの中にInbox.solを作成します。vi Inbox.sol または、AtomなどのIDEで作成していきましょう。Atomのインストールは、https://atom.io/です。

solデータの作成

Inbox.solが作成できたら前節のRemixで作成したInboxの内容をコピーして貼り付けましょう。

Inbox.sol
pragma solidity ^0.4.0;
contract Inbox {
    string public message;
    function Inbox(string initialMessage) public{
        message = initialMessage;
    }    
    function setMessage(string newMessage) public{
        message = newMessage;
    }
    function getMessage() public view returns(string){
        return message;
    }
}

コンパイル用のモジュールを利用するために、npmでインストールします。npm install --save solc と入力しましょう。すると新しく node_modules というフォルダが作成されます。

次に、compile.jsを作成していきます。

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

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

console.log(solc.compile(source,1));

Solデータのコンパイル

compile.jsを実行してcontractsを生成します。node compile.js と入力しましょう。英数字がたくさん流れるはずです。console.log()によりcontractsのコンパイルした内容が表示されています。中身をみるとまだ実装していない機能もたくさんありそうです。

最後にこのファイルを他のファイルから利用できるようにmodule.exportsします。

compile.js
module.exports = solc.compile(source,1);

しかし、このままではconsole.logで出したように中身が全部出てしまっています。そのため内容を絞って利用できるようにします。

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

web3.jsをインストールする

web3.jsは、ethereumの中身にアクセスするためのjavascript APIです。まずは、インストールしていきます。下記をコマンドプロンプトで入力しましょう。

npm install --save mocha ganache-cli

npm install web3

少しインストールに時間がかかるため、その間まだ作っていないフォルダを作成していきます。(エラーの場合npm と nodeのバージョンのアップデートにて解決することが多いです。)web3.jsはv0.x.xと v1.x.xで扱いが大きく異なるため注意してください。今回はv1.x.xを利用します。

testフォルダとtest/Inbox.test.jsを作成しましょう。

mochaはjavascriptの単体テスト用のパッケージで、 ganache-cliは、ethereumのローカル環境です。

簡単なテストコードを作成する

テストコードを書いていきますが、まずはどんな風に書くかを確認していきます。まずは、コントラクトに関係ないクラスを作成し正常系のみをテストします。

Inbox.test.js
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());

class Tag{
    agile(){
        return 'hoge';
    }
    
    guild(){
        return 'fuga';
    }
}

describe('Tag', () => {
    it('say hoge', () => {
        const tag = new Tag();
        assert.equal(tag.agile(),'hoge');        
    });
});

その後、package.jsonの中でtestのscriptsを「mocha」で指定します。

package.json
{
  "name": "inbox",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC"
}

テストコードを実行する

テストコードが書けましたら、実行しましょう。コマンドプロンプトで npm run test を入力してください。テストが無事通った場合、下記が表示されるはずです。

ターミナル出力
>mocha

Tag
   say fuga
1 passing

コントラクトを含めたテストコードを作成する

先ほど、作成したInbox.test.jsのconst部分以外を削除します。その後、web3を利用してアカウントリストを取得するまでのテストコードを作成します。

Inbox.test.js
beforeEach(() => {
    //Get a list of all accounts
    web3.eth.getAccounts().then(fetchedAccounts => {
        console.log(fetchedAccounts);
    });
    
    //Use one of those accounts to deploy
    //the contract
});

 describe('Inbox', () => {
     it('deploy a contract', () => {}); 
 });

非同期処理を書く

アカウントが作られる時間が少しだけかかるため、非同期処理に変更します。

Inbox.test.js
let accounts;

beforeEach(async() => {
  //Get a list of all accounts
  accounts = await web3.eth.getAccounts();

});
describe('Inbox', () => {
  it('deploy a contract', () => {
    console.log(accounts);
  });
});

Inboxのテストコードを作成する STEP1

Inbox.test.js
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const { interface, bytecode } = require('../compile');  //追記

let accounts;
let inbox; //追記

beforeEach(async() => {
  //Get a list of all accounts
  accounts = await web3.eth.getAccounts();
  //Use one of those accounts to deploy
  //the contract  以下追記
  inbox = await new web3.eth.Contract(JSON.parse(interface))
  .deploy({ data: bytecode, arguments: ['Hi there!'] })
  .send({ from: accounts[0],gas:'1000000' });
});
describe('Inbox', () => {
  it('deploy a contract', () => {
    console.log(inbox);  //追記
  });
});

console.logで出力された中で「method」という部分があります。その中に今回作成したInboxのsetMessageなどが含まれているはずです。このmessage内容が正常かテストしましょう。下記のようにInbox.test.jsをのdescribe部分を変更します。

Inbox.test.js
describe('Inbox', () => {
  it('deploy a contract', () => {
    assert.ok(inbox.options.address);
  });
  it('has a default message', async() => {
    const message = await inbox.methods.message().call();
    assert.equal(message, 'Hi there!');
  });
});

Inboxのテストコードを作成する STEP2

messageが正常に動いていることが確認できました。次にsetMessageメソッドが正常に動いているかをテストします。Inbox.test.jsのdescribeに追記します。

Inbox.test.js
describe('Inbox',() =>{
ー略ー
  it('can change the message', async(){
    await inbox.methods.setMessage('bye').send({ from: accounts[0]});
    const message = await inbox.methods.message().call();
    assert.equal(message, 'bye');
  });
});

Last updated