Search code examples
ethereumsoliditysmartcontractschainlinkkovan

Chainlink job is not being called by the Solidity Smart Contract using Operator.sol contract


The job spec deployed and running on Chainlink node is being successfully triggered and completed when called using the Solidity smart contract that is using the Oracle.sol contract.

But, since the requirement is to return the large response (https://docs.chain.link/docs/large-responses/), so I have to use Operator.sol contract instead of Oracle.sol. Then, the job is not being called.

The deployed Operator contract is looking as:

Deployed Operator.sol


The LINK Token and Owner addresses that I have used to deploy the Operator.sol contract are:

LINK and Owner addresses


The LINK Token address is basically taken from the official Chainlink doc (https://docs.chain.link/docs/fulfilling-requests/) mentioning the Kovan Testnet LINK token address:

LINK Token address


And the owner address is taken from the Account address of the running Chainlink node:

Owner address


And the Solidity smart contract code is:

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

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

contract GenericLargeResponse is ChainlinkClient {
  using Chainlink for Chainlink.Request;

  bytes public data;

  constructor(
  ) {
    setChainlinkToken(0xa36085F69e2889c224210F603D836748e7dC0088);
    setChainlinkOracle(0x8114f13FaF377FFc7A5AD32fb8a1e448667b871D);
  }

  function requestBytes(
  )
    public
  {
    bytes32 specId = "a3d1b2c945244e44bdb412c5b5287df3";
    uint256 payment = 100000000000000000;
    Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.fulfillBytes.selector);
    req.add("data", "{\"agg_x\": \"agg_mean\", \"dataset_code\":\"MODIS/006/MOD14A1\", \"selected_band\":\"MaxFRP\", \"image_scale\":1000, \"start_date\":\"2021-09-01\", \"end_date\":\"2021-09-10\", \"geometry\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"id\":1},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[29.53125,19.642587534013032],[29.53125,27.059125784374068],[39.90234375,27.059125784374068],[39.90234375,19.642587534013032],[29.53125,19.642587534013032]]]}},{\"type\":\"Feature\",\"properties\":{\"id\":2},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[46.40625,13.752724664396988],[46.40625,20.138470312451155],[56.25,20.138470312451155],[56.25,13.752724664396988],[46.40625,13.752724664396988]]]}}]}}");
       
    sendOperatorRequest(req, payment);
  }

  function fulfillBytes(
    bytes32 requestId,
    bytes memory bytesData
  )
    public
    recordChainlinkFulfillment(requestId)
  {
    data = bytesData;
  }

}

The contract is compiled and deployed successfully. But the requestBytes function is unable to trigger the job-spec running on the Chainlink node.

PS: The function of the similar smart contract that is using the Oracle.sol is successfully triggering the job-spec.


The TOML Job spec running on Chainlink node is:

type = "directrequest"
schemaVersion = 1
name = "shamba-fire-data"
contractAddress = "0x8114f13FaF377FFc7A5AD32fb8a1e448667b871D"
maxTaskDuration = "0s"
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="bridge" name="shamba-fire-bridge" requestData="{\\"id\\": $(jobSpec.externalJobID), \\"data\\":$(decode_cbor.data)}"]
    parse        [type="jsonparse" path="result,1,0" data="$(fetch)"]
    encode_data  [type="ethabiencode" abi="(uint256 value)" data="{ \\"value\\": $(parse) }"]
    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="0x8114f13FaF377FFc7A5AD32fb8a1e448667b871D" data="$(encode_tx)"]

    decode_log -> decode_cbor -> fetch -> parse -> encode_data -> encode_tx -> submit_tx
"""
externalJobID = "a3d1b2c9-4524-4e44-bdb4-12c5b5287df3"

Can anyone please point me in the right direction, like what I'm doing wrong in these steps due to which the smart contract is unable to trigger the job-spec ?


Solution

  • First of all, the owner address of the Operator.sol should be the Metamask wallet Kovan testnet address (instead of the account address of the running Chainlink node) from which the transactions are being done.

    Then, after the Operator.sol being deployed successfully, call the setAuthorizedSenders function of the Operator.sol by passing in the account address of the running Chainlink node in the senders address[] field like:

    setAuthorizedSenders function


    Then, the job-spec's encode_data should be having requestIdparameter as well:

    encode_data  [type="ethabiencode" abi="(bytes32 requestId, uint256[][] value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(parse) }"]
    

    The entire job-spec should be:

    type = "directrequest"
    schemaVersion = 1
    name = "shamba-fire-data"
    contractAddress = "0xf4434feDd55D3d6573627F39fA39867b23f4Bf7F"
    maxTaskDuration = "0s"
    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="bridge" name="shamba-fire-bridge" requestData="{\\"id\\": $(jobSpec.externalJobID), \\"data\\":$(decode_cbor.data)}"]
        parse        [type="jsonparse" path="result" data="$(fetch)"]
        encode_data  [type="ethabiencode" abi="(bytes32 requestId, uint256[][] value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(parse) }"]
        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="0xf4434feDd55D3d6573627F39fA39867b23f4Bf7F" data="$(encode_tx)"]
    
        decode_log -> decode_cbor -> fetch -> parse -> encode_data -> encode_tx -> submit_tx
    """
    externalJobID = "66229880-79e1-43c6-9d9e-0eb4b668729d"
    

    Similarly, the solidity smart contract code should be:

    //SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    
    import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
    
    contract GenericLargeResponse is ChainlinkClient {
      using Chainlink for Chainlink.Request;
    
      uint256[][] public data;
    
      constructor(
      ) {
        setChainlinkToken(0xa36085F69e2889c224210F603D836748e7dC0088);
        setChainlinkOracle(0xf4434feDd55D3d6573627F39fA39867b23f4Bf7F);
      }
    
      function requestBytes(
      )
        public
      {
        bytes32 specId = "6622988079e143c69d9e0eb4b668729d";
        //0x3065666666656632313564353466316339663332623636376466613061346536;
        uint256 payment = 1000000000000000000;
        Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.fulfillBytes.selector);
        req.add("data", "{\"agg_x\": \"agg_mean\", \"dataset_code\":\"MODIS/006/MOD14A1\", \"selected_band\":\"MaxFRP\", \"image_scale\":1000, \"start_date\":\"2021-09-01\", \"end_date\":\"2021-09-10\", \"geometry\":{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"id\":1},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[29.53125,19.642587534013032],[29.53125,27.059125784374068],[39.90234375,27.059125784374068],[39.90234375,19.642587534013032],[29.53125,19.642587534013032]]]}},{\"type\":\"Feature\",\"properties\":{\"id\":2},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[46.40625,13.752724664396988],[46.40625,20.138470312451155],[56.25,20.138470312451155],[56.25,13.752724664396988],[46.40625,13.752724664396988]]]}}]}}");
           
        sendOperatorRequest(req, payment);
      }
    
      function fulfillBytes(
        bytes32 requestId,
        uint256[][] memory bytesData
      )
        public
        recordChainlinkFulfillment(requestId)
      {
        data = bytesData;
      }
    
    }