当前位置: 首页 > news >正文

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

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

    • 引言
      • 1.1 启动本地测试网络(如果使用 Anvil)
      • 1.2 获取合约 ABI
    • 2. 创建 React 项目
      • 2.1 初始化项目
      • 2.2 安装 ethers.js
      • 2.3 项目结构调整
    • 3. 编写 React 组件与合约交互
      • 3.1 创建交互组件
      • 3.2 集成到主应用
      • 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 界面。

前提条件

  • 已部署 Counter 合约(本地 Anvil 网络或其他测试网),并记录合约地址和 ABI。
  • Windows 10 或更高版本。
  • 已安装 VSCode、Node.js 和 Foundry(参考前文: Web3:在 VSCode 中基于 Foundry 快速构建 Solidity 智能合约本地开发环境)。
  • 基本 React 和 JavaScript 知识。

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

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

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

  1. 在 VSCode 终端 Git bash 中运行:
    anvil --accounts 10 --balance 100
    
  2. 记录 RPC URL(http://127.0.0.1:8545)和测试账户私钥(用于签名交易)。

1.2 获取合约 ABI

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

  1. 在 Foundry 项目目录运行:
    forge build
    
  2. ABI 文件位于 out/Counter.sol/Counter.json 中的 “abi” 字段。复制 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"}]

**建议截图位置**:ABI JSON 文件在 VSCode 中的视图。


2. 创建 React 项目

2.1 初始化项目

  1. 在 VSCode 终端导航到项目目录(例如 C:\Projects)。
  2. 创建 React 项目:
    npx create-react-app react-contract-interaction
    cd react-contract-interaction
    
  3. React 目录结构
    在这里插入图片描述

2.2 安装 ethers.js

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

npm install ethers

2.3 项目结构调整

  • 创建 src/contracts/ 目录,添加 CounterABI.js
    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)调用写函数。
  • 读取合约状态。
  • 处理加载状态和错误。

完整代码:

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><inputtype="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

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

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

3.3 启动应用

在 VSCode 终端运行:

npm start

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

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

界面如下图所示:
在这里插入图片描述

结语

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

http://www.lryc.cn/news/605467.html

相关文章:

  • 从渠道渗透到圈层渗透:开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新路径研究
  • 【09】大恒相机SDK C#开发 ——库函数 IntPtr ConvertToRGB24详细解释 及 示例
  • 【JEECG】JVxeTable表格拖拽排序功能
  • 动态规划Day5学习心得
  • python的异步、并发开发
  • (C++)C++类和类的方法(基础教程)(与Python类的区别)
  • C++提高编程学习--模板
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-50,(知识点:TCP/IP 模型)
  • 磁盘IO优先级控制对美国服务器存储子系统的调优验证
  • 02 基于sklearn的机械学习-KNN算法、模型选择与调优(交叉验证、朴素贝叶斯算法、拉普拉斯平滑)、决策树(信息增益、基尼指数)、随机森林
  • 【动态规划 | 多状态问题】动态规划求解多状态问题
  • YOLO---01目标检测基础
  • windows环境下MySQL 8.0 修改或重置密码
  • Mybatis分页查询当前页数据条数大于实际返回的数据条数
  • 7.5将模块分离到不同的文件中
  • 【JEECG】BasicTable单元格编辑,插槽添加下拉组件样式错位
  • 跳跃表可视化深度解析:动态演示数据结构核心原理
  • Docker-01.Docker课程介绍
  • 分层解耦(Controller,Service,Dao)
  • 从映射到共生:元宇宙、物联网与AI的智能融合生态图谱
  • nav2--安装/教程
  • 如何保证数据库的持久性与一致性:从 Linux 磁盘缓存策略到 MySQL 的设计
  • [SKE]使用gmssl库实现AES、SM4、DES、RSA、3DES_EDE和3DES_EEE算法的加密/解密参考模型
  • GitPython01-依赖排查
  • 8. 网络层
  • Linux系统编程Day1-- 免费云服务器获取以及登录操作
  • 【25届数字IC秋招总结】面试经验12——海康威视
  • LeetCode 面试经典 150_数组/字符串_轮转数组(6_189_C++_中等)(额外数组;转置)
  • DIV 指令概述
  • kali Linux 2025.2安装教程(解决安装失败-图文教程超详细)