0

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
}

1 Answer 1

1

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="cborparse" data="$(decode_log.data)"]
    fetch        [type="http" method=GET url="$(decode_cbor.get)"]
    parse        [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"]
    encode_large [type="ethabiencode"
                abi="(bytes32 requestId, bytes _data)"
                data="{\\"requestId\\": $(decode_log.requestId), \\"_data\\": $(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_large)}"
                ]

    submit_tx    [type="ethtx" to="YOUR_OPERATOR_CONTRACT_ADDRESS" data="$(encode_tx)"]

    decode_log -> decode_cbor -> fetch -> parse  -> encode_large -> encode_tx -> submit_tx
"""
3
  • Do you need to call also the setAuthorizedSendersOn method of Operator.sol and with which targets and senders?
    – Radu
    May 10 at 1:23
  • setAuthorizedSenders(["your_node_address"]) May 10 at 11:25
  • Thanks now it worked
    – Radu
    May 12 at 0:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.