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事件,当事件触发后,调用合约的另一个函数执行后续操作:
- 合约新增一个带权限检查的执行函数:
function executeMyStuff() public { require(risk > 2, "Risk score not high enough or not updated"); // DO MY STUFF }
- 前端监听
RequestRisk事件,收到事件后调用executeMyStuff()。
内容的提问来源于stack exchange,提问作者Giacomo Bertazzolo
相关产品推荐
相关产品推荐

