...
代码块 | ||
---|---|---|
| ||
// DaoSpace.move module DaoSpace { struct Space { 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. meta_data: vector<u8>, // 空间元数据,编码后存储在链上(可考虑存在链下) } struct NFTMeta { user: address, } struct NFTBody { weight: u128 } struct SpaceCapability { } // 创建 space,一个账号只能创建一个(是否只能一个账户创建一个,待讨论) public fun create(signer: &signer, voting_delay: u128, voting_duration: u128, meta_data: &vector<u8>): SpaceCapability; // 查询 space public fun query(broker: address) : (u128, u128, u128); // 非托管账号 register public fun register(signer: &signer, space_broker: address, cap: &SpaceCapability) : NFT<NFTMeta, NFTBody>; // 非托管账号 unregister public fun unregister(signer: &signer, nft: NFT<NFTMeta, NFTBody>); // NFT 添加权重 public fun add_weight(signer: &signer, nft: &mut NFT<NFTMeta, NFTBody>, weight: u128); // NFT 减少权重 public fun reduce_weight(signer: &signer, nft: &mut NFT<NFTMeta, NFTBody>, weight: u128); } |
...
DaoRegistry
Dao 注册中心,保存在 0x1 账号下的全局注册表,提供 DaoT → Dao address 的反向索引
代码块 |
---|
...
...
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
}
} |
Dao:代表治理的相关流程,该合约中处理底层Space与Proposal的拼装,将一些权限托管到该合约中,考虑还可以往上拆分,即将执行策略部分拆分到上层合约中。
代码块 | ||||
---|---|---|---|---|
| ||||
// Dao.move module Dao { use DaoSpace; struct DaoSignerDelegate has key { 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 } /// Proposal data struct. /// 是否需要通过 DaoT 来区分 Proposal,如果不需要,则需要把 DAO id 记录在 proposal 中 /// 但 Action 的泛型无法消除 struct Proposal<phantom DaoT: store, Action: store> has key { /// countid of voters who're against the proposal against_votesid: u128u64, /// executablecreator afterof thisthe time.proposal etaproposer: u64address, /// afterwhen how long, the agreed proposal can be executedvoting begins. actionstart_delaytime: u64, /// howwhen many votes to reach to make the proposal passvoting ends. quorumend_votestime: u128u64, /// proposal action.count of voters who agree with the proposal actionfor_votes: Option::Option<Action>u128, ///在原来的 Proposalcount 字段上新增两个字段of voters who're against the proposal block_num: u64, // 快照高度 against_votes: u128, root_hash: vector<u8> /// executable after 快照根hashthis time. } //Set 上也需要用泛型来区分eta: u64, struct DaoProposalSet { /// after proposal_set: HashSet<u64how long, DaoProposal>the agreed proposal }can be executed. //初始化过程中分配给 creator 的 cap struct RootCapability has key{}action_delay: u64, struct/// DaoMemberMeta<phontemhow DaoT>many hasvotes copy{to reach to make user: address,the proposal pass. } quorum_votes: u128, //这里是否需要 DaoT 来区分不同的 Dao? ///如果不区分的话,同一个用户加入多个 Daoproposal 的情况,当前的action. IdentifierNFT 无法表达 struct DaoMemberBody<DaoT>{ action: sbt: Token<DaoT>Option::Option<Action>, } //在原来的 执行策略:转账Proposal 字段上新增两个字段 struct ExecutionTransferStrategy<phontem TokenT> { amountblock_num: u128u64, // 快照高度 to: address, } root_hash: vector<u8> // 快照根hash // 直接创建} Dao public fun new_dao( //Set 上也需要用泛型来区分 creator: &signer,struct DaoProposalSet { votingproposal_delayset: u64HashSet<u64, DaoProposal> } voting_period: u64, //初始化过程中分配给 creator 的 cap min_action_delay: u64) { struct RootCapability has key{} //这里 Account 需要提供一个新的方法,允许用一个账号去创建另外一个 Delegate 账号。 struct InstallPluginCapability has key{ } let signer_cap = Account::create_delegate_account(&signer); struct ProposalPluginCapability<phontem ActionT> hash key{ let dao_signer = Account::create_signer_with_cap(&siger_cap);installer: address, //下面面的操作切换到通过 dao_signerBitSet 身份进行操作的不同位置标识插件的不同权限 letcapabilities: BitSet, dao = Dao{..}; move_to(&dao_signer, dao); struct DaoMemberMeta<phontem DaoT> has copy{ // 托管 Daouser: 账号的address, SignerCapability 到该合约 } move_to(&dao_signer, DaoSignerDelegate{cap: signer_cap}); issue_member_nft(creator,&dao_signer); //这里是否需要 DaoT 来区分不同的 Dao? //如果不区分的话,同一个用户加入多个 Dao 的情况,当前的 IdentifierNFT 无法表达 struct DaoMemberBody<DaoT>{ sbt: Token<DaoT>, } //初始化 执行策略:转账 dao 的时候无法一次性完成,需要先把 capstruct 都存到ExecutionTransferStrategy<phontem creatorTokenT> 账号下{ //然后按照 plugin 的方式逐步初始化amount: u128, }to: address, } // 将一个账号直接升级为 Dao, sender// 会变为直接创建 Dao 账号 public fun upgrade_tonew_dao(sender:signer, ...) { 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 dao_address<DaoT>(){ } fun dao_signer<DaoT>(): signer { let signer_cap = borrow_global<DaoSignerDelegate>(dao_address<DaoT>()); Account::create_signer_with_cap(&siger_cap) } public fun install_proposal_plugin<PluginT>(cap: &dao::InstallPluginCapability, sender:&signer, capabilites: vector<u8>){ //基本逻辑同上,省去了创建账号的流程move_to } // 这里有个难题是 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 action 插件都需要提供两个公开的 entry script function,参数保持一致 module XProposalPlugin{ use Dao::{Self, ProposalPluginCapability} // XPlugin 的类型标识,用于保存配置 struct XPlugin{ cfg_a: u64, } 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); //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) } } |
...