Web3:在 VSCode 中使用 Vue 前端与已部署的 Solidity 智能合约进行交互

在 VSCode 中使用 React 前端与已部署的 Solidity 智能合约进行交互

    • 引言
      • [1.1 启动本地测试网络(如果使用 Anvil)](#1.1 启动本地测试网络(如果使用 Anvil))
      • [1.2 获取合约 ABI](#1.2 获取合约 ABI)
    • [2. 创建 React 项目](#2. 创建 React 项目)
      • [2.1 初始化项目](#2.1 初始化项目)
      • [2.2 安装 ethers.js](#2.2 安装 ethers.js)
      • [2.3 项目结构调整](#2.3 项目结构调整)
    • [3. 编写 React 组件与合约交互](#3. 编写 React 组件与合约交互)
      • [3.1 创建交互组件](#3.1 创建交互组件)
      • [3.2 集成到主应用](#3.2 集成到主应用)
      • [3.3 启动应用](#3.3 启动应用)
    • 结语
相关文章推荐 链接
Web3专栏 https://blog.csdn.net/qq_42392981/category_13016259.html

引言

如果你已经掌握了 Solidity 智能合约的编写、测试和本地部署 Web3:在 VSCode 中基于 Foundry 快速构建 Solidity 智能合约本地开发环境,下一步便是构建前端界面来与之交互。本教程聚焦在 VSCode 环境中使用 React.js 框架调用已部署的合约,适合 Web3 初学者。我们将以一个简单的 Counter 合约为例,演示如何使用 ethers.js 库在 React 组件中读取合约状态、调用函数,并处理交易响应。整个过程强调交互细节、错误处理和调试技巧,确保你能构建一个可靠的前端 DApp 界面。

前提条件


1.1 启动本地测试网络(如果使用 Anvil)

前面的文章已经介绍了怎么部署Web3:在 VSCode 中基于 Foundry 快速构建 Solidity 智能合约本地开发环境

假设合约已部署到本地 Anvil:

  1. 在 VSCode 终端 Git bash 中运行:

    git 复制代码
    anvil --accounts 10 --balance 100
  2. 记录 RPC URL(http://127.0.0.1:8545)和测试账户私钥(用于签名交易)。

1.2 获取合约 ABI

ABI 是合约接口定义,用于前端调用。从 Foundry 项目中提取:

  1. 在 Foundry 项目目录运行:

    powershell 复制代码
    forge build
  2. ABI 文件位于 out/Counter.sol/Counter.json 中的 "abi" 字段。复制 ABI 数组:

json 复制代码
[
    {
      "type": "function",
      "name": "increment",
      "inputs": [],
      "outputs": [],
      "stateMutability": "nonpayable"
    },
    {
      "type": "function",
      "name": "number",
      "inputs": [],
      "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }],
      "stateMutability": "view"
    },
    {
      "type": "function",
      "name": "setNumber",
      "inputs": [
        { "name": "newNumber", "type": "uint256", "internalType": "uint256" }
      ],
      "outputs": [],
      "stateMutability": "nonpayable"
    }
  ]

2. 创建 React 项目

2.1 初始化项目

  1. 在 VSCode 终端导航到项目目录(例如 C:\Projects)。

  2. 创建 React 项目:

    powershell 复制代码
    npx create-react-app react-contract-interaction
    cd react-contract-interaction
  3. React 目录结构

2.2 安装 ethers.js

安装 ethers.js 以处理合约交互:

powershell 复制代码
npm install ethers

2.3 项目结构调整

  • 创建 src/contracts/ 目录,添加 CounterABI.js

    javascript 复制代码
    export const COUNTER_ABI = [
      {
        "type": "function",
        "name": "increment",
        "inputs": [],
        "outputs": [],
        "stateMutability": "nonpayable"
      },
      {
        "type": "function",
        "name": "number",
        "inputs": [],
        "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }],
        "stateMutability": "view"
      },
      {
        "type": "function",
        "name": "setNumber",
        "inputs": [
          { "name": "newNumber", "type": "uint256", "internalType": "uint256" }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
      }
    ];
    export const COUNTER_ADDRESS = '0x5FbDB2315678afecb367f032d93F642f64180aa3'; // 替换为实际合约地址
    export const RPC_URL = 'http://127.0.0.1:8545'; // 测试网 RPC

3. 编写 React 组件与合约交互

3.1 创建交互组件

src/components/ 下创建 CounterInteraction.js(使用类组件或函数组件;这里用 hooks 的函数组件)。这个组件将:

  • 使用 useState 管理状态。
  • 使用 useEffect 初始化连接。
  • 连接到提供者(Provider)。
  • 使用签名者(Signer)调用写函数。
  • 读取合约状态。
  • 处理加载状态和错误。

完整代码:

jsx 复制代码
import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import { COUNTER_ABI, COUNTER_ADDRESS, RPC_URL } from '../contracts/CounterABI';

const CounterInteraction = () => {
  const [number, setNumber] = useState(0);
  const [newNumber, setNewNumber] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [txHash, setTxHash] = useState('');
  const [contract, setContract] = useState(null);

  useEffect(() => {
    const initialize = async () => {
      try {
        // 初始化提供者(无签名,用于读取)
        const provider = new ethers.JsonRpcProvider(RPC_URL);
        
        // 初始化签名者(使用私钥,用于写入;生产环境用钱包如 MetaMask)
        const privateKey = '0xYourPrivateKeyFromAnvil'; // 替换为 Anvil 私钥,安全起见勿硬编码
        const signer = new ethers.Wallet(privateKey, provider);
        
        // 初始化合约实例(连接签名者以支持写入)
        const contractInstance = new ethers.Contract(COUNTER_ADDRESS, COUNTER_ABI, signer);
        setContract(contractInstance);
        
        await fetchNumber(contractInstance);
      } catch (err) {
        setError('初始化失败: ' + err.message);
      }
    };
    initialize();
  }, []);

  const fetchNumber = async (contractInstance) => {
    setLoading(true);
    try {
      const currentNumber = (await contractInstance.number()).toString();
      setNumber(currentNumber);
      setError('');
    } catch (err) {
      setError('读取失败: ' + err.message);
    } finally {
      setLoading(false);
    }
  };

  const increment = async () => {
    if (!contract) return;
    setLoading(true);
    try {
      const tx = await contract.increment();
      await tx.wait(); // 等待交易确认
      setTxHash(tx.hash);
      await fetchNumber(contract);
    } catch (err) {
      setError('递增失败: ' + err.message);
    } finally {
      setLoading(false);
    }
  };

  const handleSetNumber = async () => {
    if (!contract || !newNumber) return;
    setLoading(true);
    try {
      const tx = await contract.setNumber(newNumber);
      await tx.wait();
      setTxHash(tx.hash);
      await fetchNumber(contract);
      setNewNumber('');
    } catch (err) {
      setError('设置失败: ' + err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="counter-container">
      <h2>与 Counter 合约交互</h2>
      {loading ? <p>加载中...</p> : <p>当前数值: {number}</p>}
      <button onClick={increment} disabled={loading}>递增</button>
      <input
        type="number"
        value={newNumber}
        onChange={(e) => setNewNumber(e.target.value)}
        placeholder="设置新值"
      />
      <button onClick={handleSetNumber} disabled={loading}>设置数值</button>
      {error && <p className="error">{error}</p>}
      {txHash && <p>最近交易哈希: {txHash}</p>}
    </div>
  );
};

export default CounterInteraction;

解释细节

  • Hooks 使用useState 管理本地状态,useEffect 处理初始化(仅运行一次)。
  • Provider vs Signer :Provider 用于只读调用(如 number()),Signer 用于写入(如 increment()),需私钥签名。
  • 异步处理 :使用 async/await 管理交易,tx.wait() 等待区块确认。
  • 错误处理:捕获异常(如 gas 不足、网络错误),显示用户友好消息。
  • 加载状态:禁用按钮防止重复点击。
  • 安全性提示 :本地开发用私钥硬编码;生产环境集成 MetaMask,使用 window.ethereum 请求签名。

3.2 集成到主应用

编辑 src/App.js

jsx 复制代码
import React from 'react';
import CounterInteraction from './components/CounterInteraction';
import './App.css';

function App() {
  return (
    <div className="App">
      <CounterInteraction />
    </div>
  );
}

export default App;

添加简单 CSS 到 src/App.css

css 复制代码
.App { text-align: center; margin-top: 50px; }
.counter-container { margin: 20px; }
.error { color: red; }

3.3 启动应用

在 VSCode 终端运行:

powershell 复制代码
npm start

访问 http://localhost:3000,测试交互:

  • 点击"递增":数值 +1。
  • 输入并设置:更新数值。

界面如下图所示:

结语

在 VSCode 中使用 React 调用 Solidity 合约的核心流程:从初始化连接到处理读写操作和错误。实践这些步骤,能轻松扩展到更复杂的 DApp,如 NFT 市场或 DeFi 界面。记住,生产部署时优先使用钱包集成,并审计合约安全。

相关推荐
zhaqonianzhu8 小时前
【vsc】cpptools占用内存过大
vscode
MQLYES13 小时前
04-BTC-协议
区块链
智慧地球(AI·Earth)14 小时前
Codex配置问题解析:wire_api格式不匹配导致的“Reconnecting...”循环
开发语言·人工智能·vscode·codex·claude code
m0_6038887115 小时前
Decentralized Autoregressive Generation
ai·去中心化·区块链·论文速览
markvivv17 小时前
在 Kylin Linux Advanced Server for Kunpeng V10 上构建 VSCode 1.106
linux·vscode·kylin
zhangfeng113317 小时前
Kiro python环境的设置 中文语言包设置,通用vscode ,因为kiro是vscode基础上做的
开发语言·vscode·python
微醺的老虎18 小时前
【工具】vscode格式化json文件
ide·vscode·编辑器
乔宕一19 小时前
vscode 设置每次调试 powershell 脚本都使用临时的 powershell 终端
ide·vscode·编辑器
反向跟单策略20 小时前
期货反向跟单—高频换人能够提高跟单效率?
大数据·人工智能·学习·数据分析·区块链
唐装鼠20 小时前
linux vscode解压版 AI账号无法登陆问题(浏览器无法打开vscode)
linux·运维·vscode