概述
本教程演示如何使用子图索引器查询全链合约事件数据。示例中我们将使用 Goldsky (opens in a new tab) 子图。
在开始之前,假设你已经完成跨链兑换教程。如果需要合约完整源码,可在 example-contracts 仓库 (opens in a new tab) 获取。
为 Swap 合约添加事件
新增 SwapCompleted 事件,用于在跨链兑换成功后触发:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol";
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol";
import "@zetachain/toolkit/contracts/SwapHelperLib.sol";
import "@zetachain/toolkit/contracts/BytesHelperLib.sol";
contract Swap is zContract {
SystemContract public immutable systemContract;
uint256 constant BITCOIN = 18332;
event SwapCompleted(
address indexed zrc20,
address indexed targetToken,
uint256 amount,
bytes recipient
);
constructor(address systemContractAddress) {
systemContract = SystemContract(systemContractAddress);
}
modifier onlySystem() {
require(
msg.sender == address(systemContract),
"Only system contract can call this function"
);
_;
}
function onCrossChainCall(
zContext calldata context,
address zrc20,
uint256 amount,
bytes calldata message
) external virtual override onlySystem {
address targetTokenAddress;
bytes memory recipientAddress;
if (context.chainID == BITCOIN) {
targetTokenAddress = BytesHelperLib.bytesToAddress(message, 0);
recipientAddress = abi.encodePacked(
BytesHelperLib.bytesToAddress(message, 20)
);
} else {
(address targetToken, bytes memory recipient) = abi.decode(
message,
(address, bytes)
);
targetTokenAddress = targetToken;
recipientAddress = recipient;
}
(address gasZRC20, uint256 gasFee) = IZRC20(targetTokenAddress)
.withdrawGasFee();
uint256 inputForGas = SwapHelperLib.swapTokensForExactTokens(
systemContract.wZetaContractAddress(),
systemContract.uniswapv2FactoryAddress(),
systemContract.uniswapv2Router02Address(),
zrc20,
gasFee,
gasZRC20,
amount
);
uint256 outputAmount = SwapHelperLib._doSwap(
systemContract.wZetaContractAddress(),
systemContract.uniswapv2FactoryAddress(),
systemContract.uniswapv2Router02Address(),
zrc20,
amount - inputForGas,
targetTokenAddress,
0
);
IZRC20(gasZRC20).approve(targetTokenAddress, gasFee);
IZRC20(targetTokenAddress).withdraw(recipientAddress, outputAmount);
emit SwapCompleted(
zrc20,
targetTokenAddress,
outputAmount,
recipientAddress
);
}
}编译并部署合约
yarn
npx hardhat compile --force
npx hardhat deploy --network zeta_testnet🔑 Using account: 0x2cD3D070aE1BD365909dD859d29F387AA96911e1
🚀 Successfully deployed contract on ZetaChain.
📜 Contract address: 0x9846BBdE15B857d88DDad4e00CD76962245E1b6f
🌍 Explorer: https://zetascan.com/address/0x9846BBdE15B857d88DDad4e00CD76962245E1b6f配置 Goldsky
安装 Goldsky CLI (opens in a new tab):
curl https://goldsky.com | sh然后在 https://app.goldsky.com (opens in a new tab) 创建账号 (opens in a new tab),于设置页生成 API key,并登录 CLI:
goldsky login按提示粘贴 API key。
在项目根目录创建 Goldsky 配置文件:
{
"version": "1",
"name": "swap",
"abis": {
"swap": {
"path": "artifacts/contracts/Swap.sol/Swap.json"
}
},
"chains": ["zetachain-testnet"],
"instances": [
{
"abi": "swap",
"address": "0x9846BBdE15B857d88DDad4e00CD76962245E1b6f",
"chain": "zetachain-testnet",
"startBlock": 3065396
}
]
}将 address 与 startBlock 更新为你部署合约时的实际值。
创建子图:
goldsky subgraph deploy swap/v1 --from-abi goldsky.json◇ Subgraph generated, deploying to your goldsky project
│
◇ Deployed subgraph API: https://api.goldsky.com/api/public/project_clnujea22c0if34x5965c8c0j/subgraphs/swap-zetachain-testnet/v1/gn与合约交互
子图部署完成后,可执行跨链兑换与合约交互。示例中我们将 5 枚 tMATIC 兑换为 BTC。
npx hardhat interact --contract 0x9846BBdE15B857d88DDad4e00CD76962245E1b6f --amount 5 --network mumbai_testnet --target-token 0x65a45c57636f9BcCeD4fe193A602008578BcA90b --recipient tb1q2dr85d57450xwde6560qyhj7zvzw9895hq25tx🔑 Using account: 0x2cD3D070aE1BD365909dD859d29F387AA96911e1
🚀 Successfully broadcasted a token transfer transaction on mumbai_testnet network.
📝 Transaction hash: 0xb4318f04329d6ddd398b11ccba40d0404e1872494a054fb382267e2f1de160e9可通过以下命令跟踪交易:
npx hardhat cctx 0xb4318f04329d6ddd398b11ccba40d0404e1872494a054fb382267e2f1de160e9CCTXs on ZetaChain found.
✓ 0xf5fbf1ba190e074c64adaba044e2c4f6724aeebe70ca01b0998919d0b1059338: 80001 → 7001: OutboundMined (Remote omnichain contract call completed)
⠏ 0xa0cfd783f991bd060239193082594dd3fe5ae239e97b8baaa0a303ee6ba6ba79: 7001 → 18332: PendingOutbound当看到跨链交易进入 PendingOutbound 状态(示例中为 ZetaChain → Bitcoin,即 7001 → 18332)时,说明兑换已成功,SwapCompleted 事件应已触发。
查询子图事件
访问部署子图时输出的 API 地址(你的地址可能不同):
https://api.goldsky.com/api/public/project_clnujea22c0if34x5965c8c0j/subgraphs/swap-zetachain-testnet/v1/gn页面会显示 GraphQL Playground,可用于查询事件:
query {
swapCompleteds(first: 5) {
id
}
}你将看到 SwapCompleted 事件 ID 列表:
{
"data": {
"swapCompleteds": [
{
"id": "0xbfcc4e8ea59625da42aa3eec6e5aba66bcd120f8e83dea8dc855ebd1d834e1e6-25"
}
]
}
}还可查询特定事件的详细信息:
query {
swapCompleteds(where: { id: "0xbfcc4e8ea59625da42aa3eec6e5aba66bcd120f8e83dea8dc855ebd1d834e1e6-25" }) {
id
block_number
timestamp_
transactionHash_
contractId_
zrc20
targetToken
amount
recipient
}
}返回结果将包含从合约事件中发出的全部数据:
{
"data": {
"swapCompleteds": [
{
"id": "0xbfcc4e8ea59625da42aa3eec6e5aba66bcd120f8e83dea8dc855ebd1d834e1e6-25",
"block_number": "3065437",
"timestamp_": "1704357233",
"transactionHash_": "0xbfcc4e8ea59625da42aa3eec6e5aba66bcd120f8e83dea8dc855ebd1d834e1e6",
"contractId_": "0x9846bbde15b857d88ddad4e00cd76962245e1b6f",
"zrc20": "0x48f80608b672dc30dc7e3dbbd0343c5f02c738eb",
"targetToken": "0x65a45c57636f9bcced4fe193a602008578bca90b",
"amount": "369767",
"recipient": "0x74623171326472383564353734353078776465363536307179686a377a767a7739383935687132357478"
}
]
}
}恭喜!你已通过 Goldsky 子图索引器成功查询全链合约事件。想进一步了解 Goldsky,请访问 Goldsky 文档 (opens in a new tab)。
常见问题
事件未被索引
请等待来自 Goldsky 的邮件,确认子图已完成索引。***