Making HTTP Requests in Solidity

HTTP requests work on Ethereum and in Solidity with what’s known as oracles

Patrick Collins
Sep 4, 2020 · 7 min read
http requests in solidity
http requests in solidity
Original background image from ©peshkov via Canva.com

Making HTTP requests in Solidity is simple with Chainlink. If you’d like more insight on best practices, then after reading this article be sure to read API calls on Blockchain.

Let’s take a step back to start.

How Does Making a Chainlink Request Work?

Remember that Chainlink oracles are blockchain middleware, so we need something to programmatically tell them to make an HTTP request. We need something to initiate them to start getting data for us. These instructions for telling a chainlink node to start getting data are called Chainlink Initiators.

A Chainlink node can have as many initiators as it likes. Each will define a different way for the node to start getting data.

Initiator 1: Start getting data when x occurs

Initiator 2: Start getting data when y occurs

Initiator 3: Start getting data when z occurs

Etc…

Along with the initiators, as well as a set of instructions for what to do when they are initiated. This set of instructions about what to do are called the “tasks” or “adapters” list.

Adapter 1: Do q to data once received

Adapter 2: Do r to data once received

Adapter 3: Do s to data once received

Etc…

The combination of initiators and adapters makes up a single job on a chainlink node. Chainlink nodes can have multiple jobs defined so that they can do many different things in a simple interface.

Here is what a sample job definition looks like, you can see it has a list of initiators defined at the top, and then a list of adapters/tasks defined after. You can also see, that the initiators can take parameters to allow them to do even more. This sample job definition is known as a jobspec.

{
"initiators": [
{
"type": "runlog",
"params": {
"address": "0xd8c819674b79c7372d56db03280a5695a9254894"
}
}
],
"tasks": [
{
"type": "httpget"
},
{
"type": "jsonparse"
},
{
"type": "multiply"
},
{
"type": "ethuint256"
},
{
"type": "ethtx"
}
]
}

Most nodes have a lot of similar jobs, and the one in the sample above is one of the most common jobs out there. This jobspec defines how to make simple httpget requests into your smart contract. There is also an httppost adapter if you’d like to make a POST request instead of a GET. This job is defined by a:

  1. Runlog initiator
  2. This specific list of adapters/tasks: httpget, jsonparse, multiply, ethuint256, and ethTx adapters.

The Runlog initiator is one of the most common initiators used. It defines that the chainlink node will watch the blockchain for any log events that include that job’s ID. Once it finds an event, it will execute the adapters, and post the data on-chain. We will go over what each one of these adapters does soon.

Your smart contract will have to wait a little for the Chainlink node to return your data, but once it does, your smart contract will pull the data from this transaction.

A runlog initiator means the node watches the blockchain for logs containing the jobID
A runlog initiator means the node watches the blockchain for logs containing the jobID
Image from chain.link

What Does This Look Like in Solidity?

To show what this looks like in code, let’s look at getting the numerical price of ETH. You can use this Remix link to follow along, but here is a subset of that contract code.

// Creates a Chainlink request with the uint256 multiplier job
// Ideally, you'd want to pass the oracle payment, address, and jobID as
function requestEthereumPrice()
public
onlyOwner
{
// newRequest takes a JobID, a callback address, and callback function as input
Chainlink.Request memory req = buildChainlinkRequest(JOBID, address(this), this.fulfill.selector);
req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");
req.add("path", "USD");
req.addInt("times", 100);
sendChainlinkRequestTo(ORACLE_ADDRESS, req, ORACLE_PAYMENT);
}
// fulfill receives a uint256 data type
function fulfill(bytes32 _requestId, uint256 _price)
public
// Use recordChainlinkFulfillment to ensure only the requesting oracle can fulfill
recordChainlinkFulfillment(_requestId)
{
currentPrice = _price;
}

To start making a request to a chainlink node, we call the buildChainlinkRequest function that is imported from the ChainlinkClient.sol, this function returns a Chainlink.Request struct. In this function, we pass the jobID, return address, and fulfillment function when we start to build it.

We can find a jobID of an independent Chainlink node by heading over to a node listing service, like market.link, and searching for what we want. Node listing services are where independent nodes post their information about how to connect to their Chainlink node. You can use them to build your network of decentralized oracles. Make sure you’re on the correct network (ropsten, mainnet, kovan, etc).

For this, we want a job that can make an httpget call and return a uint256 , so let’s try searching for that job on the ropsten network.

HTTP Get request jobs on market.link
HTTP Get request jobs on market.link
Searching for a job on market.link

Let’s pick a job, and just make sure that the jobspec is what we are looking for. We can head on over to the jobspec tab to see what the initiators and adapters are.

You’ll need to find the right jobspec so you can put the JobID into your solidity
You’ll need to find the right jobspec so you can put the JobID into your solidity
Image from market.link

To make sure this job has the right adapters, we can check out their tasks/adapters list. The image above shows that this job has exactly the initiators and adapters/tasks that we want.

Once we verify that the job has the adapters we want, we can then copy the job ID to use in our code. You can see other nodes that have a similar job, and you’ll need this list when you make your contract decentralized, but again for testing and developing we can just pull from a single node. Make sure you also grab the oracle address, as you’ll need this later.

Chainlink.Request memory req = buildChainlinkRequest(JOBID, address(this), this.fulfill.selector);

We then put the JOBID in the first parameter of our buildChainlinkReqeust. The second parameter is the address of the contract to return the data to also known as the callbackaddress. The last parameter is the function that will process the data once collected aka thecallbackFunctionSignature. We want to return the data to this contract, so we put in address(this), and our function that will be processing the data will be fulfill. We defined the fulfill function right below this one.

function fulfill(bytes32 _requestId, uint256 _price)
public
// Use recordChainlinkFulfillment to ensure only the requesting oracle can fulfill
recordChainlinkFulfillment(_requestId)
{
currentPrice = _price;
}

The uint256 _price parameter is where the Chainlink node will input the price gathered from making the http get request. In our example above, we are just setting the return value from the node to our currentPrice variable.

Back in the requestEthereumPrice function, we can then add parameters to our adatpers/tasks. Let’s go over what each adapter does:

The Chainlink node will make an HTTP GET request; usually, this is a simple API call.

The HTTP GET request that we want to make in the parameters passed to the req variable.

req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");

At the time of writing, the return value of this HTTP GET request is:

{"USD":391.41}

Once the node makes the HTTP GET request, it will go through the json and find only the value that we want. Storing the entire return of the HTTP GET request would be really expensive, as the more you store on the Ethereum chain the more ETH gas you have to pay. So, we want to return as little as possible.

req.add("path", "USD");

This will condense the return of the HTTP GET request to just the value
391.41 . It walks down the json of the {“USD”:391.41} return value. For longer json returns, you can add indexes of lists too. For example, if you had an object like:

{"USD": [ "price": {"ETH": 391.41}]}

We could walk down the path to that value with:

req.add("path", "USD.0.ETH");

Once we get the 391.41 value, we have to make it a whole number. Decimals don’t work in solidity, and we need to represent decimals using fixed-point math.

req.addInt("times", 100);

This will adapt our result once more to be 39141 (since we move the decimal place over twice by multiplying by 100).

You’ll notice in our code above, we don’t pass any parameters to this adapter; that’s because we don’t need to. This adapter just converts our answer of 39141 to the solidity understandable format.

This one also doesn’t need any parameters. This is the adapter that actually posts the data back on-chain.

Now we use the sendChainlinkRequestTo method, with the address of the oracle we got from market.link, the request itself, and the oracle_payment.

sendChainlinkRequestTo(ORACLE_ADDRESS, req, ORACLE_PAYMENT);

We get the ORACLE_ADDRESS and ORACLE_PAYMENT from market.link as well. The Oracle payment is how much LINK oracle gas we want to send the oracle, each job has its own minimum cost to send.

Send It

Now, let’s run it again! Let’s deploy the contract, fund it with link, and then request the price of ETH. You can go through the example walkthrough if you’re confused about how to do this. Or the more step-by-step YouTube video:

Intro to Solidity and Remix and Deploy your first smart contract

The Chainlink node will pick up on the request by reading it off the blockchain. Again, it can read it off the blockchain because it is using the Runlog initiator. Once it picks up the job, it runs through the adapters, and posts it back on-chain for your smart contract to use!

Oracles can define the parameters right in their job definitions as well (called the jobspec), and you could just call the job without having to put in any parameters at all.

This has been a pretty deep dive on exactly how working with Chainlink nodes work, but with this, you now have a lot of the basic tools to build some amazing projects in the blockchain ecosystem.

As you can see, getting off-chain data in ethereum and any blockchain is easy, safe, and reliable with chainlink. Let’s see some building now!

Better Programming

Advice for programmers.

By Better Programming

A weekly newsletter sent every Friday with the best articles we published that week. Code tutorials, advice, career opportunities, and more! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Thanks to Zack Shapiro

Patrick Collins

Written by

Chainlink developer advocate, fintech data engineer, and human performance buff. I love hearing about how people make their lives better. https://alphachain.io

Better Programming

Advice for programmers.

Patrick Collins

Written by

Chainlink developer advocate, fintech data engineer, and human performance buff. I love hearing about how people make their lives better. https://alphachain.io

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store