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之股东协会