使用Hardhat测试Chainlink API调用



我在本地测试Chainlink API调用时遇到麻烦。我有一个契约,它调用我的API消费者契约来进行API调用。然而,当我尝试这样做时,MockOracle(用于本地测试的模拟oracle,来自chainlink)合约返回(VM异常)说"必须使用白名单功能"。我正在为bytes32 get请求进行API调用。

这是函数修饰符(在LinkTokenReceiver内部),其中MockOracle恢复(它在接收LINK令牌时被MockOracle调用):

/**
* @dev Reverts if the given data does not begin with the `oracleRequest` function selector
* @param _data The data payload of the request
*/
modifier permittedFunctionsForLINK(bytes memory _data) {
bytes4 funcSelector;
assembly {
// solhint-disable-next-line avoid-low-level-calls
funcSelector := mload(add(_data, 32))
}
require(funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions");
_;
}

ApiConsumer得到了适当的资助,LINK令牌是chainlink令牌的本地部署。那么,我做错了什么?或者使用hardhat本地测试Chainlink API调用(bytes32 get request)的最佳方法是什么?

在Chainlink oracle工作流中,消费者基本上应该向链下oracle节点发送请求,以便oracle节点可以从消费者指定的API中获取数据。在工作流中,用户发送的请求必须被链下oracle节点捕获。

你在这里做的是将你的请求发送给智能合约oracle,oracle将你的请求保存到事件日志oracleRequest中,以便它可以被链下oracle捕获。

您这里的问题是由修饰符permittedFunctionForLINK建议,函数选择器不是oracleRequest,因此链下oracle无法识别请求。

我猜你目前使用的oracle.solChainlinkClient.sol版本v0.6。在v0.6版本中,AFAIK, ChainlinkClient不帮助用户在用户的Chainlink请求中添加函数选择器oracleRequest,这意味着如果您没有在请求中手动添加函数选择器,您将获得错误。在v0.7中,ChainlinkClient帮助您在请求中添加选择器。请自行检查ChainlinkClient v0.6和ChainlinkClient v0.7中的sendChainlinkRequestTo功能。

解决方案:

  1. 使用v0.7的ChainlinkClient和操作符。Sol(智能合约oracle)Sol被重命名为operator。(在v0.7中)进行测试。文件可在此找到。
  2. 在Chainlink官方github中有一个名为hardhat-starter-kit的回购,您可以尝试示例代码。或者你可以直接使用startkit来测试AnyApi和任何其他服务。

如果您在本地测试,chainlink节点将无法检测到任何请求。
你有几个选择:

  1. 将您的APIConsumer合约部署到测试网并使用已部署运营商合同。你可以在chainlinks网站[https://docs.chain.link/any-api/testnet-oracles/]。然后选择jobId用于一个节点作业,它可以提供你需要的API数据。字节32,整型,多字等。jobId可以在你的APIConsumer契约中指定。
  2. 部署你自己的运营商合同,设置你自己的节点(chainlink有一个设置临时开发节点的选项),然后为你的API创建一个自定义作业。然后,您需要将您的节点地址作为授权发送方放入运营商合同中,以允许从您的节点返回数据。
  3. 对于本地测试,您需要在测试脚本中编写一个函数来发送一个对模拟操作符契约的模拟响应。确保您为用于发送fulfillOracleRequest事务的帐户设置了authorisedsender,为了正确测试,我建议您使用本地测试环境中的第二个帐户进行测试,而不是用于部署的帐户。你会需要从事务事件中侦听初始请求数据你的APIConsumer契约,以获得requestId值,如果你使用的是一个多值API调用的fulfillOracleRequest2监听链接请求的事件从模拟操作符契约到检索您可能需要的任何其他值,例如'expiration',到控件上的实现函数创建事务模拟操作员合同。
    如果使用fulfillOracleRequest2的多响应,可能需要修改你的模拟操作符validateMultiWordResponseId函数一点,使修改器通过(Solidity不喜欢比较字符串或不同的数据类型),技巧是删除。offset(我的编译器不喜欢这样,因为它只适用于存储值)分配给新变量,只是keccak256abi。encodePacked两个requestid从履行参数和Calldata然后将它们一起比较。见下文:

modifier validateMultiWordResponseId(bytes32 requestId, bytes calldata _data) {
require(_data.length >= 32, "Response must be > 32 bytes"); 
bytes memory firstDataWord = new bytes(32);
for(uint256 i=0; i<=31; i++){
firstDataWord[i] = _data[i];
}
requestIdToBytes = string(abi.encodePacked(requestId));
requestIdFromCalldata = string(abi.encodePacked(firstDataWord));
require(keccak256(abi.encodePacked(requestIdToBytes)) == keccak256(abi.encodePacked(requestIdFromCalldata)), "First word must be requestId");
_;
}

//Typescript snippet for test script 
//If local network - Set Authorised senders on Mock Oracle to account[1]
const [owner, mockNode] = await ethers.getSigners();
const mockNodeAccount = mockNode.address
await mockOracle.setAuthorizedSenders([mockNodeAccount])


const transaction: ContractTransaction = await apiConsumer.requestWithWords(ANY_ARGS_FOR_API)
const transactionReceipt: ContractReceipt = await transaction.wait(1)
const requestId = transactionReceipt.events[0].topics[1]
const apiFilter = apiConsumer.filters.ChainlinkRequested()
const apiEvents = await apiConsumer.queryFilter(apiFilter)
console.log(apiEvents)
const filter = mockOracle.filters.OracleRequest()
const events = await mockOracle.queryFilter(filter)
const {specId, requester, requestIdEvent, payment, callbackAddr, callbackFunctionId, cancelExpiration, dataVersion, data} = events[0].args

const encodeMwr = web3.eth.abi.encodeParameters(
['bytes32','int256','int256','int256','string','string'],
[requestId, '123', '456', '789', 'text', 'moreText'])

const encodeRequestId = web3.eth.abi.encodeParameters(
['bytes32'],
[requestId])

await mockOracle.connect(mockNode).fulfillOracleRequest2(requestIdfirst, payment, callbackAddr, callbackFunctionId, cancelExpiration, encodeMwr)

为了改进这一点,你可以在你的测试脚本中添加一个API http GET调用,以获取fulfuloraclerequest或fulfuloraclerequest2函数的回调数据的其余部分。

最新更新