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

ChainLink API调用异步问题:如何等待risk变量更新?

问题描述

我正在开发一个需要进行API调用的智能合约项目,遵循ChainLink官方指南。我希望在myStuff()函数中调用requestRiskData(),并使用更新后的risk变量执行后续操作,但Oracle调用后risk变量需要约30秒才能更新,不知道该如何等待变量更新完成。附上相关合约代码:

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

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

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

    uint256 public risk;
    bytes32 private jobId;
    uint256 private fee;
    string public a= "0x6e1db9836521977ee93651027768f7e0d5722a33";

    event RequestRisk(bytes32 indexed requestId, uint256 risk);

    /**
     * @notice Initialize the link token and target oracle
     *
     * Goerli Testnet details:
     * Link Token: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
     * Oracle: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 (Chainlink DevRel)
     * jobId: ca98366cc7314957b8c012c72f05aeeb
     *
     */

    constructor() ConfirmedOwner(msg.sender) {
        setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
        setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
        jobId = "ca98366cc7314957b8c012c72f05aeeb";
        fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
    }
    // Set the path to find the desired data in the API response, where the response format is:
    // {
    //"data":{
    //  "0x6e1db9836521977ee93651027768f7e0d5722a33":{
    //      "risk":{
    //          "score":....
    //              }
    //      }
    //    }
    // }
    function requestRiskData() public returns (bytes32 requestId) {
        Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
        
        req.add("get","https://demo.anchainai.com/api/address_risk_score?proto=eth&address=0x6e1db9836521977ee93651027768f7e0d5722a33&apikey=demo_api_key");
        req.add("path", "data,0x6e1db9836521977ee93651027768f7e0d5722a33,risk,score");

        return sendChainlinkRequest(req, fee);
    }

    function fulfill(
        bytes32 _requestId,
        uint256 _risk
        
    ) public recordChainlinkFulfillment(_requestId) {
        emit RequestRisk(_requestId, _risk);
        risk = _risk;
    }

    function withdrawLink() public onlyOwner {
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(
            link.transfer(msg.sender, link.balanceOf(address(this))),
            "Unable to transfer"
        );
    }
    function myStuff() public payable{
    requestRiskData();
        if(risk>2){
           //DO MY STUFF
        }
    }

    }
解决方案

Solidity是同步执行的智能合约语言,无法在合约内部实现“等待异步操作完成”的逻辑——ChainLink的Oracle回调是独立的交易,和发起请求的交易完全分离。你需要把依赖risk的逻辑从myStuff()中拆分出来,以下是三种可行方案:

方案1:将业务逻辑移到回调函数fulfill中

直接在获取到risk值后执行后续操作,这是最直接的方式:

function fulfill(
    bytes32 _requestId,
    uint256 _risk
) public recordChainlinkFulfillment(_requestId) {
    emit RequestRisk(_requestId, _risk);
    risk = _risk;
    
    // 在这里执行原来myStuff()中的业务逻辑
    if(risk > 2){
       // DO MY STUFF
    }
}

function myStuff() public payable{
    // 只发起请求,不处理后续逻辑
    requestRiskData();
}

方案2:用状态机追踪请求与业务上下文

如果需要关联发起请求的用户或特定业务参数,可以新增状态变量记录请求对应的业务信息,在回调中匹配执行:

// 新增状态:记录每个请求对应的调用者和业务参数
mapping(bytes32 => address) public requestToCaller;
mapping(bytes32 => uint256) public requestToValue;

function requestRiskData() public payable returns (bytes32 requestId) {
    Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
    
    req.add("get","https://demo.anchainai.com/api/address_risk_score?proto=eth&address=0x6e1db9836521977ee93651027768f7e0d5722a33&apikey=demo_api_key");
    req.add("path", "data,0x6e1db9836521977ee93651027768f7e0d5722a33,risk,score");

    bytes32 requestId = sendChainlinkRequest(req, fee);
    // 保存请求对应的调用者和传入的ETH金额
    requestToCaller[requestId] = msg.sender;
    requestToValue[requestId] = msg.value;
    return requestId;
}

function fulfill(
    bytes32 _requestId,
    uint256 _risk
) public recordChainlinkFulfillment(_requestId) {
    emit RequestRisk(_requestId, _risk);
    risk = _risk;
    
    address caller = requestToCaller[_requestId];
    uint256 value = requestToValue[_requestId];
    
    // 执行业务逻辑,比如根据caller和value处理
    if(risk > 2){
       // DO MY STUFF,比如给caller转账、 mint NFT等
    }
    
    // 清理状态(可选)
    delete requestToCaller[_requestId];
    delete requestToValue[_requestId];
}

function myStuff() public payable{
    requestRiskData{value: msg.value}();
}

方案3:前端监听事件触发后续操作

如果业务逻辑需要用户交互,可以在前端监听RequestRisk事件,当事件触发后,调用合约的另一个函数执行后续操作:

  1. 合约新增一个带权限检查的执行函数:
function executeMyStuff() public {
    require(risk > 2, "Risk score not high enough or not updated");
    // DO MY STUFF
}
  1. 前端监听RequestRisk事件,收到事件后调用executeMyStuff()。

内容的提问来源于stack exchange,提问作者Giacomo Bertazzolo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 23:31:05