2

I cannot update the string value in my contract. But if I change the output of my api to a int and use uint256 everywhere it works fine. Any ideas?

    type = "directrequest"
schemaVersion = 1
name = "Get Robot Url"
externalJobID = "24f1447a-a11e-4a17-8b73-d9fedd12755c"
maxTaskDuration = "0s"
contractAddress = "0x4f6B69dd4043961Ce23F9e5569e11FbBeC375465"
minIncomingConfirmations = 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" allowUnrestrictedNetworkAccess=true method=GET url="$(decode_cbor.get)"]
parse        [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"]
encode_data  [type="ethabiencode" abi="(bytes value)" data="{\\"value\\": $(parse) }"]
encode_tx    [type="ethabiencode"
              abi="fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes 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="0x4f6B69dd4043961Ce23F9e5569e11FbBeC375465" data="$(encode_tx)"]

decode_log -> decode_cbor -> fetch -> parse -> encode_data -> encode_tx -> submit_tx

"""


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

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

/**
 * Request testnet LINK and ETH here: https://faucets.chain.link/
 * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
 */

/**
 * THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
 * PLEASE DO NOT USE THIS CODE IN PRODUCTION.
 */
contract APIConsumer is ChainlinkClient {
    using Chainlink for Chainlink.Request;

    string public robot_url;

    address private oracle;
    bytes32 private jobId;
    uint256 private fee;

    event RequestRobotUrlFulfilled(
        bytes32 indexed requestId,
        string indexed robot_url
    );

    /**
     * Network: Kovan
     * Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel   
     * Node)
     * Job ID: d5270d1c311941d0b08bead21fea7747
     * Fee: 0.1 LINK
     */
    constructor() {
        setPublicChainlinkToken();
        oracle = 0x4f6B69dd4043961Ce23F9e5569e11FbBeC375465;
        jobId = "24f1447aa11e4a178b73d9fedd12755c";
        fee = 0.1 * 10 ** 18; // (Varies by network and job)
    }

    /**
     * Create a Chainlink request to retrieve API response, find the target
     * data, then multiply by 1000000000000000000 (to remove decimal places from data).
     */
    function requestRobotUrl() public returns (bytes32 requestId) 
    {
        Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);

        // Set the URL to perform the GET request on
        request.add("get", "http://192.168.2.198:6000/get_robot_url");

        // Set the path to find the desired data in the API response, where the response format is:
        // {"RAW":
        //   {"ETH":
        //    {"USD":
        //     {
        //      "VOLUME24HOUR": xxx.xxx,
        //     }
        //    }
        //   }
        //  }
        // request.add("path", "RAW.ETH.USD.VOLUME24HOUR"); // Chainlink nodes prior to 1.0.0 support this format
        // request.add("path", "RAW,ETH,USD,VOLUME24HOUR"); // Chainlink nodes 1.0.0 and later support this format
        request.add("path", "robot_url");
        // Multiply the result by 1000000000000000000 to remove decimals
        // int timesAmount = 10**18;
        // request.addInt("times", timesAmount);

        // Sends the request
        return sendChainlinkRequestTo(oracle, request, fee);
    }

    /**
     * Receive the response in the form of uint256
     */ 
    function fulfill(bytes32 _requestId, bytes32 _robot_url) public recordChainlinkFulfillment(_requestId)
    {
        robot_url = bytes32ToString(_robot_url);
        emit RequestRobotUrlFulfilled(_requestId, robot_url);
    }

    function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) 
    {
        uint8 i = 0;
        while(i < 32 && _bytes32[i] != 0) {
            i++;
        }
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
            bytesArray[i] = _bytes32[i];
        }
        return string(bytesArray);
    }

    // function withdrawLink() external {} - Implement a withdraw function to avoid locking your LINK in the contract
}
Radu
  • 23
  • 2

1 Answers1

2

The v2 job-spec of get > bytes32 is currently bugged such that it is only able to return a fixed number of bytes, which is 32 only. Anything less and it will not work as intended. The chainlink labs team is aware of this issue already I believe. A work around to using (get > bytes32) is only if the intended bytes you are trying to obtain is a fixed amount and editing this following line. Ex: fixed bytes8.

encode_data  [type="ethabiencode" abi="(bytes8 value)"

If you need variable bytes, I would look at using a (get > large/variable bytes) which can return any variation of bytes per request from an api endpoint json. See following (get > large/variable bytes) job-spec. Keep in mind, you need to deploy operator.sol and point the job-spec at that smart contract address and not the oracle.sol contract address.

type = "directrequest"
schemaVersion = 1
name = "[DR] (Get > Large/Variable Bytes)"
maxTaskDuration = "0s"
contractAddress = "YOUR_OPERATOR_CONTRACT_ADDRESS"
minIncomingConfirmations = 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=&quot;cborparse&quot; data=&quot;$(decode_log.data)&quot;]
fetch        [type=&quot;http&quot; method=GET url=&quot;$(decode_cbor.get)&quot;]
parse        [type=&quot;jsonparse&quot; path=&quot;$(decode_cbor.path)&quot; data=&quot;$(fetch)&quot;]
encode_large [type=&quot;ethabiencode&quot;
            abi=&quot;(bytes32 requestId, bytes _data)&quot;
            data=&quot;{\\&quot;requestId\\&quot;: $(decode_log.requestId), \\&quot;_data\\&quot;: $(parse)}&quot;
            ]
encode_tx  [type=&quot;ethabiencode&quot;
            abi=&quot;fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)&quot;
            data=&quot;{\\&quot;requestId\\&quot;: $(decode_log.requestId), \\&quot;payment\\&quot;:   $(decode_log.payment), \\&quot;callbackAddress\\&quot;: $(decode_log.callbackAddr), \\&quot;callbackFunctionId\\&quot;: $(decode_log.callbackFunctionId), \\&quot;expiration\\&quot;: $(decode_log.cancelExpiration), \\&quot;data\\&quot;: $(encode_large)}&quot;
            ]

submit_tx    [type=&quot;ethtx&quot; to=&quot;YOUR_OPERATOR_CONTRACT_ADDRESS&quot; data=&quot;$(encode_tx)&quot;]

decode_log -&gt; decode_cbor -&gt; fetch -&gt; parse  -&gt; encode_large -&gt; encode_tx -&gt; submit_tx

"""

Matt
  • 171
  • 5