//tips
//smart contract
最後にfinalizeRequestで十分な承認が集まった後にvenderへの送金を行わせる仕組みを追加する。donaterの半分以上の賛成でrequestの実行とする。
function finalizeRequest(uint index) public restricted{//request番号を明確にしてmanagerのみ呼び出せる
Request storage request=requests[index];
//承認数がdonaterの50%以上ならfinalizeしたい
require(request.approvalCount>(approversCount/2));
require(!request.complete);
request.receipient.transfer(request.value);
request.complete=true;
}
approversCountを新たに作成したが特に問題になるところはなさそう。
ここからはフロントエンドとの関係性を込みで作り込んでいく。もしこちらのコントラクトをフロントエンドで実行するときに複数のcampaignが作成されることになるが、campaign1,2,3などは独立しており、ユーザーがそれぞれの取得を別途取得するのが手間がかかるので、こちらで生成campaignをリスト化して、ユーザーがアクセスしやすいように整理する必要がある。
また、ユーザーがcreate campaignボタンから新規campaignを作成する際に、こちらのソースコードを渡すと問題が起こることから、上手い方法を考える必要がある。
ここを初期契約の中に別のコントラクトfactoryを作成する機能を作ることで、自分でデプロイ費用を負担する必要もなく、セキュリティホールも消せる。
このようにcampaign contractの中にfactory contractを作成することで、セキュリティ対応と生成campaignの管理を行わせる。
campaignコントラクトの上に、Factoryコントラクトを追加していく。追加内容はシンプルで新規キャンペーンの作成機能とキャンペーン一覧作成及び確認機能である。大元のcampaignコンストラクターの引数にアドレスを指定している。
pragma solidity ^0.4.17;
contract CampaignFactory{
address[] public deployedCampaigns;
function createCampaign(uint minimum)public{//Campaignを生成するために必要な引数
address newCampaign=new Campaign(minimum,msg.sender);
deployedCampaigns.push(newCampaign);
}
function getDeployedCampaigns()public view returns(address[]){
return deployedCampaigns;
}
}
contract Campaign{//crowdfunding Campaign
struct Request{//これはインスタンスではなく定義なので、別途インスタンス生成が必要
string description;//送金理由
uint value;//金額
address receipient;//送金先のアドレス
bool complete;//実行可否
uint approvalCount;//リクエストを承認した人数
mapping(address=>bool)approvals;//approversとは異なるので注意。誰が投票したか。
}
//storageデータ
Request[] public requests;
address public manager;
uint public minimumContribution;//最低限の寄付額
//address[] public approvers;
mapping(address=>bool) public approvers;//donate実行者リスト
uint public approversCount;//donate実行者数のカウント
//managerのみ使用可能な制約
modifier restricted(){
require(msg.sender==manager);
_;
}
//contractの初期設定
//引数のminimumはmemory
function Campaign(uint minimum,address creator)public {
ここからは実際のテストネットワークでのテストを行っている。テストファイルをアップロードして
Nmp install
npm install ganache-cli mocha solc@0.4.17 fs-extra web3
Web3をベースにした開発準備を整える。
今回はethとフロントエンドのソースがよりボリューミーになるので、フォルダ分けして進める。
compile.jsから作成していく。
const path=require('path');
const solc=require('solc');
const fs=require('fs-extra');
const buildPath=path.resolve(__dirname,'build');
fs.removeSync(buildPath);//これが読み込まれたらbuildPath内容を初期化
//コンパイル済みのjson abiを削除する
const campaignPath=path.resolve(__dirname,'contracts','Campaign.sol');
const source=fs.readFileSync(campaignPath,'utf8');
const output=solc.compile(source,1).contracts;//campaignとfactoryを含む
fs.ensureDirSync(buildPath);//既存のパスを見つけ、なければbuildフォルダを作る。
//buildフォルダにoutputから抽出する
for(let contract in output){
fs.outputJSONSync(
path.resolve(buildPath,contract+'.json'),//pathをbuildする
output[contract]
);
}
作成後に下記を実行。
cd ethereum
Node compile.js
これによりbuildフォルダに2つのファイルが作られた。
新たにtestフォルダを作成。