Integrating Real-World Data into Smart Contracts and Blockchain Applications with Chainlink

Blockchain technology has forever changed how we store, process, and transfer data. But an inherent limitation is that blockchains are isolated networks that cannot communicate with each other or with servers outside the blockchain. This is a critical challenge for anyone looking to use blockchain technology to build an application.

For example, an eCommerce company that uses a blockchain for secure product tracking and supply chain management will face a lot of issues if they are unable to link blockchain data with other services outside of it like its supply chain data or currency exchanges.

That's where Chainlink comes in.

Chainlink is a decentralized oracle network that provides real-world data to smart contracts in blockchain development. It's become an important tool for Web3 applications and has been widely adopted in blockchain development for providing real-world data to smart contracts. In 2022 alone, Chainlink oracle services reportedly enabled more than $6.9 trillion in transaction value.

Despite its versatility, its usage has predominantly been focused on accessing accurate cryptocurrency prices.

You should note that a big percentage of Chainlink/oracle use cases involve price retrieval of different tokens in decentralized finance (DeFi) applications. In actuality, Chainlink can be used for a wide range of other purposes.

So today, we will not focus on using Chainlink in DeFi, but rather, on Chainlink’s ability to retrieve any type of data from off-chain sources.

In this article, we cover: 

But first, let's understand why data plays an important role here.

It's About Data. Always.

Data is the backbone of many applications; it's the fuel that powers them.

It’s the key business line for companies like Meta, Google, and Twitter. Storing data on regular servers off-chain is exceptionally cheap for these companies, and accessing it is usually easy and convenient.

But storing data in smart contracts can be challenging due to the cost of storage on the blockchain, which operates in isolation from the outside world. This is where oracles come in as a solution.

Oracles, like Chainlink, are to Web3 applications what APIs are to regular web applications, and they are the bridge between the blockchain and real-world data. So before diving into the specifics of Chainlink, it's essential to understand what an oracle is and how it works.

Chainlink ecosystem overview via coin98

Chainlink ecosystem overview via coin98

Another common problem in off-chain data is the centralization of data, especially when it is requested and used in decentralized environments because it introduces a single point of failure. 

This defeats the entire purpose of a decentralized network.

Centralized oracles, which provide data to smart contracts, can go offline or become corrupted, leading to inaccuracies in data and potentially causing smart contracts to execute incorrectly. 

This is commonly referred to as the “garbage in, garbage out” problem, where bad inputs lead to bad outputs. 

Additionally, because blockchain transactions are automated and immutable, a smart contract outcome based on faulty data cannot be reversed, meaning user funds can be permanently lost.

Chainlink addresses these issues by providing a decentralized oracle network that allows smart contracts to access data from multiple sources, ensuring that the data is accurate and reliable.

Centralized oracles are a single point of failure

Centralized oracles are a single point of failure via Chainlink

By removing the reliance on a single data provider, Chainlink eliminates the potential for a single point of failure and ensures that smart contracts are not affected by inaccuracies in data. 

Chainlink's reputation system and token incentives further ensure that data provided by the network is of high quality.

For developers, that means data received in their contracts is unbiased and accurate.

For users, it means that data is more reliable and the application is safer and less prone to data errors.

How to Use Chainlink in Your Smart Contracts

If you, as a smart-contract developer, want to use one of the nodes to input data to their functions, you should:

  1. Deposit LINK tokens that will be used to pay the node owners. This deposit is made by creating a Chainlink subscription.
  2. Then, from your contract, you should call the appropriate Chainlink contract, specify the data you need and set the number of tokens you are willing to pay for it. Available nodes will bet against each other to match the LINK token reward and the data the smart contract needs at the best price. Once the node provides the data, the node operator will receive the agreed payment.

For example, if you want to retrieve data from the weather in a specific place, first you should create a subscription and deposit your LINK tokens. Then, you should check the contract that can deliver you this data from this page https://chain.link/data-feeds.

Once you have the contract address, you are ready to write your smart contract. At some point in your functions, you should call the contract, wait for their response, and process the data received.

This sounds easy (and it is!), but it is always better to see a real-life example.

pragma solidity ^0.8.13;


import "chainlink/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "chainlink/v0.8/VRFConsumerBaseV2.sol";
import "chainlink/v0.8/ConfirmedOwner.sol";

After this, let’s define the contract and the variables needed:

contract Random is IRandom, VRFConsumerBaseV2, ConfirmedOwner {


 event RandomRequestSent(uint256 requestId, uint32 numWords);
 event RandomFullfilled(uint256 requestId, uint256[] randomWords);


 // Structure to store past requests, needed to check if the requests is funded and requested
 struct RequestStatus {
 bool fulfilled;
 bool exists;
 uint256[] randomWords;
 }


// A collection of pasts requests, will be fulfilled and checked later
 mapping(uint256 => RequestStatus) public s_requests;


 VRFCoordinatorV2Interface COORDINATOR;
 uint64 subscriptionId;


 // Goerli VRF v2 coordinator address as specified in https://docs.chain.link/vrf/v2/subscription/supported-networks
 address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;


 // past requests Id.
 uint256[] public requestIds;
 uint256 public lastRequestId;


 // As specified in https://docs.chain.link/vrf/v2/subscription/supported-networks
 bytes32 keyHash =
 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;


 //Goerli has a max gas limit of 2.5 million, but 200000 is enough
 uint32 callbackGasLimit = 200_000;


 // The default is 3, but you can set this higher.
 uint16 requestConfirmations = 5;


 // For this example, retrieve 2 random values in one request.
 // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
 uint32 numWords = 2;


 // Finally, the variable where we will store the random number each time
 uint256 private number;


// THE REST OF THE CONTRACT
}

Once we have the variables, let’s set up the constructor for the contract. When deploying the contract on the chain, you need to provide the subscription ID we have got at the beginning of the tutorial. In the constructor, we store the subscription ID and we initialize the number to 0.

 constructor(uint64 _subscriptionId)
 VRFConsumerBaseV2(vrfCoordinator)
 ConfirmedOwner(msg.sender)
 {
 COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
 subscriptionId = _subscriptionId;
 number = 0;
 }

Now, we are going to check the two functions that will request the random number from Chainlink. The first one getNumber will be the only public function and the one we have to call to retrieve a new random number. The second one, requestRandomWords, will create the request and emit the event to the chainlink contract.

 function getNumber() public view returns (uint256) {
 return number;
 }


 function requestRandomWords() internal returns (uint256 requestId) {
 // We assume the subscription is created and funded, otherwise, this check will revert the transaction and you will have lost the gas fee.
 requestId = COORDINATOR.requestRandomWords(
 keyHash, subscriptionId, requestConfirmations, callbackGasLimit, numWords
 );


 // We store the request in the hash we created earlier
 s_requests[requestId] = RequestStatus({
 randomWords: new uint256[](0),
 exists: true,
 fulfilled: false
 });
 requestIds.push(requestId);
 lastRequestId = requestId;


 // We emit the `requestSent` event, we will capture the response in another function later on.
 emit RequestSent(requestId, numWords);
 return requestId;
 }

Now, the only thing left is to create the fulfillRandomWords function. This function comes from the VRFConsumerBaseV2 interface and has to be implemented for our contract to work. This function will be called once chainlink has the random words prepared for us and it will receive them through the arguments. The function looks like this:

 function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords)
 internal
 override
 {
 // First, we check the request exists in our hash and update it
 require(s_requests[_requestId].exists, "request not found");
 s_requests[_requestId].fulfilled = true;
 s_requests[_requestId].randomWords = _randomWords;


 // After that we retrieve the random number and store it in our variable. In this case, we want an integer between 1 and 100
 number = (_randomWords[0] % 100) + 1;


 // Finally, we emit the event to let the contract know we already have the random number stored
 emit RandomFullfilled(_requestId, _randomWords);
 }

And that’s it, folks! After putting all the pieces together and deploying the contract to your favorite network (adjusting the values for that network and your subscription ID) you can just call the getNumber function. After a few seconds, you will have a new random number in the number variable of the contract (or you can listen to the RandomFullfilled event if you want to know when the contract receives the number).

Let's Recap

Chainlink has proven to be a reliable and trustworthy solution for connecting smart contracts to real-world data and events. Its decentralized oracle network provides a high level of security and reliability, while its extensive list of partnerships demonstrates its versatility and widespread adoption.

As we’ve seen, Chainlink's features, such as its support for multiple blockchains, diverse data source options, and customizable oracle networks, make it a valuable tool for a wide range of use cases, from DeFi to gaming to supply chain management.

But this is just the tip of the iceberg when it comes to Chainlink's capabilities. In future articles, we'll explore other features of Chainlink, such as the Keepers networks or the ability to connect to any regular API off-chain, which can further expand the functionality of smart contracts.


This article shares some of the recent work and thinking we’re doing as part of our Blockchain initiative for Whitespectre Labs. Whitespectre Labs operates as both an internal product incubator and a core building block to ensure we’re using the latest tools, technology, and best practices to deliver great solutions for our client partners. Want to co-create with us? Get in touch here.

Let’s Chat