You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Web应用签名并调用Solana链上Anchor智能合约

完整实现步骤

1. 前置准备

  • 安装前端所需依赖:
npm install @project-serum/anchor
  • 找到你Anchor工程部署后生成的IDL文件,路径是target/idl/basic_2.json,把这个文件复制到前端项目的src目录下。同时记录你部署合约时生成的程序ID(Program ID),确认IDL里的address字段和你实际部署到对应网络(devnet/localnet)的程序ID一致。
  • 提前明确:调用increment方法前必须先调用create方法创建counter账户,保存好counter账户的公钥,increment需要传入这个已存在的链上账户,无法凭空调用。如果还没创建,可以直接在前端实现create逻辑,创建时需要新生成一个Keypair作为counter账户的签名者。

2. 前端代码修改

首先修改文件头部的导入部分,新增Anchor相关引入和IDL导入:

import { Program, AnchorProvider, web3 } from '@project-serum/anchor';
// 导入你复制过来的IDL文件,路径按实际存放位置调整
import idl from './basic_2.json';
// 替换成你自己部署得到的合约Program ID
const PROGRAM_ID = new web3.PublicKey("你的合约Program ID");

在组件里connection定义的位置后面,新增初始化Anchor程序实例的逻辑:

// 放在connection定义代码之后
const anchorProvider = useMemo(() => {
  if (!selectedWallet?.publicKey) return null;
  // 把现有wallet实例包装成Anchor认可的provider格式
  const walletAdaptor = {
    publicKey: selectedWallet.publicKey,
    signTransaction: selectedWallet.signTransaction.bind(selectedWallet),
    signAllTransactions: selectedWallet.signAllTransactions?.bind(selectedWallet),
  };
  return new AnchorProvider(connection, walletAdaptor, {
    commitment: 'confirmed'
  });
}, [connection, selectedWallet]);

const program = useMemo(() => {
  if (!anchorProvider) return null;
  return new Program(idl as any, PROGRAM_ID, anchorProvider);
}, [anchorProvider]);

替换你原来的sendTransaction方法,补充创建counter和调用increment的逻辑:

// 创建counter账户,首次使用调用一次即可
async function createCounter() {
  try {
    if (!program || !selectedWallet?.publicKey) {
      throw new Error('请先连接钱包');
    }
    addLog('生成新的counter账户密钥对');
    const counterKeypair = web3.Keypair.generate();
    
    addLog('发起create交易');
    const txHash = await program.rpc.create(selectedWallet.publicKey, {
      accounts: {
        counter: counterKeypair.publicKey,
        user: selectedWallet.publicKey,
        systemProgram: web3.SystemProgram.programId,
      },
      signers: [counterKeypair]
    });
    
    addLog(`创建成功,counter地址:${counterKeypair.publicKey.toBase58()},交易哈希:${txHash}`);
    // 必须把counter公钥存下来,后续increment要用到
    localStorage.setItem('counterAddress', counterKeypair.publicKey.toBase58());
    
    // 查询链上初始状态
    const counterAccount = await program.account.counter.fetch(counterKeypair.publicKey);
    addLog(`初始count值:${counterAccount.count.toNumber()}`);
  } catch (e) {
    console.warn(e);
    addLog(`错误: ${(e as Error).message}`);
  }
}

// 调用increment方法
async function incrementCounter() {
  try {
    if (!program || !selectedWallet?.publicKey) {
      throw new Error('请先连接钱包');
    }
    // 读取之前存储的counter地址
    const counterAddressStr = localStorage.getItem('counterAddress');
    if (!counterAddressStr) {
      throw new Error('请先创建counter账户');
    }
    const counterPubkey = new web3.PublicKey(counterAddressStr);
    
    addLog('发起increment交易');
    const txHash = await program.rpc.increment({
      accounts: {
        counter: counterPubkey,
        authority: selectedWallet.publicKey
      }
    });
    
    addLog(`交易已提交,哈希:${txHash},等待链上确认`);
    await connection.confirmTransaction(txHash, 'confirmed');
    
    // 查询更新后的链上数据
    const counterAccount = await program.account.counter.fetch(counterPubkey);
    addLog(`increment执行成功,当前count值:${counterAccount.count.toNumber()}`);
  } catch (e) {
    console.warn(e);
    addLog(`错误: ${(e as Error).message}`);
  }
}

最后修改页面的按钮区域,替换原来的测试转账按钮,加上对应操作入口:

<button onClick={createCounter}>创建Counter账户</button>
<button onClick={incrementCounter}>调用Increment</button>

常见问题说明

  • 本地测试时需要把network从devnet改成本地节点地址http://localhost:8899,同时确保本地solana测试节点在运行、合约已经部署到对应节点。
  • create方法生成的counterKeypair仅在创建交易时需要签名,创建完成后只需要存储公钥即可,不需要保存私钥。
  • 如果调用时报账户不存在错误,先确认传入的counter地址是create方法生成的对应地址,且前端连接的网络和合约部署的网络一致。

内容的提问来源于stack exchange,提问作者DeeHawk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 05:18:25