docs
Example Contract

Example Contract for Oracle Integration

This example contract illustrates how to interact with an Oracle. Clients can use this code as a reference to create their own contract based on this model.

Contract Code

Below is the code for the ExampleContract.sol contract:

// SPDX-License-Identifier: MIT
// Example Contract
pragma solidity ^0.8.19;
 
import {OracleRequest} from "./oracle/lib/OracleRequest.sol";
import {OracleClient} from "./oracle/OracleClient.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; //not sure if this is needed
 
contract ExampleContract is OracleClient, ReentrancyGuard {
    using OracleRequest for OracleRequest.Request;
 
    address public oracleRouter;
 
    struct RequestData {
        uint256 exampleUint256;
        address exampleAddress;
    }
 
    mapping(uint256 requestId => RequestData request)
        public s_requestIdToRequest;
 
    bytes public exampleFulfillResponseBytes;
    uint256 public exampleFulfillResponseUint256;
 
    string public testerCustomBody;
 
    constructor(address oracleRouterAddress) OracleClient(oracleRouterAddress) {
        oracleRouter = oracleRouterAddress;
    }
 
    // First calculate the gas units for the callback off-chain
    function exampleSendRequestPOST(
        uint256 fulfillGasUsed,
        string memory clientId,
        string memory name
    ) external payable nonReentrant {
        uint256 gasCost = gasCostFulfill(fulfillGasUsed);
 
        require(
            msg.value > gasCost,
            "ETH sent is less than gas cost for the callback"
        );
 
        string memory url = "http://<your host>:<port>/api/data/";
 
        string memory requestBody = string(
            abi.encodePacked(
                '{"clientId":"',
                clientId,
                '","name":"',
                name,
                '"}'
            )
        );
 
        OracleRequest.Request memory req;
 
        req.url = url; //requiered
        req.method = "POST"; //required
        req.slot = "0"; // <-- SELECT SLOT YOU WANT TO USE FOR THIS REQUEST
        req.jsonResponsePath = "data.result";
        req.requestBody = requestBody;
        testerCustomBody = req.requestBody;
        req.bodyValuesDataTypes = '["uint256","string"]'; // remember double quote in elements of string array for match json format
        //req.allowFloatResponse = true; <-- uncomment this line if you are specting a float to return (IF YOU DO, BOTH UINT AND FLOAT WIL BE MUL BY 10**18 BEFORE SENDING THE ORACLE RESPONSE)
 
        bytes memory requestData = req.encodeCBOR();
 
        //this will emit a event on OracleRouter that nodes will caputure
        //msg.value is sent here
        uint256 requestId = _sendRequest(requestData, fulfillGasUsed);
 
        // Dummy data to show how to save the requestId of this request, for later build your code
        // inside fulfillRequest() based on the same requestId
        uint256 dummyUint256 = 11;
        address dummyAddress = address(0);
 
        s_requestIdToRequest[requestId] = RequestData(
            dummyUint256,
            dummyAddress
        );
    }
 
    function exampleSendRequestGET(
        uint256 fulfillGasUsed
    ) external payable nonReentrant {
        uint256 gasCost = gasCostFulfill(fulfillGasUsed);
        require(
            msg.value > gasCost,
            "ETH sent is less than gas cost for the callback"
        );
 
        string memory url = "http://<your host>:<port>/api/data/";
        OracleRequest.Request memory req;
 
        req.url = url;
        req.method = "GET";
        req.jsonResponsePath = "data.clientId[3]";
 
        bytes memory requestData = req.encodeCBOR();
 
        uint256 requestId = _sendRequest(requestData, fulfillGasUsed); //this will emit a event on OracleRouter that nodes will caputure
 
        uint256 dummyUint256 = 11;
        address dummyAddress = address(0);
 
        s_requestIdToRequest[requestId] = RequestData(
            dummyUint256,
            dummyAddress
        );
    }
 
    // THIS FUNCTION IS CALLED BY THE ORACLE
    // oracle will call fulfill() on the OracleRouter address
    function fulfillRequest(
        uint256 requestId,
        bytes memory response
    ) internal override {
        exampleFulfillResponseBytes = response;
    }
 
    function decodeBytesToUint256() public view returns (uint256) {
        uint256 value = abi.decode(exampleFulfillResponseBytes, (uint256));
        return value;
    }
 
    function decodeBytesToString() public view returns (string memory) {
        string memory value = string(exampleFulfillResponseBytes);
        return value;
    }
 
    function decodeBytesToBool() public view returns (bool) {
        bool value = abi.decode(exampleFulfillResponseBytes, (bool));
        return value;
    }
 
    function gasCostFulfill(
        uint256 fulfillGasUsed
    ) public view returns (uint256) {
        uint256 gasPrice = tx.gasprice;
        return fulfillGasUsed * gasPrice;
    }
}
 
 

Let's start by analyzing the sendRequestPost() function

As you can see, the function is payable. Why is that? This is because EyeOracle, unlike other oracles that use subscription-based systems to cover the gas cost for the oracle callback, allows the user to pay directly for the callback.

This is a different approach, but we believe it's necessary since the user who calls sendRequestPost() should cover all the costs involved in that transaction.

How to know the gas used in the callback?

In your dashboard, there is a useful tool to simulate the callback cost once you have the contract deployed. See Gas calculations


Let's continue analyzing the contract

Now, let's break down the Request object and understand the purpose of each attribute:

  • req.url ==> The URL to which the oracle's request will point.
  • req.method ==> The method the request will use (e.g., POST, GET).
  • req.slot ==> Memory slot position for the headers that will be used in the request. You will have configured these headers beforehand in the dashboard. See How configure secrets
  • req.jsonResponsePath ==> Specifies how the oracle should capture data from the response. In this case, from the JSON response, the oracle will retrieve the value from response.data.result.
  • req.allowFloatResponse ==> DEFAUT=flase, Allow oracle to float reponses, IMPORTANT if you pass req.allowFloatResponse=true, both uint and float responses wil be multiplicated by 10**18 before sending to your contract.
  • req.requestBody ==> The request body, which is a JSON string that allows you to configure your key-value pairs to be incorporated into the body of the request. All input values MUST be strings, but with req.bodyValuesDataTypes you can specify the type of data being input.
  • req.bodyValuesDataTypes ==> JSON string of the data type of the values you are sending in the body, enabling the oracle to transform them before making the API request.

The correct syntaxis of a JSON string to pass to the oracle:

This is correct

'["uint256","string"]';

This is wrong

"['uint256','string']";

Note --> If you chose uint256 but you number is too big (BigInt) oracle will auto convert to string. Example if you pass 5 and you set 5 as uint256 oracle will use 5 as number class, but if you pass 5*10**18 oracle will auto transform to string class even if you use uint256. This is due JSON problems to parse BinInts

Finally, when all the req parameters are set, you can encode them for the oracle by calling req.encodeCBOR().