コアポイント
これらに混乱している場合、完全なソースコードはコードベースに記載されています。
投票と提案投票に提案と投票を使用します。 2つの新しい構造が必要です
提案には、人々が1つの提案で2回投票するのを防ぐための有権者マップと、自明であるべき他のメタデータが含まれます。投票は、はいまたは反対の投票であり、投票者の理由と投票権、つまりこの提案に投票するために使用したいトークンの数を覚えています。また、提案の数をカウントするためのカウンターをどこかに保存できるように、提案の配列も追加しました。投票機能から始めて、サポート機能を構築しましょう。
<code>struct Proposal { string description; bool executed; int256 currentResult; uint8 typeFlag; // 1 = delete bytes32 target; // 提案目标的ID。例如,标志1,目标XXXXXX(哈希)表示删除submissions[hash]的提案 uint256 creationDate; uint256 deadline; mapping (address => bool) voters; Vote[] votes; address submitter; } Proposal[] public proposals; uint256 proposalCount = 0; event ProposalAdded(uint256 id, uint8 typeFlag, bytes32 hash, string description, address submitter); event ProposalExecuted(uint256 id); event Voted(address voter, bool vote, uint256 power, string justification); struct Vote { bool inSupport; address voter; string justification; uint256 power; }</code>
関数修飾子に注意:この修飾子を契約に追加することにより、将来の関数に追加し、トークンホルダーのみが関数を実行できることを確認できます。これは再利用可能なセキュリティチェックです!
投票機能は、投票権が肯定的であり、投票者は実際に投票するのに十分なトークンを持っているなど、いくつかの衛生チェックを実行します。次に、ストレージから提案を受け取り、それが期限切れも実行されていないことを確認します。完成した提案に投票することは意味がありません。また、この人がまだ投票していないことを確認する必要があります。投票権を変更することはできますが、これにより、土壇場で投票を撤回するなど、いくつかの抜け穴に直面することができます。おそらく将来のバージョンの候補者ですか?
<code>modifier tokenHoldersOnly() { require(token.balanceOf(msg.sender) >= 10**token.decimals()); _; } function vote(uint256 _proposalId, bool _vote, string _description, uint256 _votePower) tokenHoldersOnly public returns (int256) { require(_votePower > 0, "At least some power must be given to the vote."); require(uint256(_votePower) <= token.balanceOf(msg.sender), "Vote power exceeds token balance."); Proposal storage p = proposals[_proposalId]; require(p.executed == false, "Proposal must not have been executed already."); require(p.deadline > now, "Proposal must not have expired."); require(p.voters[msg.sender] == false, "User must not have already voted."); uint256 voteid = p.votes.length++; Vote storage pvote = p.votes[voteid]; pvote.inSupport = _vote; pvote.justification = _description; pvote.voter = msg.sender; pvote.power = _votePower; p.voters[msg.sender] = true; p.currentResult = (_vote) ? p.currentResult + int256(_votePower) : p.currentResult - int256(_votePower); token.increaseLockedAmount(msg.sender, _votePower); emit Voted(msg.sender, _vote, _votePower, _description); return p.currentResult; }</code>
このロジックは、ユーザーのロックされたトークンの数を増やします。この関数は、トークン契約の所有者によってのみ実行できます(今回はDAOになることが期待されています)、ユーザーがアカウントに登録されたロックされた金額以上を送信することを防ぎます。このロックは、提案が失敗した後、または実行された後にリリースされます。
削除エントリを提示するための関数を書きましょう。
削除およびブラックリストへの投票
このシリーズの最初の部分では、3つのエントリ削除関数を計画しました。
私たちが今これをどのように行うか見てみましょう。最初に、関数を削除します:
<code>struct Proposal { string description; bool executed; int256 currentResult; uint8 typeFlag; // 1 = delete bytes32 target; // 提案目标的ID。例如,标志1,目标XXXXXX(哈希)表示删除submissions[hash]的提案 uint256 creationDate; uint256 deadline; mapping (address => bool) voters; Vote[] votes; address submitter; } Proposal[] public proposals; uint256 proposalCount = 0; event ProposalAdded(uint256 id, uint8 typeFlag, bytes32 hash, string description, address submitter); event ProposalExecuted(uint256 id); event Voted(address voter, bool vote, uint256 power, string justification); struct Vote { bool inSupport; address voter; string justification; uint256 power; }</code>
次に、提案を実装する方法を見てみましょう。強制力を持たせるには、提案には十分な投票が必要であり、その期限を超えなければなりません。実行関数は、実行される提案のIDを受け入れます。 EVMにすべての保留中の提案を一度に実行させる簡単な方法はありません。実行する保留中の提案が多すぎる可能性があり、DAOのデータに大幅な変更を加える可能性があります。これは、イーサリアムブロックのガス制限を超えてトランザクションの障害をもたらす可能性があります。明確に定義されたルールに準拠している人が呼び出すことができる手動実行機能を構築する方がはるかに簡単です。そうすれば、コミュニティは実行する必要がある提案に集中できます。
<code>modifier tokenHoldersOnly() { require(token.balanceOf(msg.sender) >= 10**token.decimals()); _; } function vote(uint256 _proposalId, bool _vote, string _description, uint256 _votePower) tokenHoldersOnly public returns (int256) { require(_votePower > 0, "At least some power must be given to the vote."); require(uint256(_votePower) <= token.balanceOf(msg.sender), "Vote power exceeds token balance."); Proposal storage p = proposals[_proposalId]; require(p.executed == false, "Proposal must not have been executed already."); require(p.deadline > now, "Proposal must not have expired."); require(p.voters[msg.sender] == false, "User must not have already voted."); uint256 voteid = p.votes.length++; Vote storage pvote = p.votes[voteid]; pvote.inSupport = _vote; pvote.justification = _description; pvote.voter = msg.sender; pvote.power = _votePower; p.voters[msg.sender] = true; p.currentResult = (_vote) ? p.currentResult + int256(_votePower) : p.currentResult - int256(_votePower); token.increaseLockedAmount(msg.sender, _votePower); emit Voted(msg.sender, _vote, _votePower, _description); return p.currentResult; }</code>
同じ方法を使用して他のタイプの提案を追加できますが、最初に、deletesubmission関数を更新して、アカウントに5つ以上の削除を持つユーザーを禁止しましょう。これは、コミュニティが異議を唱えたコンテンツを常に送信していることを意味します。 deleteSubmission関数を更新しましょう:
<code>struct Proposal { string description; bool executed; int256 currentResult; uint8 typeFlag; // 1 = delete bytes32 target; // 提案目标的ID。例如,标志1,目标XXXXXX(哈希)表示删除submissions[hash]的提案 uint256 creationDate; uint256 deadline; mapping (address => bool) voters; Vote[] votes; address submitter; } Proposal[] public proposals; uint256 proposalCount = 0; event ProposalAdded(uint256 id, uint8 typeFlag, bytes32 hash, string description, address submitter); event ProposalExecuted(uint256 id); event Voted(address voter, bool vote, uint256 power, string justification); struct Vote { bool inSupport; address voter; string justification; uint256 power; }</code>
これはより良いです。自動的にブラックリストに登録され、5回削除されました。ブラックリストに登録されたアドレスに償還の機会を与えないことは不公平です。また、ブラックリスト機能自体を定義する必要があります。これらの両方を実行し、たとえば0.05エーテルにブラックリストをキャンセルする料金を設定しましょう。
<code>modifier tokenHoldersOnly() { require(token.balanceOf(msg.sender) >= 10**token.decimals()); _; } function vote(uint256 _proposalId, bool _vote, string _description, uint256 _votePower) tokenHoldersOnly public returns (int256) { require(_votePower > 0, "At least some power must be given to the vote."); require(uint256(_votePower) <= token.balanceOf(msg.sender), "Vote power exceeds token balance."); Proposal storage p = proposals[_proposalId]; require(p.executed == false, "Proposal must not have been executed already."); require(p.deadline > now, "Proposal must not have expired."); require(p.voters[msg.sender] == false, "User must not have already voted."); uint256 voteid = p.votes.length++; Vote storage pvote = p.votes[voteid]; pvote.inSupport = _vote; pvote.justification = _description; pvote.voter = msg.sender; pvote.power = _votePower; p.voters[msg.sender] = true; p.currentResult = (_vote) ? p.currentResult + int256(_votePower) : p.currentResult - int256(_votePower); token.increaseLockedAmount(msg.sender, _votePower); emit Voted(msg.sender, _vote, _votePower, _description); return p.currentResult; }</code>
ブラックリストのアカウントのトークンは、ブラックリストをキャンセルするために料金を送信するまでロックされることに注意してください。
その他の種類の投票
上記の機能のインスピレーションに基づいて、他の提案を書いてみてください。ネタバレについては、プロジェクトのGitHubコードベースをチェックして、そこから最終コードをコピーしてください。簡潔にするために、DAOにまだ持っている他の機能に進みましょう。
章の終わり
ストーリーの時間または章の制限に達したら、ストーリーを終了する時が来ました。日付の後、誰でも最終関数を呼び出すことができます。これにより、配当が撤回されます。まず、新しいStoryDaoのプロパティとイベントが必要です:
<code>modifier memberOnly() { require(whitelist[msg.sender]); require(!blacklist[msg.sender]); _; } function proposeDeletion(bytes32 _hash, string _description) memberOnly public { require(submissionExists(_hash), "Submission must exist to be deletable"); uint256 proposalId = proposals.length++; Proposal storage p = proposals[proposalId]; p.description = _description; p.executed = false; p.creationDate = now; p.submitter = msg.sender; p.typeFlag = 1; p.target = _hash; p.deadline = now + 2 days; emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender); proposalCount = proposalId + 1; } function proposeDeletionUrgent(bytes32 _hash, string _description) onlyOwner public { require(submissionExists(_hash), "Submission must exist to be deletable"); uint256 proposalId = proposals.length++; Proposal storage p = proposals[proposalId]; p.description = _description; p.executed = false; p.creationDate = now; p.submitter = msg.sender; p.typeFlag = 1; p.target = _hash; p.deadline = now + 12 hours; emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender); proposalCount = proposalId + 1; } function proposeDeletionUrgentImage(bytes32 _hash, string _description) onlyOwner public { require(submissions[_hash].image == true, "Submission must be existing image"); uint256 proposalId = proposals.length++; Proposal storage p = proposals[proposalId]; p.description = _description; p.executed = false; p.creationDate = now; p.submitter = msg.sender; p.typeFlag = 1; p.target = _hash; p.deadline = now + 4 hours; emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender); proposalCount = proposalId + 1; }</code>
次に、関数を構築しましょう:
<code>function executeProposal(uint256 _id) public { Proposal storage p = proposals[_id]; require(now >= p.deadline && !p.executed); if (p.typeFlag == 1 && p.currentResult > 0) { assert(deleteSubmission(p.target)); } uint256 len = p.votes.length; for (uint i = 0; i < len; i++) { token.decreaseLockedAmount(p.votes[i].voter, p.votes[i].power); } p.executed = true; emit ProposalExecuted(_id); }</code>
シンプル:収集された料金を所有者に送信してイベントを送信した後、ストーリーを無効にします。しかし、実際には、これはDAOの全体的な状況を実際に変えません。他の機能はその終わりに反応しません。それでは、別の修飾子を構築しましょう:
<code>function deleteSubmission(bytes32 hash) internal returns (bool) { require(submissionExists(hash), "Submission must exist to be deletable."); Submission storage sub = submissions[hash]; sub.exists = false; deletions[submissions[hash].submitter] += 1; if (deletions[submissions[hash].submitter] >= 5) { blacklistAddress(submissions[hash].submitter); } emit SubmissionDeleted( sub.index, sub.content, sub.image, sub.submitter ); nonDeletedSubmissions -= 1; return true; }</code>
DAOにまだ残っているトークンがある場合は、それらを取り戻してそれらのトークンの所有権を取って、後で別のストーリーに使用できるようにしましょう。
<code>function blacklistAddress(address _offender) internal { require(blacklist[_offender] == false, "Can't blacklist a blacklisted user :/"); blacklist[_offender] == true; token.increaseLockedAmount(_offender, token.getUnlockedAmount(_offender)); emit Blacklisted(_offender, true); } function unblacklistMe() payable public { unblacklistAddress(msg.sender); } function unblacklistAddress(address _offender) payable public { require(msg.value >= 0.05 ether, "Unblacklisting fee"); require(blacklist[_offender] == true, "Can't unblacklist a non-blacklisted user :/"); require(notVoting(_offender), "Offender must not be involved in a vote."); withdrawableByOwner = withdrawableByOwner.add(msg.value); blacklist[_offender] = false; token.decreaseLockedAmount(_offender, token.balanceOf(_offender)); emit Blacklisted(_offender, false); } function notVoting(address _voter) internal view returns (bool) { for (uint256 i = 0; i < proposals.length; i++) { if (proposals[i].executed == false && proposals[i].voters[_voter] == true) { return false; } } return true; }</code>
lockmytokens関数は、特定のユーザーがロックする可能性のあるすべてのロックされたトークンのロックを解除するために使用されます。これは発生しないでください。この機能は、多くのテストで削除する必要があります。
<code>bool public active = true; event StoryEnded();</code>
配当分布と撤回
浸漬 ストーリーが終了したので、提出に対して請求される料金はすべてのトークン保有者に割り当てる必要があります。ホワイトリストを再利用して、すでに料金を撤回したすべての人をマークすることができます。
これらの配当が特定の時間制限内に撤回されない場合、所有者は残りの部分を取得できます。宿題として、この展開されたスマートコントラクトを再利用し、データをクリアし、プールにトークンを保持し、再配置せずにここから別の章を再起動することがどれほど簡単か困難であるかを検討してください。これを自分で行ってみて、このシリーズがカバーする将来の更新のためにコードベースに従ってください!また、追加のインセンティブを検討してください。アカウント内のトークンの数は、ストーリーの終わりから受け取る配当に影響を与える可能性がありますか?あなたの想像力は無限です!
<code>function endStory() storyActive external { withdrawToOwner(); active = false; emit StoryEnded(); }</code>
<code>modifier storyActive() { require(active == true); _; }</code>
私たちの契約が非常に大きいことを考えると、それを展開および/またはテストすることは、イーサリアムブロックのガス制限を超える可能性があります。これは、大規模なアプリケーションがEthereum Networkへの展開を制限するものです。とにかくそれを展開するには、Truffle.jsファイルを変更して最適化されたSOLC設定を次のように含めることにより、コンパイル中にコードオプティマイザーを使用してみてください。
<code>struct Proposal { string description; bool executed; int256 currentResult; uint8 typeFlag; // 1 = delete bytes32 target; // 提案目标的ID。例如,标志1,目标XXXXXX(哈希)表示删除submissions[hash]的提案 uint256 creationDate; uint256 deadline; mapping (address => bool) voters; Vote[] votes; address submitter; } Proposal[] public proposals; uint256 proposalCount = 0; event ProposalAdded(uint256 id, uint8 typeFlag, bytes32 hash, string description, address submitter); event ProposalExecuted(uint256 id); event Voted(address voter, bool vote, uint256 power, string justification); struct Vote { bool inSupport; address voter; string justification; uint256 power; }</code>
結論
これで詳細なDAO開発が終了しますが、コースはまだ終わっていません!このストーリーのためにUIを構築して展開する必要があります。幸いなことに、バックエンドがブロックチェーンで完全にホストされているため、フロントエンドの構築ははるかに簡単です。このシリーズの最後から2番目の部分でこれを見てみましょう。
イーサリアムダップの構築とカスタムトークンでの投票に関するよくある質問
ブロックチェーン投票は実際にどのように機能しますか?DAO投票メカニズムは何ですか?
ガバナンスはセキュリティトークンでどのように機能しますか?
DAOガバナンスをセットアップする方法は?
DAOガバナンストークンを保持するリスクは何ですか?
ブロックチェーン投票は、透明性、セキュリティ、不変性など、さまざまな利点を提供します。投票はブロックチェーンのトランザクションとして記録され、透明で検証可能になります。また、ブロックチェーンの分散型の性質により、単一の当事者が投票プロセスを操作することも困難になります。
Ethereum DAPPでの投票用のカスタムトークンの作成には、イーサリアムブロックチェーンにスマートコントラクトを作成および展開することが含まれます。この契約は、その名前、シンボル、総供給など、トークンの属性を定義します。契約が展開されると、トークンをユーザーに配布し、ユーザーを使用してDAPPの提案に投票できます。
ブロックチェーンを使用して投票することの利点は何ですか?
ブロックチェーン投票における有権者の匿名性を確保する方法は?
ブロックチェーン投票における有権者の匿名性は、ブロックチェーントランザクションの透明な性質のために困難になる可能性があるためです。ただし、ゼロ知識証明などのテクニックを使用して、有権者の身元を明らかにすることなく、有権者の有効性を検証できます。
ブロックチェーン投票の実装は、技術的な複雑さ、規制の不確実性、潜在的なセキュリティリスクのために困難な場合があります。ユーザーは投票プロセスに参加するためにブロックチェーンテクノロジーに精通する必要があり、規制当局はブロックチェーン投票システムの正当性とセキュリティについて懸念を表明する場合があります。
DAOガバナンストークンに関連するリスクの緩和には、DAOの慎重な管理、徹底的なセキュリティ対策、常に規制の発展に注目することが含まれます。また、ポートフォリオを多様化し、余裕がある以上に投資しないことも重要です。
以上がビルディングイーサリアムダップ:カスタムトークンでの投票の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。