You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法在智能合约中获取Chainlink请求的数据问题求助

无法通过Chainlink外部适配器获取数据到智能合约的问题解析

问题背景

我正尝试通过Chainlink外部适配器将数据获取到智能合约中,Chainlink任务已执行完成,但智能合约无法收到请求的数据。

Chainlink任务执行详情

任务步骤详情

  • fetch bridge

    {"data":{"result":{"id":"UCAl9Ld79qaZxp9JzEOwd3aA","statistics":{"subscriberCount":"209000"}},"jobRunId":"0x84aae6f7b7144fbc8196230158918797","statusCode":200}
    

    name: yutu
    requestData: {"id": $(jobSpec.externalJobID), "data": { "chid": $(decode_cbor.chid)},"key": $(decode_cbor.key)}

  • parse jsonparse
    结果: "209000"
    path: data,result,statistics,subscriberCount
    data: $(fetch)

  • encode_data ethabiencode
    结果: "0x0000000000000000000000000000000000000000000000000000000000033068"
    abi: (uint256 value)
    data: { "value": $(parse) }

  • encode_tx ethabiencode
    结果: "0x4ab0d1902a02a37be17119894c2d9400f9bcf563bd4be47496af9a493a96eb412676916c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000002e495ea15b191aff2dea171abba537dc597d6f117642d375000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006571ad180000000000000000000000000000000000000000000000000000000000033068"
    abi: fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data)
    data: {"requestId": $(decode_log.requestId), "payment": $(decode_log.payment), "callbackAddress": $(decode_log.callbackAddr), "callbackFunctionId": $(decode_log.callbackFunctionId), "expiration": $(decode_log.cancelExpiration), "data": $(encode_data)}

  • submit_tx ethtx
    to: 0x34b33d85A6a642A6Eaa1fE554f576c6122FF64b6
    data: $(encode_tx)

智能合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";

/**
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/

contract ConsumerContract is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;

    uint256 private constant ORACLE_PAYMENT = 1 * LINK_DIVISIBILITY; // 1 * 10**18
    uint256 public lastRetrievedSubscriberCount;

    event RequestForSubscriberCountFulfilled(
        bytes32 indexed requestId,
        uint256 indexed subscriberCount
    );

    /**
     *  Goerli
     * @dev LINK address in Sepholia network: 0x779877A7B0D9E8603169DdbD7836e478b4624789
     */
    constructor() ConfirmedOwner(msg.sender) {
        setChainlinkToken(0x779877A7B0D9E8603169DdbD7836e478b4624789);
    }

    function requestSubscriberCount(
        address _oracle,
        string memory _jobId,
        string memory channelId,
        string memory apiKey
    ) public onlyOwner {
        Chainlink.Request memory req = buildOperatorRequest(
            stringToBytes32(_jobId),
            this.fulfillRequestInfo.selector
        );

        req.add("chid", channelId);
        req.add("key", apiKey);
        sendOperatorRequestTo(_oracle, req, ORACLE_PAYMENT);
    }

    function fulfillRequestInfo(bytes32 _requestId, string memory _info)
        public
        recordChainlinkFulfillment(_requestId)
    {
        // Parse the JSON data to extract the subscriberCount
        (uint256 subscriberCount, ) = abi.decode(bytes(_info), (uint256, uint256));

        emit RequestForSubscriberCountFulfilled(_requestId, subscriberCount);
        lastRetrievedSubscriberCount = subscriberCount;
    }

    function getSubscriberCount() public view returns (uint256) {
        return lastRetrievedSubscriberCount;
    }

    /*
    ========= UTILITY FUNCTIONS ==========
    */

    function contractBalances()
        public
        view
        returns (uint256 eth, uint256 link)
    {
        eth = address(this).balance;

        LinkTokenInterface linkContract = LinkTokenInterface(
            chainlinkTokenAddress()
        );
        link = linkContract.balanceOf(address(this));
    }

    function getChainlinkToken() public view returns (address) {
        return chainlinkTokenAddress();
    }

    function withdrawLink() public onlyOwner {
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(
            link.transfer(msg.sender, link.balanceOf(address(this))),
            "Unable to transfer Link"
        );
    }

    function withdrawBalance() public onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }

    function cancelRequest(
        bytes32 _requestId,
        uint256 _payment,
        bytes4 _callbackFunctionId,
        uint256 _expiration
    ) public onlyOwner {
        cancelChainlinkRequest(
            _requestId,
            _payment,
            _callbackFunctionId,
            _expiration
        );
    }

    function stringToBytes32(string memory source)
        private
        pure
        returns (bytes32 result)
    {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }

        assembly {
            // solhint-disable-line no-inline-assembly
            result := mload(add(source, 32))
        }
    }
}

问题原因及修复方案

核心问题

智能合约的fulfillRequestInfo函数参数类型与Chainlink任务的输出不匹配:任务中encode_data将订阅者数量编码为uint256对应的bytes32数据,但合约回调函数却期望接收string类型参数,并且错误地尝试将字符串字节解码为两个uint256,直接导致解码失败,无法获取有效数据。

修复步骤

  1. 修改回调函数参数类型:将string memory _info改为bytes32 _data,匹配任务输出的编码格式。
  2. 调整解码逻辑:直接将bytes32转换为uint256,无需多余的ABI解码操作。

修复后的fulfillRequestInfo函数:

function fulfillRequestInfo(bytes32 _requestId, bytes32 _data)
    public
    recordChainlinkFulfillment(_requestId)
{
    uint256 subscriberCount = uint256(_data);
    
    emit RequestForSubscriberCountFulfilled(_requestId, subscriberCount);
    lastRetrievedSubscriberCount = subscriberCount;
}
  1. 验证回调选择器:构建Chainlink请求时,this.fulfillRequestInfo.selector会自动匹配修改后的函数,无需手动调整。

额外验证点

  • 确认任务中的callbackFunctionId与合约修改后的fulfillRequestInfo函数选择器一致。
  • 检查合约部署地址、调用requestSubscriberCount时传入的预言机地址、Job ID是否正确。
  • 确保合约账户中有足够的LINK代币用于支付预言机服务费用。

内容的提问来源于stack exchange,提问作者János Barna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:09:53