或者

Web3.js 入门:如何调用以太坊智能合约与交互**


在区块链的世界里,以太坊作为领先的智能合约平台,允许开发者构建去中心化应用(DApps),而要在前端应用(如网页)与以太坊网络进行交互,Web3.js 无疑是最核心、最常用的工具之一,本文将带你深入了解 Web3.js,并展示如何使用它来调用以太坊网络上的智能合约。

什么是 Web3.js

Web3.js 是一个 JavaScript 库,它封装了与以太坊节点进行通信的 JSON-RPC API,它就像一座桥梁,让你的网页应用能够“读懂”以太坊的语言,并与区块链上的数据进行交互,例如读取账户余额、查询智能合约状态、发起交易、甚至部署新的智能合约等。

Web3.js 的最新版本是 4.x(目前也有 1.x 的旧版本仍在使用,但新项目推荐使用 4.x),它提供了更现代、更模块化的 API 设计。

准备工作:在项目中引入 Web3.js

在使用 Web3.js 之前,你需要将其集成到你的项目中,如果你使用 npm 或 yarn:

npm install web3yarn add web3

然后在你的 JavaScript 文件中引入:

import Web3 from 'web3';
// 或者对于 CommonJS
// const Web3 = require('web3');

连接到以太坊网络

Web3.js 需要通过一个以太坊节点来与以太坊网络通信,你可以选择以下几种方式连接:

  1. 连接到本地节点:如果你在本地运行了以太坊客户端(如 Geth, Parity),节点通常运行在 http://localhost:8545
  2. 连接到公共节点:Infura、Alchemy 等服务提供商提供公共的以太坊节点 RPC URL,你需要在它们的官网上注册获取 API Key。
  3. 连接到浏览器钱包:如 MetaMask,当用户安装了 MetaMask 并授权你的网站访问时,你可以通过 window.ethereum 对象连接到用户的钱包节点,这实际上是通过 MetaMask 提供的节点服务。

示例:连接到 MetaMask 钱包

let web3;
// 检查是否安装了 MetaMask
if (typeof window.ethereum !== 'undefined') {
  console.log('MetaMask is installed!');
  // 使用 MetaMask 提供的 provider
  web3 = new Web3(window.ethereum);
  // 请求用户授权账户
  try {
    await window.ethereum.request({ method: 'eth_requestAccounts' });
  } catch (error) {
    console.error('User denied account access');
  }
} else {
  console.log('MetaMask is not installed. You should consider installing it!');
  // 可以在这里提供连接到其他节点的选项,或提示用户安装
  // 连接到 Infura 节点(需要替换为你的 Infura URL)
  // const infuraUrl = 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID';
  // web3 = new Web3(new Web3.providers.HttpProvider(infuraUrl));
}

调用智能合约

调用智能合约是 Web3.js 最核心的功能之一,这通常包括两个主要操作:

  1. 读取合约状态(常量函数):这些函数不会修改区块链上的状态,因此不需要用户签名交易,可以直接调用。
  2. 写入合约状态(非常量函数):这些函数会修改区块链上的状态,需要构建一笔交易,并由用户签名后发送到以太坊网络进行打包。

步骤 1:获取智能合约的 ABI 和地址

在调用智能合约之前,你需要知道:

  • ABI (Application Binary Interface):这是智能合约的接口描述,定义了合约有哪些函数、参数类型、返回值类型等,通常在编译智能合约时(使用 Solidity 和 Remix IDE、Truffle、Hardhat 等工具)会生成 ABI 文件(通常是 JSON 格式)。
  • 合约地址:这是智能合约部署到以太坊网络后的唯一标识符。

步骤 2:创建合约实例

有了 ABI 和合约地址后,你可以创建一个合约实例:

// 假设你已经有了 ABI 和合约地址
const contractABI = [
  // 这里是 ABI 的 JSON 数组,
  {
    "inputs": [],
    "name": "get",
    "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [{"internalType": "uint256", "name": "_x", "type": "uint256"}],
    "name": "set",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
  // ... 其他 ABI 条目
];
const contractAddress = '0x1234567890123456789012345678901234567890'; // 替换为你的合约地址
// 创建合约实例
const contract = new web3.eth.Contract(contractABI, contractAddress);

步骤 3:调用合约函数

示例 1:读取合约状态(调用 get() 函数)

async function getContractValue() {
  try {
    // 调用 get 函数,注意不需要交易参数
    const result = await contract.methods.get().call();
    console.log('Contract value is:', result.toString());
    return result;
  } catch (error) {
    console.error('Error calling get function:', error);
  }
}
getContractValue();

示例 2:写入合约状态(调用 set() 函数)

async function setContractValue(newValue) {
  try {
    // 获取当前账户(通常是 MetaMask 首选账户)
    const accounts = await web3.eth.getAccounts();
    const fromAccount = accounts[0];
    // 调用 set 函数,并指定发送交易的账户
    const tx = contract.methods.set(newValue);
    // 估算 gas(可选)
    const gas = await tx.estimateGas({ from: fromAccount });
    // 发送交易
    const receipt = await tx.send({
      from: fromAccount,
      gas: gas,
      // gasPrice: web3.utils.toWei('20', 'gwei'), // 可选,指定 gas 价格
    });
    console.log('Transaction hash:', receipt.transactionHash);
    console.log('Transaction was mined in block:', receipt.blockNumber);
    console.log('New contract value should be:', newValue);
  } catch (
随机配图
error) { console.error('Error calling set function:', error); } } // 调用写入函数,例如设置值为 42 // setContractValue(42);

其他常用 Web3.js 功能

除了调用智能合约,Web3.js 还提供了许多其他实用功能:

  • 获取账户余额

    async function getBalance(address) {
      const balance = await web3.eth.getBalance(address);
      console.log(`Balance of ${address}: ${web3.utils.fromWei(balance, 'ether')} ETH`);
      return balance;
    }
  • 发送以太坊转账

    async function sendEth(fromAccount, toAddress, amountInEther) {
      const amountInWei = web3.utils.toWei(amountInEther.toString(), 'ether');
      const tx = {
        from: fromAccount,
        to: toAddress,
        value: amountInWei,
      };
      const receipt = await web3.eth.sendTransaction(tx);
      console.log('ETH sent, transaction hash:', receipt.transactionHash);
    }
  • 监听事件

    // 假设合约有一个名为 ValueChanged 的事件
    contract.events.ValueChanged({
      fromBlock: 'latest' // 从最新区块开始监听
    }, function(error, event) {
      if (error) {
        console.error('Error listening to event:', error);
      } else {
        console.log('ValueChanged event:', event.returnValues);
      }
    })
    .on('data', event => {
      console.log('New event data:', event); // 单个事件
    })
    .on('changed', changedEvent => {
      console.log('Changed event:', changedEvent); // 过滤掉的事件
    })
    .on('error', err => {
      console.error('Event error:', err);
    });

注意事项

  1. Gas 费用:所有修改区块链状态的操作都需要支付 Gas 费用,你需要确保调用账户有足够的 ETH 来支付 Gas。
  2. 网络状态:以太坊网络拥堵时,交易可能需要较长时间才能被打包,Gas 价格也可能较高。
  3. 安全性:始终验证用户输入,注意智能合约调用的安全性,避免重入攻击等常见漏洞,在前端进行必要的错误处理。
  4. ABI 的准确性:ABI 必须与实际部署的智能合约完全一致,否则调用会失败。
  5. **异步
本文由用户投稿上传,若侵权请提供版权资料并联系删除!