====== Truffleを使ったスマートコントラクト開発 ====== Truffle は ethereum スマートコントラクトのコンパイル、マイグレーション、テストを行うためのフレームワークです。 ====== 必要なソフトウエア ====== Truffle は [[https://github.com/npm/cli|npm]] を使ってインストールしますので、事前に npm をインストールしてください。 [[https://nodejs.org/en/download/|npm パッケージ配布サイト]] また、ethereum ブロックチェーンも使用しますので [[blockchain:ethereum構築ハンズオン_ganache編|ethereumブロックチェーンの構築(Ganache編)]] を参考に Ganache をインストールしてください。 ====== インストール ====== > npm install -g truffle ====== 初期化 ====== > mkdir my_project > cd my_project > truffle init Starting init... ================ > Copying project files to C:\Users\shinobu\NoNameSeminer\ethereum\my_project Init successful, sweet! Try our scaffold commands to get started: $ truffle create contract YourContractName # scaffold a contract $ truffle create test YourTestName # scaffold a test http://trufflesuite.com/docs 生成されたファイルを確認してみます。 > tree /f ボリューム シリアル番号は EEAF-D230 です C:. │ truffle-config.js │ ├─contracts │ Migrations.sol │ ├─migrations │ 1_initial_migration.js │ └─test .gitkeep 各ファイルとディレクトリの説明。 * truffle-config.js: truffle の設定ファイル。ethereum ブロックチェーンのアドレス等を設定する。 * contracts: スマートコントラクトのソースコードを格納するディレクトリ。最初からマイグレーションの現在のバージョンを管理するための「Migrations.sol」が作られています。 * migrations: マイグレーションの設定ファイルを格納するディレクトリ。最初からマイグレーションの現在のバージョンを管理するための「1_initial_migration.js」が作られています。 * test: テストコードを格納するディレクトリ。 ====== 設定 ====== truffle-config.js を開いて、コメントを削除し以下のように変更してください。 * *Ganache に接続する場合の設定です。 * *__**port番号は 8545 から 7545 に変更しています。**__ networks: { // Useful for testing. The `development` name is special - truffle uses it by default // if it's defined here and no other network is specified at the command line. // You should run a client (like ganache-cli, geth or parity) in a separate terminal // tab if you use this network and you must also set the `host`, `port` and `network_id` // options below to some value. // development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, ====== Ganacheとの連携 ====== Ganache を起動し「NEW WORKSPACE」をクリックします。 {{:blockchain:ganache_truffle01.png?800|}} 「ADD PROJECT」をクリックし、先ほど作成した「truffle-config.js」を選択します。 {{:blockchain:ganache_truffle02.png?800|}} {{:blockchain:ganache_truffle03.png?800|}} これで Ganache と Truffle の連携設定は完了です。「SAVE WORKSPACE」をクリックしてください。 {{:blockchain:ganache_truffle04.png?800|}} ====== Truffle コンソール ====== > truffle console truffle(development)> ====== スマートコントラクト ====== ===== 生成 ===== truffle(development)> truffle create contract Counter truffle(development)> 上記のコマンドで constracts ディレクトリの中に Counter.sol ファイルが生成されます。 ===== 実装 ===== エディタで Counter.sol を開き、以下のように編集してください。 // SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; contract Counter { uint public count; // Function to get the current count function get() public view returns (uint) { return count; } // Function to increment count by 1 function inc() public { count += 1; } // Function to decrement count by 1 function dec() public { count -= 1; } } ===== コンパイル ===== truffle(development)> truffle compile Compiling your contracts... =========================== > Compiling .\contracts\Counter.sol > Compiling .\contracts\Counter.sol > Compiling .\contracts\Migrations.sol > Artifacts written to C:\Users\shinobu\NoNameSeminer\ethereum\my_project\build\contracts > Compiled successfully using: - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang truffle(development)> ===== マイグレーションファイルの作成 ===== Counter コントラクトをデプロイするためのマイグレーションファイルを作成します。 truffle(development)> truffle create migration Counter truffle(development)> 上記のコマンドで migrations ディレクトリの中に nnnnnnnn_counter.js ファイルが生成されます。 ファイルの以下のように書き換えて、Counter コントラクトをデプロイするように設定します。 const Counter = artifacts.require("Counter"); module.exports = function(_deployer) { // Use deployer to state migration tasks. _deployer.deploy(Counter) }; ===== デプロイ ===== truffle(development)> truffle migrate Compiling your contracts... =========================== > Compiling .\contracts\Counter.sol > Compiling .\contracts\Migrations.sol > Artifacts written to C:\Users\shinobu\NoNameSeminer\ethereum\my_project\build\contracts > Compiled successfully using: - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang Starting migrations... ====================== > Network name: 'development' > Network id: 5777 > Block gas limit: 6721975 (0x6691b7) 1_initial_migration.js ====================== Replacing 'Migrations' ---------------------- > transaction hash: 0x09353f6ea3c2ce57acf8de3a9c2c1110fb2abfeea6d51d896477509d6e6edc6a > Blocks: 0 Seconds: 0 > contract address: 0x9864601fA497E29d85720c336b15267Ff0a89386 > block number: 1 > block timestamp: 1651037552 > account: 0x12C1B9B5152b14CE5af7A83947971108Dc89e54D > balance: 99.99502292 > gas used: 248854 (0x3cc16) > gas price: 20 gwei > value sent: 0 ETH > total cost: 0.00497708 ETH > Saving migration to chain. > Saving artifacts ------------------------------------- > Total cost: 0.00497708 ETH 1651036112_counter.js ===================== Replacing 'Counter' ------------------- > transaction hash: 0x45b148c06609f587e5636a17a088f6685fc2e05603aed75a54bfcf01ba881129 > Blocks: 0 Seconds: 0 > contract address: 0x852560406282C788aeF29AbD5A9313588d86c3dB > block number: 3 > block timestamp: 1651037554 > account: 0x12C1B9B5152b14CE5af7A83947971108Dc89e54D > balance: 99.99086736 > gas used: 165265 (0x28591) > gas price: 20 gwei > value sent: 0 ETH > total cost: 0.0033053 ETH > Saving migration to chain. > Saving artifacts ------------------------------------- > Total cost: 0.0033053 ETH Summary ======= > Total deployments: 2 > Final cost: 0.00828238 ETH truffle(development)> ===== 実行 ===== Counter コントラクトを呼び出すためのインスタンスを取得する。 truffle(development)> let counter = await Counter.deployed() undefined truffle(development)> Counter コントラクトの get を呼び出す。 truffle(development)> counter.get() BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null } truffle(development)> :!: 「BN」は「BigNumber」という意味です。後々、テスト等で値の比較をするときに int とは扱いが変わってきますので、覚えておいてください。 Counter コントラクトの inc を呼び出す。 truffle(development)> counter.inc() { tx: '0x10e87f126ce0934123624a8bd0d9c70a7d2d492fd1ade783646e828ccad6ad42', receipt: { transactionHash: '0x10e87f126ce0934123624a8bd0d9c70a7d2d492fd1ade783646e828ccad6ad42', transactionIndex: 0, blockHash: '0xc95665ead9fae2045cff12ba600d778a99454fbcdfcafe257f6c5dee61f76917', blockNumber: 5, from: '0x12c1b9b5152b14ce5af7a83947971108dc89e54d', to: '0x852560406282c788aef29abd5a9313588d86c3db', gasUsed: 42229, cumulativeGasUsed: 42229, contractAddress: null, logs: [], status: true, logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', rawLogs: [] }, logs: [] } truffle(development)> もう一度、Counter コントラクトの get を呼び出す。(値が0から1になっている) truffle(development)> counter.get() BN { negative: 0, words: [ 1, <1 empty item> ], length: 1, red: null } truffle(development)> ====== テスト ====== ===== 生成 ===== truffle(development)> truffle create test SimpleCounterTest truffle(development)> 上記のコマンドで test ディレクトリの中に simple_counter_test.js ファイルが生成されます。 ===== 実装 ===== エディタで simple_counter_test.js を開き、以下のように編集してください。 const Counter = artifacts.require("Counter"); /* * uncomment accounts to access the test accounts made available by the * Ethereum client * See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript */ contract("SimpleCounterTest", function (/* accounts */) { it("should assert true", async function () { await Counter.deployed(); return assert.isTrue(true); }); it("Counter inc", async function () { let counter = await Counter.deployed(); let before = await counter.get(); await counter.inc(); let after = await counter.get(); // before and after is BN(BigNumber) return assert.equal(after.toNumber(), before.toNumber() + 1); }); it("Counter dec", async function () { let counter = await Counter.deployed(); let before = await counter.get(); await counter.dec(); let after = await counter.get(); // before and after is BN(BigNumber) return assert.equal(after.toNumber(), before.toNumber() - 1); }); }); ===== 実行 ===== truffle(development)> truffle test Using network 'development'. Compiling your contracts... =========================== > Compiling .\contracts\Counter.sol > Compiling .\contracts\Migrations.sol > Artifacts written to C:\Users\shinobu\AppData\Local\Temp\test--15628-Lt5184Kc9NTX > Compiled successfully using: - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang Contract: SimpleCounterTest √ should assert true (55ms) √ Counter inc (1361ms) √ Counter dec (1419ms) 3 passing (3s) truffle(development)>