版本比较

密钥

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

...

Dao 注册中心,保存在 0x1 账号下的全局注册表,提供 DaoT → Dao address 的反向索引

代码块
breakoutModewide
module DaoRegistry{
  
  ///
  struct DaoRegistryEntry<phantom DaoT>{
    dao_id: u64,
    dao_address: address,
  }

  //这个方法必须由 Dao module 调用
  public(friend) fun register<DaoT>(dao_id: u64,dao_address: address){
    let genesis_account = //get 0x1 signer
    move_to(&genesis_account, DaoRegistryEntry{
        dao_id,
        dao_address,
      }
    )
  }
  
  public fun dao_address<DaoT>():address{
    *&borrow_global<DaoRegistryEntry<DaoT>>.dao_address
  }
}

...

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

  struct DaoSignerDelegate has key {
    cap: SignerCapability, // 托管的签名(这里一旦放进来,就没法再还回去了,且只有一份,没想好是放在space还是放在这里,暂时放这里)
  }
  
  struct Dao {
    id: u64,
    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
  }
  
  /// Proposal data struct.
  /// 是否需要通过 DaoT 来区分 Proposal,如果不需要,则需要把 DAO id 记录在 proposal 中
  /// 但 Action 的泛型无法消除
  struct Proposal<phantom DaoT: store, Action: store> has key {
        /// id of the proposal
        id: u64,
        /// creator of the proposal
        proposer: address,
        /// when voting begins.
        start_time: u64,
        /// when voting ends.
        end_time: u64,
        /// count of voters who agree with the proposal
        for_votes: u128,
        /// count of voters who're against the proposal
        against_votes: u128,
        /// executable after this time.
        eta: u64,
        /// after how long, the agreed proposal can be executed.
        action_delay: u64,
        /// how many votes to reach to make the proposal pass.
        quorum_votes: u128,
        /// proposal action.
        action: Option::Option<Action>,
        //在原来的 Proposal 字段上新增两个字段
        block_num: u64, // 快照高度
        root_hash: vector<u8> // 快照根hash
  }
  
  //Set 上也需要用泛型来区分   
  struct DaoProposalSet {
    proposal_set: HashSet<u64, DaoProposal>
  }
  
  //初始化过程中分配给 creator 的 cap
  struct RootCapability has key{}
  
  struct InstallPluginCapability has key{
  }
  
  struct ProposalPluginCapability<phontem ActionT> hash key{
     installer: address,
     //通过 BitSet 的不同位置标识插件的不同权限
     capabilities: BitSet,
  }
  
  struct DaoMemberMeta<phontem DaoT> has copy{
    user: address,
  }
  
  //这里是否需要 DaoT 来区分不同的 Dao?
  //如果不区分的话,同一个用户加入多个 Dao 的情况,当前的 IdentifierNFT 无法表达
  struct DaoMemberBody<DaoT>{
    sbt: Token<DaoT>,
  }
  
  // 执行策略:转账
  struct ExecutionTransferStrategy<phontem TokenT> {
    amount: u128,
    to: address,
  }
  
  // 直接创建 Dao
  public fun new_dao(
     creator: &signer, 
     voting_delay: u64, 
     voting_period: u64, 
     min_action_delay: u64) {
     //这里 Account 需要提供一个新的方法,允许用一个账号去创建另外一个 Delegate 账号。
     let signer_cap = Account::create_delegate_account(&signer);
     let dao_signer = Account::create_signer_with_cap(&siger_cap);
     //下面面的操作切换到 dao_signer 身份进行操作
     let dao = Dao{..};
     move_to(&dao_signer, dao);
     // 托管 Dao 账号的 SignerCapability 到该合约
     move_to(&dao_signer, DaoSignerDelegate{cap: signer_cap});
     
     issue_member_nft(creator,&dao_signer);
     
     //初始化 dao 的时候无法一次性完成,需要先把 cap 都存到 creator 账号下
     //然后按照 plugin 的方式逐步初始化
  }
  
  // 将一个账号直接升级为 Dao, sender 会变为 Dao 账号
  public fun upgrade_to_dao(sender:signer, ...) {
     //基本逻辑同上,省去了创建账号的流程
  }
  
  public fun///这里好像没办法检测 DaoT 和 dao_address<DaoT>(){address 的关系
  public fun   }
  
  fun dao_signer<DaoT>(): signer {register<DaoT>(creator: &signer, dao_address: address){
      let signer_capdao = borrow_global<DaoSignerDelegate>global<Dao>(DaoRegsitry::dao_address<DaoT>());
      Account::create_signer_with_cap(&siger_cap)
  }
  
  public fun install_proposal_plugin<PluginT>(cap: &dao::InstallPluginCapability, sender:&signer, capabilites: vector<u8>){//check the dao creator and creator args
      DaoRegistry::register<DaoT>(dao_address, dao.id)
  }
  
  fun dao_signer<DaoT>(): signer {
    let signer_cap = borrow_global<DaoSignerDelegate>(DaoRegsitry::dao_address<DaoT>());
    Account::create_signer_with_cap(&siger_cap)
  }
  
  public fun install_proposal_plugin<DaoT, PluginT>(cap: &dao::InstallPluginCapability, sender:&signer, capabilites: vector<u8>){
      let dao_signer = dao_signer<DaoT>();
      move_to(&dao_signer, ProposalPluginCapability<PluginT>{
        installer: Signer:address_of(sender),
        capabilites: BiteSet::from_bytes(capabilites),
      })
  }
  
  // _plugin 参数这里没有用,只是确保这个调用来自 PluginT 的实现模块
  public fun extract_proposal_plugin_capability<DaoT, PluginT>(_plugin: &PluginT): ProposalPluginCapability<PluginT>{ 
      move_tofrom<ProposalPluginCapability<PluginT>>(dao_address<DaoT>())
  }
  
  // 这里有个难题是 TokenT 从哪里来。
  // 一种方法是先生成一个账号,部署合约,然后升级为 dao
  // 另外一种方法是通过 Dao 的合约升级方式进行部署合约
  fun issue_member_nft<DaoT>(creator: &signer, dao_signer: &signer){
     Token::register<DaoT>(dao_signer);
     let basemeta = NFT::new_meta_with_image();
     NFT::register_nft_v2<DaoMemberMeta<DaoT>>(dao_signer, basemeta);
     let creator_address = Signer::address_of(creator);
     // issue 第一个 NFT 给 creator
     let meta = DaoMemberMeta<DaoT>{
        user: creator;
     };
     //如何初始化 creator 的 sbt?
     let sbt = Token::zero<DaoT>();
     let body = DaoMemberBody<DaoT>{
      sbt,
     }
     let nft = 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 do_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,该合约只处理提案的相关处理过程,不做其他无关的事情。

代码块
breakoutModewide
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

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

module XProposalPlugin{
  use Dao::{Self,  ProposalPluginCapability}
  
  // XPlugin 的类型标识,用于保存配置
  struct XPlugin{
     cfg_a: u64,
  }
  
  struct XAction{
  }
  
  struct ProposalPluginCapabilityHolder{
     cap: ProposalPluginCapability<XPlugin>
  }
  
  fun new_action(args: ProposalPluginCapability<XAction>vector<u8>): XAction{
       //bcs decode args
       //construct XAction
  }
  
  public fun newinit_action(plugin<DaoT>(sender: &signer, args:vector<u8>): XAction{
       //bcs decode args
     let  //construct XActionplugin_config = XPlugin{};
  }   let cap  public fun plugin(sender: &signer){= Dao::extract_proposal_plugin_capability<DaoT, XPlugin>();
     move_to()
  }
  
  public fun execute_proposal<DaoT>(sender: &signer, proposal_id: u64){
      let dao_address = DaoDaoRegistry::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);
      //execute the action
      //update the executor
  }
  
  //the entry script function
  public(script) fun execute_proposal_entry<DaoT>(sender: signer, proposal_id: u64){
        execute_proposal<DaoT>(&sender, proposal_id)
  }
  
  public fun propose<DaoT>(sender:&signer, args:vector<u8>, exec_delay: u64){
      let action = new_action(args);
      
      let dao_address = Dao::get_dao_address<DaoT>();
      let cap_holder = borrow_global<ProposalPluginCapabilityHolder>(dao_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, exec_delay)
  }
}

...