第三节:DApp调用智能合约前言在上一节中,我们成功实现了DApp与钱包的连接。这意味着用户已经可以在我们的应用里点击“ConnectWallet”,并授权DApp获取他们的地址信息。但是,光有钱包还不够。DApp真正的核心在于——调用智能合约。智能合约是区块链上的“后端逻辑”
在上一节中,我们成功实现了 DApp 与钱包的连接。这意味着用户已经可以在我们的应用里点击“Connect Wallet”,并授权 DApp 获取他们的地址信息。
但是,光有钱包还不够。DApp 真正的核心在于——调用智能合约。
智能合约是区块链上的“后端逻辑”。当我们要转账、铸造 NFT、参加投票、质押代币时,实际上就是在调用智能合约的方法。
因此,学习如何调用合约,是让 DApp 具备实际业务功能的关键一步。
在本节中,我们将学习:
ABI,全称 Application Binary Interface(应用二进制接口)。
它定义了智能合约的方法签名、参数类型、返回类型。
举个例子,ERC721 标准里的 balanceOf 方法的 ABI 描述如下:
{
  "type": "function",
  "name": "balanceOf",
  "stateMutability": "view",
  "inputs": [{ "name": "owner", "type": "address" }],
  "outputs": [{ "type": "uint256" }]
}这就告诉我们:
addressuint256view(只读,不会改写链上状态)当合约被部署到区块链上时,会获得一个唯一的地址,比如:
0xEcd0D12E21805803f70de03B72B1C162dB0898d9这个地址和 ABI 一起,就可以让 DApp 确定如何与合约交互。
调用智能合约方法,本质上就是发起一个以太坊交易(Transaction):
特别要注意:
DApp 与合约交互有两种方式:
window.ethereum 对象。ethereum.request({ method, params }) 来与区块链交互。例如,获取当前网络 ID:
await window.ethereum.request({ method: "eth_chainId" });
// 0x1 表示以太坊主网获取用户的账户:
async function getAccount() {
  const accounts = await window.ethereum
    .request({ method: "eth_requestAccounts" })
    .catch((err) => {
      if (err.code === 4001) {
        console.log("用户拒绝了连接请求");
      } else {
        console.error(err);
      }
    });
  return accounts[0];
}// 不需要用户钱包
const account = "0xEcd0D12E21805803f70de03B72B1C162dB0898d9";
const response = await fetch("https://mainnet.infura.io/v3/<你的API_KEY>", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "eth_getBalance",
    params: [account, "latest"]
  }),
});
const data = await response.json();
console.log("用户余额:", parseInt(data.result, 16) / 1e18, "ETH");
如果只是查余额 / 读合约数据 → 节点 RPC 足够。
如果要转账 / 调用合约写方法 → 必须通过钱包,因为只有钱包有用户的私钥能签名。
我们来实现一个简单的需求:读取当前钱包地址下有多少个 NFT。
useReadContract Hook 读取数据。useAccount 获取当前连接的钱包地址。balanceOf 方法。import { http, useReadContract } from "wagmi";
import { Mainnet, WagmiWeb3ConfigProvider, MetaMask } from '@ant-design/web3-wagmi';
import { Address, NFTCard, ConnectButton, Connector, useAccount } from "@ant-design/web3";
const CallTest = () => {
  const { account } = useAccount();
  const result = useReadContract({
    abi: [
      {
        type: 'function',
        name: 'balanceOf',
        stateMutability: 'view',
        inputs: [{ name: 'account', type: 'address' }],
        outputs: [{ type: 'uint256' }],
      },
    ],
    address: '0xEcd0D12E21805803f70de03B72B1C162dB0898d9',
    functionName: 'balanceOf',
    args: [account?.address as `0x${string}`],
  });
  return (
    <div>
      你的 NFT 数量:{result.data?.toString()}
    </div>
  );
};
export default function Web3() {
  return (
    <WagmiWeb3ConfigProvider
      chains={[Mainnet]}
      transports={{ [Mainnet.id]: http() }}
      wallets={[MetaMask()]}
    >
      <Connector>
        <ConnectButton />
      </Connector>
      <CallTest />
    </WagmiWeb3ConfigProvider>
  );
}useReadContract:读取合约方法的 Hook。abi:定义方法的 ABI。args:传入参数,这里是用户钱包地址。result.data:返回值(用户 NFT 数量)。account 为空,需要先点击“Connect Wallet”。仅仅能读取数据还不够,一个真正的 DApp 必须能 写入链上数据。\
我们来实现调用合约的 mint 方法,铸造一个 NFT。
useWriteContract Hook 调用写方法。import { parseEther } from "viem";
import { Button, message } from "antd";
import { http, useReadContract, useWriteContract } from "wagmi";
import { Mainnet, WagmiWeb3ConfigProvider, MetaMask } from '@ant-design/web3-wagmi';
import { Address, NFTCard, ConnectButton, Connector, useAccount } from "@ant-design/web3";
const CallTest = () => {
  const { account } = useAccount();
  const result = useReadContract({
    abi: [
      {
        type: 'function',
        name: 'balanceOf',
        stateMutability: 'view',
        inputs: [{ name: 'account', type: 'address' }],
        outputs: [{ type: 'uint256' }],
      },
    ],
    address: '0xEcd0D12E21805803f70de03B72B1C162dB0898d9',
    functionName: 'balanceOf',
    args: [account?.address as `0x${string}`],
  });
  const { writeContract } = useWriteContract();
  return (
    <div>
      你的 NFT 数量:{result.data?.toString()}
      <Button
        onClick={() => {
          writeContract(
            {
              abi: [
                {
                  type: "function",
                  name: "mint",
                  stateMutability: "payable",
                  inputs: [{ name: "quantity", type: "uint256" }],
                  outputs: [],
                },
              ],
              address: "0xEcd0D12E21805803f70de03B72B1C162dB0898d9",
              functionName: "mint",
              args: [BigInt(1)],
              value: parseEther("0.01"),
            },
            {
              onSuccess: () => {
                message.success("Mint 成功!");
              },
              onError: (err) => {
                message.error(err.message);
              },
            }
          );
        }}
      >
        Mint NFT
      </Button>
    </div>
  );
};value。onSuccess 与 onError 用于用户提示。全局配置 WagmiWeb3ConfigProvider\ 建议放在应用的最外层,避免在多个组件中重复配置。
ABI 文件管理
src/abis/ 目录下统一管理。安全性考虑
错误处理
在本节中,我们完成了 DApp 的一次跨越式提升:
到这里,我们的 DApp 已经具备了“读 + 写”的链上交互能力。
 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!