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)
      })
    
    });
复制代码
  
相关推荐
莫彩3 分钟前
【大模型论文阅读】2503.01821_On the Power of Context-Enhanced Learning in LLMs
论文阅读·人工智能·语言模型
hhhh明12 分钟前
【调试Bug】网络在训练中输出NaN
人工智能·算法
里昆23 分钟前
【AI】Jupyterlab中数据集的位置和程序和Pycharm中的区别
人工智能·学习
WSSWWWSSW31 分钟前
基于模拟的流程为灵巧机器人定制训练数据
人工智能·chatgpt·机器人
大视码垛机35 分钟前
协作机器人掀起工厂革命:码垛场景如何用数据重塑制造业命脉?
大数据·数据库·人工智能
呆头鹅AI工作室37 分钟前
[2025CVPR-图象分类方向]SPARC:用于视觉语言模型中零样本多标签识别的分数提示和自适应融合
图像处理·人工智能·python·深度学习·神经网络·计算机视觉·语言模型
_abab38 分钟前
图书推荐-由浅入深的大模型构建《从零构建大模型》
人工智能·语言模型·自然语言处理
初恋叫萱萱39 分钟前
Kimi K2 大语言模型技术特性与应用实践分析
人工智能·语言模型·自然语言处理
居然JuRan1 小时前
RAG 技术落地:从文档处理到模型输出,细节决定大模型应用效果
人工智能
钉钉开发者社区1 小时前
AI开放课堂:钉钉MCP开发实战
人工智能·钉钉