教程区块链区块链技术ch1717.4 前端合约调用:create_poll 与 cast_vote

本页目录

sequenceDiagram
    用户 ->> 前端: 点击创建投票/投票/查看结果
    前端 ->> AnchorProvider: 请求签名交易
    AnchorProvider ->> 钱包: 弹出签名确认
    钱包 -->> 用户: 用户确认
    钱包 -->> AnchorProvider: 签名完成
    AnchorProvider ->> 程序: 发送指令至 Solana devnet
    程序 -->> 程序: 更新合约状态/投票数据
    程序 -->> AnchorProvider: 返回确认交易ID
    前端 ->> 区块浏览器: 轮询 / 订阅确认状态
    前端 ->> 前端: 更新 UI

17.4.1 Anchor Provider 配置

typescript
// hooks/useVoteProgram.ts
import { useEffect, useState } from 'react';
import { Connection, PublicKey, clusterApiUrl } from '@solana/web3.js';
import { Program, AnchorProvider, web3 } from '@coral-xyz/anchor';
import { Vote, IDL } from '../types/vote';  // 自动生成的 IDL

const PROGRAM_ID = new PublicKey("Fg6...your_program_id...");
const connection = new Connection(clusterApiUrl('devnet'));

export const useVoteProgram = (wallet: any) => {
  const [program, setProgram] = useState<Program<Vote>>();
  
  useEffect(() => {
    if (!wallet) return;
    const provider = new AnchorProvider(
      connection,
      wallet,  // 需适配以 Phantom 签名器
      { commitment: 'confirmed' }
    );
    const program = new Program<Vote>(IDL, PROGRAM_ID, provider);
    setProgram(program);
  }, [wallet]);
  
  return program;
};

// 适配 Phantom 为 Anchor 钱包适配器
const getPhantomWallet = () => {
  const phantom = (window as any).phantom?.solana;
  return {
    publicKey: phantom?.publicKey ? new PublicKey(phantom.publicKey.toString()) : null,
    signTransaction: async (tx: any) => {
      const signed = await phantom.signTransaction(tx);
      return signed;
    },
    signAllTransactions: async (txs: any[]) => {
      return await phantom.signAllTransactions(txs);
    },
  };
};

17.4.2 调用 create_poll

typescript
// components/CreatePollForm.tsx
import { useState } from 'react';
import { PublicKey, Keypair } from '@solana/web3.js';

export const CreatePollForm = ({ program, wallet }: { program: any; wallet: any }) => {
  const [question, setQuestion] = useState('');
  const [options, setOptions] = useState(['Option A', 'Option B']);
  const [txId, setTxId] = useState('');

  const createPoll = async () => {
    // 生成新投票账户
    const voteAccount = Keypair.generate();
    
    const tx = await program.methods
      .createPoll(
        question,
        options,
        Math.floor(Date.now() / 1000) + 86400  // 24h 过期
      )
      .accounts({
        voteAccount: voteAccount.publicKey,
        creator: wallet.publicKey,
        systemProgram: web3.SystemProgram.programId,
      })
      .signers([voteAccount])
      .rpc();  // 自动发送并确认
    
    setTxId(tx);
    console.log("投票已创建:", tx);
  };

  return (
    <form onSubmit={(e) => { e.preventDefault(); createPoll(); }}>
      <h3>创建新投票</h3>
      <input value={question} onChange={e => setQuestion(e.target.value)} placeholder="问题" />
      {options.map((o, i) => (
        <input key={i} value={o} onChange={e => {
          const newOpts = [...options]; newOpts[i] = e.target.value; setOptions(newOpts);
        }} />
      ))}
      <button type="submit">创建投票</button>
      {txId && <div>交易: <a href={`https://explorer.solana.com/tx/${txId}?cluster=devnet`} target="_blank">{txId.slice(0, 16)}...</a></div>}
    </form>
  );
};

17.4.3 调用 cast_vote

typescript
// components/VoteCard.tsx
export const VoteCard = ({ program, wallet, voteAccount }: any) => {
  const [voteData, setVoteData] = useState<any>(null);

  // 加载投票数据
  const loadPoll = async () => {
    const data = await program.account.voteAccount.fetch(voteAccount);
    setVoteData(data);
  };

  const castVote = async (optionIndex: number) => {
    const tx = await program.methods
      .castVote(optionIndex)
      .accounts({
        voteAccount,
        voter: wallet.publicKey,
        systemProgram: web3.SystemProgram.programId,
      })
      .rpc();
    
    await loadPoll();  // 刷新数据
  };

  if (!voteData) { loadPoll(); return <div>加载中...</div>; }

  return (
    <div className="vote-card">
      <h4>{voteData.question}</h4>
      <p>状态: {voteData.isActive ? "进行中" : "已结束"}</p>
      {voteData.options.map((opt: string, i: number) => (
        <div key={i}>
          <span>{opt}: {voteData.votes[i]} 票</span>
          {voteData.isActive && !voteData.hasVoted.includes(wallet?.publicKey?.toString()) && (
            <button onClick={() => castVote(i)}>投票</button>
          )}
        </div>
      ))}
    </div>
  );
};

, 前往 → 17.5 完整 UI |*

评论

0

评论加载中…

发表评论

0/2000