引言

经过之前的学习,准备进行实战开发一个简单的dao项目,实现一个去中心化自治组织,用于管理共享资金、社区任务、提案和投票等功能,本篇文章分享了提案模块、错误定义。

提案设计

关于提案模式的具体设计:

  1. dao member又提案的权利
  2. 为了减少废案,规定提案需要支付一些dao代币(5)
  3. 提案被成功接受,根据提案等级,提案者会获得不同的dao代币奖励
  4. 其余dao member可以对提案进行投票,1 dao代币对应1 票,提案时需要将dao代币质押,获取对应凭证,在提案结束后可以销毁凭证取回dao代币
  5. 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 proposal
        describe: String, //Content of the Proposal
        level: u64,
        proposer: address, //Initiator of the Proposal
        lock_balance: u64, //DAO Tokens Locked by the Proposal
        support: u64, //Number of votes in favor of the proposal
        against: u64, //Number of votes against the proposal
        is_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 DAO
        check_membercap_role(member_cap, ctx);
        //2.Pay Proposal Fee
        transfer_coin_to_treasury(treasury,coin, PROPOSAL_FEE);
        //3.get duration time

        //4.Create Proposal
        let proposal = Proposal {
            id: object::new(ctx),
            title: title, //The title of the proposal
            describe: describe, //Content of the Proposal
            level: level,
            proposer: member_cap.role_address, //Initiator of the Proposal
            lock_balance: 0, //DAO Tokens Locked by the Proposal
            support: 0, //Number of votes in favor of the proposal
            against: 0, //Number of votes against the proposal
            is_closed: false,
            is_passed: false,
            is_claimed_reward: false,
        };

        transfer::share_object(proposal);
    }
  1. 首先验证提案者的身份
  2. 支付提案费
  3. 创建提案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. verify
        check_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 dao
        transfer_coin_to_treasury(treasury,coin,votes);

        //3. change proposal
        update_proposal_vote(proposal,votes,is_support);

        //4. distrubte voteCap
        let 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);
    }
  1. 验证投票者身份
  2. 对提案的状态进行检查 查看是否关闭 提案者不能为自己投票 投票数不能超贵最大限制
  3. 质押相应的dao 代币
  4. 更改提案数据
  5. 颁发投票凭证

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 parameters
        check_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 voteCap
        let VoteCap {id, proposal_id, voter, is_support, votes} = vote_cap;
        object::delete(id);

        // 3.take coin from treasury
        let reward_coin = take_coin_from_treasury(treasury,votes,ctx);

        // 4.transfer coin to voter
         transfer::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 parameters
        check_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 calculation
        let 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时返回,方便交互失败时定位回滚位置
这些错误在之前代码的检查中有用到

Move语言学习交流QQ群: 79489587
Sui官方中文开发者电报群: https://t.me/sui_dev_cn

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐