引言
经过之前的学习,准备进行实战开发一个简单的dao项目,实现一个去中心化自治组织,用于管理共享资金、社区任务、提案和投票等功能,本篇文章分享了提案模块、错误定义。
提案设计
关于提案模式的具体设计:
- dao member又提案的权利
- 为了减少废案,规定提案需要支付一些dao代币(5)
- 提案被成功接受,根据提案等级,提案者会获得不同的dao代币奖励
- 其余dao member可以对提案进行投票,1 dao代币对应1 票,提案时需要将dao代币质押,获取对应凭证,在提案结束后可以销毁凭证取回dao代币
- core member可以管理提案,控制提案关闭时间,变更提案等级等。
const
const MAX_VOTES_ONE_TIME: u64 = 10;const TOTAL_SUPPLY:u64 = 100_000_000_000_000_000;const PROPOSAL_FEE:u64 = 5;const LEVEL0_REWARD:u64 = 0;const LEVEL1_REWARD:u64 = 10;const LEVEL2_REWARD:u64 = 15;const LEVEL3_REWARD:u64 = 30;
首先定义了一些常量,代表提案花费,各个等级的提案奖励。
proposal
struct Proposal has key,store{id: UID,title: String, //The title of the proposaldescribe: String, //Content of the Proposallevel: u64,proposer: address, //Initiator of the Proposallock_balance: u64, //DAO Tokens Locked by the Proposalsupport: u64, //Number of votes in favor of the proposalagainst: u64, //Number of votes against the proposalis_closed: bool, is_passed: bool,is_claimed_reward: bool,}
提案结构体,包括提案题目,描述,等级,提案者,投票总数,赞成反对票数,是否关闭,是否通过,是否已经领取奖励等字段。
投票凭证
struct VoteCap has key{id: UID,proposal_id: ID,voter: address,is_support: bool,votes: u64,}
投票凭证,用户投票后会生成相应的投票凭证,在提案结束后销毁凭证取回质押的dao
发起提案
public fun submit_proposal(member_cap:&MemberCap,title: String, describe:String, level:u64, coin:&mut Coin<DAO>,treasury:&mut Treasury<DAO>,ctx:&mut TxContext){//1.Verify Membership in the DAOcheck_membercap_role(member_cap, ctx);//2.Pay Proposal Feetransfer_coin_to_treasury(treasury,coin, PROPOSAL_FEE);//3.get duration time//4.Create Proposallet proposal = Proposal {id: object::new(ctx),title: title, //The title of the proposaldescribe: describe, //Content of the Proposallevel: level,proposer: member_cap.role_address, //Initiator of the Proposallock_balance: 0, //DAO Tokens Locked by the Proposalsupport: 0, //Number of votes in favor of the proposalagainst: 0, //Number of votes against the proposalis_closed: false,is_passed: false,is_claimed_reward: false,};transfer::share_object(proposal);}
- 首先验证提案者的身份
- 支付提案费
- 创建提案obj 设置为share对象
投票
public fun vote(member_cap:&MemberCap, proposal:&mut Proposal, votes:u64, is_support:bool, coin:&mut Coin<DAO>, treasury:&mut Treasury<DAO>,ctx:&mut TxContext){//1. verifycheck_membercap_role(member_cap,ctx);assert!(!proposal.is_closed,EProposalClosed);assert!(member_cap.role_address != proposal.proposer, EVoteSelf);assert!(votes >= 1 && votes <= MAX_VOTES_ONE_TIME, EInvailVotes);check_membercap_role(member_cap, ctx);//2. transfer daotransfer_coin_to_treasury(treasury,coin,votes);//3. change proposalupdate_proposal_vote(proposal,votes,is_support);//4. distrubte voteCaplet vote_cap = VoteCap {id: object::new(ctx),proposal_id:object::uid_to_inner(&proposal.id),voter: member_cap.role_address,is_support: is_support,votes: votes,};transfer::transfer(vote_cap, member_cap.role_address);}
- 验证投票者身份
- 对提案的状态进行检查 查看是否关闭 提案者不能为自己投票 投票数不能超贵最大限制
- 质押相应的dao 代币
- 更改提案数据
- 颁发投票凭证
manage
public fun change_proposal_level(core_cap:& CoreCap, proposal:&mut Proposal, new_level:u64 ,ctx:&mut TxContext){check_corecap_role(core_cap,ctx);assert!(!proposal.is_closed, EProposalClosed);proposal.level = new_level;}public fun close_proposal(core_cap:& CoreCap,proposal:&mut Proposal,ctx:&mut TxContext){check_corecap_role(core_cap,ctx);assert!(!proposal.is_closed, EProposalClosed);if(proposal.against == 0 || (proposal.support * 2) / 3 >= proposal.against){proposal.is_passed = true;};proposal.is_closed = true;}
core member可以对提案进行管理 更改等级或关闭提案
取回票据
public fun claim_proposal_vote(member_cap:&MemberCap, proposal:&mut Proposal, vote_cap: VoteCap,treasury:&mut Treasury<DAO>, ctx:&mut TxContext){// 1.veriry the parameterscheck_membercap_role(member_cap,ctx);assert!(&vote_cap.proposal_id == object::borrow_id(proposal), EProposalCheck);assert!(proposal.is_closed, EProposalNotClosed);assert!(member_cap.role_address == vote_cap.voter, ERoleCheck);// 2.delete voteCaplet VoteCap {id, proposal_id, voter, is_support, votes} = vote_cap;object::delete(id);// 3.take coin from treasurylet reward_coin = take_coin_from_treasury(treasury,votes,ctx);// 4.transfer coin to votertransfer::public_transfer(reward_coin,voter);}
在投票结束后,投票者销毁Votecap取回dao代币
public fun claim_proposal_reward(member_cap:&MemberCap, proposal:&mut Proposal, treasury:&mut Treasury<DAO> ,ctx:&mut TxContext){// 1.veriry the parameterscheck_membercap_role(member_cap,ctx);assert!(proposal.is_passed, EProposalNotPassed);assert!(!proposal.is_claimed_reward, EAlreadyClaimed);assert!(proposal.proposer == tx_context::sender(ctx), ERoleCheck);proposal.is_claimed_reward = true;//2.reward calculationlet level = proposal.level;let reward_amount:u64;if(level == 1){reward_amount = LEVEL1_REWARD;}else if(level == 2){reward_amount = LEVEL2_REWARD;}else{reward_amount = LEVEL3_REWARD;};//3.send reward token;let reward_coin = take_coin_from_treasury(treasury,reward_amount,ctx);transfer::public_transfer(reward_coin,proposal.proposer);}
如果提案被接受,提案者可以获取奖励
内部函数
fun check_init_corecap_role(init_core_cap:& InitCoreCap,ctx: &mut TxContext){assert!(init_core_cap.role_address== tx_context::sender(ctx), ERoleCheck);}fun check_corecap_role(core_cap:& CoreCap,ctx: &mut TxContext){assert!(core_cap.role_address== tx_context::sender(ctx), ERoleCheck);}fun check_membercap_role(member_cap:& MemberCap,ctx: &mut TxContext){assert!(member_cap.role_address == tx_context::sender(ctx), ERoleCheck);}fun transfer_coin_to_treasury(treasury:&mut Treasury<DAO>,coin:&mut Coin<DAO>,amount: u64){assert!(balance::value<DAO>(&treasury.supply) >= amount, EInsufficientTreasurySupply);balance::join<DAO>(&mut treasury.supply, balance::split<DAO>(coin::balance_mut(coin), amount));}fun take_coin_from_treasury(treasury:&mut Treasury<DAO>,amount: u64,ctx:&mut TxContext): Coin<DAO>{let supply = &mut treasury.supply;let reward_coin = coin::take<DAO>(supply, amount, ctx);reward_coin}fun update_proposal_vote(proposal:&mut Proposal,votes:u64,is_support:bool){let lock_balance = & proposal.lock_balance;proposal.lock_balance =*lock_balance + votes;if(is_support){let support = & proposal.support;proposal.support =*support + votes;}else{let against = & proposal.against;proposal.against =*against + votes;}}
重构了一些重复的功能,用于代码简化
check_init_corecap_role和check_corecap_role和check_membercap_role用于角色检查
transfer_coin_to_treasury和take_coin_from_treasury用于将代币转到金库或从金库转出
update_proposal_vote用来更新提案选票
视图函数
public fun is_closed(proposal: &Proposal):bool{proposal.is_closed}public fun is_passed(proposal: &Proposal):bool{proposal.is_passed}public fun support(proposal: &Proposal):u64{proposal.support}public fun against(proposal: &Proposal):u64{proposal.against}public fun proposer(proposal: &Proposal):address{proposal.proposer}public fun total_members(dao: &Dao<DAO>):u64{dao.total_members}public fun treasury_supply(treasury: &Treasury<DAO>):u64{balance::value<DAO>(&treasury.supply)}
用于获取状态,分为提案状态和dao状态
错误定义
const ETaskDistributeEnded:u64 = 0;const ENotTaskCapOwner:u64 = 1;const EProposalClosed:u64 = 2;const EVoteSelf:u64 = 3;const EInvailVotes:u64 = 4;const EProposalCheck:u64 = 5;const EProposalNotClosed:u64 = 6;const ERoleCheck:u64 = 7;const EProposalNotPassed:u64 = 8;const EAlreadyClaimed:u64 = 9;const EInsufficientTreasurySupply:u64 = 10;
定义一些错误,在assert时返回,方便交互失败时定位回滚位置
这些错误在之前代码的检查中有用到