Libraryを利用したContractをデプロイする時はLibraryのアドレスをリンカーに教えないとContractがデプロイできません。(できても正常に動きません)
TruffleでLibraryとContractをlinkしてデプロイする方法はTruffleの公式ドキュメントにさらーっと書いてるだけなのでcontractの作成からtestまでの流れをまとめました。
今回作るContractはSolidityのlibraryにサンプルとして載ってるlibrary Setとcontract Cです。contract Cは名前が一文字だけだとうまくTruffleでコンパイルできなかったので、UseLibという名前に変更してます。
library Setとそれを使うcontract UseLibのソースは次の通りです。これをcontracts/以下に別々のファイルSet.solとUseLib.solとしてそれぞれ作成します。
- Set.sol
pragma solidity ^0.4.18; library Set { // We define a new struct datatype that will be used to // hold its data in the calling contract. struct Data { mapping(uint => bool) flags; } // Note that the first parameter is of type "storage // reference" and thus only its storage address and not // its contents is passed as part of the call. This is a // special feature of library functions. It is idiomatic // to call the first parameter 'self', if the function can // be seen as a method of that object. function insert(Data storage self, uint value) public returns (bool) { if (self.flags[value]) return false; // already there self.flags[value] = true; return true; } function remove(Data storage self, uint value) public returns (bool) { if (!self.flags[value]) return false; // not there self.flags[value] = false; return true; } function contains(Data storage self, uint value) public constant returns (bool) { return self.flags[value]; } }
- UseLib.sol
pragma solidity ^0.4.18; import 'contracts/Set.sol'; contract UseLib { Set.Data knownValues; function register(uint value) public { // The library functions can be called without a // specific instance of the library, since the // "instance" will be the current contract. require(Set.insert(knownValues, value)); } // In this contract, we can also directly access knownValues.flags, if we want. function contains(uint value) public constant returns(bool) { return Set.contains(knownValues, value); } }
そして、これらをデプロイするためのmigrationのソースです。linkerへの指示はこのmigrationファイルで設定します。
- 2_deploy_library_set.js
var Set = artifacts.require("Set"); var UseLib = artifacts.require("UseLib"); module.exports = function(deployer) { // Use deployer to state migration tasks. deployer.deploy(Set); deployer.link(Set, UseLib) deployer.deploy(UseLib); };
最後は正常にデプロイできたか確認するためのテストです。
- test_uselib.js
var UseLib = artifacts.require("UseLib"); contract('UseLibTest', function(accounts) { it("Set value and contains check.", function() { var strage; return UseLib.deployed().then(function(instance) { strage = instance; return strage.register(5, {from: accounts[0]}); }).then(function () { return strage.contains(5); }).then(function(message) { assert.equal(message, true, "value 5 is contains"); return strage.contains(3); }).then(function(message) { assert.equal(message, false, "value 3 is not contains"); }); }); });
Truffleではコンパイル後、contractをデプロイする直前にlibrary addressの識別子(今回だと__Set__________)を実アドレスに置き換えてるみたいです。