SmartContract
を開発するためのフレームワーク Truffle.js
などがありますが、今回は、仕組みを理解するために手動でコンパイルして Bytecode
と ABI
(Application Binary Interface) を取得します。
そして、ローカル環境の Blockchain
ネットワークにデプロイ&テストをします。
Bytecode と ABI に関して
Bytecode
(バイトコード)は、ブロックチェーンに上げるための SmartContract
のバイナリコードです。EVM
(Ethereum Virtual Machine) 仮想マシンによって実行されます。
ABI
(Application Binary Interface) は、 Bytecode
の機能や入力と出力などの情報が Json
形式で記載されています。ABI
を見れば SmartContract
にどういう機能を持っているのかがざっくり分かります。
コンパイルするファイルたち
MyProkject/
├─ .vscode
│ └─ settings.json
├─ contracts
│ └─ SampleContract.sol
└─ scripts
└─ compile.js
-
/.vscode/settings.json
に関しては、こちらの記事をご参照ください。 -
/contracts/
フォルダーには、開発するSmartContract
プログラムを入れます。この例では、SampleContract.sol
を入れました。 -
/scripts/compile.js
には、コンパイルするためのJavascript
で記載したスクリプトを入れます。
// ライブラリのインポート
const path = require('path'); // ファイルパスを操作するためのライブラリ
const fs = require('fs'); // ファイルを読み書きするためのライブラリ
const solc = require('solc'); // Solidity言語のプログラムをコンパイルするためのライブラリ
const chalk = require('chalk'); // ログ出力に色をつけるためのライブラリ
// プログラムファイルのパスを設定
const contractPath = path.resolve(__dirname, "../contracts", "SampleContract.sol");
// プログラムファイルを読み込む
const source = fs.readFileSync(contractPath, 'utf8');
// プログラムをコンパイルし、Bytecode と ABI を取得する
const {abi, bytecode} = solc.compile(source, 1).contracts[':SampleContract'];
// Bytecode と ABI をログ出力する
console.log(chalk.green(bytecode));
console.log(chalk.cyan(abi));
コンパイル
ターミナルでプロジェクトのパスを開いて、次のコマンドを実行します。
node ./scripts/compile.js
すると、コンパイルが行われ、結果としてログに緑色で Bytecode
とシアン色で ABI
が出力されます。
でも、これだと Bytecode
と ABI
をログ出力するだけなので、次は、ローカル環境に Blockchain
の開発用のネットワーク環境を構築して、そこに開発した SmartContract
をデプロイしたいと思います。
Ganache
Ganache
は、 Blockchain
の開発用のネットワーク環境です。
-
次のアドレスからダウンロード、インストールします。
https://truffleframework.com/ganache -
立ち上げると、次の画面が表示されるので、「QUICKSTART」をクリックします。
- すると、ネットワークが構築され、100ETHを持つ10のアカウントが用意されます。
-
BLOCKS
タブをクリックすると、ネットワークに保持されるBlockchain
が表示されます。最初は、開始ブロックであるBLOCK 0
だけが存在します。このブロックはBlock Genesys
とも呼ばれています。
Deploy & Test
- テスト用のライブラリをインストールします。
npm install --save-dev mocha@{バージョン}
-
/scripts/compile.js
を次のように変更します。
// ライブラリのインポート
const path = require('path'); // ファイルパスを操作するためのライブラリ
const fs = require('fs'); // ファイルを読み書きするためのライブラリ
const solc = require('solc'); // Solidity言語のプログラムをコンパイルするためのライブラリ
// プログラムファイルのパスを設定
const contractPath = path.resolve(__dirname, "../contracts", "SampleContract.sol");
// プログラムファイルを読み込む
const source = fs.readFileSync(contractPath, 'utf8');
// プログラムをコンパイルし、外部へ export する
module.exports = solc.compile(source, 1).contracts[':SampleContract'];
- テスト用のスクリプト
/test/SampleContract.test.js
を作成します。
// ライブラリのインポート
const assert = require('assert'); // テスト assert をするためのライブラリ
const Web3 = require('web3'); // ローカル環境(Ganache)と接続するためのライブラリ
// ローカル環境(Ganache)と接続するためのオブジェクトを生成
const provider = new Web3.providers.HttpProvider("HTTP://127.0.0.1:7545");
const web3 = new Web3(provider);
// コンパイルスクリプトからBytecodeとABIを取得する
const {abi, bytecode} = require('../scripts/compile');
// 変数定義
let accounts; // Ganacheで提供される10アカウントを保持する
let sampleContract; // デプロイしたSmartContractを保持する
// 各テストを実行する前に実行する
beforeEach(async() => {
// アカウントを取得する
accounts = await web3.eth.getAccounts();
// ABIとBytecodeを一番最初のアカウントを使ってデプロイ
usersContract = await new web3.eth.Contract(JSON.parse(abi)) // ABI
.deploy({data: bytecode}) // Bytecode
.send({from: accounts[0], gas: '1000000'}); // 一番最初のアカウント、GAS=1000000
});
// テスト 'The UsersContract' 開始
describe('The UsersContract', async() => {
// 1つ目のテスト: デプロイできたか?
it('should deploy', () => {
console.log(usersContract.options.address);
assert.ok(usersContract.options.address);
});
});
-
/package.json
の"test": "mocha"
にする。
{
"name": "myproject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"author": "",
"license": "ISC",
"devDependencies": {
"chalk": "^2.4.2",
"mocha": "^5.2.0",
"solc": "^0.4.24",
"web3": "^1.0.0-beta.35"
}
}
- 次のコマンドでテストを実行して、Deployを確認します。
npm run test
- 成功すると、ログに次のような出力を確認できます。
> myproject@1.0.0 test /myproject
> mocha
The UsersContract
0xdabBD1955e2873bB498A6F1f149A8A0d74f48170
✓ should deploy
1 passing (72ms)
- また、
Ganache
のTRANSACTIONS
タブでSmartContract
のデプロイができたことも確認できます。