合約交互與 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 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)
使用步驟
- 導入 Web3.js
- 調起 MetaMask 登錄,獲取用戶賬戶地址
- 用 ABI + 合約地址生成 contract 對象
- 調用合約方法:
- 讀操作(view/pure):
.methods.funcName().call() - 寫操作(修改狀態):
.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 });
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/7492
