我們為什么要寫一個眾籌Dapp?
區塊鏈的公開,透明,不可篡改,可追溯的特點,可保障投資者的利用,讓專案方踏實做事
<最想解決的就是投資者不管是投資專案,還是捐款,都能大部分的掌控自己的錢以及知道自己的錢花在哪里,剛好,可以利用區塊鏈的,公開,透明,可追溯,不可篡改的特點,來完成我們的目的>
2017年以太坊創始人V神提出了一個非常有創意的DAICO模型:

我們可以用一個簡單的案例來了解一下:

專案方:發起希望圖書室建設----投資人進行投資并獲得相應的代幣---當專案發要花投資者投的這筆錢時,需要進行請求---
單一花費單一請求----當投資者同意數過半--智能合約自動把這筆錢給專案方,
全程序公開透明,錢用在何處,轉給誰都公開透明,可追溯,減少作假可能,
基于以上思想,我們開發了一個基于以太坊智能合約的Dapp應用:
1.專案方能發起眾籌
2.專案方能提出花費請求
3.投資者能參與眾籌
4.投資者能對參與眾籌的專案進行資金支出的投票
5.投資者和專案方均能看見花費的詳細資訊
一.功能分析
(1)角色
專案方:發起眾籌
投資方:參與眾籌
(2)功能
專案方發起眾籌,指定時間內眾籌未達目標,則退款,
專案方能發起花費請求,花費請求必須通過參與者的投票票數決定是否執行,超過一半既可以執行,
投資方一個花費請求只能投一次票
投資方和專案方都能查看專案的完整資訊,
二.合約撰寫
單個合約funding.sol撰寫
(1).一個專案需包含的資訊:專案方,專案名,專案目標籌集金額,平均支持金額,專案結束時間,參與者
//1.專案發起人
//2.專案名稱
//3.專案目標籌集金額
//4.每個人支持多少錢
//5.專案持續多少天
address public manager;
string public projectName;
uint256 public targetMoney;
uint256 public supportMoney;
uint256 public endTime;//終結時間
address[] investors;//所有參與方陣列
SupportorFundingContract supportorFundings;//參與這個專案的人員
//建構式
constructor(string _projectName,uint256 _targetMoney,uint256 _supportMoney,uint256 _duration,address _creator,SupportorFundingContract _supportorFundings) public{
manager = _creator;
projectName = _projectName;
targetMoney =_targetMoney;
supportMoney=_supportMoney;
endTime =block.timestamp +_duration;//block.timestamp當前時間
supportorFundings = _supportorFundings;
}
(2).單一專案需具有參與眾籌(invest),查看籌集金額(getBalance),查看所有參與者(getInvestors),退款(refund)等基本方法
//使用一個mapping來判斷一個地址是否是投資人,這樣可以快速識別是否有投票權
mapping(address=>bool)isInvestorMap;
//投資 如果等于支付金額,則寫入參與人串列
function invest() payable public{
require(msg.value == supportMoney);
investors.push(msg.sender);
isInvestorMap[msg.sender]=true;
//將投資人與當前合約的地址傳遞到FundingFactory中
//supportorFundings[msg.sender].push(this);
supportorFundings.setFunding(msg.sender,this);
}
//退款,由前端呼叫
function refund() onlyManager public {
for (uint256 i=0;i<investors.length;i++){
investors[i].transfer(supportMoney);
}
delete investors;
}
//查看當前余額
function getBalance() public view returns(uint256){
return address(this).balance;
}
//查看當前參與人
function getInvestors() public view returns(address[]){
return investors;
}
(3).專案方需進行花費請求,所以我們需要定義一個花費結構體,它應該包含(用途(purpose):買什么,金額(cost):需要花費多少,商家地址(seller):向誰購買,贊成票數(approveCount):多少參與方贊成(因為只有超過半數才能花費),花費申請狀態(status):完成(已經花費),帶批準(超過半數贊成),待執行(發起花費請求), 標記投過票的參與者(isVotedMap):防止重復投票)
//產品狀態列舉:0:進行中,1:已批準,2:已完成
enum RequstStatus{
Voting,Approved,Completed
}
//花費請求結構
struct Request {
string purpose;//用途
uint256 cost;//花費金額
address seller;//商家地址
uint256 approveCount;//贊成票數
RequstStatus status;
//記錄投資人對這個請求的投票狀態,只有未投票的才能投票,每人一票
mapping(address =>bool) isVotedMap;
}
(4).一個專案可發起多個花費申請,定義一個陣列(allRequests)記錄所有發起的支付申請,定義發起申請的方法(createRequest)
//請求可能有多個,定義一個請求陣列
Request[] public allRequests;
function createRequest(string _purpose,uint256 _cost,address _seller)onlyManager public{
Request memory req = Request({
purpose:_purpose,
cost:_cost,
seller:_seller,
approveCount:0,
status:RequstStatus.Voting
});
allRequests.push(req);//將請求放入請求陣列里
}
(5).專案方發起花費申請后,需參與者進行投票,然后實作花費申請是否執行,為此我們需檢查參與者是否已經投過票,定義了一個支持花費申請的函式(approveRequest)
function approveRequest(uint256 i) public {
//識別投資人是否投過票
require(isInvestorMap[msg.sender]);
//一定要使用storage型別,參考型別,否則無法修改allRequests里面的資料
Request storage req=allRequests[i];
require(req.isVotedMap[msg.sender]==false);
req.approveCount++;
req.isVotedMap[msg.sender]=true;
}
(6).當參與者完成投票后,我們就要檢查花費請求是否通過,通過條件:贊成票數過半,賬戶余額大于花費申請金額
定義了一個完成花費請求的函式(finalizeRequest),點擊完成,即完成交易和轉賬,
function finalizeRequest(uint256 i) onlyManager public{
Request storage req=allRequests[i];
//金額足夠
require(address(this).balance >= req.cost);
//票數過半
require(req.approveCount * 2 >investors.length);
//執行轉賬
req.seller.transfer(req.cost);
// 更新request狀態
req.status =RequstStatus.Completed;
}
(7).退款(refund),花費申請表(createRequest),完成花費(finalizeRequest)都應該是發起方才能進行的,所有我們需給這三個函式增加權限控制,定義一個修飾器,然后在方法上定義修飾器
//權限控制
modifier onlyManager{
require(msg.sender==manager);
_;
}
(8).我們還需要創建幾個輔助函式,如,查看眾籌專案剩余時間(getLeftTime),投資人數有多少(getInvestorsCount),
花費申請數量(getRequestsCount),某花費申請的具體資訊(getRequestByIndex)
//眾籌剩余時間 回傳秒
function getLeftTime() public view returns(uint256){
return (endTime-block.timestamp);
}
//投資人數
function getInvestorsCount() public view returns(uint256){
return investors.length;
}
//請求數量
function getRequestsCount() public view returns(uint256) {
return allRequests.length;
}
//某花費申請具體資訊
function getRequestByIndex(uint256 i) public view returns(string,uint256,address,uint256,RequstStatus){
Request memory req=allRequests[i];
return (req.purpose,req.cost,req.seller,req.approveCount,req.status);
}
合約工廠 FundingFactory.sol撰寫
每個人都可以創建單個眾籌合約,一個人也可創建多個眾籌合約,為了方便管理這些合約實體,便設定了合約工廠模式,方便管理合約,
(1).工廠合約因包含平臺管理員(platformManager),平臺所有的眾籌合約(allFundings),自己創建合約集合(reatorFundings),自己參與過的合約集合(supportorFundings)
// 0.平臺管理員
address public platformManager;
//1.所有眾籌合約集合
address [] allFundings;
//2.創建人的合約集合
mapping(address => address[]) creatorFundings;
//3.參與人的合約集合
// mapping(address => address[]) supportorFundings;
SupportorFundingContract supportorFundings;
//建構式
constructor() public{
platformManager =msg.sender;
//建構式時創建一個全域的合約實體
supportorFundings= new SupportorFundingContract();
}
(2).合約工廠是用來管理和創建合約的,所以我們需定義創建合約的方法(createFunding)
function createFunding(string _name,uint _targetMoney,uint _supportMoney,uint _duration)public{
//創建一個合約,使用new方法,同時傳入引數,回傳一個地址
address funding= new Funding(_name,_targetMoney,_supportMoney,_duration,msg.sender,supportorFundings);
allFundings.push(funding);
//維護創建者所創建的合約集合
creatorFundings[msg.sender].push(funding);
}
(3).由于我們要將這些資訊回傳到平臺上,定義幾個輔助函式,回傳平臺所有合約(getAllFundings),回傳創建者所有合約(getCreatorFundings),獲取參與者所有合約(getSupportorFunding)
//回傳平臺所有合約
function getAllFundings() public view returns(address[]){
return allFundings;
}
//回傳創建者所有合約
function getCreatorFundings() public view returns(address[]){
return creatorFundings[msg.sender];
}
//獲取參與合約集合
function getSupportorFunding() public view returns(address[]){
return supportorFundings.getFundings(msg.sender);
}
由于solidity不支持在函式引數中定義復雜型別,所以我們無法直接把(支持合約的參與者集合)supportFundings變數傳給invest方法,這里我們創建一個臨時合約,該合約維護了所有參與者參與過的眾籌合約,
SupportorFundingContract.sol
//這個合約維護全域所有參與人所參與的合約
contract SupportorFundingContract{
mapping(address=>address[])supportorFundingsMap;
function setFunding(address _supptor,address _funding){
supportorFundingsMap[_supptor].push(_funding);
}
function getFundings(address _supptor)public view returns(address[]){
return supportorFundingsMap[_supptor];
}
}
到這里,眾籌的智能合約已經開發完畢了!!
然后讓我們看看結果吧:
1.打開ganache-clli(模擬區塊鏈):node_modules/.bin/ganache-cli

2.啟動專案:npm run start (react開發的啟動命令)

3.在瀏覽器中訪問 http://localhost:3000/ 主界面

4.在metaMask錢包中添加賬戶
在ganache-cli中獲取私鑰:

在metamesk中添加



5.初始創世塊的創建,需在代碼中添加合約地址


6.現在網頁就可以查看當前賬戶的地址了,與錢包里的地址一致(下面我們直接叫他account3(0x1b))
目前沒有一個合約!!



7.使用account3作為專案方創建倆個眾籌專案--重繪--合約顯示



8.accounts3對《希望圖書室建設》進行投資:--重繪


9.accounts2對《希望圖書室建設》進行投資:在錢包切換賬號--重繪--投票后,在我參與的界面可查詢



10.account3在我發起的界面對“希望圖書室建設”進行花費請求


11.創建花費請求成功后可在申請詳情里查看

12.切換acount2賬戶進行投票支持

13.切換account3進行花費申請完成交易

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/195021.html
標籤:其他
