当前位置: 首页 > 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.dtcms.com/a/307470.html

相关文章:

  • Kotlin -> 普通Lambda vs 挂起Lambda
  • Astra主题WooCommerce如何添加可变产品Astra variation product
  • tplink er2260t配置vlan透传iptv
  • python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件理解
  • 数据结构: 双向列表
  • 银河麒麟桌面操作系统:自定义截图快捷键操作指南
  • NXP i.MX8MP GPU 与核心库全景解析
  • rapidocr_web v1.0.0发布了
  • 旧物重生,交易有温度——旧物回收二手交易小程序,让生活更美好
  • 从“碎片化”到“完美重组”:IP报文的分片艺术
  • 从遮挡难题到精准测量:激光频率梳技术如何实现深孔 3D 轮廓的 2um 级重复精度?
  • 《Java 程序设计》第 15 章 - 事件处理与常用控件
  • 【Python修仙编程】(二) Python3灵源初探(9)
  • 无人机飞控系统3D (C++)实践
  • Coze Studio概览(四)--Prompt 管理功能详细分析
  • React的基本语法和原理
  • 力扣 Pandas 挑战(6)---数据合并
  • 融媒体中心网络安全应急预案(通用技术框架)
  • 【Debian】4-‌2 Gitea搭建
  • 专业鼠标点击器,自定义间隔次数
  • 前端核心技术Node.js(五)——Mongodb、Mongoose和接口
  • [mind-elixir]Mind-Elixir 的交互增强:单击、双击与鼠标 Hover 功能实现
  • 解决宇道项目关于接收日期格式yyyy-MM-dd HH:mm:ss后端自动转为1970-01-01 00:00:00的问题
  • 思途JSP学习 0731
  • 红黑树×协程×内存序:2025 C++后端核心三体问题攻防手册
  • LeetCode Hot 100:42. 接雨水
  • MCU中的RTC(Real-Time Clock,实时时钟)是什么?
  • 聊聊接口测试依赖第三方数据测试策略
  • mysql主从搭建(docker)
  • Verilog与SytemVerilog差别