anchor 智能合约案例2 之 vote

前言:

前面我们介绍了 solana 关于 anchor 智能合约的环境搭建 配置,简单编写 发布 部署 调用 的一些落地。等等。接下来我们借着案例。详细剖析下智能合约编写。

案例 vote 简介:

这是一个 关于调查投票 的智能合约; 主要功能 记录 基于某个 事情 或 主题,对获选人进行投票。

1. poll 投票 (关于一个事情的调查)
2. candidate 获选人 基于poll_id 和 candidate_name ,

智能合约功能分析:

只能合约的结构:instruction

  1. Initialize_poll 初始化 调查 (给你设置一个 给一个事情设置 poll_id)
  2. Initialize_candidate 初始化 候选人 (基于 poll_id 和 candidate_name 作为种子 生成 pda )
  3. vote 投票

很多都是固定写法案例之 favorites ,我们着重 看下不一样的地方。一样的地方大家可以回到 favorites 案例中 详细看下

以下是 智能合约的 方法定义:
  • Initialize_poll
rust 复制代码
//初始化 poll
pub fn initialize_poll(ctx: Context<InitializePoll>,
                       poll_id:u64,
                       poll_description:String,
                       poll_start:u64,
                       poll_end:u64) ->  Result<()> {
    
    //#[derive(Accounts)]  通过derive 指令,这里的  poll 以帮只我们创建好了,这里只是做得 赋值操作
    let poll = &mut ctx.accounts.poll;
    poll.poll_id = poll_id;
    poll.description   = poll_description;
    poll.poll_start = poll_start;
    poll.poll_end = poll_end;
    poll.candidate_amount = 0;
    Ok(())
}
  • Initialize_candidate
rust 复制代码
//初始化 获选人
pub fn initialize_candidate(_ctx: Context<InitializeCandidate>,
                            _candidate_name: String,
                            _poll_id: u64,
                            ) ->  Result<()> {

    let candidate = &mut _ctx.accounts.candidate;
    candidate.candidate_name = _candidate_name;
    candidate.candidate_votes  = 0;

    let poll = &mut _ctx.accounts.poll;
    poll.candidate_amount += 1;
    Ok(())
}
  • vote 投票
rust 复制代码
//投票
pub fn vote(_ctx: Context<Vote>,candidate_name:String, _poll_id: u64,) -> Result<()> {
    let candidate = &mut _ctx.accounts.candidate;
    candidate.candidate_votes += 1;
    // msg!("candidate.candidate_votes:{}", candidate.candidate_votes);
    // msg!("candidate.candidate_name:{}", candidate.candidate_name);
    // msg!("candidate.candidate_poll_id:{}", _poll_id);
    Ok(())
}
以下是 智能合约的 接头体定义:(这里的结构体用了一个新姿势 等下着重介绍下)
  • InitializePoll

    复制代码
    instruction 宏 可以从长下文中获取 智能合约 调用参数
    rust 复制代码
    #[derive(Accounts)]
    #[instruction(poll_id:u64)] //可以获取上下文中的 传递参数
    pub struct InitializePoll<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
        #[account(
            init,
            payer = signer,
            space = 8 + Poll::INIT_SPACE,
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        pub system_program: Program<'info, System>,
    }
    
    #[account]
    #[derive(InitSpace)]
    pub struct Poll {
        pub poll_id: u64,
        #[max_len(64)]
        pub description:String,
        pub poll_start: u64,
        pub poll_end: u64,
        pub candidate_amount: u64,
    }
  • InitializeCandidate

    rust 复制代码
    #[derive(Accounts)]
    #[instruction(candidate_name: String, poll_id: u64)] //从上下文中的 传递参数
    pub struct InitializeCandidate<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        //这里 poll 需要通过 智能合约修改 所以需要添加mut
        #[account(
            mut,
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        // 这里是需要创建一个 candidate, 是用的是 创建相关的写法
        #[account(
            init,
            payer = signer,
            space = 8 + Candidate::INIT_SPACE,
            seeds = [poll_id.to_le_bytes().as_ref(),candidate_name.as_bytes().as_ref()],
            bump,
        )]
        pub candidate: Account<'info, Candidate>,
        pub system_program: Program<'info, System>,
    }
    
    
    #[account]
    #[derive(InitSpace)]
    pub struct Candidate{
        #[max_len(32)]
        pub candidate_name: String,
        pub candidate_votes: u64,
    }
  • Vote

    rust 复制代码
    #[derive(Accounts)]
    #[instruction(candidate_name: String, poll_id: u64)]  //获取智能合约 调用函数的 传参
    pub struct Vote<'info>{
        //不需要mut,因为这里书需要创建 不用付费 相关,只需要签名
        #[account()]
        pub signer: Signer<'info>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        #[account(
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        //这里 candidate 智能合约修改 所以需要添加mut
        #[account(
            mut,
            seeds = [poll_id.to_le_bytes().as_ref(),candidate_name.as_bytes().as_ref()],
            bump,
        )]
        pub candidate: Account<'info, Candidate>,
        pub system_program: Program<'info, System>,
    }

总结:

该 案例和 anchor 智能合约 案例之 favorites ,中的 使用差不多一致,具体注释 可以参考前文。

  • 完整脚本

    rust 复制代码
    use anchor_lang::prelude::*;
    
    declare_id!("8t44emZSpEShL9QtNSLsLEyq1GZFCExwdpfefGR5PjVe");
    
    #[program]
    pub mod anchor_voting {
        use super::*;
    
    //初始化 poll
    pub fn initialize_poll(ctx: Context<InitializePoll>,
                           poll_id:u64,
                           poll_description:String,
                           poll_start:u64,
                           poll_end:u64) ->  Result<()> {
    
        //#[derive(Accounts)]  通过derive 指令,这里的  poll 以帮只我们创建好了,这里只是做得 赋值操作
        let poll = &mut ctx.accounts.poll;
        poll.poll_id = poll_id;
        poll.description   = poll_description;
        poll.poll_start = poll_start;
        poll.poll_end = poll_end;
        poll.candidate_amount = 0;
        Ok(())
    }
    
    //初始化 获选人
    pub fn initialize_candidate(_ctx: Context<InitializeCandidate>,
                                _candidate_name: String,
                                _poll_id: u64,
                                ) ->  Result<()> {
    
        let candidate = &mut _ctx.accounts.candidate;
        candidate.candidate_name = _candidate_name;
        candidate.candidate_votes  = 0;
    
        let poll = &mut _ctx.accounts.poll;
        poll.candidate_amount += 1;
        Ok(())
    }
    
    //投票
    pub fn vote(_ctx: Context<Vote>,candidate_name:String, _poll_id: u64,) -> Result<()> {
        let candidate = &mut _ctx.accounts.candidate;
        candidate.candidate_votes += 1;
        // msg!("candidate.candidate_votes:{}", candidate.candidate_votes);
        // msg!("candidate.candidate_name:{}", candidate.candidate_name);
        // msg!("candidate.candidate_poll_id:{}", _poll_id);
        Ok(())
    }
    }
    
    #[derive(Accounts)]
    #[instruction(candidate_name: String, poll_id: u64)]
    pub struct Vote<'info>{
        //不需要mut,因为这里书需要创建 不用付费 相关,只需要签名
        #[account()]
        pub signer: Signer<'info>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        #[account(
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        //这里 candidate 智能合约修改 所以需要添加mut
        #[account(
            mut,
            seeds = [poll_id.to_le_bytes().as_ref(),candidate_name.as_bytes().as_ref()],
            bump,
        )]
        pub candidate: Account<'info, Candidate>,
        pub system_program: Program<'info, System>,
    }
    
    
    #[derive(Accounts)]
    #[instruction(candidate_name: String, poll_id: u64)]
    pub struct InitializeCandidate<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
        //这里区别于创建,这里只是通过 随机数寻找到 对应的引用,所以不需要init 不需要payer
        //这里 poll 需要通过 智能合约修改 所以需要添加mut
        #[account(
            mut,
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        // 这里是需要创建一个 candidate, 是用的是 创建相关的写法
        #[account(
            init,
            payer = signer,
            space = 8 + Candidate::INIT_SPACE,
            seeds = [poll_id.to_le_bytes().as_ref(),candidate_name.as_bytes().as_ref()],
            bump,
        )]
        pub candidate: Account<'info, Candidate>,
        pub system_program: Program<'info, System>,
    }
    
    
    #[account]
    #[derive(InitSpace)]
    pub struct Candidate{
        #[max_len(32)]
        pub candidate_name: String,
        pub candidate_votes: u64,
    }
    
    
    
    #[derive(Accounts)]
    #[instruction(poll_id:u64)] //可以获取上下文中的 传递参数
    pub struct InitializePoll<'info> {
        #[account(mut)]
        pub signer: Signer<'info>,
        #[account(
            init,
            payer = signer,
            space = 8 + Poll::INIT_SPACE,
            seeds = [poll_id.to_le_bytes().as_ref()],
            bump,
        )]
        pub poll: Account<'info,Poll>,
        pub system_program: Program<'info, System>,
    }
    
    #[account]
    #[derive(InitSpace)]
    pub struct Poll {
        pub poll_id: u64,
        #[max_len(64)]
        pub description:String,
        pub poll_start: u64,
        pub poll_end: u64,
        pub candidate_amount: u64,
    }
  • 测试脚本

    rust 复制代码
    import * as anchor from "@coral-xyz/anchor";
    import { BankrunProvider, startAnchor } from "anchor-bankrun";
    import { Program } from "@coral-xyz/anchor";
    import { Keypair,PublicKey } from "@solana/web3.js"
    import {program} from "@coral-xyz/anchor/dist/cjs/native/system";
    import { AnchorVoting } from "../target/types/anchor_voting"
    const IDL = require("./anchor_voting.json");
    
    const  votingAddress = new PublicKey("8t44emZSpEShL9QtNSLsLEyq1GZFCExwdpfefGR5PjVe")
    
    describe("anchor-voting", () => {
    
      let context;
      let provider;
      let votingProgram;
    
    
    
      before(async ()=>{
         context = await startAnchor("",[{name:"anchor_voting",programId:votingAddress}],[])
         provider = new BankrunProvider(context);
         votingProgram = new Program<AnchorVoting>(
            IDL,
            provider,
        );
      })
    
    
      it("initialized! poll", async () => {
        //初始化 民意调查
        await  votingProgram.methods.initializePoll(
            new anchor.BN(1),
            "投票开始",
            new anchor.BN(0),
            new anchor.BN(18212646480)
    
        ).rpc();
    
        const [pollAddress] = PublicKey.findProgramAddressSync(
            [new anchor.BN(1).toArrayLike(Buffer,'le',8)],
            votingAddress
        );
        const poll = await votingProgram.account.poll.fetch(pollAddress);
        console.log("pollAddress:",pollAddress)
        console.log("poll:",poll)
      });
    
    
      it("initialized! candidate", async()=>{
        //初始化 候选人
        await votingProgram.methods.initializeCandidate(
            "野猪",
          new anchor.BN(1),
        ).rpc();
    
        await votingProgram.methods.initializeCandidate(
            "鸵鸟",
            new anchor.BN(1),
        ).rpc();
    
        const [crunchyAddress] = PublicKey.findProgramAddressSync(
            [new anchor.BN(1).toArrayLike(Buffer,'le',8),Buffer.from("鸵鸟")],
            votingAddress,
        )
        const  crunchyCandidate = await  votingProgram.account.candidate.fetch(crunchyAddress);
        console.log("crunchyAddress:",crunchyAddress)
        console.log("crunchyCandidate:",crunchyCandidate)
    
    
    
        const [crunchyAddress1] = PublicKey.findProgramAddressSync(
            [new anchor.BN(1).toArrayLike(Buffer,'le',8),Buffer.from("野猪")],
            votingAddress,
        )
        const  crunchyCandidate1 = await  votingProgram.account.candidate.fetch(crunchyAddress1);
        console.log("crunchyAddress1:",crunchyAddress1)
        console.log("crunchyCandidate1:",crunchyCandidate1)
    
      })
    
      it("vote", async()=>{
    
        await votingProgram.methods.vote(
            "鸵鸟",
            new anchor.BN(1),).rpc();
    
        await votingProgram.methods.vote(
            "鸵鸟",
            new anchor.BN(1),).rpc();
    
        const [crunchyAddress] = PublicKey.findProgramAddressSync(
            [new anchor.BN(1).toArrayLike(Buffer,'le',8),Buffer.from("鸵鸟")],
            votingAddress,
        )
        const  crunchyCandidate = await  votingProgram.account.candidate.fetch(crunchyAddress);
        console.log("crunchyAddress:",crunchyAddress)
        console.log("crunchyCandidate:",crunchyCandidate)
      })
    
    });
复制代码
  
相关推荐
美狐美颜sdk44 分钟前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
DeepSeek-大模型系统教程1 小时前
推荐 7 个本周 yyds 的 GitHub 项目。
人工智能·ai·语言模型·大模型·github·ai大模型·大模型学习
郭庆汝1 小时前
pytorch、torchvision与python版本对应关系
人工智能·pytorch·python
小雷FansUnion3 小时前
深入理解MCP架构:智能服务编排、上下文管理与动态路由实战
人工智能·架构·大模型·mcp
资讯分享周3 小时前
扣子空间PPT生产力升级:AI智能生成与多模态创作新时代
人工智能·powerpoint
叶子爱分享4 小时前
计算机视觉与图像处理的关系
图像处理·人工智能·计算机视觉
鱼摆摆拜拜4 小时前
第 3 章:神经网络如何学习
人工智能·神经网络·学习
一只鹿鹿鹿4 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
张较瘦_5 小时前
[论文阅读] 人工智能 | 深度学习系统崩溃恢复新方案:DaiFu框架的原位修复技术
论文阅读·人工智能·深度学习
cver1235 小时前
野生动物检测数据集介绍-5,138张图片 野生动物保护监测 智能狩猎相机系统 生态研究与调查
人工智能·pytorch·深度学习·目标检测·计算机视觉·目标跟踪