EOLink 0.1.2: An updated GET request

If you like this content consider tipping in ETH, LINK or an EC20 stablecoin at this address: 0xf9f6849230cBe10200B43dB103511b778898e71C

Note, in this series I am using linux (Ubuntu 20.04) and Python 3.8 – some instructions will be specific to this OS/env

In the last post, I described the process of making a simple GET request via a Chainlink Oracle that was ingested into a smart contract deployed on Ethereum. In that post, the GET request pointed to the default API that was included in the chainlink-mix brownie bake (the cryptocompare API). In this post, I will update to a different API, just to explore in some more detail how these requests are structured and handled by the oracle.

Choosing an API

In the chainlink-mix brownie bake the example GET request grabs the 24-hour ETH volume from the cryptocompare API:

## APIConsumer.sol

...

function requestVolumeData() 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", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
        

        request.add("path", "RAW.ETH.USD.VOLUME24HOUR");
        
        // Multiply the result by 1000000000000000000 to remove decimals
        int timesAmount = 10**18;
        request.addInt("times", timesAmount);
        
        // Sends the request
        return sendChainlinkRequestTo(oracle, request, fee);
    }

In this post I will update the contract to grab different data from alternative APIs. First, I’ll get the spot price of LINK from the Coingecko API. I chose this simply because I use Coingecko when I want to check spot prices and I already know there is an API that can be called for free.

First task was to get information about the Coingecko API. Thankfully, there is detailed documentation here. For simply grabbing the spot price of an asset, the GET/simple/price API is sufficient. The documentation includes the url for making this request, and an example of the returned json data.

Screenshot of the Coingecko API documentation showing the params, request URL and response.

This is all the information needed to push ahead and integrate this API into the contract. I will start with the APIConsumer.sol file used in the previous post, but save it as a new contract (APICoinGecko.sol).

Updating the contract

Inside APICoinGecko.sol, I first renamed the contract so that instead of reading “APIConsumer is ChainlinkClient” it now reads “APICoinGecko is ChainlinkClient”. No changes are needed to the pragma statement or the import of ChainlinkClient.sol.

In the variable definitions I swapped out the variable “volume” for the variable “price” – this will eventually contain the LINK token price in USD. The constructor remains the same and still takes the same arguments as in APIConsumer.sol.

The important changes come inside the request function. Here, the request url and the path to the necessary data are updated to be consistent with the new Coingecko API identified earlier. I have renamed the functions to update references to “volume data” to “price data”. The returned value will easily fit into a bytes32 object, so I did not even update the oracle – the same exact GET request from the same oracle will complete this task.

With the necessary changes made, my new APICoinGecko contract looks like this:

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

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

contract APICoinGecko is ChainlinkClient {
  
    uint256 public price;
    
    address private oracle;
    bytes32 private jobId;
    uint256 private fee;
    

    constructor(address _oracle, string memory _jobId, uint256 _fee, address _link) public {
        if (_link == address(0)) {
            setPublicChainlinkToken();
        } else {
            setChainlinkToken(_link);
        }
        // oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
        // jobId = "29fa9aa13bf1468788b7cc4a500a45b8";
        // fee = 0.1 * 10 ** 18; // 0.1 LINK
        oracle = _oracle;
        jobId = stringToBytes32(_jobId);
        fee = _fee;
    }
    

    function requestPriceData() 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", "https://api.coingecko.com/api/v3/simple/price?ids=Chainlink&vs_currencies=USD");
        

        request.add("path", "chainlink.usd");
        
        // Multiply the result by 1000000000000000000 to remove decimals
        int timesAmount = 10**18;
        request.addInt("times", timesAmount);
        
        // Sends the request
        return sendChainlinkRequestTo(oracle, request, fee);
    }
    

    function fulfill(bytes32 _requestId, uint256 _price) public recordChainlinkFulfillment(_requestId)
    {
        price = _price;
    }

    function stringToBytes32(string memory source) public pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }

        assembly {
            result := mload(add(source, 32))
        }
    }
}

deploy

This contract now needs to be deployed to the Kovan testnet. To do this I used brownie console. In the terminal:

brownie console --network kovan

# load kovan wallet (previously added to brownie accounts)

account  = accounts.load('main')

# APICoinGecko is already available in brownie because it is in the /contracts directory
# deploy contract: args are 1. oracle address, 2. jobID, 3. fee, 4. LINK address

APICoinGecko.deploy('0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e','29fa9aa13bf1468788b7cc4a500a45b8',100000000000000000,'0xa36085F69e2889c224210F603D836748e7dC0088',{'from
':account})

These commands opened the brownie console, loaded my Kovan wallet, then deployed the APICoinGecko contract to the Kovan testnet. The arguments required for the deployment are the same as for APIConsumer.sol and consistent with the constructor function definition. The first argument is the oracle address, then the jobID, then the fee, then the address for the link token. Once, deployed, the deployment transaction details are displayed in the terminal, critically including the deployment address.

fund

Now, this contract requires LINK tokens to pay the oracle for the GET request. I already loaded my wallet with ETH and LINK from the Kovan faucets. To send LINK to the contract I used MetaMask (select LINK token >> Send and provide the deployment address) and sent 5 LINK. NB make sure this is the Kovan network and test-LINK or real assets will be burned!

Request

With the contract funded, a GET request can now be made to the CoinGecko API. First, the contract must be instantiated in the brownie console using the deployment address.

contract = Contract('0xB148Dbb98cAAAdb2A330102F23454dBACC7b1796')

Now the contract object can be used to call the requestPriceData() function, which returns a transaction receipt which we will pass to the variable transactionID.

Now, the result of the GET request is stored in the “price” variable in the contract, and it can be retrieved using:

price = contract.price()
price = price/1e18
price

>>> 23.75

One thought on “EOLink 0.1.2: An updated GET request

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s