Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。
投票支付合约的所有费用和行动需要时间,并要求用户始终保持活跃,知情和专注。另一个有趣的方法是选择一个可以控制合约的指定账户,然后能够迅速做出决定。
流动民主合约
我们将实施一种通常称为流动民主Liquid Democracy
的版本,这是一种更灵活的代议制民主。在这种民主制度下,任何选民都可以成为潜在的代表:你只需说出你信任哪位选民就可以为你处理这一决定。你的投票权重被委托给他们,他们可以将其委托给他们信任的另一个选民,依此类推。最终结果应该是投票最多的账户是与最大数量的选民有信任关系的账户。
代码:
pragma solidity >=0.4.22 <0.6.0;
contract token {
mapping (address => uint256) public balanceOf;
}
contract LiquidDemocracy {
token public votingToken;
bool underExecution;
address public appointee;
mapping (address => uint) public voterId;
mapping (address => uint256) public voteWeight;
uint public delegatedPercent;
uint public lastWeightCalculation;
uint public numberOfDelegationRounds;
uint public numberOfVotes;
DelegatedVote[] public delegatedVotes;
string public forbiddenFunction;
event NewAppointee(address newAppointee, bool changed);
struct DelegatedVote {
address nominee;
address voter;
}
/**
* Constructor
*/
constructor(
address votingWeightToken,
string memory forbiddenFunctionCall,
uint percentLossInEachRound
) public {
votingToken = token(votingWeightToken);
delegatedVotes.length++;
delegatedVotes[0] = DelegatedVote({nominee: address(0), voter: address(0)});
forbiddenFunction = forbiddenFunctionCall;
delegatedPercent = 100 - percentLossInEachRound;
if (delegatedPercent > 100) delegatedPercent = 100;
}
/**
* Vote for an address
*
* Send your vote weight to another address
*
* @param nominatedAddress the destination address receiving the sender's vote
*/
function vote(address nominatedAddress) public returns (uint voteIndex) {
if (voterId[msg.sender]== 0) {
voterId[msg.sender] = delegatedVotes.length;
numberOfVotes++;
voteIndex = delegatedVotes.length++;
numberOfVotes = voteIndex;
}
else {
voteIndex = voterId[msg.sender];
}
delegatedVotes[voteIndex] = DelegatedVote({nominee: nominatedAddress, voter: msg.sender});
return voteIndex;
}
/**
* Perform Executive Action
*
* @param target the destination address to interact with
* @param valueInWei the amount of ether to send along with the transaction
* @param bytecode the data bytecode for the transaction
*/
function execute(address target, uint valueInWei, bytes32 bytecode) public {
require(msg.sender == appointee // If caller is the current appointee,
&& !underExecution // if the call is being executed,
&& bytes4(bytecode) != bytes4(keccak256(abi.encodePacked(forbiddenFunction))) // and it's not trying to do the forbidden function
&& numberOfDelegationRounds >= 4); // and delegation has been calculated enough
underExecution = true;
(bool success, ) = target.call.value(valueInWei)(abi.encode(bytecode)); // Then execute the command.
require(success);
underExecution = false;
}
/**
* Calculate Votes
*
* Go thruogh all the delegated vote logs and tally up each address's total rank
*/
function calculateVotes() public returns (address winner) {
address currentWinner = appointee;
uint currentMax = 0;
uint weight = 0;
DelegatedVote storage v = delegatedVotes[0];
if (now > lastWeightCalculation + 90 minutes) {
numberOfDelegationRounds = 0;
lastWeightCalculation = now;
// Distribute the initial weight
for (uint i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].nominee] = 0;
}
for (uint i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].voter] = votingToken.balanceOf(delegatedVotes[i].voter);
}
}
else {
numberOfDelegationRounds++;
uint lossRatio = 100 * delegatedPercent ** numberOfDelegationRounds / 100 ** numberOfDelegationRounds;
if (lossRatio > 0) {
for (uint i=1; i< delegatedVotes.length; i++){
v = delegatedVotes[i];
if (v.nominee != v.voter && voteWeight[v.voter] > 0) {
weight = voteWeight[v.voter] * lossRatio / 100;
voteWeight[v.voter] -= weight;
voteWeight[v.nominee] += weight;
}
if (numberOfDelegationRounds>3 && voteWeight[v.nominee] > currentMax) {
currentWinner = v.nominee;
currentMax = voteWeight[v.nominee];
}
}
}
}
if (numberOfDelegationRounds > 3) {
emit NewAppointee(currentWinner, appointee == currentWinner);
appointee = currentWinner;
}
return currentWinner;
}
}
部署
首先,你需要一个代币。如果你已按照上面的股东协会Shareholder association
教程,你可以使用与之前相同的代币,否则只需部署新代币并在某些帐户中分配。复制代币地址。
部署民主合约,并将代币地址放在投票权重代币上 ,将75作为每轮中的百分比损失,并将转让所有权(地址)(没有任何空格或额外字符!)作为禁止功能 。
选择代表
现在部署Liquid民主并转到其页面。首先,任何股东都会投票决定他们信任谁代表本合约做出决定。如果你想成为最终决策者,你可以自己投票,或者如果你宁愿没有人代表你担任这个角色,你可以在零地址上投票。
在足够多的人投票后,你可以执行计算投票功能,以便计算每个人的投票权重。这个功能需要多次运行,所以第一次运行它只会将每个人的体重设置为所选代币中的余额。在下一轮中,投票权重将转到你投票指定的人,在下一轮它会去由你选择的人投票的人等等。为了防止无限循环的投票授权,每次投票都会转发,它会失去一些权力,在percentLossInEachRound合约启动时设定。因此,如果损失设定为75%,则意味着你投票的人获得了100%的体重,但如果他们将投票委托给其他人,则只有75%的体重被转发。那个人可以委托给别人,但他们只能获得56%的投票权,依此类推。如果比率低于100%,将会有一个有限的时刻,重新计算投票授权将不再改变结果,但如果它是100%,则意味着投票权重将简单地围绕任何潜在的循环进行循环。
如果这一轮调用计算投票开始已超过一个半小时,所有权重将重置并将根据原始代币余额重新计算,因此如果你最近收到更多代币,则应再次执行此功能。
众议机构
投票代表团的所有优点是什么?例如,你可以在关联上使用它而不是代币权重。首先,获取股东协会的代码,但替换描述代币的第一行:
contract Token {
mapping (address => uint256) public balanceOf;
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}
进入这个:
contract Token {
mapping (address => uint256) public voteWeight;
uint public numberOfDelegationRounds;
function balanceOf(address member) public view returns (uint256 balance) {
if (numberOfDelegationRounds < 3)
return 0;
else
return this.voteWeight(member);
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}
在编写合约时,你可以描述主合约使用的多个其他合约。有些可能是已在目标合约上定义的函数和变量,如voteWeight
和numberOfDelegationRounds
。但请注意,balanceOf
是一个新功能,它在Liquid Democracy
或Association
合约上都不存在,我们现在正在定义它,作为一个函数,如果至少计算了三轮,它将返回voteWeight
。
使用Liquid democracy
作为代币地址而不是原始代币,并像往常一样继续部署股东协会。就像以前一样,用户可以针对这些问题创建新的提案或投票,但现在,我们使用委托流程,不是使用代币余额作为投票权。所以,如果你是一个代币持有者,你可以选择一个你信任的人并指定他们,而不是让你自己不断地通知所有问题,然后他们可以选择他们信任的人:结果就是你的代表,而不是被限制在给定的任意地理位置附近,将是你社交距离的人。
此外,这意味着你可以随时切换投票:如果你的代表在某些问题上投票反对你的利益,你可以在提案投票结算之前,转换你的被任命者,或者只是选择代表你自己处理问题并投自己投票。
行政部门
代议制民主国家是选择代表的一种很好的方式,但对于一些重要或简单的决策,对个别提案进行投票可能太慢:这就是为什么大多数民主政府都有一个行政部门,而被任命的人有权代表国家。
在四轮代表之后,更多权重的地址将被设定为被任命者。如果有许多委托投票,那么可能需要再多几轮计算投票才能在最终指定的地址中解决。
被任命者是唯一可以调用Execute函数的地址,它可以执行(几乎)任何代表整个民主的函数。如果液体民主合约中存有任何以太或代币,被任命者将被允许在任何地方移动。
如果你遵循我们的示例并使用此流动民主作为代币创建了股东协会 ,那么你应该能够以有趣的方式使用行政部门:转到主协会地址并执行转移所有权功能到流动民主。
传输完成后,将功能切换为更改投票规则。这允许你更改一些基本的投票规则,例如投票通过所需的最低法定人数或新提案需要留在场上的时间。尝试更改这些设置并单击执行:当弹出确认窗口时,它将告诉你该交易无法执行。当然,这种情况发生,因为只有设置为所有者的地址才能更改这些设置,合约将拒绝此交易尝试。因此,不是键入密码而是复制数据字段上的代码并将其保存到文本文件中。单击取消,滚动到顶部并单击复制地址,并将其保存到文本文件。
现在转到Liquid民族页面并选择执行。在目标上放置关联合约的地址,将ether amount
保留为0并将先前复制的代码粘贴到字节码数据字段中。确保你从作为被任命者设置的帐户执行它,然后单击执行。
一旦交易被收回,Liquid民主将把订单传递给协会,并且新的投票规则可能适用。被任命者有绝对的权力去做液体民主合约可以执行的任何事情。你可以使用相同的技术创建委派民主所拥有的Mintable代币,然后允许被任命者填写代币或冻结帐户。
为了防止滥用权力,你可以设置一个被禁止的人无法做的禁止功能。如果你按照我们的例子,禁止的功能是transferOwnership
(地址),以防止被任命者将协会的所有权转让给他们自己(在政治上,当总统利用他的行政权力转移给自己以前属于总统,这是政变或贪污)。
======================================================================
分享一些以太坊、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之流动民主智能合约