普通视图

发现新文章,点击刷新页面。
昨天以前首页

ethers.js 开发的核心场景

作者 微客鸟窝
2025年8月13日 15:50

一、核心概念与安装(需掌握)

  • 核心定位:ethers.js 是轻量级以太坊交互库,专注于与区块链、智能合约的高效交互,相比 Web3.js 更简洁,安全性更高。
  • 安装方式
npm install ethers  # 基础安装
# 若需兼容 TypeScript,无需额外安装类型文件,库本身自带类型定义

二、Provider(需掌握)

作用:作为与区块链节点的连接层,负责读取链上数据(区块、交易、余额等),不涉及签名操作。

1. 常用 Provider 实例化
  • JsonRpcProvider(连接自定义节点,如本地 Ganache 或私链):
// 连接本地 8545 端口的节点(Ganache 默认端口)
const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
  • InfuraProvider(连接 Infura 公共节点,需注册获取项目 ID):
// 连接以太坊主网('mainnet'),替换为你的 Infura 项目 ID
const provider = new ethers.providers.InfuraProvider(
  'mainnet', 
  'your-infura-project-id-here'
);
// 支持测试网:'goerli'、'sepolia' 等
const goerliProvider = new ethers.providers.InfuraProvider('goerli', 'your-id');
2. 核心方法及示例
  • getBlockNumber() :获取当前区块高度
const blockNumber = await provider.getBlockNumber();
console.log('当前区块号:', blockNumber); // 输出类似:18000000
  • getBalance(address) :获取指定地址的 ETH 余额(单位:wei)
const address = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'; // 示例地址
const balanceWei = await provider.getBalance(address);
// 转换为以太单位(1 ETH = 1e18 wei)
const balanceEth = ethers.utils.formatEther(balanceWei);
console.log('ETH 余额:', balanceEth); // 输出类似:'1.2345'
  • getTransaction(transactionHash) :根据交易哈希查询交易详情
const txHash = '0x0a8...'; // 交易哈希(66 字符,0x 开头)
const transaction = await provider.getTransaction(txHash);
console.log('交易详情:', {
  from: transaction.from,    // 发送地址
  to: transaction.to,        // 接收地址(合约调用时为合约地址)
  value: ethers.utils.formatEther(transaction.value), // 转账金额(ETH)
  gasPrice: ethers.utils.formatUnits(transaction.gasPrice, 'gwei'), // gas 价格(gwei)
  blockNumber: transaction.blockNumber // 所在区块号
});
  • getBlock(blockNumber) :获取指定区块的详细信息
const block = await provider.getBlock(18000000); // 传入区块号
console.log('区块信息:', {
  hash: block.hash,         // 区块哈希
  timestamp: new Date(block.timestamp * 1000), // 区块时间(转换为北京时间)
  transactions: block.transactions.length     // 区块内交易数量
});
  • getCode(address) :获取地址的合约字节码(判断是否为合约地址,普通地址返回 0x)
const isContract = await provider.getCode(address);
console.log('是否为合约:', isContract !== '0x'); // 合约地址返回 true

三、Signer(需掌握)

作用:代表一个以太坊账户,具备签名交易和消息的能力,可发送交易(修改链上状态)。

1. 常用 Signer 实例化
  • 从私钥创建(适用于后端或测试环境,私钥需保密):
// 私钥(64 字符,0x 开头)
const privateKey = '0x123...'; // 实际开发中从环境变量获取,避免硬编码
const signer = new ethers.Wallet(privateKey, provider); // 关联 provider
  • 从浏览器钱包获取(如 MetaMask,适用于前端):
// 假设页面已注入 window.ethereum(MetaMask 提供)
await window.ethereum.enable(); // 请求用户授权
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner(); // 获取当前选中的账户签名器
2. 核心方法及示例
  • getAddress() :获取签名者地址
const signerAddress = await signer.getAddress();
console.log('签名者地址:', signerAddress); // 输出类似:'0xabc...'
  • sendTransaction(transaction) :发送 ETH 转账交易
const toAddress = '0x123...'; // 接收地址
const transaction = {
  to: toAddress,
  value: ethers.utils.parseEther('0.01') // 转账 0.01 ETH(转换为 wei)
};
// 发送交易并等待确认(返回交易收据)
const txResponse = await signer.sendTransaction(transaction);
const txReceipt = await txResponse.wait(); // 等待区块确认(默认 1 个确认)
console.log('交易确认:', txReceipt.transactionHash); // 输出交易哈希
  • signMessage(message) :对消息进行签名(用于身份验证)
const message = '请确认登录,时间:' + new Date().toISOString();
const signature = await signer.signMessage(message);
console.log('消息签名:', signature); // 输出 132 字符的签名
// 验证签名(可选,用于后端校验)
const recoveredAddress = ethers.utils.verifyMessage(message, signature);
console.log('签名是否匹配:', recoveredAddress === signerAddress); // true

四、Contract(需掌握)

作用:与智能合约交互的核心类,需传入合约地址、ABI 和 Signer/Provider(只读操作可用 Provider,写操作需 Signer)。

1. 实例化 Contract
// 合约地址(示例:USDC 合约主网地址)
const contractAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
// ABI(仅需包含调用的函数/事件,可从 Etherscan 或编译输出中获取)
const abi = [
  'function balanceOf(address) view returns (uint256)', // 只读方法
  'function transfer(address to, uint256 amount) returns (bool)' // 写方法
];
// 实例化合约(写操作需传入 signer,只读操作可传入 provider)
const contract = new ethers.Contract(contractAddress, abi, signer);
2. 调用合约方法
  • 调用 view/pure 方法(只读,不产生交易):
// 调用 ERC20 的 balanceOf 方法查询余额(单位:代币最小单位,如 USDC 为 6 位小数)
const userAddress = '0x742d...';
const balanceWei = await contract.balanceOf(userAddress); // 返回 BigNumber
// 转换为可读格式(USDC 精度为 6,即 1 USDC = 1e6 最小单位)
const balanceUsdc = ethers.utils.formatUnits(balanceWei, 6);
console.log('USDC 余额:', balanceUsdc); // 输出类似:'100.50'
  • 调用修改状态的方法(产生交易,需签名):
// 调用 ERC20 的 transfer 方法转账
const to = '0x123...'; // 接收地址
const amount = ethers.utils.parseUnits('50', 6); // 转账 50 USDC(转换为最小单位)
const txResponse = await contract.transfer(to, amount);
const txReceipt = await txResponse.wait(); // 等待交易确认
console.log('转账成功,交易哈希:', txReceipt.transactionHash);
3. 监听合约事件
// 监听 ERC20 的 Transfer 事件(事件名区分大小写)
contract.on('Transfer', (from, to, value, event) => {
  console.log('Transfer 事件:');
  console.log('从:', from);
  console.log('到:', to);
  console.log('金额(USDC):', ethers.utils.formatUnits(value, 6));
  console.log('事件详情:', event); // 包含事件所在区块、日志等信息
});
// 停止监听(避免内存泄漏)
// contract.removeAllListeners('Transfer');

五、ABI 与合约交互细节(需掌握)

  • ABI 结构:JSON 数组,每个元素描述函数或事件,例如:
[
  {
    "constant": true, // view/pure 方法为 true
    "inputs": [{"name": "owner", "type": "address"}],
    "name": "balanceOf",
    "outputs": [{"name": "", "type": "uint256"}],
    "type": "function"
  },
  {
    "anonymous": false,
    "inputs": [
      {"indexed": true, "name": "from", "type": "address"},
      {"indexed": true, "name": "to", "type": "address"},
      {"indexed": false, "name": "value", "type": "uint256"}
    ],
    "name": "Transfer",
    "type": "event"
  }
]
    • 函数的 constant: true 对应 Solidity 的 view/pure,调用时无需交易。
    • 事件的 indexed 字段表示该参数可用于过滤(最多 3 个)。
  • 函数调用参数处理
    • 基本类型(uint256、address 等)直接传入对应值。
    • 数组类型需传入 JavaScript 数组,例如 [1, 2, 3]。
    • 结构体需按 ABI 定义的顺序传入字段值。

六、Utils 工具函数(需掌握部分)

1. 单位转换
  • parseEther(etherString) :将 ETH 字符串转换为 wei(BigNumber)
const oneEthInWei = ethers.utils.parseEther('1.0'); // 1e18 wei
console.log(oneEthInWei.toString()); // '1000000000000000000'
  • formatEther(wei) :将 wei 转换为 ETH 字符串
const wei = ethers.BigNumber.from('2500000000000000000'); // 2.5 ETH
const eth = ethers.utils.formatEther(wei); // '2.5'
  • parseUnits(value, decimals) :通用单位转换(适用于代币,如 USDC 为 6 位小数)
// 50 USDC(6 位小数)转换为最小单位
const usdcAmount = ethers.utils.parseUnits('50', 6); // 50 * 1e6 = 50000000
  • formatUnits(wei, decimals) :反向转换(代币最小单位转可读格式)
const usdcInSmallestUnit = ethers.BigNumber.from('12345678'); // 12.345678 USDC
const usdc = ethers.utils.formatUnits(usdcInSmallestUnit, 6); // '12.345678'
2. 地址操作
  • isAddress(address) :验证地址合法性(返回布尔值)
console.log(ethers.utils.isAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')); // true
console.log(ethers.utils.isAddress('invalid-address')); // false
  • getAddress(address) :标准化地址(转换为 checksum 格式,大小写区分)
const normalized = ethers.utils.getAddress('0x742d35cc6634c0532925a3b844bc454e4438f44e');
console.log(normalized); // '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'(带大小写)

七、错误处理(需掌握)

区块链交互可能因网络、Gas、权限等原因失败,需用 try/catch 捕获错误。

async function sendTransaction() {
  try {
    const txResponse = await signer.sendTransaction({
      to: '0x123...',
      value: ethers.utils.parseEther('0.01')
    });
    console.log('交易发送成功,哈希:', txResponse.hash);
    const receipt = await txResponse.wait();
    console.log('交易确认,区块:', receipt.blockNumber);
  } catch (error) {
    // 常见错误处理
    if (error.code === ethers.errors.CALL_EXCEPTION) {
      console.error('合约调用失败(可能是方法不存在或参数错误)');
    } else if (error.code === ethers.errors.INSUFFICIENT_FUNDS) {
      console.error('余额不足,无法支付转账金额或 Gas');
    } else if (error.message.includes('User rejected transaction')) {
      console.error('用户拒绝了交易签名(MetaMask 等钱包)');
    } else {
      console.error('未知错误:', error.message);
    }
  }
}

以上内容覆盖了 ethers.js 开发的核心场景,掌握这些函数和用法后,可应对绝大多数智能合约交互、交易处理和链上数据查询需求。实际开发中,建议结合官方文档(docs.ethers.io/v5/)和具体场景灵活运用。

❌
❌