← 返回
Web3与WASM 2026.03.09

合约交互与 ABI

合约交互与 ABI

学习目标

掌握合约间调用方式、接口定义、ABI 数据结构、Web3.js 访问合约的方法。


合约间调用基础

  • EOA(外部账号)发起调用,可能触发合约间的调用链
  • 调用者必须持有被调用合约的地址

方式一:同文件内直接调用

当两个合约在同一个文件中时,可以直接通过合约类型和地址进行调用:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract Callee {
    uint256 public x;

    function setX(uint _x) public {
        x = _x;
    }
}

contract Caller {
    address calleeAddress;

    constructor(address _callee) {
        calleeAddress = _callee;
    }

    function setCalleeX(uint _x) public {
        Callee callee = Callee(calleeAddress);
        callee.setX(_x);
    }
}

方式二:跨文件 import 调用

当合约分布在不同文件中时,使用 import 引入:

// 文件: Callee.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract Callee {
    uint256 public x;

    function setX(uint _x) public {
        x = _x;
    }
}
// 文件: Caller.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
import "contracts/Callee.sol";

contract Caller {
    address calleeAddress;

    constructor(address _callee) {
        calleeAddress = _callee;
    }

    function setCalleeX(uint _x) public {
        Callee callee = Callee(calleeAddress);
        callee.setX(_x);
    }
}

方式三:通过接口调用

接口(Interface)等价于 ABI,不依赖被调用合约的源码,只需知道函数签名即可调用。

定义接口

// 文件: ICallee.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

interface ICallee {
    function setX(uint _x) external;
}

实现接口(不强制)

// 文件: Callee.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
import "contracts/ICallee.sol";

contract Callee is ICallee {
    uint256 public x;

    function setX(uint _x) public {
        x = _x;
    }
}

通过接口调用

// 文件: Caller.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
import "contracts/ICallee.sol";

contract Caller {
    address calleeAddress;

    constructor(address _callee) {
        calleeAddress = _callee;
    }

    function setCalleeX(uint _x) public {
        ICallee callee = ICallee(calleeAddress);
        callee.setX(_x);
    }
}

实际案例:访问 USDT 合约

只需知道合约地址和接口,就能与链上已部署的合约交互。

  • USDT 合约地址:0xdAC17F958D2ee523a2206206994597C13D831ec7
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

interface IERC20 {
    function name() external view returns(string memory);
    function symbol() external view returns(string memory);
    function decimals() external view returns(uint8);
}

通过接口 + 地址即可读取 USDT 的名称、符号和精度,无需获取 USDT 的源码。


ABI(Application Binary Interface)

ABI 是合约的二进制接口描述,包含以下特征:

  • 只有函数签名信息,没有实现代码
  • EOA 通过 ABI 调用合约,合约之间通过接口调用
  • 编译合约时自动生成

ABI 示意图

ABI JSON 示例

{
    "abi": [
        {
            "inputs": [
                {
                    "internalType": "uint256",
                    "name": "_x",
                    "type": "uint256"
                }
            ],
            "name": "setX",
            "outputs": [
                {
                    "internalType": "uint256",
                    "name": "",
                    "type": "uint256"
                }
            ],
            "stateMutability": "nonpayable",
            "type": "function"
        }
    ]
}

Web3.js 合约访问

Web3.js 是浏览器端调用合约的 JavaScript 库,支持两种引入方式:

  • 直接引用 <script> 标签
  • Node.js 模块(npm install web3

使用步骤

  1. 导入 Web3.js
  2. 调起 MetaMask 登录,获取用户账户地址
  3. 用 ABI + 合约地址生成 contract 对象
  4. 调用合约方法
  5. 读操作(view/pure):.methods.funcName().call()
  6. 写操作(修改状态):.methods.funcName().send({ from: account })

代码示例

// 1. 导入 Web3(浏览器环境,MetaMask 注入 window.ethereum)
const web3 = new Web3(window.ethereum);

// 2. 请求 MetaMask 授权登录
const accounts = await window.ethereum.request({
    method: "eth_requestAccounts"
});
const account = accounts[0];

// 3. 用 ABI + 合约地址创建 contract 对象
const abi = [ /* 合约 ABI JSON */ ];
const contractAddress = "0x1234...abcd";
const contract = new web3.eth.Contract(abi, contractAddress);

// 4a. 读操作(view 函数,不消耗 gas)
const value = await contract.methods.getX().call();
console.log("当前值:", value);

// 4b. 写操作(修改状态,消耗 gas,需要签名)
await contract.methods.setX(42).send({ from: account });