I'm trying to return an array of custom type (struct) MyStruct[] of ambiguous size to my Chainlink consumer contract, and am having trouble developing the appropriate job config (TOML) for it.
我试图将大小不明确的自定义类型(Struct)MyStruct[]数组返回给我的Chainlink消费者契约,但在为它开发适当的作业配置(TOML)时遇到了问题。
I'm aware of the gas concerns here, but would like to ignore this for now, as I'll be handling this via separate circuit breakers in the API endpoint.
我知道这里的气体问题,但现在我想忽略这一点,因为我将通过API端点的单独断路器来处理这一问题。
I have a consumer contract that looks similar to this (pseudo-code):
我有一个消费者合同,看起来类似于这样(伪代码):
//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";
contract ConsumerContract is ChainlinkClient, ConfirmedOwner {
. . .
struct MyStruct {
uint a;
uint b;
string x;
}
MyStruct[] public result;
function request() public {
Chainlink.Request memory req = buildOperatorRequest(jobId, this.fulfill.selector);
// (add parameters)
// Send the request to the Chainlink oracle
sendOperatorRequest(req, fee);
}
function fulfill(bytes32 requestId, bytes[] memory _result) public recordChainlinkFulfillment(requestId) {
emit RequestFulfilled(requestId, _result);
// Process the response
for(uint i = 0; i < _result.length; i++){
MyStruct memory myStruct = abi.decode(_result[i], (MyStruct));
result.push(myStruct);
}
}
. . .
Example response from API endpoint (JSON representation of an unknown number of structs). Since I have control over the API endpoint, I can format this response however necessary:
来自API终结点的示例响应(未知数量的结构的JSON表示)。由于我可以控制API端点,因此我可以根据需要格式化此响应:
[
[
0,
1,
"myString-1"
],
[
2,
3,
"myString-2"
]
. . .
]
Example job TOML (not working, but I took a stab at what I think it should look like):
示例工作TOML(不工作,但我尝试了一下我认为它应该是什么样子):
type = "directrequest"
schemaVersion = 1
name = "my-job"
forwardingAllowed = false
maxTaskDuration = "0s"
contractAddress = "XXX"
minContractPaymentLinkJuels = "0"
observationSource = """
decode_log [type="ethabidecodelog"
abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
data="$(jobRun.logData)"
topics="$(jobRun.logTopics)"]
decode_cbor [type="cborparse" data="$(decode_log.data)"]
fetch [type="http" method=GET url="$(decode_cbor.url)" allowunrestrictednetworkaccess="true"]
encode_data [type="ethabiencode"
abi="(bytes32 requestId, bytes[] _data)"
data="{\\"requestId\\": $(decode_log.requestId), \\"_data\\": [$(fetch)]}"
]
encode_tx [type="ethabiencode"
abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata 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 [type="ethtx" to="XXX" data="$(encode_tx)"]
decode_log -> decode_cbor -> fetch -> encode_data -> encode_tx -> submit_tx
"""
While the Chainlink job pipeline completes interally, the error occurs during the fulfill transaction, causing the fulfill transaction to fail (likely because my consumer contract's fulfill() function errors out trying to translate the bytes[] response into a struct[]
).
虽然Chainlink作业管道在内部完成,但错误发生在fulfill transaction期间,导致fulfill transaction失败(可能是因为我的消费者合约的fulfill()函数在尝试将bytes[]响应转换为struct[]时出错)。
I checked out the Chainlink documentation, but nowhere can I find any information on how to return a custom data type (ie, struct) using a Direct Request job.
我查看了Chainlink文档,但我在任何地方都找不到任何关于如何使用Direct RequestJOB返回定制数据类型(即结构)的信息。
UPD: After scouring the internet, there is a similar question here, but it does not relate to an array of structs, and the struct included is quite basic (contains only one field):
https://ethereum.stackexchange.com/questions/148499/chainlink-any-api-ethabiencode-while-parsing-abi-string-bad-abi-specification
更新:在互联网上搜索后,这里有一个类似的问题,但它与结构数组无关,并且包含的结构非常基本(只包含一个字段):https://ethereum.stackexchange.com/questions/148499/chainlink-any-api-ethabiencode-while-parsing-abi-string-bad-abi-specification
Thank you for your help!
谢谢你的帮助!
更多回答
I don't think encode_data can be used to encode an array. Can you try to encode the array of struct with encode_large
? In order to use encode_large, please replace the codes
我不认为encode_data可以用来对数组进行编码。您可以尝试使用encode_Large对结构数组进行编码吗?要使用ENCODE_LARGE,请替换代码
encode_data [type="ethabiencode"
abi="(bytes32 requestId, bytes[] _data)"
data="{\\"requestId\\": $(decode_log.requestId), \\"_data\\": [$(fetch)]}"
]
to
至
encode_large [type="ethabiencode"
abi="(bytes32 requestId, bytes[] _data)"
data="{\\"requestId\\": $(decode_log.requestId), \\"_data\\": $(fetch)}"
]
In addition, in your TOML file, there is no parse
to parse the data you fetched from API in fetch
and no path is provided either. You are trying to parse the entire JSON file, so please make sure that you get the correct value. You can add a parse
with the codes below:
此外,在您的TOML文件中,没有解析从Fetch中的API获取的数据,也没有提供路径。您正在尝试解析整个JSON文件,因此请确保获得正确的值。您可以使用以下代码添加解析:
parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"]
Hope it helps
希望能有所帮助
更多回答
Won't the two job tasks you've posted above (encode_data
and encode_large
) have the same behavior, as they are both of type ethabiencode
? What you've changed is the name: docs.chain.link/chainlink-nodes/oracle-jobs/…
您在上面发布的两个作业任务(encode_data和encode_Large)不会有相同的行为吗,因为它们都是ethabiencode类型?您更改的是名称:docs.chain.link/Chainlink-Nodes/Oracle-JOBS/…
Also, I omitted the parse
task in my example for brevity, but please consider it present in my real-world implementation. Thanks
另外,为了简洁起见,我在示例中省略了解析任务,但请考虑它存在于我的实际实现中。谢谢
oops, I thought you were using abi="(bytes32 requestId, bytes _data)"
, just try to update it to abi="(bytes32 requestId, bytes[] _data)". You are right,
encode_data and encode_large
represents the same task. I can get the array of uint256 successfully but have not tried the array of struct. Can you provide a API you are using and I can give it a try.
哎呀,我还以为您使用的是ABI=“(bytes32 questID,bytes32 questID,bytes_data)”,试着把它更新为abi=“(bytes32 questID,bytes32 questID,bytes[]_data)”。您说得对,encode_data和encode_Large代表相同的任务。我可以成功地获取uint256的数组,但还没有尝试结构的数组。你能提供你正在使用的API吗,我可以试一试。
Thanks. You could send a POST request to httpbin.org/post with the following body: {"data":[[0,1,"myString-1"],[2,3,"myString-2"]]}
. This service will echo the provided body back to you at the following JSON path: json,data
, which essentially is an array of 2 objects that could theoretically be mapped into an array of MyStruct
(uint,uint,string). For the record, I am definitely able to return an array of uint256 - it's specifically a custom data type (struct) that I am having issues with!
谢谢。您可以将POST请求发送到httpbin.org/POST,正文如下:{“data”:[[0,1,“myString-1”],[2,3,“myString-2”]]}。该服务将通过以下JSON路径向您回显提供的正文:json,data,它本质上是一个由2个对象组成的数组,理论上可以映射到MyStruct(uint,uint,string)数组中。需要说明的是,我绝对能够返回uint256数组--这是我遇到问题的一个特别的定制数据类型(结构)!
我是一名优秀的程序员,十分优秀!