15.10.17 - 15-28
«На блоч-цепи никто не знает, что вы холодильник»
Ричард Браун
До сих пор все перечисленные нами контракты принадлежали и исполнялись другими учетными записями, которые, вероятно, были у людей. Но нет никакой дискриминации в отношении роботов или людей в экосистеме Эфириума, и контракты могут создавать произвольные действия, как любой другой аккаунт. Контракты могут владеть жетонами, участвовать в толпах и даже участвовать в голосовании других контрактов.
В этом разделе мы собираемся создать децентрализованную и демократическую организацию, которая существует исключительно на блочной цепочке, но которая может делать все, что может сделать простая учетная запись. У организации есть центральный менеджер, который решает, кто является членами и правилами голосования, но, как мы увидим, это также можно изменить.
То, как эта конкретная демократия работает, состоит в том, что у нее есть Владелец, который работает как администратор, генеральный директор или президент. Владелец может добавить (или удалить) члены с правом голоса в организации. Любой участник может сделать предложение, которое заключается в форме транзакции эфира, чтобы либо отправить эфир, либо выполнить какой-либо контракт, а другие участники могут голосовать в поддержку или против предложения. После того, как проголосовали предопределенное количество времени и определенное число членов, предложение может быть выполнено: контракт подсчитывает голоса, и если голосов достаточно, он выполнит данную транзакцию.
КОД
pragma solidity ^0.4.16; contract owned { address public owner; function owned() 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 _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, this, _value)); receivedTokens(_from, _value, _token, _extraData); } function () payable public { receivedEther(msg.sender, msg.value); } } interface Token { function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } contract Congress is owned, tokenRecipient { // Contract Variables and events uint public minimumQuorum; uint public debatingPeriodInMinutes; int public majorityMargin; Proposal[] public proposals; uint public numProposals; mapping (address => uint) public memberId; Member[] public members; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter, string justification); event ProposalTallied(uint proposalID, int result, uint quorum, bool active); event MembershipChanged(address member, bool isMember); event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, int newMajorityMargin); struct Proposal { address recipient; uint amount; string description; uint votingDeadline; bool executed; bool proposalPassed; uint numberOfVotes; int currentResult; bytes32 proposalHash; Vote[] votes; mapping (address => bool) voted; } struct Member { address member; string name; uint memberSince; } struct Vote { bool inSupport; address voter; string justification; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyMembers { require(memberId[msg.sender] != 0); _; } /** * Constructor function */ function Congress ( uint minimumQuorumForProposals, uint minutesForDebate, int marginOfVotesForMajority ) payable public { changeVotingRules(minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority); // It’s necessary to add an empty first member addMember(0, ""); // and let's add the founder, to save a step later addMember(owner, 'founder'); } /** * Add member * * Make `targetMember` a member named `memberName` * * @param targetMember ethereum address to be added * @param memberName public name for that member */ function addMember(address targetMember, string memberName) onlyOwner public { uint id = memberId[targetMember]; if (id == 0) { memberId[targetMember] = members.length; id = members.length++; } members[id] = Member({member: targetMember, memberSince: now, name: memberName}); MembershipChanged(targetMember, true); } /** * Remove member * * @notice Remove membership from `targetMember` * * @param targetMember ethereum address to be removed */ function removeMember(address targetMember) onlyOwner public { require(memberId[targetMember] != 0); for (uint i = memberId[targetMember]; i<members.length-1; i++){ members[i] = members[i+1]; } delete members[members.length-1]; members.length--; } /** * Change voting rules * * Make so that proposals need tobe discussed for at least `minutesForDebate/60` hours, * have at least `minimumQuorumForProposals` votes, and have 50% + `marginOfVotesForMajority` votes to be executed * * @param minimumQuorumForProposals how many members must vote on a proposal for it to be executed * @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed * @param marginOfVotesForMajority the proposal needs to have 50% plus this number */ function changeVotingRules( uint minimumQuorumForProposals, uint minutesForDebate, int marginOfVotesForMajority ) onlyOwner public { minimumQuorum = minimumQuorumForProposals; debatingPeriodInMinutes = minutesForDebate; majorityMargin = marginOfVotesForMajority; ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, majorityMargin); } /** * 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 jobDescription, bytes transactionBytecode ) onlyMembers public returns (uint proposalID) { proposalID = proposals.length++; Proposal storage p = proposals[proposalID]; p.recipient = beneficiary; p.amount = weiAmount; p.description = jobDescription; p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; p.executed = false; p.proposalPassed = false; p.numberOfVotes = 0; 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 jobDescription, bytes transactionBytecode ) onlyMembers 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 transactionBytecode ) constant public returns (bool codeChecksOut) { Proposal storage p = proposals[proposalNumber]; return p.proposalHash == keccak256(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 * @param justificationText optional justification text */ function vote( uint proposalNumber, bool supportsProposal, string justificationText ) onlyMembers public returns (uint voteID) { Proposal storage p = proposals[proposalNumber]; // Get the proposal require(!p.voted[msg.sender]); // If has already voted, cancel p.voted[msg.sender] = true; // Set this voter as having voted p.numberOfVotes++; // Increase the number of votes if (supportsProposal) { // If they support the proposal p.currentResult++; // Increase score } else { // If they don't p.currentResult--; // Decrease the score } // Create a log of this event Voted(proposalNumber, supportsProposal, msg.sender, justificationText); return p.numberOfVotes; } /** * 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 transactionBytecode) public { Proposal storage p = proposals[proposalNumber]; require(now > p.votingDeadline // If it is past the voting deadline && !p.executed // and it has not already been executed && p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode) // and the supplied code matches the proposal && p.numberOfVotes >= minimumQuorum); // and a minimum quorum has been reached... // ...then execute result if (p.currentResult > majorityMargin) { // Proposal passed; execute the transaction p.executed = true; // Avoid recursive calling require(p.recipient.call.value(p.amount)(transactionBytecode)); p.proposalPassed = true; } else { // Proposal failed p.proposalPassed = false; } // Fire Events ProposalTallied(proposalNumber, p.currentResult, p.numberOfVotes, p.proposalPassed); } }
КАК РАЗВЕРНУТЬ
Откройте кошелек (если вы только тестирование, перейдите в меню разработки> сеть> testnet), перейдите к контрактамвкладке , а затем нажмите развернуть контракт , и на код солидности поле, вставьте код , приведенный выше. В разделе выбора контрактов выберите Конгресс, и вы увидите переменные установки.
Минимальный кворум для предложений - это минимальное количество голосов, которое должно иметь предложение, прежде чем оно может быть выполнено.
Протокол для обсуждения - это минимальный промежуток времени (в минутах), который должен пройти до того, как он может быть выполнен
Маржа голосов для большинства . Предложение распространяется, если есть более 50% голосов плюс маржа. Оставьте в 0 для простого большинства, поставьте его на число членов - 1, чтобы требовать абсолютного консенсуса.
Вы можете изменить эти параметры позже, выбрать имя, 5 минут для дебатов и оставить оставшиеся из них на 0. Немного ниже на странице вы увидите оценочную стоимость вашего контракта в эфире. Вы можете попытаться снизить цену, если хотите сохранить, но это может означать, что вам нужно подождать дольше, пока ваш контракт будет создан. Нажмите « Развернуть» , введите пароль и подождите.
Через несколько секунд вы попадете на панель инструментов, прокрутите вниз, и вы сможете увидеть, как создается ваша транзакция. Через минуту вы увидите успешную транзакцию, и будет создан новый уникальный значок. Нажмите на название контракта, чтобы увидеть его (вы можете получить его в любое время на вкладке « Контракты »).
ОБМЕН С ДРУГИМИ
Если вы хотите поделиться своим DAO с другими, тогда им нужен как адрес контракта, так и файл интерфейса, небольшая текстовая строка, которая работает как инструкция по эксплуатации контракта. Нажмите адрес копирования, чтобы получить первый и показать интерфейс, чтобы открыть последний.
На другом компьютере перейдите на вкладку « Контракты », а затем нажмите на часовой контракт . Добавьте правильный адрес и интерфейс и нажмите OK .
ВЗАИМОДЕЙСТВИЕ С КОНТРАКТОМ
В «read from contract» вы можете увидеть все функции, которые вы можете выполнить бесплатно по контракту, поскольку они просто читают информацию из блокноя. Здесь вы можете увидеть, например, текущий «владелец» контракта (который должен быть учетной записью, загрузившей контракт).
На «Запись на контракт» у вас есть список всех функций, которые будут пытаться выполнить некоторые вычисления, которые сохраняют данные в блок-цепочке и, следовательно, будут стоить эфира. Выберите «newProposal», и в нем будут показаны все параметры этой функции.
Прежде чем взаимодействовать с контрактом, вам нужно будет добавить новых членов, чтобы они могли голосовать. На вкладке «Выбор функции» выберите «Добавить участника» . Добавьте адрес лица, которого хотите сделать участником (чтобы удалить участника, выберите «Удалить участника» ). В «execute from» убедитесь, что у вас есть та же учетная запись, которая установлена как владелец, поскольку это действие, которое может выполнить только главный администратор. Нажмите « выполнить» и подождите несколько секунд, чтобы следующий блок прошел через ваше изменение.
Список участников отсутствует, но вы можете проверить, является ли кто-либо членом, поместив свой адрес в функцию « Участники» в столбце « Читать из договора» .
Кроме того, если вы хотите, чтобы у контракта были какие-то собственные деньги, вам нужно внести в него какой-то эфир (или другой токен), иначе у вас будет красивая беззубая организация. Нажмите Transfer Ether & Tokens в правом верхнем углу.
ДОБАВЬТЕ ПРОСТОЕ ПРЕДЛОЖЕНИЕ: ОТПРАВИТЬ ЭФИР
Теперь давайте добавим первое предложение к контракту. На панели выбора функций выберите Новое предложение .
Для «получателя» добавьте адрес того, кому вы хотите отправить эфир, а затем укажите, сколько эфиров вы хотите на «etherAmount» (должно быть целым) и, наконец, некоторый текст, описывающий причину, по которой вы хотите это сделать. Оставьте транзакциюByteCode пустой сейчас. Нажмите выполнить и введите пароль. Через несколько секунд numProposals увеличится до 1, и первое предложение, число 0, появится в левой колонке. Когда вы добавляете больше предложений, вы можете увидеть любой из них, просто разместив номер предложения в поле «предложения», и вы сможете прочитать все об этом.
Голосование по предложению также очень простое. Выберите «голос» на функции выбора. Введите номер предложения в первом поле и установите флажок «Да», если вы согласны с ним (или оставьте его пустым, чтобы проголосовать против него). Нажмите « выполнить », чтобы отправить свой голос.
Когда пройдет время голосования, вы можете выбрать «executeProposal» . Если предложение просто отправляет эфир, вы также можете оставить поле « transactionBytecode » пустым. После нажатия «выполнить», но прежде чем вводить пароль, обратите внимание на экран, который появляется.
Если в поле «оценочное потребление платы» есть предупреждение, это означает, что по какой-то причине вызванная функция не будет выполняться и будет резко прекращена. Это может означать много вещей, но в контексте этого контракта это предупреждение будет отображаться всякий раз, когда вы пытаетесь выполнить контракт до истечения срока его действия, или если пользователь пытается отправить другие данные байт-кода, чем исходное предложение.По соображениям безопасности, если происходит какое-либо из этих действий, выполнение контракта резко прекращается, и пользователь, который пытался совершить незаконную транзакцию, потеряет все эфиры, которые он отправил для оплаты транзакций.
Если транзакция была выполнена, то через несколько секунд вы сможете увидеть результат: выполнение вернется к истине, и правильное количество эфира должно быть вычтено из баланса этого контракта и в адрес получателя.
ДОБАВИТЬ КОМПЛЕКСНОЕ ПРЕДЛОЖЕНИЕ: СОБСТВЕННЫЙ ТОКЕН
Вы можете использовать эту демократию для выполнения любой транзакции на ethereum, если вы можете определить байт-код, который генерирует эта транзакция. К счастью для нас, вы можете использовать кошелек, чтобы сделать именно это!
В этом примере мы будем использовать токен, чтобы показать, что этот контракт может содержать больше, чем простой эфир, и может выполнять транзакции в любом другом активе на основе эфира. Сначала создайте токен , принадлежащий одной из ваших обычных учетных записей. На странице контракта нажмите Transfer Ether & Tokens, чтобы перенести некоторые из них на новый контракт с конгрессом (для простоты не отправляйте больше половины ваших монет в DAO). После этого мы собираемся моделировать действие, которое вы хотите выполнить. Поэтому, если вы хотите предложить, чтобы DAO отправил 500 млн. Золотого токена лицу в качестве платежа, следуйте инструкциям, которые вы выполните для выполнения этой транзакции из вашей учетной записи и нажмите «отправить», но когда на экране подтверждения всплывает, не вводите пароль .
Вместо этого нажмите ссылку «SHOW RAW DATA» и скопируйте код, отображаемый в поле «RAW DATA», и сохраните его в текстовом файле или в блокноте. Отмените транзакцию. Вам также понадобится адрес контракта, на который вы будете называть эту операцию, в этом случае - токен. Вы можете найти его на вкладке « Контракты »: сохранить это тоже.
Теперь вернитесь к конгрессу и создайте новое предложение с этими параметрами:
В качестве бенефициара поместите адрес вашего токена (обратите внимание, если это тот же значок)
Оставьте количество эфира пустым
В описании работы просто напишите небольшое описание того, что вы хотите выполнить
В байт -коде транзакции вставьте байт-код, который вы сохранили из поля данных на предыдущем шаге
Через несколько секунд вы сможете увидеть детали предложения. Вы заметите, что байт-код транзакции там не будет показан, и вместо этого существует только «хеш-транзакция». В отличие от других полей, Bytecode может быть чрезвычайно длинным и, следовательно, дорогостоящим для хранения на блок-цепочке, поэтому вместо его архивирования лицо, выполняющее вызов позже, предоставит байт-код.
Но это, конечно же, создает дыру в безопасности: как можно проголосовать за предложение без фактического кода? И что мешает пользователю выполнять другой код после того, как предложение было проголосовано? Вот где и происходит транзакционный хеш. Прокрутите бит в списке функций «читать из контракта», и вы увидите функцию проверки предложения, где каждый может поместить все параметры функции и проверить, соответствуют ли они тому, на что проголосовали. Это также гарантирует, что предложения не будут выполняться, если хэш байт-кода не соответствует именно тому, который указан в предоставленном коде.
Любой человек может реально проверить предложение очень легко, выполнив те же шаги, чтобы получить правильный байт-код, а затем добавив номер предложения и другие параметры к функции, называемой « Проверить код предложения» в нижней части « Чтение из контракта» .
Остальная часть процесса голосования остается неизменной: все участники могут голосовать и после истечения срока, кто-то может выполнить это предложение. Единственное различие заключается в том, что на этот раз вам придется предоставить тот же байт-код, который вы отправили ранее. Обратите внимание на любые предупреждения в окне подтверждения: если он говорит, что не выполнит ваш код, проверьте, прошел ли последний срок, если голосов достаточно, и если ваш байт-код транзакции проверяется.
СДЕЛАТЬ ЕГО ЛУЧШЕ
Вот некоторые недостатки этого текущего DAO, которые мы оставляем в качестве упражнения для читателя:
Можете ли вы сделать список участников общедоступным и проиндексированным?
Можете ли вы разрешить членам менять свои голоса (после того, как голоса поданы, но до подсчета голосов)?
В настоящее время голосовое сообщение отображается только в журналах, вы можете сделать функцию, которая отобразит все голоса?
В предыдущем разделе мы создали контракт, который работает как клуб только для приглашения, где члены приглашаются или запрещены по прихоти президента. Но у этого есть несколько недостатков: что, если кто-то хочет изменить свой главный адрес? Что делать, если у некоторых членов больше веса, чем у других? Что делать, если вы действительно хотите торговать или продавать членство или акции на открытом рынке? Что, если вы хотите, чтобы ваша организация работала как постоянная машина принятия решений акционерами?
Мы собираемся немного изменить наш контракт, чтобы подключить его к определенному токену, который будет работать как холдинговые доли контракта. Сначала нам нужно создать этот токен: перейдите в учебник по токенам и создайте простой токен с начальной подачей 100, десятичными знаками 0 и знаком процента (%) в качестве символа . Если вы хотите иметь возможность торговать долями процента, затем увеличивайте запас на 100x или 1000x, а затем добавьте соответствующее количество нулей в виде десятичных знаков . Разверните этот контракт и сохраните его адрес в текстовом файле.
Теперь к коду акционера:
РАЗВЕРТЫВАНИЕ И ИСПОЛЬЗОВАНИЕ
Код развертывается почти так же, как и предыдущий код, но вам также нужно указать адрес токена акций, который является адресом токена, который будет работать как доля с правом голоса.
Обратите внимание на эти строки кодов: сначала мы описываем токен контракта на наш новый контракт. Поскольку он использует функцию balanceOf , нам нужно только добавить эту единственную строку.
Затем мы определяем переменную токена типа , что означает, что он наследует все функции, описанные ранее. Наконец, мы указываем переменную токена на адрес в цепочке, поэтому он может использовать это и запрашивать живую информацию. Это самый простой способ, чтобы один контракт понимал другое в ethereum.
Эта ассоциация представляет собой вызов, которого не было в предыдущем конгрессе: поскольку каждый, у кого есть токены, может голосовать, а баланс может меняться очень быстро, фактический балл предложения не может быть засчитан, когда голосует акционер, иначе кто-то сможет проголосовать несколько раз, просто отправив свою долю на разные адреса. Таким образом, в этом контракте записывается только позиция голосования, а затем реальная оценка подсчитывается на этапе исполнения заявки .
pragma solidity ^0.4.16; contract owned { address public owner; function owned() { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner { 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 _extraData){ Token t = Token(_token); require(t.transferFrom(_from, this, _value)); receivedTokens(_from, _value, _token, _extraData); } function () payable { receivedEther(msg.sender, msg.value); } } contract Token { mapping (address => uint256) public balanceOf; function transferFrom(address _from, address _to, uint256 _value) 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 votingDeadline; 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 function * * First time setup */ function Association(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) payable { changeVotingRules(sharesAddress, minimumSharesToPassAVote, minutesForDebate); } /** * Change voting rules * * Make so that proposals need tobe 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 { sharesTokenAddress = Token(sharesAddress); if (minimumSharesToPassAVote == 0 ) minimumSharesToPassAVote = 1; minimumQuorum = minimumSharesToPassAVote; debatingPeriodInMinutes = minutesForDebate; ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, 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 jobDescription, bytes transactionBytecode ) onlyShareholders returns (uint proposalID) { proposalID = proposals.length++; Proposal storage p = proposals[proposalID]; p.recipient = beneficiary; p.amount = weiAmount; p.description = jobDescription; p.proposalHash = sha3(beneficiary, weiAmount, transactionBytecode); p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; p.executed = false; p.proposalPassed = false; p.numberOfVotes = 0; 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 jobDescription, bytes transactionBytecode ) onlyShareholders 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 transactionBytecode ) constant returns (bool codeChecksOut) { Proposal storage p = proposals[proposalNumber]; return p.proposalHash == sha3(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 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; 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 transactionBytecode) { Proposal storage p = proposals[proposalNumber]; require(now > p.votingDeadline // If it is past the voting deadline && !p.executed // and it has not already been executed && p.proposalHash == sha3(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; require(p.recipient.call.value(p.amount)(transactionBytecode)); p.proposalPassed = true; } else { // Proposal failed p.proposalPassed = false; } // Fire Events ProposalTallied(proposalNumber, yea - nay, quorum, p.proposalPassed); } }
contract token { mapping (address => uint256) public balanceOf; }
contract Association { token public sharesTokenAddress; // ...function Association(token sharesAddress, uint minimumSharesForVoting, uint minutesForDebate) { sharesTokenAddress = token(sharesAddress);
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; } }
Другой способ достижения тех же целей были бы создать единое целое число , чтобы сохранить счет голосов и проверить , если он был положительным или отрицательным в конце концов, но вы должны преобразовать беззнаковое целое balanceOf в знаковом целом , используя междунар score = int (voteWeight);
Использование этого DAO в точности аналогично предыдущему: участники создают новые предложения, голосуют на них, ждут окончания срока, а затем каждый может подсчитать голоса и выполнить его.
НО КАК Я МОГУ ОГРАНИЧИТЬ ВЛАСТЬ ВЛАДЕЛЬЦА?
По этому контракту адрес, заданный как владелец, обладает определенными полномочиями: они могут добавлять или запрещать участников по своему усмотрению, изменять маржу, необходимую для победы, изменять время, необходимое для обсуждения, и кворум, необходимый для голосования. Но это можно решить, используя еще одну силу, которую владелец имеет: изменить право собственности.
Владелец может изменить право собственности на никому не указав новый владелец как 0x00000 ... . Это гарантирует, что правила никогда не изменятся, но это необратимое действие. Владелец также может изменить право собственности на сам контракт: просто нажмите «адрес копирования» и добавьте его в поле «новый владелец». Это приведет к тому, что все полномочия владельца могут быть выполнены путем создания предложений.
Если вы хотите, вы также можете установить один контракт как владелец другого: предположите, что вам нужна корпоративная структура, в которой вы хотели, чтобы президент на всю жизнь имел право назначать членов совета директоров, которые затем могли выпускать больше акций, и, наконец, эти акции проголосовали о том, как потратить бюджет. Вы могли бы создать договор Ассоциации , который использовал монетный токен, принадлежащий конгрессу, который,наконец, принадлежал одной учетной записи.
Но что, если вы хотите принять другие правила для голосования? Возможно, чтобы изменить правила голосования, вам понадобится 80% -ный консенсус, или, может быть, члены разные. В этом случае вы можете создать другой идентичный DAO или использовать какой-либо другой исходный код и подключить его к владельцу первого.
Голосование по всем расходам и действиям контракта требует времени и требует, чтобы пользователи постоянно были активными, информированными и внимательными. Другой интересный подход заключается в том, чтобы выбрать назначенный аккаунт, который будет контролировать контракт, а затем принять быстрые решения по нему.
Мы собираемся реализовать версию того, что обычно называется Liquid Democracy , которая является более гибкой делегатской демократией. В такой демократии любой избиратель может быть потенциальным делегатом: вместо того, чтобы голосовать за кандидата, которого вы хотите, вы просто говорите, какой избиратель вы доверяете, чтобы справиться с этим решением для вас. Ваш голос голосования делегирован им, и они могут, в свою очередь, делегировать его другому избирателю, которому они доверяют, и так далее. Конечным результатом должно быть то, что наиболее проголосовавшая учетная запись - это та, которая имеет доверительные связи с наибольшим количеством избирателей.
КОД
pragma solidity ^0.4.16; 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 function */ function LiquidDemocracy( address votingWeightToken, string forbiddenFunctionCall, uint percentLossInEachRound ) { votingToken = token(votingWeightToken); delegatedVotes.length++; delegatedVotes[0] = DelegatedVote({nominee: 0, voter: 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) 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) { require(msg.sender == appointee // If caller is the current appointee, && !underExecution // // if the call is being executed, && bytes4(bytecode) != bytes4(sha3(forbiddenFunction)) // and it's not trying to do the forbidden function && numberOfDelegationRounds >= 4); // and delegation has been calculated enough underExecution = true; assert(target.call.value(valueInWei)(bytecode)); // Then execute the command. underExecution = false; } /** * Calculate Votes * * Go thruogh all the delegated vote logs and tally up each address's total rank */ function calculateVotes() 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 (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 (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) { NewAppointee(currentWinner, appointee == currentWinner); appointee = currentWinner; } return currentWinner; } }
РАЗВЕРТЫВАНИЕ
Во-первых, вам нужен токен. Если вы следовали вышеописанному руководству ассоциации акционеров , вы можете использовать тот же токен, что и ранее, иначе просто установите новый токен и распределите его среди некоторых учетных записей. Скопируйте адрес маркера.
Разверните договор о демократии и поместите маркерный адрес в токен Голосования , поместите 75 как процентную потерю в каждом раунде и transferOwnership (адрес) (без пробелов или дополнительных символов!) В качестве запрещенной функции .
ВЫБОР ДЕЛЕГАТА
Теперь разверните жидкую демократию и перейдите на ее страницу. Сначала кто-нибудь из акционеров голосует за то, кому они будут доверять, чтобы принимать решения от имени этого контракта. Вы можете проголосовать самостоятельно, если вы хотите быть конечным лицом, принимающим решения, или по нулевому адресу, если вы предпочтете, чтобы никто не представлял вас в этой роли.
После того, как достаточно людей отдали свои голоса, вы можете выполнить функцию Calculate Votes, чтобы она подсчитала вес каждого голоса. Эта функция должна запускаться несколько раз, поэтому первый запуск будет просто устанавливать вес каждого в качестве баланса в выбранном токене, в следующем раунде, что весовые коэффициенты голосования будут направлены на человека, которого вы проголосовали, а в следующем он пойдет в человек, проголосовавший выбранным вами человеком и так далее. Чтобы предотвратить бесконечные циклы голосов делегаций, каждый раз, когда голосование направляется, оно теряет немного мощности, установленной при запуске контракта в процентахLossInEachRound, Таким образом, если потеря установлена на уровне 75%, это означает, что человек, которого вы голосуете, получает 100% от вашего веса, но если они делегируют голосование кому-то еще, только 75% их веса направляется. Этот человек может делегировать кому-то другому, но они получат только 56% от вашего веса голосования и так далее. Если соотношение составляет менее 100%, то будет конечный момент, когда пересчет голосов делегации больше не изменит результат, но если это 100%, это означает, что весовые коэффициенты для голосования будут просто циркулировать вокруг любых потенциальных циклов.
Если прошло более полутора часов с момента запуска этого раунда голосования «Вычисление голосов », все веса будут сброшены и будут пересчитаны на основе исходного баланса токена, поэтому, если вы недавно получили больше токенов, вы должны снова выполнить эту функцию ,
ПАЛАТА ПРЕДСТАВИТЕЛЕЙ
Для чего нужна всем, кому нравится голосование? Во-первых, вы можете использовать его вместо веса токена в Ассоциации . Прежде всего, получите код для ассоциации акционеров, но замените первые строки, где он описывает токен:
В этом:
contract token { mapping (address => uint256) public balanceOf; }
contract token { mapping (address => uint256) public voteWeight; uint public numberOfDelegationRounds; function balanceOf(address member) constant returns (uint256 balance) { if (numberOfDelegationRounds < 3) return 0; else return this.voteWeight(member); } }
Когда вы пишете свой контракт, вы можете описать несколько других контрактов, используемых в вашем основном контракте. Некоторые из них могут быть функциями и переменными, которые уже определены в целевом контракте, например voteWeight и numberOfDelegationRounds . Но обратите внимание, что balanceOf - это новая функция, которая не существует ни на договоре «Жидкая демократия», ни в Ассоциации, мы сейчас определяем ее как функцию, которая вернет голосование,если будет рассчитано не менее трех раундов делегаций.
Используйте жидкую демократию как адрес токена вместо оригинального токена и приступайте к развертыванию ассоциации акционеров, как обычно. Как и раньше, пользователи могут создавать новые предложения о том, что делать или голосовать за эти вопросы, но теперь вместо использования баланса токенов в качестве полномочий голосования мы используем делегационный процесс . Поэтому, если вы являетесь владельцем токена, вместо того, чтобы постоянно информировать себя о всех проблемах, вы можете просто выбрать кого-то, кому вы доверяете, и назначить их, а затем они могут выбрать кого-то, кому они доверяют: в результате ваш представитель вместо будучи ограниченным данной произвольной географической близостью , будет кто-то в вашей социальной близости .
Также это означает, что вы можете в любой момент переключить свое голосование: если ваш представитель проголосовал против ваших интересов в какой-то проблеме, вы можете, до того, как голоса предложений будут подсчитаны, переключите своего назначенного лица или просто решите представить себя по проблеме и бросить голосование самостоятельно.
ИСПОЛНИТЕЛЬНАЯ ВЛАСТЬ
Делегированные демократии - отличный способ выбрать представителей, но голосование по отдельным предложениям может быть слишком медленным для принятия некоторых важных или более простых решений: поэтому большинство демократических правительств имеют исполнительную ветвь власти, где назначенное лицо имеет право представлять государство.
После четырех раундов делегаций адрес с большим весом будет установлен в качестве Назначенного . Если есть много делегированных голосов, тогда может потребоваться еще несколько раундов « Рассчитать голоса» , чтобы урегулировать окончательный адрес.
Назначение - это единственный адрес, который может вызывать функцию Execute , которая сможет выполнять (почти) любую функцию, представляющую демократию в целом. Если есть какой-либо эфир или токен, хранящийся в контракте «Жидкая демократия», то Назначенному лицу будет разрешено его перемещать в любом месте.
Если вы следовали нашему примеру и создали ассоциацию Акционеров, использующую эту ликвидную демократию в качестве знака, то вы должны иметь возможность использовать исполнительную ветвь интересным образом: перейдите на главный адрес Ассоциации и выполните функцию передачи права собственности на жидкую демократию ,
По завершении этой передачи переключите функцию « Изменить правила голосования» . Это позволяет вам изменить некоторые важные правила голосования, такие как минимальный кворум, необходимый для прохождения голосования, или время, когда новое предложение должно оставаться на полу. Попробуйте изменить эти параметры и нажмите « выполнить» : при появлении окна подтверждения он скажет вам, что транзакция не может быть выполнена . Это происходит, конечно, потому что только адрес, установленный как « Владелец», может изменить эти настройки, и контракт отклонит эту попытку транзакции. Поэтому вместо ввода пароля скопируйте код в поле данных и сохраните его в текстовом файле. Нажмите «Отмена», прокрутите вверх и нажмите адрес копирования а также сохранить это в текстовом файле.
Теперь перейдите на страницу «Жидкая демократия» и выберите « выполнить» . В цель укажите адрес договора ассоциации, оставьте количество эфира равным 0 и вставьте код, который вы скопировали ранее, в поле данных байткода . Убедитесь, что вы выполняете его из учетной записи, установленной в качестве назначенного, и нажмите « выполнить» .
После того, как сделка будет получена, ликвидная демократия передаст приказ ассоциации, и новые правила голосования могут применяться. У назначенного есть абсолютная власть делать все, что может выполнить контракт Жидкой демократии . Вы можете использовать ту же технику для создания Mintable Token, принадлежащего делегатской демократии, а затем разрешить назначенцу отмечать токены или замораживать счета.
Чтобы предотвратить злоупотребления полномочиями, вы можете установить одну Запрещенную функцию, которую Назначение не может делать. Если вы следовали нашему примеру, запрещенной функцией является transferOwnership (адрес) , чтобы не допустить, чтобы назначенное лицо передало право собственности на ассоциацию себе (в политике, когда президент использует свою исполнительную власть для передачи себе того, что раньше принадлежало президентство, это переворот или хищение).
Иногда время также можно использовать как отличный механизм безопасности. Следующий код основан на конгрессе DAO, но с другим изменением. Вместо каждого действия, требующего одобрения X-членов, вместо этого любые транзакции могут инициироваться одним членом, но все они потребуют минимальной задержки, прежде чем они могут быть выполнены, что зависит от поддержки, которую имеет транзакция , Чем больше утверждений имеет предложение, тем скорее это может быть выполнено. Участник может голосовать против транзакции, что будет означать, что оно отменит одну из других утвержденных подписей.
Это означает, что если у вас нет срочности, может потребоваться одна или две подписи для выполнения любой транзакции. Но если один ключ взломан, другие ключи могут отложить эту транзакцию в течение нескольких месяцев или года или даже остановить ее выполнение.
КАК ЭТО РАБОТАЕТ
Транзакция, одобренная всеми ключами, может быть выполнена через десять минут (эта сумма настраивается) и количество времени, которое требуется удваивать каждый раз для каждых 5% участников, которые не голосуют (и вчетверо против). Если это простая эфирная транзакция, транзакция выполняется, как только голосование поддержки помещает ее в требуемое время, но более сложная транзакция потребует, чтобы она выполнялась вручную с правильным байтовым кодом. Это значения по умолчанию, но при создании контракта это можно установить по-разному:
Количество членов, одобряющих транзакцию: приблизительная временная задержка
100% одобрение: 10 минут (минимальное значение по умолчанию)
90% одобрение: 40 минут
80%: 2h40
50%: около недели
40%: 1 месяц
30%: 4 месяца
20%: более года
10% или менее: 5 лет или никогда
Как только минимальное количество времени прошло, любой может выполнить транзакцию (см. «Конгресс» для более полного прохода) . Это преднамеренно, поскольку это позволяет кому-то планировать транзакцию или нанимать кого-то другого для ее выполнения.
КОД
pragma solidity ^0.4.16; contract owned { address public owner; function owned() { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner { 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 _extraData){ Token t = Token(_token); require(t.transferFrom(_from, this, _value)); receivedTokens(_from, _value, _token, _extraData); } function () payable { receivedEther(msg.sender, msg.value); } } interface Token { function transferFrom(address _from, address _to, uint256 _value) returns (bool success); } contract TimeLockMultisig is owned, tokenRecipient { Proposal[] public proposals; uint public numProposals; mapping (address => uint) public memberId; Member[] public members; uint minimumTime = 10; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter, string justification); event ProposalExecuted(uint proposalID, int result, uint deadline); event MembershipChanged(address member, bool isMember); struct Proposal { address recipient; uint amount; string description; bool executed; int currentResult; bytes32 proposalHash; uint creationDate; Vote[] votes; mapping (address => bool) voted; } struct Member { address member; string name; uint memberSince; } struct Vote { bool inSupport; address voter; string justification; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyMembers { require(memberId[msg.sender] != 0); _; } /** * Constructor function * * First time setup */ function TimeLockMultisig(address founder, address[] initialMembers, uint minimumAmountOfMinutes) payable { if (founder != 0) owner = founder; if (minimumAmountOfMinutes !=0) minimumTime = minimumAmountOfMinutes; // It’s necessary to add an empty first member addMember(0, ''); // and let's add the founder, to save a step later addMember(owner, 'founder'); changeMembers(initialMembers, true); } /** * Add member * * @param targetMember address to add as a member * @param memberName label to give this member address */ function addMember(address targetMember, string memberName) onlyOwner { uint id; if (memberId[targetMember] == 0) { memberId[targetMember] = members.length; id = members.length++; } else { id = memberId[targetMember]; } members[id] = Member({member: targetMember, memberSince: now, name: memberName}); MembershipChanged(targetMember, true); } /** * Remove member * * @param targetMember the member to remove */ function removeMember(address targetMember) onlyOwner { require(memberId[targetMember] != 0); for (uint i = memberId[targetMember]; i<members.length-1; i++){ members[i] = members[i+1]; } delete members[members.length-1]; members.length--; } /** * Edit existing members * * @param newMembers array of addresses to update * @param canVote new voting value that all the values should be set to */ function changeMembers(address[] newMembers, bool canVote) { for (uint i = 0; i < newMembers.length; i++) { if (canVote) addMember(newMembers[i], ''); else removeMember(newMembers[i]); } } /** * 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 jobDescription, bytes transactionBytecode ) onlyMembers returns (uint proposalID) { proposalID = proposals.length++; Proposal storage p = proposals[proposalID]; p.recipient = beneficiary; p.amount = weiAmount; p.description = jobDescription; p.proposalHash = sha3(beneficiary, weiAmount, transactionBytecode); p.executed = false; p.creationDate = now; ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); numProposals = proposalID+1; vote(proposalID, true, ''); 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 jobDescription, bytes transactionBytecode ) onlyMembers 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 transactionBytecode ) constant returns (bool codeChecksOut) { Proposal storage p = proposals[proposalNumber]; return p.proposalHash == sha3(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 * @param justificationText optional justification text */ function vote( uint proposalNumber, bool supportsProposal, string justificationText ) onlyMembers { Proposal storage p = proposals[proposalNumber]; // Get the proposal require(p.voted[msg.sender] != true); // If has already voted, cancel p.voted[msg.sender] = true; // Set this voter as having voted if (supportsProposal) { // If they support the proposal p.currentResult++; // Increase score } else { // If they don't p.currentResult--; // Decrease the score } // Create a log of this event Voted(proposalNumber, supportsProposal, msg.sender, justificationText); // If you can execute it now, do it if ( now > proposalDeadline(proposalNumber) && p.currentResult > 0 && p.proposalHash == sha3(p.recipient, p.amount, '') && supportsProposal) { executeProposal(proposalNumber, ''); } } function proposalDeadline(uint proposalNumber) constant returns(uint deadline) { Proposal storage p = proposals[proposalNumber]; uint factor = calculateFactor(uint(p.currentResult), (members.length - 1)); return p.creationDate + uint(factor * minimumTime * 1 minutes); } function calculateFactor(uint a, uint b) constant returns (uint factor) { return 2**(20 - (20 * a)/b); } /** * 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 transactionBytecode) { Proposal storage p = proposals[proposalNumber]; require(now >= proposalDeadline(proposalNumber) // If it is past the voting deadline && p.currentResult > 0 // and a minimum quorum has been reached && !p.executed // and it is not currently being executed && checkProposalCode(proposalNumber, p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... p.executed = true; assert(p.recipient.call.value(p.amount)(transactionBytecode)); // Fire Events ProposalExecuted(proposalNumber, p.currentResult, proposalDeadline(proposalNumber)); } }
РАЗВЕРТЫВАНИЕ И ИСПОЛЬЗОВАНИЕ
Разверните этот код, как вы это делали ранее, в этих руководствах. По параметрам развертывания, оставляя минимальное время пустым, по умолчанию будет 30 минут, если вы хотите более быстрое время блокировки, тогда поставьте 1 минуту. После загрузки выполните функции «Добавить участников», чтобы добавить новых членов вашей группы, они могут быть либо другими людьми, которых вы знаете, либо учетными записями на разных компьютерах или хранятся в автономном режиме.
Учетная запись, установленная как «владелец», очень эффективна, так как она может добавлять или удалять участников по своему усмотрению. Поэтому после того, как вы добавили основных участников, мы рекомендуем вам установить «владельца» на другую учетную запись, выполнив функцию « Перенос членства» . Установите это для самого мультисига, если вы хотите, чтобы все добавления или удаления участников были проголосованы, как и любая другая транзакция. Другой вариант - установить это на другой доверенный многосегментный кошелек или, может быть, на 0x000, если вы хотите, чтобы число членов фиксировалось навсегда. Помните, что средства по этому контракту безопасны только как учетная запись «владелец».
Как и в любом из вышеперечисленных DAO, этот контракт может содержать эфир, любые маркеры на основе ethereum и выполнять любой контракт. Чтобы сделать это, проверьте, как выполнять сложные предложения в конгрессе DAO.
ПРЕДОСТЕРЕЖЕНИЯ И УЛУЧШЕНИЯ
Для простоты, голосование против предложения просто считается одним меньше голосов поддержки. Если вы хотите, вы можете поиграть с идеей о том, что отрицательные голоса имеют больший вес, но это означает, что меньшинство членов может иметь эффективное право вето на любую предлагаемую транзакцию!
Как еще вы могли бы улучшить этот контракт?
Вы дошли до конца этого урока, но это только начало отличного приключения. Оглянитесь назад и посмотрите, как много вы достигли: вы создали живого, говорящего робота, свою собственную криптовалюту, собрали средства через беззаботный crowdfunding и использовали его, чтобы начать свою собственную личную демократическую организацию.
ЧТО МОЖЕТ СЛУЧИТЬСЯ ДАЛЬШЕ?
Токены, которые вы по-прежнему контролируете, могут продаваться на децентрализованном обмене или торговаться за товары и услуги для финансирования дальнейшего развития первого контракта и развития организации.
Ваш DAO может иметь собственное имя в регистраторе имен, а затем изменять, где он перенаправляется, чтобы обновить себя, если одобрены владельцы токенов.
Организация могла содержать не только эфиры, но и любую другую монету, созданную на эфируме, включая активы, ценности которых привязаны к биткойну или доллару.
DAO можно запрограммировать, чтобы разрешить предложение с несколькими транзакциями, некоторые из которых запланированы на будущее. Он также может владеть акциями других DAO, что означает, что он может голосовать в более крупной организации или быть частью федерации DAO.
Контракт-токен может быть перепрограммирован для хранения эфира или для хранения других токенов и распространения его на держатели токенов. Это свяжет ценность токена с стоимостью других активов, поэтому выплата дивидендов может быть достигнута путем простого перемещения средств на адрес токена.
Все это означает, что это крошечное общество, которое вы создали, может расти, получать финансирование от третьих сторон, выплачивать текущие зарплаты, иметь какие-либо криптоактивные активы и даже использовать crowdsales для финансирования своей деятельности. Все с полной прозрачностью, полной ответственностью и полным иммунитетом от любого вмешательства человека. Пока сеть живет, контракты будут выполнять точно код, который они были созданы для выполнения, без каких-либо исключений, навсегда.
Итак, каков будет ваш контракт? Будет ли это страна, компания, некоммерческая группа? Что будет делать ваш код?
Это зависит от вас.
Теперь ваша очередь: начать строить то, что вы мечтаете создать в Эфириуме! Может ли ваш бизнес быть расширен, работая в криптографически безопасной, децентрализованной, защищенной от несанкционированного доступа сети?
Посмотрите на множество замечательных проектов *, уже построенных на Ethereum. И поскольку вы станете одним из первых разработчиков в мире, которые могут программировать децентрализованные приложения, некоторым из них может понадобиться ваша помощь.
* Приведенный выше список поддерживается независимой стороной, и Фонд не поддерживает его содержание или какой-либо конкретный проект в этом списке