Decentralized Autonomous Organization,簡稱DAO,以太坊中重要的概念。通常翻譯爲去中心化的自治組織。php
在上一節中,咱們爲了展現什麼是DAO建立了一個合約,就像一個採用邀請制的俱樂部,會員被總統的心血來潮邀請或禁止。但這有一些缺點:若是有人想改變他的主要地址怎麼辦?若是一些成員比其餘成員更重要?怎麼辦? 若是你真的想在公開市場上交易或出售會員資格或股票怎麼辦?若是你但願你的組織做爲股東的不改變決策的持續工做,該怎麼辦?java
咱們將修改咱們的合約以將其鏈接到特定代幣,該代幣將做爲合約的持有份額。首先,咱們須要建立此代幣:轉到代幣教程並建立一個簡單代幣,初始供應爲100,小數爲0,百分號(%)爲符號。若是你但願可以以百分比的百分比進行交易,則將供應量增長100倍或1000倍,而後將相應數量的零添加爲小數。部署此合約並將其地址保存在文本文件中。node
那麼修改後的股東協會合約代碼:python
pragma solidity >=0.4.22 <0.6.0; contract owned { address public owner; constructor() public { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { owner = newOwner; } } contract tokenRecipient { event receivedEther(address sender, uint amount); event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, address(this), _value)); emit receivedTokens(_from, _value, _token, _extraData); } function () payable external { emit receivedEther(msg.sender, msg.value); } } contract Token { mapping (address => uint256) public balanceOf; function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } /** * The shareholder association contract itself */ contract Association is owned, tokenRecipient { uint public minimumQuorum; uint public debatingPeriodInMinutes; Proposal[] public proposals; uint public numProposals; Token public sharesTokenAddress; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter); event ProposalTallied(uint proposalID, uint result, uint quorum, bool active); event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress); struct Proposal { address recipient; uint amount; string description; uint minExecutionDate; bool executed; bool proposalPassed; uint numberOfVotes; bytes32 proposalHash; Vote[] votes; mapping (address => bool) voted; } struct Vote { bool inSupport; address voter; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyShareholders { require(sharesTokenAddress.balanceOf(msg.sender) > 0); _; } /** * Constructor * * First time setup */ constructor(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) payable public { changeVotingRules(sharesAddress, minimumSharesToPassAVote, minutesForDebate); } /** * Change voting rules * * Make so that proposals need to be discussed for at least `minutesForDebate/60` hours * and all voters combined must own more than `minimumSharesToPassAVote` shares of token `sharesAddress` to be executed * * @param sharesAddress token address * @param minimumSharesToPassAVote proposal can vote only if the sum of shares held by all voters exceed this number * @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed */ function changeVotingRules(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) onlyOwner public { sharesTokenAddress = Token(sharesAddress); if (minimumSharesToPassAVote == 0 ) minimumSharesToPassAVote = 1; minimumQuorum = minimumSharesToPassAVote; debatingPeriodInMinutes = minutesForDebate; emit ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, address(sharesTokenAddress)); } /** * Add Proposal * * Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. * * @param beneficiary who to send the ether to * @param weiAmount amount of ether to send, in wei * @param jobDescription Description of job * @param transactionBytecode bytecode of transaction */ function newProposal( address beneficiary, uint weiAmount, string memory jobDescription, bytes memory transactionBytecode ) onlyShareholders public returns (uint proposalID) { proposalID = proposals.length++; Proposal storage p = proposals[proposalID]; p.recipient = beneficiary; p.amount = weiAmount; p.description = jobDescription; p.proposalHash = keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode)); p.minExecutionDate = now + debatingPeriodInMinutes * 1 minutes; p.executed = false; p.proposalPassed = false; p.numberOfVotes = 0; emit ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); numProposals = proposalID+1; return proposalID; } /** * Add proposal in Ether * * Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. * This is a convenience function to use if the amount to be given is in round number of ether units. * * @param beneficiary who to send the ether to * @param etherAmount amount of ether to send * @param jobDescription Description of job * @param transactionBytecode bytecode of transaction */ function newProposalInEther( address beneficiary, uint etherAmount, string memory jobDescription, bytes memory transactionBytecode ) onlyShareholders public returns (uint proposalID) { return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode); } /** * Check if a proposal code matches * * @param proposalNumber ID number of the proposal to query * @param beneficiary who to send the ether to * @param weiAmount amount of ether to send * @param transactionBytecode bytecode of transaction */ function checkProposalCode( uint proposalNumber, address beneficiary, uint weiAmount, bytes memory transactionBytecode ) view public returns (bool codeChecksOut) { Proposal storage p = proposals[proposalNumber]; return p.proposalHash == keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode)); } /** * Log a vote for a proposal * * Vote `supportsProposal? in support of : against` proposal #`proposalNumber` * * @param proposalNumber number of proposal * @param supportsProposal either in favor or against it */ function vote( uint proposalNumber, bool supportsProposal ) onlyShareholders public returns (uint voteID) { Proposal storage p = proposals[proposalNumber]; require(p.voted[msg.sender] != true); voteID = p.votes.length++; p.votes[voteID] = Vote({inSupport: supportsProposal, voter: msg.sender}); p.voted[msg.sender] = true; p.numberOfVotes = voteID +1; emit Voted(proposalNumber, supportsProposal, msg.sender); return voteID; } /** * Finish vote * * Count the votes proposal #`proposalNumber` and execute it if approved * * @param proposalNumber proposal number * @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it */ function executeProposal(uint proposalNumber, bytes memory transactionBytecode) public { Proposal storage p = proposals[proposalNumber]; require(now > p.minExecutionDate // If it is past the voting deadline && !p.executed // and it has not already been executed && p.proposalHash == keccak256(abi.encodePacked(p.recipient, p.amount, transactionBytecode))); // and the supplied code matches the proposal... // ...then tally the results uint quorum = 0; uint yea = 0; uint nay = 0; for (uint i = 0; i < p.votes.length; ++i) { Vote storage v = p.votes[i]; uint voteWeight = sharesTokenAddress.balanceOf(v.voter); quorum += voteWeight; if (v.inSupport) { yea += voteWeight; } else { nay += voteWeight; } } require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached if (yea > nay ) { // Proposal passed; execute the transaction p.executed = true; (bool success, ) = p.recipient.call.value(p.amount)(transactionBytecode); require(success); p.proposalPassed = true; } else { // Proposal failed p.proposalPassed = false; } // Fire Events emit ProposalTallied(proposalNumber, yea - nay, quorum, p.proposalPassed); } }
代碼的部署幾乎與前面的代碼徹底相同,但你還須要放置一個共享代幣地址shares token address
,該地址是代幣的地址,它將做爲具備投票權的共享。android
注意這些代碼行:首先咱們描述新合約的代幣合約。因爲它只使用了balanceOf
函數,咱們只須要添加那一行。程序員
contract Token { mapping (address => uint256) public balanceOf; }
而後咱們定義一個類型標記的變量,這意味着它將繼承咱們以前描述的全部函數。最後,咱們將代幣變量指向區塊鏈上的地址,所以它可使用它並請求實時信息。這是使一個合約在以太坊中理解另外一個的最簡單方法。web
contract Association { token public sharesTokenAddress; // ... constructor(token sharesAddress, uint minimumSharesForVoting, uint minutesForDebate) { sharesTokenAddress = token(sharesAddress);
這個協會association
提出了前一屆大會congress
沒有的挑戰:由於任何擁有代幣的人均可以投票並且餘額能夠很快變化,當股東投票時,提案的實際分數不能計算,不然有人可以經過簡單地將他的份額發送到不一樣的地址來屢次投票。所以,在本合約中,僅記錄投票位置,而後在執行提案階段計算實際得分。mongodb
uint quorum = 0; uint yea = 0; uint nay = 0; for (uint i = 0; i < p.votes.length; ++i) { Vote v = p.votes[i]; uint voteWeight = sharesTokenAddress.balanceOf(v.voter); quorum += voteWeight; if (v.inSupport) { yea += voteWeight; } else { nay += voteWeight; } }
計算加權投票的另外一種方法是建立一個單一的有符號整數來保持投票得分並檢查結果是正面仍是負面,但你必須使用int將無符號整數 voteWeight轉換爲有符號整數 得分= int(voteWeight);編程
使用這個DAO就像之前同樣:成員建立新的提案,對它們進行投票,等到截止日期過去,而後任何人均可以計算投票並執行它。app
在此合約中,設置爲全部者owner
的地址具備一些特殊權力:他們能夠隨意添加或禁止成員,更改獲勝所需的保證金,更改辯論所需的時間以及投票經過所需的法定人數。但這能夠經過使用業主擁有的另外一種權力來解決:改變全部權。
全部者能夠經過將新全部者指向0x00000來將全部權更改成任何人....這將保證規則永遠不會改變,但這是不可逆轉的行動。全部者還能夠將全部權更改成合約自己:只需單擊複製地址copy address
並將其添加到新全部者new owner
字段便可。這將使得全部者的全部權力均可以經過建立提案來執行。
若是你願意,你也能夠設置一個合約做爲另外一個合約的全部者:假設你想要一個公司結構,你想要一個有權任命董事會成員的終身總統,而後能夠發行更多的股票,最後這些股票被投票關於如何花費預算。你能夠建立一個關聯合約Association
,該合約mintable token使用最終由單個賬戶擁有的會議congress
所擁有的mintable代幣。
可是若是你想要不一樣的投票規則呢?也許改變投票規則你須要80%的共識,或者成員可能不一樣。在這種狀況下,你能夠建立另外一個相同的DAO或使用其餘一些源代碼並將其做爲第一個的全部者插入。
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的交互式在線編程實戰教程:
- java以太坊開發教程,主要是針對java和android程序員進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智能合約開發交互,進行帳號建立、交易、轉帳、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智能合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括帳戶管理、狀態與交易、智能合約開發與交互、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、帳戶與錢包、發行代幣、智能合約開發與部署、使用代碼與智能合約交互等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Java代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Php代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- tendermint區塊鏈開發詳解,本課程適合但願使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操代碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裏是原文以太坊DAO之股東協會