版本比较

密钥

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

...

代码块
languagerust
// Dao.move
module Dao {
  use DaoSpace;

  struct DaoSignerDelegate {
    cap: SignerCapability, // 托管的签名(这里一旦放进来,就没法再还回去了,且只有一份,没想好是放在space还是放在这里,暂时放这里)
  }
  
  struct Dao {
    voting_delay: u128, // The delay between when a proposal is created, and when the voting starts. A voting_delay of 0 means that as soon as the proposal is created, anyone can vote. A voting_delay of 3600 means that voting will start 3600 seconds after the proposal is created.
    voting_duration: u128, // The duration of the voting period, i.e. how long people will be able to vote for a proposal.
    name: vector<u8>, //人类可识别的名称标识,是否需要?
    creator: address,
    meta_data: vector<u8>, // 空间元数据,编码后存储在链上(可考虑存在链下)
    cap: SpaceCapability
  }
  
  struct/// DaoProposalProposal {data struct.
  /// cap:是否需要通过 ProposalCapabilityDaoT 来区分 Proposal,如果不需要,则需要把 }DAO id 记录在 proposal 
  /// struct DaoProposalSetAction {的泛型无法消除
  struct Proposal<phantom proposal_setDaoT: HashSet<u64store, DaoProposal>Action: store> has }key {
    //初始化过程中分配给 creator  cap /// id structof RootCapability has key{}
the proposal
    struct DaoMemberMeta<phontem DaoT> has copy{
    user id: addressu64,
  }      //这里是否需要 DaoT 来区分不同的 Dao?
  //如果不区分的话,同一个用户加入多个 Daocreator 的情况,当前的of IdentifierNFTthe 无法表达proposal
  struct DaoMemberBody<DaoT>{     sbtproposer: Token<DaoT>address,
  }      /// 执行策略:转账when voting begins.
struct ExecutionTransferStrategy<phontem TokenT> {     amountstart_time: u128u64,
    to: address,   }/// when voting ends.
  // 直接创建 Dao   public fun new_dao(end_time: u64,
     creator: &signer,  /// count of voters who voting_delay: u64, 
agree with the proposal
    voting_period: u64,    for_votes: u128,
   min_action_delay: u64) {   /// count of //这里 Account 需要提供一个新的方法,允许用一个账号去创建另外一个 Delegate 账号。voters who're against the proposal
     let signer_cap = Account::create_delegate_account(&signer);against_votes: u128,
     let dao_signer = Account::create_signer_with_cap(&siger_cap);
     //下面面的操作切换到 dao_signer 身份进行操作
  /// executable after this time.
   let dao = Dao{..};  eta: u64,
  move_to(&dao_signer, dao);      /// 托管after Daohow 账号的long, SignerCapabilitythe 到该合约agreed proposal can be executed.
 move_to(&dao_signer, DaoSignerDelegate{cap: signer_cap});     action_delay: u64,
     issue_member_nft(creator,&dao_signer);   /// how many votes to reach to make the //初始化 dao 的时候无法一次性完成,需要先把 cap 都存到 creator 账号下proposal pass.
        quorum_votes: u128,
//然后按照 plugin 的方式逐步初始化   }   
  /// proposal 将一个账号直接升级为action.
Dao, sender 会变为 Dao 账号   public fun upgrade_to_dao(sender:signer, ...) {action: Option::Option<Action>,
        //基本逻辑同上,省去了创建账号的流程在原来的 Proposal 字段上新增两个字段
    }    block_num: u64, // 这里有个难题是快照高度
 TokenT 从哪里来。   // 一种方法是先生成一个账号,部署合约,然后升级为 dao root_hash: vector<u8> // 快照根hash
另外一种方法是通过 Dao 的合约升级方式进行部署合约}
  fun
issue_member_nft<DaoT>(creator: &signer, dao_signer: &signer){ //Set 上也需要用泛型来区分   
  Token::register<DaoT>(dao_signer);
 struct DaoProposalSet {
   let basemeta = NFT::new_meta_with_image();proposal_set: HashSet<u64, DaoProposal>
  }
  NFT::register_nft_v2<DaoMemberMeta<DaoT>>(dao_signer,
basemeta);  //初始化过程中分配给 creator  cap
let creator_address = Signer::address_of(creator);
 struct RootCapability has key{}
  
//  issuestruct 第一个DaoMemberMeta<phontem NFTDaoT> has creatorcopy{
    user: letaddress,
meta = DaoMemberMeta<DaoT>{}
  
  //这里是否需要 DaoT 来区分不同的 user:Dao?
creator;  //如果不区分的话,同一个用户加入多个 Dao 的情况,当前的 IdentifierNFT };无法表达
  struct DaoMemberBody<DaoT>{
 //如何初始化 creator  sbt?sbt: Token<DaoT>,
  }
 let sbt
= Token::zero<DaoT>();
  // 执行策略:转账
  struct letExecutionTransferStrategy<phontem body =TokenT> DaoMemberBody<DaoT>{
    amount:  sbtu128,
    to: }address,
  }
  let
nft = NFT::mint(basemeta, meta, body); // 直接创建 Dao
  public   IdentifierNFT::accept<DaoMemberMeta<DaoT>,DaoMemberBody<DaoT>>(creator);fun new_dao(
     IdentifierNFT::grant(dao_creator: &signer, creator);
     voting_delay: u64, 
}     voting_period: u64, 
 // 参与投票方/创建投票方注册到space   public fun register_to_space(
 min_action_delay: u64) {
    signer: &signer,  //这里 Account 需要提供一个新的方法,允许用一个账号去创建另外一个 Delegate 账号。
      broker: address) {let signer_cap = Account::create_delegate_account(&signer);
     let dao_signer // 创建对应的NFT= Account::create_signer_with_cap(&siger_cap);
  }    // 参与投票方/创建投票方注册到space
下面面的操作切换到 dao_signer 身份进行操作
 // 方便再上一层的DAO调用   publiclet fun regiter_to_space_get_nft(
dao = Dao{..};
     signer: &move_to(&dao_signer, dao);
      broker: address) : Option::Option<NFT<DaoSpace::NFTMeta, DaoSpace::NFTData>> {
  }// 托管 Dao 账号的 SignerCapability 到该合约
     move_to(&dao_signer, DaoSignerDelegate{cap: signer_cap});
     //
 根据 space_broker 来创建对应的proposal   public fun create_proposal<ExecutionStrategy>(issue_member_nft(creator,&dao_signer);
     
   signer: &signer, //初始化 创建者dao 的时候无法一次性完成,需要先把 cap 都存到 creator 账号下
   space_broker: u64, //然后按照 spaceplugin 代理人的方式逐步初始化
  }
  
block_num: u64, // 快照高度将一个账号直接升级为 Dao, sender 会变为 Dao 账号
 root_hash: vector<u8>public // 快照根hash
  );fun upgrade_to_dao(sender:signer, ...) {
      //基本逻辑同上,省去了创建账号的流程
投票  }
public fun do_vote(
  // 这里有个难题是 TokenT 从哪里来。
signer: &signer, // 一种方法是先生成一个账号,部署合约,然后升级为 dao
  // 另外一种方法是通过 broker:Dao address,的合约升级方式进行部署合约
      id: u64, 
fun issue_member_nft<DaoT>(creator: &signer, dao_signer: &signer){
     amount: u128,Token::register<DaoT>(dao_signer);
     let choice:basemeta u8,
= NFT::new_meta_with_image();
     proof: &vector<u8>,NFT::register_nft_v2<DaoMemberMeta<DaoT>>(dao_signer, basemeta);
     let sidecreator_nodes: &vector<u8>) {
address = Signer::address_of(creator);
     // 证明...
 issue 第一个 NFT 给 creator
   // 取本地NFT let meta = DaoMemberMeta<DaoT>{
// 用NFT投票     do_vote_with_nft(broker, id, choice,user: )creator;
     };
     //如何初始化 用NFT来投票creator  sbt?
// 方便上层DAO来调用   public fun do_vote_with_nft(
    broker: address,let sbt = Token::zero<DaoT>();
     id:let u64,body =   DaoMemberBody<DaoT>{
choice: u8,     nft: Option::Option<NFT<DaoSpace::NFTMetasbt,
DaoSpace::NFTData>>   ) { }
     let }nft }

proposal:提案,即一个提案代表一次投票过程,该部分跟原有的流程类似,但是去掉了TokenType,该合约只处理提案的相关处理过程,不做其他无关的事情。

代码块
languagerust
// DaoProposal.move
module DaoProposal {

  struct ProposalCapability {
    proposal_id: u64,
  }

  struct Proposal<Action> {
    ... // 现有的一些结构
    proposal_id: u64,
    voting_system: u8, // 投票类型有单选投票、二次投票、排名投票、加权投票等投票类型
    voting_start_time: u64,
    voting_end_time: u64,
    voting_block_num: u64, // 投票快照高度
    voting_block_root: vector<u8>, // 投票快照高度的root hash
    action: Option::Option<Action>, // 执行策略的结构参数
  }
  
  struct Vote {
    /// vote for the proposal under the `proposer`.
    proposer: address,
    /// proposal id.
    id: u64,
    choie: u8, // 同意,反对,拒绝
    weight: u128, // 投票权重
  }
  = NFT::mint(basemeta, meta, body);
     IdentifierNFT::accept<DaoMemberMeta<DaoT>,DaoMemberBody<DaoT>>(creator);
     IdentifierNFT::grant(dao_signer, creator);
     
  }
     
  // 参与投票方/创建投票方注册到space
  public fun register_to_space(
      signer: &signer, 
      broker: address) {
      // 创建对应的NFT
  }

  // 参与投票方/创建投票方注册到space
  // 方便再上一层的DAO调用
  public fun regiter_to_space_get_nft(
      signer: &signer, 
      broker: address) : Option::Option<NFT<DaoSpace::NFTMeta, DaoSpace::NFTData>> {
  }
  
  // 根据 space_broker 来创建对应的proposal
  public fun create_proposal<ExecutionStrategy>(
      signer: &signer, // 创建者
      space_broker: u64, // space 代理人
      block_num: u64, // 快照高度
      root_hash: vector<u8> // 快照根hash
  );
  
  // 投票
  public fun createdo_proposal<Action>(...,vote(
      signer: &signer, 
      broker: address,
      id: u64, 
      amount: u128,
      choice: u8,
      proof: &vector<u8>,
      side_nodes: &vector<u8>) {
    // 证明...
    // 取本地NFT
    // 用NFT投票
    do_vote_with_nft(broker, id, choice, );
  }
  
  // 用NFT来投票
  // 方便上层DAO来调用
  public fun do_vote_with_nft(
    broker: address,
    id: u64,
    choice: u8,
    nft: Option::Option<NFT<DaoSpace::NFTMeta, DaoSpace::NFTData>>
  ) {
    
  }
}

proposal:提案,即一个提案代表一次投票过程,该部分跟原有的流程类似,但是去掉了TokenType,该合约只处理提案的相关处理过程,不做其他无关的事情。

代码块
languagerust
// DaoProposal.move
module DaoProposal {

  struct ProposalCapability {
    proposal_id: u64,
  }

  struct Proposal<Action> {
    ... // 现有的一些结构
    proposal_id: u64,
    voting_system: u8, // 投票类型有单选投票、二次投票、排名投票、加权投票等投票类型
    voting_start_time: u64,
    voting_end_time: u64,
    voting_block_num: u64, // 投票快照高度
    voting_block_root: vector<u8>, // 投票快照高度的root hash
    action: Option::Option<Action>, // 执行策略的结构参数
  }
  
  struct Vote {
    /// vote for the proposal under the `proposer`.
    proposer: address,
    /// proposal id.
    id: u64,
    choie: u8, // 同意,反对,拒绝
    weight: u128, // 投票权重
  }
  
  public fun create_proposal<Action>(...,
      space_broker: address, 
      space_id: u64,
      root_hash: &vector<u8>): ProposalCapability;
  
  public fun cast_vote(
      signer: &signer,
      proposer_broker: address,
      id: u64, 
      amount: u128,
      choice: u8,
      cap: &ProposalCapability
    );
    
  public fun extract_strategy<Action>(id: u64) : Action;
};

Proposal action plugin

代码块
//所有的 proposal action 插件都需要提供两个公开的 entry script function,参数保持一致

module XProposalPlugin{
  use Dao::{ProposalPluginCapability}
  struct XAction{
  }
  
  struct ProposalPluginCapabilityHolder{
     cap: ProposalPluginCapability<XAction>
  }
  
  fun new_action(args:vector<u8>): XAction{
       //bcs decode args
       //construct XAction
  }
  
  public fun plugin(sender: &signer){
    //这里的 sender 必须是 dao 账号,但这里如何获得 dao 账号,得想一个方式
  }
  
  public fun execute_proposal<DaoT>(sender: &signer, proposal_id: u64){
      let dao_address = Dao::get_dao_address<DaoT>();
      let cap_holder = borrow_global<ProposalPluginCapabilityHolder>(dao_address);
      let actoin = Dao::extract_proposal_action<DaoT, XAction>(&cap_holder.cap, sender, proposal_id);
       space_broker: address,//execute the action
       space_id: u64,//update the executor
  }
  
root_hash: &vector<u8>): ProposalCapability;
   //the entry script function
  public fun cast_vote((script) fun execute_proposal_entry<DaoT>(sender: signer, proposal_id: u64){
       signer: execute_proposal<DaoT>(&signersender, proposal_id)
  }
  proposer_broker:
address,  public fun propose<DaoT>(sender:&signer,   idargs:vector<u8>, exec_delay: u64,){
      let amount:action u128,
 = new_action(args);
    choice: u8, 
     cap: &ProposalCapabilitylet dao_address    = Dao::get_dao_address<DaoT>();
      let publiccap_holder fun= extractborrow_strategy<Action>(id: u64) : Action;
};

action

代码块
module XProposalAction{global<ProposalPluginCapabilityHolder>(dao_address);
  struct XAction{   
  }      public fun new_action(args...): XAction{
   //construct action
  }
  
  //理想的情况是 action 的方法是标准化的,但如果牵扯到带有范型的 action,可能不好统一。
  public fun execute_action(proposer_address: address,Dao::propose<DaoT, XAction>(&cap_holder.cap, sender, action, exec_delay, exec_delay);
  }
  
  public(script) fun propose_entry<DaoT>(sender:signer, args:vector<u8>, exec_delay: u64){
      propose(&sender, args, proposal_id: u64){exec_delay)
  }
}

三、流程

TODO:

四、问题

  1. Move实现层面,区分不同的项目方来创建DAO,是通过Template Type 来对其进行异化还是通过 address+id的方式,若按照前者,如何解决合约动态部署的问题?

    这个当前有两个方案,这个 issue 中提了两个方案 [Feature Request] Support Issue a Token on webpage · Issue #4 · starcoinorg/dapps (http://github.com ) ,未来可以支持合约中部署合约。

  2. Snapshot中,存在不同的准入策略,即参与/创建投票的人在DAO中创建/参与投票的时候会有一个准入的门槛,这个准入的门槛是一系列判断条件,这个条件有预置的也有用户。在solidity中可以动态调用,故可以通过一个ABI的字符数组来进行表示,实际执行的时候也是动态去调用这个ABI,且这些策略是可以拼接的。而Move中如果也按照这个思路显然无法实现

    感觉可以简化一下,小额抵押某种 Token 即可,如果是发垃圾投票就通过投票没收。

  3. 类似的,Snapshot也存在不同的Execution Action,在Move中也可以通过预置Execution的模式去实现,是否还有其他的实现思路?

    当前 DAO 的 Action 是支持第三方扩展的,并不是只能用预置的 Action。
    执行在前端触发。

  4. 治理权重的计算(基于长线hodl:锁仓时长、锁仓量),基于余额的权重计算有难度。锁仓有激励。

    1. 不同类型项目采用不同机制。Swap自实现weight维护。

  5. 扩展点的设计,包括Action等动态调用的扩展,通过interface形式,前端来触发。DAO的平滑升级如何扩展?

    1. Action的扩展性;

    2. DAO类型的扩展性,不同DAO类型如何转换;

    3. 投票权重的扩展性;

    4. 投票策略类的扩展性;

五、参考

Snapshot 说明文档

Snapshot X 说明文档

...

DAO 畅想曲 — jolestar (mirror.xyz)

CodeforDAO (github.com)

CodeforDAO 的诞生与自组织的互联网未来