0

I have a simple contract that request a string to the API:

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

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

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

    mapping(bytes32 => string) public quotes;
    address private immutable oracle;
    bytes32 private immutable jobId;
    uint256 private immutable fee;

    event DataFullfilled(bytes32 indexed requestId, string quotes);

    constructor(
        address _oracle,
        bytes32 _jobId,
        uint256 _fee,
        address _link
    ) {
        if (_link == address(0)) {
            setPublicChainlinkToken();
        } else {
            setChainlinkToken(_link);
        }
        oracle = _oracle;
        jobId = _jobId;
        fee = _fee;
    }

    function requestQuoteData() public returns (bytes32 requestId) {
        Chainlink.Request memory request = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfill.selector
        );

        request.add("get", "https://api.kanye.rest/");

        request.add("path", "quote");

        int256 timesAmount = 1;
        request.addInt("times", timesAmount);

        return sendChainlinkRequestTo(oracle, request, fee);
    }

    function fulfill(bytes32 _requestId, string memory _quote)
        public
        recordChainlinkFulfillment(_requestId)
    {
        quotes[_requestId] = _quote;
        emit DataFullfilled(_requestId, _quote);
    }

    function withdrawLink() external {}
}

I already deployed at Goerli, works just fine... But I got to know about this new framework: Foundry, at least new to me hehe

I wrote a test (actually I just adapted the StaterKit test, changing unit256 to string, like my API res needs):

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../Vjk_APIConsumer.sol";
import "./mocks/LinkToken.sol";
import "forge-std/Test.sol";
import "./mocks/MockOracle.sol";

contract Vjk_APIConsumerTest is Test {
    Vjk_APIConsumer public apiConsumer;
    LinkToken public linkToken;
    MockOracle public mockOracle;

    bytes32 jobId;
    uint256 fee;
    bytes32 blank_bytes32;

    uint256 constant AMOUNT = 2 * 10**18;
    string constant RESPONSE = "quote";

    function setUp() public {
        linkToken = new LinkToken();
        mockOracle = new MockOracle(address(linkToken));
        apiConsumer = new Vjk_APIConsumer(
            address(mockOracle),
            jobId,
            fee,
            address(linkToken)
        );
        linkToken.transfer(address(apiConsumer), AMOUNT);
    }

    function testCanMakeRequest() public {
        bytes32 requestId = apiConsumer.requestQuoteData();
        assertTrue(requestId != blank_bytes32);
    }

    function testCanGetResponse() public {
        bytes32 requestId = apiConsumer.requestQuoteData();
        mockOracle.fulfillOracleRequest(
            requestId,
            bytes32(abi.encodePacked(RESPONSE))
        );
        assertEq(apiConsumer.quotes(requestId), RESPONSE);
    }
}

Result:

[FAIL. Reason: Assertion failed.] testCanGetResponse() (gas: 104885)

Encountered a total of 1 failing tests, 1 tests succeeded

The fulfillOracleRequest function received the parameters:

MockOracle::fulfillOracleRequest(0x25f95fcd71dc88da11f27f5725261a58ffdcfdb02d707eac69cf6a9f9106f808, 0x71756f7465000000000000000000000000000000000000000000000000000000)

0x71756f7465000000000000000000000000000000000000000000000000000000 to string = quote

But fails to call: fulfill()

Vjk_APIConsumer::fulfill()
    │   │   └─ ← "EvmError: Revert"

After several tests, I conclude that the function: fulfillOracleRequest has a resturn (bytes32 , unit256 ), the original test/mock tests an API that has unit256 as a response, but my API returns a string.

the mock file

:

    function fulfillOracleRequest(bytes32 _requestId, bytes32 _data)
        external
        isValidRequest(_requestId)
        returns (bool)
    {
        Request memory req = commitments[_requestId];
        delete commitments[_requestId];
        require(
            gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT,
            "Must provide consumer enough gas"
        );
        // All updates to the oracle's fulfillment should come before calling the
        // callback(addr+functionId) as it is untrusted.
        // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern
        (bool success, ) = req.callbackAddr.call(
            abi.encodeWithSelector(req.callbackFunctionId, _requestId, _data)
        ); // solhint-disable-line avoid-low-level-calls
        return success;
    }

I searched in ChainkLink's libs and interfaces, but I didn't find where I can change this type of value.

Can you help me? Thank you

1 Answer 1

1

What do the oracle contract and the node Job spec expect? Your contract may be written to expect a string, but the chainlink oracle node's Job specificationdocs may not be returning that.

In short: https://api.kanye.rest/quote returns a quote, but what is the Node's Job configured to do? And the oracle address may need to change too.

if you're using Chainlink ANY API then check these docs to make sure you've got the right oracle/operator contract address to handle returning the type you want.

4
  • Exactly. I managed to change the JobId in my Mock, it didn't work, but you commented on having to change the address too, I'll test it now. Thank you very much.
    – brunovjk
    Commented Oct 21, 2022 at 12:33
  • No, I cant make it work at all hehhehe I`ll go back to hardhat hehe.. thank you for your time.
    – brunovjk
    Commented Oct 22, 2022 at 12:38
  • What sort of error did you get? if you dont get it to work on HardHat then its definitely a config error. Each Oracle Node will specify the corresponding Oracle/Operator smart contract that it is "coupled" with. Make sure those details are right. Swapping either of those out wont work because of the coupling. So check network, node job spec, and oracle/smartcontract address (correct smart contract address and also correct network) etc
    – ZeusLawyer
    Commented Oct 24, 2022 at 4:47
  • It's not really an error. I'`s exactly what you said, config setup, I'm not able to do the settings exactly. I checked the, network, node job spec and oracle/smartcontract address. In remix, works fine, the question is, somehow I am not able to change these parameters properly working with mocks. But I'm still trying, I'll post the answer as soon as I get it :D
    – brunovjk
    Commented Oct 24, 2022 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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