Node.js 与以太坊,搭建区块链交互的桥梁
区块链技术,尤其是以太坊,以其智能合约的强大功能,正在重塑多个行业的运作方式,对于开发者而言,如何将现有的应用与以太坊网络连接,实现数据的读取与写入,是一个核心问题,Node.js,凭借其异步、事件驱动的特性以及丰富的生态系统,成为了与以太坊交互的理想选择,本文将详细介绍如何使用Node.js访问以太坊网络,包括连接节点、读取数据、发送交易以及与智能合约交互等关键步骤。
为何选择Node.js访问以太坊
在选择技术栈时,Node.js具有以下优势:
- 异步I/O模型:以太坊交互涉及大量的网络请求(如发送交易、查询状态),Node.js的非阻塞I/O特性能够高效处理这些并发操作,提升应用性能。
- JavaScript/TypeScript生态:以太坊官方和社区提供了大量基于JavaScript的库(如Web3.js、Ethers.js),使得开发者可以使用熟悉的语言进行开发,降低了学习成本。
- 全栈开发能力:如果前端或后端已经使用了Node.js,使用Node.js与以太坊交互可以保持技术栈的一致性,简化开发流程。
- 丰富的npm包:npm拥有海量的JavaScript包,其中许多可以辅助以太坊开发,如加密、数据处理等。
前置条件:以太坊节点的接入
Node.js本身不能直接与以太坊区块链通信,它需要一个“中间人”——以太坊节点,节点是运行以太坊协议的计算机,维护着区块链的副本,开发者可以通过以下方式接入以太坊节点:
-
自己运行节点:
- Geth:以太坊官方的Go语言客户端功能全面,但资源消耗较大。
- Parity:另一种流行的客户端,以性能和易用性著称。
- 优缺点:完全控制数据,隐私性好,但需要较高的硬件配置和持续的网络连接,同步区块可能耗时较长。
-
使用Infura等第三方服务:
- Infura是一个提供以太坊节点服务的平台,开发者可以免费或付费获得一个节点连接URL。
- 优缺点:无需维护节点,开箱即用,稳定可靠,适合大多数开发者,但数据依赖于第三方,且免费版可能有速率限制。
-
使用Alchemy等类似服务:
Alchemy是另一个高质量的节点服务提供商,专注于开发者体验,提供强大的API和工具。
对于初学者或大多数应用场景,推荐使用Infura或Alchemy等第三方服务,简单快捷。
核心库的选择:Web3.js vs. Ethers.js
要与以太坊交互,我们需要一个JavaScript库,目前最主流的是Web3.js和Ethers.js。
-
Web3.js:
- 以太坊官方维护的库,历史较悠久,社区庞大,文档相对完善。
- 提供了与以太坊JSON-RPC API的直接映射。
- API设计相对底层,某些操作可能需要更多代码。
-
Ethers.js:
- 由社区开发,近年来迅速崛起,以其优雅的API设计和更好的开发者体验著称。
- 提供了更高层次的抽象,如合约实例、ABI编码解码等,使用起来更便捷。
- 内置了对Provider、Signer、Contract等核心概念的清晰封装。
推荐:新项目可以考虑优先使用Ethers.js,其API设计更现代化,开发效率更高,但Web3.js依然非常稳定且广泛使用,维护现有项目时可能仍需使用。
使用Node.js访问以太坊实战
下面我们以更流行的Ethers.js为例,展示基本操作。
环境准备
确保你的系统已安装Node.js和npm,然后创建一个新的项目目录并初始化:
mkdir node-ethereum-example cd node-ethereum-example npm init -y
安装Ethers.js:
npm install ethers
连接到以太坊网络
以Infura为例,你需要注册Infura并创建一个新的项目,获取一个以https://开头的RPC URL(以太坊主网测试网如Goerli的URL)。
创建一个connect.js文件:
运行node connect.js,如果配置正确,你应该能看到最新的以太坊测试网区块号。
读取链上数据
获取账户余额:
// 延续上面的代码
async function getBalance(address) {
try {
const balance = await provider.getBalance(address);
// ethers.utils.formatEther将wei转换为ether
console.log(`地址 ${address} 的余额: ${ethers.utils.formatEther(balance)} ETH`);
} catch (error) {
console.error("获取余额失败:", error);
}
}
// 替换为你要查询的地址
const addressToQuery = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B";
getBalance(addressToQuery);
发送交易(需要钱包)
发送交易需要私签名,这通常意味着你需要一个以太坊钱包(如MetaMask)来管理账户。注意:私钥绝对不能硬编码在代码中或提交到版本控制! 应该使用环境变量或安全的密钥管理服务。
require('dotenv').config(); // 用于加载.env文件中的环境变量
// npm install dotenv
// 延续上面的代码
async function sendTransaction() {
// 从环境变量获取私钥(实际项目中应更安全地管理)
const privateKey = process.env.PRIVATE_KEY;
if (!privateKey) {
console.error("请设置PRIVATE_KEY环境变量");
return;
}
const wallet = new ethers.Wallet(privateKey, provider);
// 接收方地址
const recipientAddress = "0x1234567890123456789012345678901234567890";
// 发送的ETH数量(单位:ether)
const amountToSend = ethers.utils.parseEther("0.01"); // 0.01 ETH
// 构建交易
const tx = {
to: recipientAddress,
value: amountToSend,
gasLimit: 21000, // 转账ETH的典型gas限制
gasPrice: await provider.getGasPrice(), // 获取当前gas价格
};
// 发送交易
console.log("正在发送交易...");
const txResponse = await wallet.sendTransaction(tx);
console.log("交易哈希:", txResponse.hash);
// 等待交易被确认
const txReceipt = await txResponse.wait();
console.log("交易确认成功,区块号:", txReceipt.blockNumber);
}
// 调用发送交易函数(确保钱包地址有足够的ETH支付gas和转账金额)
// sendTransaction();
在项目根目录创建.env文件:
PRIVATE_KEY=你的钱包私钥
并确保.env文件被添加到.gitignore中。
与智能合约交互
这是Node.js访问以太坊最强大的功能之一,你需要:
- 智能合约的ABI(Application Binary Interface):一个JSON文件,描述了合约的接口(函数、事件、变量等)。
- 智能合约的地址:部署到以太坊网络后的合约地址。
假设我们有一个简单的存储合约,有一个store(uint256)函数和一个retrieve()函数。
// 延续上面的代码
async function interactWithContract() {
// 合约地址和ABI(这里简化,实际应从文件读取)
const contractAddress = "0x你的合约地址";
const contractABI = [
"function store(uint256 num)",
"function retrieve() view returns (uint256)"
];
// 创建合约实例
const contract = new ethers.Contract(contractAddress, contractABI, provider);
// 1. 读取合约数据(调用retrieve函数)
try {
const storedValue = await contract.retrieve();
console.log("合约中存储的值:", storedValue.toString());
} catch (error) {
console.error("读取合约数据失败:", error);
}
// 2. 写入合约数据(调用store函数)
// 注意:这需要签名账户,所以我们需要使用wallet而不是provider
const connectedContract = contract.connect(wallet); // 将合约实例连接到签名账户
const newValue = ethers.Big
