zh
关于 ZetaChain
应用与服务
Goldsky

本教程演示如何使用子图索引器查询全链合约事件数据。示例中我们将使用 Goldsky (opens in a new tab) 子图。

在开始之前,假设你已经完成跨链兑换教程。如果需要合约完整源码,可在 example-contracts 仓库 (opens in a new tab) 获取。

新增 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 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 配置文件:

goldsky.json
{
  "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
    }
  ]
}

addressstartBlock 更新为你部署合约时的实际值。

创建子图:

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 0xb4318f04329d6ddd398b11ccba40d0404e1872494a054fb382267e2f1de160e9
CCTXs 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 的邮件,确认子图已完成索引。***