在Web3的浪潮中,智能合约是构建去中心化应用(DApps)的核心基石,它运行在区块链网络上,自动执行预设的规则和逻辑,而与智能合约进行交互,即“调用合约”,是我们与DApps后台进行数据交互、触发功能的关键操作,本文将详细解析Web3中智能合约的调用方法,帮助开发者更好地理解和实践。

理解合约调用:交易与查询

在深入具体方法之前,我们首先要明白合约调用的两种基本类型:

  1. 发送交易(Sending a Transaction / 写调用):这种调用会修改合约的状态变量(转账、修改设置、铸造NFT等),因为它改变了区块链上的数据,所以需要矿工/验证者打包,并消耗Gas(燃料费),调用后会产生一个交易哈希,并等待区块链确认。
  2. 常量调用/查询(Calling a Constant Function / 读调用):这种调用仅读取合约的状态或计算数据,不会修改合约状态(查询账户余额、获取某个参数的值),它不需要消耗Gas,也不需要等待区块链确认,几乎可以 instantaneously 得到结果。

Web3合约调用的核心方法

在不同的Web3开发库中(如 ethers.js, web3.js),合约调用的方法名称可能略有差异,但核心逻辑是一致的,我们以目前更流行的 ethers.js 为例进行说明。

准备工作:连接与合约实例

在调用合约方法之前,你需要:

  1. Web3Provider:连接到以太坊节点(如Infura, Alchemy,或本地节点)。
  2. Signer:用于签名交易的对象,代表用户的账户(通常是钱包,如MetaMask)。
  3. Contract Instance:已部署的智能合约实例,需要合约地址、ABI(Application Binary Interface,应用程序二进制接口)来初始化。
// 示例:使用 ethers.js 初始化合约实例
const { ethers } = require("ethers");
// 假设这些值已准备好
const provider = new ethers.providers.Web3Provider(window.ethereum); // 或其他 provider
const signer = provider.getSigner();
const contractAddress = "0x1234567890123456789012345678901234567890";
const contractABI = [/* 合约的 ABI 数组 */];
const contract = new ethers.Contract(contractAddress, contractABI, signer);

读调用(Constant Functions / Calls)

读调用通常使用 call() 方法,或者直接通过合约实例的方法调用(如果该方法在ABI中标记为pureview)。

  • 使用 call()call() 方法会执行一个静态调用,不修改状态。

    // 假设合约有一个名为 balanceOf(address) 的 view 函数
    async function getBalance(userAddress) {
        try {
            const balance = await contract.balanceOf(userAddress);
            console.log("Balance:", balance.toString());
            return balance;
        } catch (error) {
            console.error("Error calling balanceOf:", error);
        }
    }
    // 调用
    getBalance("0xUserAddress...");
  • 直接调用(推荐): 对于 viewpure 函数,ethers.js 的合约实例会直接返回一个 Promise,解析为调用结果,无需显式使用 call()

    // 与上面效果相同,更简洁
    async function getBalanceDirect(userAddress) {
        const balance = await contract.balanceOf(userAddress);
        console.log("Balance:", balance.toString());
        return balance;
    }

写调用(Sending Transactions / Transactions)

写调用需要发送一个交易到区块链,因此需要用户签名(通过Signer),并消耗Gas。

  • 使用 sendTransaction(): 对于会修改状态的函数,直接通过合约实例调用,ethers.js 会自动构建并发送交易。

    // 假设合约有一个名为 transfer(address, uint256) 的函数
    async function sendTokens(toAddress, amount) {
        try {
            // 构建交易
            const tx = await contract.transfer(toAddress, amount);
            console.log("Transaction sent! Hash:", tx.hash);
            // 等待交易被矿工打包确认
            await tx.wait();
            console.log("Transaction confirmed!");
        } catch (error) {
            console.error("Error sending transaction:", error);
        }
    }
    // 调用 (amount 是 BigNumber 或字符串,取决于单位)
    sendTokens("0xRecipientAddress...", ethers.utils.parseEther("1.0")); // 转账1个ETH(假设是ERC20)
  • 使用 estimateGas随机配图