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)
      })
    
    });
复制代码
  
相关推荐
什么都想学的阿超7 分钟前
【大语言模型 01】注意力机制数学推导:从零实现Self-Attention
人工智能·语言模型·自然语言处理
大千AI助手2 小时前
SWE-bench:真实世界软件工程任务的“试金石”
人工智能·深度学习·大模型·llm·软件工程·代码生成·swe-bench
天上的光2 小时前
17.迁移学习
人工智能·机器学习·迁移学习
后台开发者Ethan3 小时前
Python需要了解的一些知识
开发语言·人工智能·python
猫头虎3 小时前
猫头虎AI分享|一款Coze、Dify类开源AI应用超级智能体快速构建工具:FastbuildAI
人工智能·开源·prompt·github·aigc·ai编程·ai-native
重启的码农3 小时前
ggml 介绍 (6) 后端 (ggml_backend)
c++·人工智能·神经网络
重启的码农3 小时前
ggml介绍 (7)后端缓冲区 (ggml_backend_buffer)
c++·人工智能·神经网络
数据智能老司机3 小时前
面向企业的图学习扩展——图简介
人工智能·机器学习·ai编程
mit6.8244 小时前
[AI React Web] 包与依赖管理 | `axios`库 | `framer-motion`库
前端·人工智能·react.js