#Rustスマートコントラクト開発日記(10-3):スプートニクDAOコアコンセプト-提案(Proposal)分析Sputnik-DAOはNEAR Protocolが提供するインフラストラクチャとして、NEARエコシステムを「分散化」方向に力強く推進しています。現在、このプラットフォームは多くのNEARプロジェクトが「分散型」自治コミュニティを設立するのを促進しており、同時に完全で柔軟かつ効率的なコミュニティ意思決定ガバナンスソリューションを提供しています。Sputnikdaov2はSputnik-DAOコミュニティガバナンス投票のためのスマートコントラクトです。本稿では、この契約の核心概念である提案(Proposal)について紹介します。今後の記事では「提案」に関して関連するDAOコミュニティガバナンスモデル(Policy)について説明します。## 1. 提案は(Add Proposal)開始されましたSputnik-DAOコミュニティの各メンバーは、プロジェクトのガバナンスや管理について意見を述べたり、提案を提出したりできます。その後、DAO内で株式を保有するコミュニティメンバーは、その提案を審議し、投票することができます。言い換えれば、Sputnik-DAO内の各メンバーは、他の提案に投票したり、自ら新しい管理提案を発起したりすることで、プロジェクトの今後の方向性に影響を与えることができます。契約レベルでは、DAOコミュニティのメンバーはsputnikdaov2契約が提供するadd_proposal()メソッドを呼び出して新しい提案を開始できます。錆Pub FN add_proposal(&Mut Self, Proposal: ProposalInput) -> U64提案者は、以下の(ProposalInput)提案の詳細を提供する必要があります。- 提案の文字記述(Description)。この情報はSputnik-DAOのホームページのフロントエンドに公開され、コミュニティメンバーが提案の目的と意義を理解するのに役立ちます。- 提案のタイプ(kind)。提案者はプロジェクト管理に対する意見のタイプに基づいて選択する必要があります(例えば、契約の重要な特権関数呼び出しにはFunctionCallタイプを選択し、契約プロジェクトの資金移転にはTransferタイプを選択し、契約のガバナンス権限の制御レベルの設定/変更にはChangePolicyAddOrUpdateRoleタイプを選択するなど)これらのProposalInput情報は、add_proposal()メソッドにパラメータとして渡され、このメソッドは関連する検証と処理をさらに実行し、完全な初期化情報を持つ提案(Proposal)を生成します。最終的にこの提案は唯一のproposal_idにバインドされ、<key, value="">形式でSputnik-DAOコントラクトがグローバルに管理するContract.proposalsマッピングに追加されます(提案プール)。Sputnik-DAOによって定義された提案には、次の完全な属性情報があります。錆pub struct 提案 { パブID:U64、 パブの提案者: AccountId, pub の説明: 文字列、 パブの種類: ProposalKind, 公開ステータス: ProposalStatus, パブvote_period_end:BlockHeight、 pub vote_counts: HashMap<votepolicy, hashmap<accountid,="" balance="">>, パブの投票: HashMap<accountid, vote="">, pub submission_time: タイムスタンプ、}この提案では、descriptionとkind属性の内容は、proposerが提案を作成する際に提供したProposalInput情報から抽出されます。具体的には、そのスマートコントラクトはRust言語のFromトレイトを利用して、ProposalInputからProposalへの型変換を実現しています。この変換プロセスには、より多くの提案の状態情報がバインドされています:- 新たに追加された提案の提案者(proposer)属性は、自動的にadd_proposal()メソッドの呼び出し者、すなわちenv::predecessor_account_id()に設定されます。この属性は真実であり、ユーザーによって制御されません。- 新たに追加された提案の状態(status)はデフォルトでProposalStatus::InProgressに初期化されており、投票段階にあることを示します。- 新しく追加された提案の発起時間(submission_time)は、このブロックのタイムスタンプenv::block_timestamp()に設定されました;- 新しい提案が提出されたときに投票がなかったため、投票の状態(vote_counts、votes)はすべて空のHashMap::default()に初期化されます。注意が必要なのは、Sputnik-DAOには提案押金(proposal_bond)の概念が存在し、その押金は具体的なSputnik-DAOコミュニティガバナンスモデル(Policy)に従って管理されるということです。関連するコードを読むと、契約は提案者がadd_proposal()メソッドを呼び出す際に、一定額のNEARトークンを新しい提案の保証金としてステークすることを要求しています。この保証金は、提案が正常に終了した場合(、コミュニティ投票でProposalStatus::Approved | コミュニティ投票でProposalStatus::Rejected)に賛成した場合に、契約の内部関数internal_return_bonds()を呼び出すことで提案者に返還されます。しかし、BlockSecは以前にその契約コードを解読した際に次のことを発見しました:Sputnik-DAOは、提案のデポジットを処理する際に、各ユーザーの履歴提案デポジット額を個別に維持していません。そして、ユーザーが取引を開始し、契約方法add_proposal()を呼び出して新しい提案を追加する際に、その取引にDAOガバナンス戦略(Policy)によって定義されたpolicy.bounty_bond NEARトークンを超えるデポジットを付加する可能性があります。これは、余分な部分のデポジットが、後続の関数internal_return_bondsが実行される際に提案者に返還されないことを意味します。BlockSecチームがプロジェクト側と迅速に連絡を取った後、最終的にこのIssue#158被确认并及时在PR#160は修正されました。より多くのSputnik-DAO内部で実行される提案に関連する検証と処理の戦略は、後にリリースされる《Rustスマートコントラクト育成日記(10-4) Sputnik DAO::コミュニティガバナンスモデル分析》で詳細に説明されます。## 2. 提案状況(Proposal Status)Sputnik-DAOの任意の標準提案は、以下のような多くの状態を経る可能性があります(新しい提案の状態が初期化されました:InProgress)錆公開列挙型 ProposalStatus { インプログレス、 承認されました、 拒否されました、 削除されました、 期限 切れ 移動しました、 失敗しました、}提案プールの提案の状態変化は、コントラクトの別のメソッドact_proposal()によって駆動されます。Sputnik-DAOのメンバーはact_proposal()メソッドを呼び出して、特定の提案(をidで指定して)以下の操作を実行できます:錆pub enum アクション { AddProposal、 RemoveProposal、 VoteApprove、 VoteReject(投票拒否)、 VoteRemove、 ファイナライズ、 MoveToHub、}典型的,对于処理中の提案に対して、DAOコミュニティのメンバーはact_proposal()を呼び出して具体的な投票操作を実行できます:- Action::VoteApprove: VoteApprove;- Action::VoteReject: 反対のテーブル。 - Action::VoteRemove:この提案は実際的な意味がないと考えられ、削除する必要があります;上記の実装に基づいて、内部で関数update_votes()を呼び出した後、プログラムは自動的にpolicy.proposal_status()を呼び出して投票作業を行います。投票閾値を満たす提案については、提案の状態が相応に変更されます。後:- 提案のステータスが [承認済み] の場合、提案は internal_execute_proposal() を呼び出して実行されます。- プロポーザルが [却下] または [削除済み] の状態の場合、プロポーザルは呼び出されinternal_reject_proposal()後続の終了操作が実行されます。值得一提的是,RejectedとRemoved状態の違いは、最終的にRemoved状態に確定された提案は提案プールから直接削除され、(は罰として)提案者に当初質押したデポジットは返還されないことです。一方、Rejected状態の提案については、その提案は引き続き提案プールに保持され、相応のデポジットが返還されます。! [](https://img-cdn.gateio.im/social/moments-84ee9ca630a4cdcdb0d2eb63450a7cf4)## 3. プロポーザル実施(Execute Proposal)投票の終了後に提案が [承認済み] のステータスと一致する場合、コントラクト メソッドは引き続き内部で呼び出されますact_proposal() internal_execute_ proposal() 関数は、提案に含まれる決定を実行します。Sputnik-DAOがサポートする提案タイプは以下の通りです(大多数の提案はDAOガバナンスモデルの設定更新に関連しています):- 提案の種類::変更設定- ProposalKind::ChangePolicy (提案の種類::変更ポリシー)- 提案の種類::AddMemberToRole- ProposalKind::RemoveMemberFromRole (提案の種類::削除メンバーからロール)- ProposalKind::FunctionCall(プロポーザルカインド::ファンクションコール)- ProposalKind::UpgradeSelf- ProposalKind::UpgradeRemote (英語)- ProposalKind::転送- ProposalKind::セットステーキングコントラクト- ProposalKind::AddBounty(プロポーザルカインド::アッドバウンティ)- 提案の種類::バウンティ完了- ProposalKind::投票- ProposalKind::FactoryInfoUpdate(ファクトリインフォアップデート)- ProposalKind::ChangePolicyAddOrUpdateRole (提案の種類::変更ポリシー追加または更新ロール)- ProposalKind::ChangePolicyRemoveRole (提案の種類::変更ポリシー削除ロール)- ProposalKind::ポリシー変更デフォルト投票ポリシー- ProposalKind::ChangePolicyUpdateParameters (提案の種類::変更ポリシー更新パラメーター)以上の各提案タイプは、関数internal_execute_proposal()において、対応する処理ブランチが実装されています。このセクションでは、2つの典型的な提案タイプの処理プロセスについて詳しく紹介します:- ProposalKind::FunctionCall(プロポーザルカインド::ファンクションコール) - ProposalKind::転送### 3.1 コントラクト関数の実行 提案実行 (ProposalKind::FunctionCall)Function internal_execute_proposal() は、ProposalKind を FunctionCall として一致する提案に対して、次の処理エントリを実装します。錆ProposalKind::FunctionCall { receiver_id, アクション } => { mut promise = Promise::new(receiver_id.clone()); actionsの中のアクションに対して{ 約束 = promise.function_call( action.method_name、 action.argsです。 action.deposit、 action.gas、 ) } promise.into()}FunctionCall 型の提案が add_proposal() メソッドを呼び出すと、提案によって実行される関数操作の(actions)に ProposalInput パラメーターが渡されます。NEAR契約は、1つのPromise内で複数の連続したfunction_callをバインドすることを許可します。したがって、最初の提案者が設定したactions内部には、以下のようなさまざまなActionCallオブジェクトを含めることができます。錆公開構造体 ActionCall { pub method_name: 文字列、 パブ引数:Vec<u8>、 パブの預金:残高、 パブガス:ガス、}各ActionCallに対応するコントラクトのメソッド名とメソッドパラメータなどを指定できます。以上のように、Sputnik-DAOはPromise Batch Actionsの形式を採用して、スマートコントラクト関数実行タイプの提案を実行しました。! [](https://img-cdn.gateio.im/social/moments-427716593b21fa32b47855ceb5e101fc)### 3.2 契約資金移動提案の実施 (ProposalKind::Transfer)デプロイされたNEARスマートコントラクトプロジェクトが長期間稼働した後、コントラクトアカウント自体は多くのFungible Token(、ネイティブNEARトークンやその他のNEP-141標準に準拠したトークン)を蓄積している可能性があります。この時、Sputnik-DAOコミュニティのメンバーは、契約資金移転提案を提出することで、これらのトークンを指定されたreceiver_idアカウントに集めることができます。同様にinternal_execute_proposal()対応する処理エントリは、Proposal Kind を Transfer として一致する提案に対して実装されます。錆ProposalKind::Transfer { token_id、receiver_id、金額 } => { self.internal_payout(token_id、receiver_id、amount) into()}この処理のブランチの下層は、internal_payout()関数を呼び出し、異なるタイプのFungible Tokenおよび異なるタイプのreceiver_id(EOAまたはコントラクトアカウント)への送金操作を実現します。! [](https://img-cdn.gateio.im/social/moments-ef0b959c42e1f5fc6263cd4a86fd078e)## 4. まとめと予告この記事では、Sputnik DAOのスマートコントラクトの核心概念である提案(Proposal)について紹介し、また、Sputnik DAOで新しい提案を作成し投票実行する方法、およびその関連提案の基本的な状態(Status)の変化ルールについて簡単に説明しました。今後のRustスマートコントラクト育成日記では、提案に基づいてSputnik-DAOにおけるガバナンスモデル(Policy)の実装と構成について、より詳細な説明を展開します。お楽しみに!! [](https://img-cdn.gateio.im/social/moments-eb73d5e15f6161f0a4b442cd4b99a91e)</u8></accountid,></votepolicy,></key,>
NEARエコシステムの新たな章: Sputnik DAO提案メカニズムの詳細解析
#Rustスマートコントラクト開発日記(10-3):スプートニクDAOコアコンセプト-提案(Proposal)分析
Sputnik-DAOはNEAR Protocolが提供するインフラストラクチャとして、NEARエコシステムを「分散化」方向に力強く推進しています。現在、このプラットフォームは多くのNEARプロジェクトが「分散型」自治コミュニティを設立するのを促進しており、同時に完全で柔軟かつ効率的なコミュニティ意思決定ガバナンスソリューションを提供しています。
Sputnikdaov2はSputnik-DAOコミュニティガバナンス投票のためのスマートコントラクトです。本稿では、この契約の核心概念である提案(Proposal)について紹介します。今後の記事では「提案」に関して関連するDAOコミュニティガバナンスモデル(Policy)について説明します。
1. 提案は(Add Proposal)開始されました
Sputnik-DAOコミュニティの各メンバーは、プロジェクトのガバナンスや管理について意見を述べたり、提案を提出したりできます。その後、DAO内で株式を保有するコミュニティメンバーは、その提案を審議し、投票することができます。言い換えれば、Sputnik-DAO内の各メンバーは、他の提案に投票したり、自ら新しい管理提案を発起したりすることで、プロジェクトの今後の方向性に影響を与えることができます。
契約レベルでは、DAOコミュニティのメンバーはsputnikdaov2契約が提供するadd_proposal()メソッドを呼び出して新しい提案を開始できます。
錆 Pub FN add_proposal(&Mut Self, Proposal: ProposalInput) -> U64
提案者は、以下の(ProposalInput)提案の詳細を提供する必要があります。
提案の文字記述(Description)。この情報はSputnik-DAOのホームページのフロントエンドに公開され、コミュニティメンバーが提案の目的と意義を理解するのに役立ちます。
提案のタイプ(kind)。提案者はプロジェクト管理に対する意見のタイプに基づいて選択する必要があります(例えば、契約の重要な特権関数呼び出しにはFunctionCallタイプを選択し、契約プロジェクトの資金移転にはTransferタイプを選択し、契約のガバナンス権限の制御レベルの設定/変更にはChangePolicyAddOrUpdateRoleタイプを選択するなど)
これらのProposalInput情報は、add_proposal()メソッドにパラメータとして渡され、このメソッドは関連する検証と処理をさらに実行し、完全な初期化情報を持つ提案(Proposal)を生成します。最終的にこの提案は唯一のproposal_idにバインドされ、<key, value="">形式でSputnik-DAOコントラクトがグローバルに管理するContract.proposalsマッピングに追加されます(提案プール)。
Sputnik-DAOによって定義された提案には、次の完全な属性情報があります。
錆 pub struct 提案 { パブID:U64、 パブの提案者: AccountId, pub の説明: 文字列、 パブの種類: ProposalKind, 公開ステータス: ProposalStatus, パブvote_period_end:BlockHeight、 pub vote_counts: HashMap<votepolicy, hashmap<accountid,="" balance="">>, パブの投票: HashMap<accountid, vote="">, pub submission_time: タイムスタンプ、 }
この提案では、descriptionとkind属性の内容は、proposerが提案を作成する際に提供したProposalInput情報から抽出されます。具体的には、そのスマートコントラクトはRust言語のFromトレイトを利用して、ProposalInputからProposalへの型変換を実現しています。
この変換プロセスには、より多くの提案の状態情報がバインドされています:
新たに追加された提案の提案者(proposer)属性は、自動的にadd_proposal()メソッドの呼び出し者、すなわちenv::predecessor_account_id()に設定されます。この属性は真実であり、ユーザーによって制御されません。
新たに追加された提案の状態(status)はデフォルトでProposalStatus::InProgressに初期化されており、投票段階にあることを示します。
新しく追加された提案の発起時間(submission_time)は、このブロックのタイムスタンプenv::block_timestamp()に設定されました;
新しい提案が提出されたときに投票がなかったため、投票の状態(vote_counts、votes)はすべて空のHashMap::default()に初期化されます。
注意が必要なのは、Sputnik-DAOには提案押金(proposal_bond)の概念が存在し、その押金は具体的なSputnik-DAOコミュニティガバナンスモデル(Policy)に従って管理されるということです。
関連するコードを読むと、契約は提案者がadd_proposal()メソッドを呼び出す際に、一定額のNEARトークンを新しい提案の保証金としてステークすることを要求しています。この保証金は、提案が正常に終了した場合(、コミュニティ投票でProposalStatus::Approved | コミュニティ投票でProposalStatus::Rejected)に賛成した場合に、契約の内部関数internal_return_bonds()を呼び出すことで提案者に返還されます。
しかし、BlockSecは以前にその契約コードを解読した際に次のことを発見しました:
Sputnik-DAOは、提案のデポジットを処理する際に、各ユーザーの履歴提案デポジット額を個別に維持していません。そして、ユーザーが取引を開始し、契約方法add_proposal()を呼び出して新しい提案を追加する際に、その取引にDAOガバナンス戦略(Policy)によって定義されたpolicy.bounty_bond NEARトークンを超えるデポジットを付加する可能性があります。これは、余分な部分のデポジットが、後続の関数internal_return_bondsが実行される際に提案者に返還されないことを意味します。
BlockSecチームがプロジェクト側と迅速に連絡を取った後、最終的にこのIssue#158被确认并及时在PR#160は修正されました。
より多くのSputnik-DAO内部で実行される提案に関連する検証と処理の戦略は、後にリリースされる《Rustスマートコントラクト育成日記(10-4) Sputnik DAO::コミュニティガバナンスモデル分析》で詳細に説明されます。
2. 提案状況(Proposal Status)
Sputnik-DAOの任意の標準提案は、以下のような多くの状態を経る可能性があります(新しい提案の状態が初期化されました:InProgress)
錆 公開列挙型 ProposalStatus { インプログレス、 承認されました、 拒否されました、 削除されました、 期限 切れ 移動しました、 失敗しました、 }
提案プールの提案の状態変化は、コントラクトの別のメソッドact_proposal()によって駆動されます。
Sputnik-DAOのメンバーはact_proposal()メソッドを呼び出して、特定の提案(をidで指定して)以下の操作を実行できます:
錆 pub enum アクション { AddProposal、 RemoveProposal、 VoteApprove、 VoteReject(投票拒否)、 VoteRemove、 ファイナライズ、 MoveToHub、 }
典型的,对于処理中の提案に対して、DAOコミュニティのメンバーはact_proposal()を呼び出して具体的な投票操作を実行できます:
上記の実装に基づいて、内部で関数update_votes()を呼び出した後、プログラムは自動的にpolicy.proposal_status()を呼び出して投票作業を行います。投票閾値を満たす提案については、提案の状態が相応に変更されます。
後:
提案のステータスが [承認済み] の場合、提案は internal_execute_proposal() を呼び出して実行されます。
プロポーザルが [却下] または [削除済み] の状態の場合、プロポーザルは呼び出されinternal_reject_proposal()後続の終了操作が実行されます。
值得一提的是,RejectedとRemoved状態の違いは、最終的にRemoved状態に確定された提案は提案プールから直接削除され、(は罰として)提案者に当初質押したデポジットは返還されないことです。一方、Rejected状態の提案については、その提案は引き続き提案プールに保持され、相応のデポジットが返還されます。
!
3. プロポーザル実施(Execute Proposal)
投票の終了後に提案が [承認済み] のステータスと一致する場合、コントラクト メソッドは引き続き内部で呼び出されますact_proposal() internal_execute_ proposal() 関数は、提案に含まれる決定を実行します。
Sputnik-DAOがサポートする提案タイプは以下の通りです(大多数の提案はDAOガバナンスモデルの設定更新に関連しています):
以上の各提案タイプは、関数internal_execute_proposal()において、対応する処理ブランチが実装されています。 このセクションでは、2つの典型的な提案タイプの処理プロセスについて詳しく紹介します:
3.1 コントラクト関数の実行 提案実行 (ProposalKind::FunctionCall)
Function internal_execute_proposal() は、ProposalKind を FunctionCall として一致する提案に対して、次の処理エントリを実装します。
錆 ProposalKind::FunctionCall { receiver_id, アクション } => { mut promise = Promise::new(receiver_id.clone()); actionsの中のアクションに対して{ 約束 = promise.function_call( action.method_name、 action.argsです。 action.deposit、 action.gas、 ) } promise.into() }
FunctionCall 型の提案が add_proposal() メソッドを呼び出すと、提案によって実行される関数操作の(actions)に ProposalInput パラメーターが渡されます。
NEAR契約は、1つのPromise内で複数の連続したfunction_callをバインドすることを許可します。したがって、最初の提案者が設定したactions内部には、以下のようなさまざまなActionCallオブジェクトを含めることができます。
錆 公開構造体 ActionCall { pub method_name: 文字列、 パブ引数:Vec、 パブの預金:残高、 パブガス:ガス、 }
各ActionCallに対応するコントラクトのメソッド名とメソッドパラメータなどを指定できます。
以上のように、Sputnik-DAOはPromise Batch Actionsの形式を採用して、スマートコントラクト関数実行タイプの提案を実行しました。
!
3.2 契約資金移動提案の実施 (ProposalKind::Transfer)
デプロイされたNEARスマートコントラクトプロジェクトが長期間稼働した後、コントラクトアカウント自体は多くのFungible Token(、ネイティブNEARトークンやその他のNEP-141標準に準拠したトークン)を蓄積している可能性があります。
この時、Sputnik-DAOコミュニティのメンバーは、契約資金移転提案を提出することで、これらのトークンを指定されたreceiver_idアカウントに集めることができます。
同様にinternal_execute_proposal()対応する処理エントリは、Proposal Kind を Transfer として一致する提案に対して実装されます。
錆 ProposalKind::Transfer { token_id、receiver_id、金額 } => { self.internal_payout(token_id、receiver_id、amount) into() }
この処理のブランチの下層は、internal_payout()関数を呼び出し、異なるタイプのFungible Tokenおよび異なるタイプのreceiver_id(EOAまたはコントラクトアカウント)への送金操作を実現します。
!
4. まとめと予告
この記事では、Sputnik DAOのスマートコントラクトの核心概念である提案(Proposal)について紹介し、また、Sputnik DAOで新しい提案を作成し投票実行する方法、およびその関連提案の基本的な状態(Status)の変化ルールについて簡単に説明しました。
今後のRustスマートコントラクト育成日記では、提案に基づいてSputnik-DAOにおけるガバナンスモデル(Policy)の実装と構成について、より詳細な説明を展開します。お楽しみに!
! </accountid,></votepolicy,></key,>