Chainlink Oracle¶
Introduction¶
Developers can now use Chainlink's decentralized Oracle network to fetch data in the Moonbase Alpha TestNet. This tutorial goes through two different ways of using Chainlink Oracles:
- Basic Request Model, where the end-user sends a request to an Oracle provider, which fetches the data through an API, and fulfils the request storing this data on-chain
- Price Feeds, where data is continuously updated by Oracle operators in a smart contract so that other smart contracts can fetch it
Basic Request Model¶
Before we go into fetching the data itself, it is important to understand the basics of the "basic request model."
An Oracle node has a set of job IDs, where each corresponds to a task that can be requested by a user, for example, fetch a price feed. To do so, the user needs to send a request through a contract, we'll name it the Client contract, passing in the following information:
- Oracle address: address of the contract deployed by the Oracle node
- Job ID: task to be executed
- Payment: payment in LINK tokens that the Oracle will receive for fulfiling the request
This request actually sends a transferAndCall to the LINK token contract, which handles the payment and relays the request to the Oracle contract. Here, an event is emited with the request, which is picked up by the Oracle node. Next, the node fetches the necessary data and executes the fulfilOracleRequest function, which executes a callback that stores the requested information in the Client contract. The following diagram explains this workflow.
The Client Contract¶
The Client contract is the element that starts the communication with the Oracle by sending a request. As shown in the diagram, it calls the transferAndCall method from the LINK token contract, but there is more processing that is needed to track the request to the Oracle. For this example, you can use the code in this file, and deploy it on Remix to try it out. Let's look at the core functions of the contract:
- constructor: runs when the contract is deployed. It sets the address of the LINK token and the owner of the contract
- requestPrice: needs the Oracle contract address, the job ID, and the payment (in LINK) tokens to the fulfiller of the request. Builds the new request that is sent using the sendChainlinkRequestTo function from the ChainlinkClient.sol import
- fulfill: callback used by the Oracle node to fulfill the request by storing the queried information in the contract
pragma solidity ^0.6.6;
import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol";
contract Client is ChainlinkClient {
//... there is mode code here
constructor(address _link) public {
// Set the address for the LINK token for the network
setChainlinkToken(_link);
owner = msg.sender;
}
function requestPrice(address _oracle, string memory _jobId, uint256 _payment)
public
onlyOwner
{
// newRequest takes a JobID, a callback address, and callback function as input
Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), address(this), this.fulfill.selector);
// Sends the request with the amount of payment specified to the oracle
sendChainlinkRequestTo(_oracle, req, _payment);
}
function fulfill(bytes32 _requestId, uint256 _price)
public
recordChainlinkFulfillment(_requestId)
{
currentPrice = _price;
}
//... there is more code here
}
Note that the Client contract must have a LINK tokens balance to be able to pay for this request. However, if you deploy your setup, you can set the LINK value to 0 in your ChainlinkClient.sol
contract, but you still need to have the LINK token contract deployed.
Try it on Moonbase Alpha¶
If you want to skip the hurdles of deploying all contracts, setting up your Oracle node, creating job IDs, and so on, we've got you covered.
A custom Client contract on Moonbase Alpha that makes all requests to our Oracle contract, with a 0 LINK token payment, is available. These requests are fulfilled by an Oracle node that we are running. You can try it out with the following interface contract and the custom Client contract deployed at 0x6355D6E4f58fB96e859A89423B9d6A8F98aF1f4c
:
pragma solidity ^0.6.6;
/**
* @title Simple Interface to interact with Universal Client Contract
* @notice Client Address 0x6355D6E4f58fB96e859A89423B9d6A8F98aF1f4c
*/
interface ChainlinkInterface {
/**
* @notice Creates a Chainlink request with the job specification ID,
* @notice and sends it to the Oracle.
* @notice _oracle The address of the Oracle contract fixed top
* @notice _payment For this example the PAYMENT is set to zero
* @param _jobId The job spec ID that we want to call in string format
*/
function requestPrice(string calldata _jobId) external;
function currentPrice() external view returns (uint);
}
This provides two functions. requestPrice()
only needs the job ID of the data you want to query. This function starts the chain of events explained before. currentPrice()
is a view function that returns the latest price stored in the contract.
Currently, the Oracle node has a set of Job IDs for different price datas for the following pairs:
Base/Quote | Job ID Reference | |
---|---|---|
BTC to USD | 0a1d7df6a47c417bb4d0ab561a37753e | |
ETH to USD | ddd85cc4bab24920bc7c605b5bed3bf0 | |
DOT to USD | 93790efc1e7641a99465ea8b1c71ce7a | |
KSM to USD | d37d9a18f92f49198ceece6d367ed77a | |
AAVE to USD | 684981fa8e264cc1afa3e1df7fe8eecc | |
ALGO to USD | f7a442a6365048799a30c02ac56f4440 | |
BAND to USD | 6daa1fe7ba7d421a8a76fa6c2adb1382 | |
LINK to USD | c1d6b9a159c64d3888b0e71104bdbb8d | |
SUSHI to USD | 861c8bd8d92f42d3b21606111173cd4a | |
UNI to USD | 14ff274fc6ac469bb3a27b34c21c0957 |
Let's go ahead and use the interface contract with the BTC to USD
Job ID in Remix.
After creating the file and compiling the contract, head to the "Deploy and Run Transactions" tab, enter the Client contract address, and click on "At Address." Make sure you have set the "Environment" to "Injected Web3" so you are connected to Moonbase Alpha. This will create an instance of the Client contract that you can interact with. Use the function requestPrice()
to query the data of the corresponding Job ID. Once the transaction is confirmed, we have to wait until the whole process explained before occurs. We can check the price using the view function currentPrice()
.
If there is any specific pair you want us to include, feel free to reach out to us through our Discord server.
Run your Client Contract¶
If you want to run your Client contract but use our Oracle node, you can do so with the following information:
Contract Type | Address | |
---|---|---|
Oracle Contract | 0xA356990bCDed8Cc6865Be606D64E7381bfe00B72 | |
LINK Token | 0xa36085F69e2889c224210F603D836748e7dC0088 |
Remember that the LINK token payment is set to zero.
Other Requests¶
Chainlink's Oracles can tentatively provide many different types of data feeds with the use of external adapters. However, for simplicity, our Oracle node is configured to deliver only price feeds.
If you are interested in running your own Oracle node in Moonbeam, please visit this guide. Also, we recommend going through Chainlink's documentation site.
Price Feeds¶
Before we go into fetching the data itself, it is important to understand the basics of price feeds.
In a standard configuration, each price feed is updated by a decentralized Oracle network. Each Oracle node is rewarded for publishing the price data to the Aggregator contract. However, the information is only updated if a minimum number of responses from Oracle nodes are received (during an aggregation round).
The end-user can retrieve price feeds with read-only operations via a Consumer contract, referencing the correct Aggregator interface (Proxy contract). The Proxy acts as a middleware to provide the Consumer with the most up-to-date Aggregator for a particular price feed.
Try it on Moonbase Alpha¶
If you want to skip the hurdles of deploying all the contracts, setting up your Oracle node, creating job IDs, and so on, we've got you covered.
We've deployed all the necessary contracts on Moonbase Alpha to simplify the process of requesting price feeds. In our current configuration, we are running only one Oracle node that fetches the price from a single API source. Price data is checked every minute and updated in the smart contracts every hour unless there is a price deviation of 1 %.
The data lives in a series of smart contracts (one per price feed) and can be fetched with the following interface:
pragma solidity ^0.6.6;
interface ConsumerV3Interface {
/**
* Returns the latest price
*/
function getLatestPrice() external view returns (int);
/**
* Returns the decimals to offset on the getLatestPrice call
*/
function decimals() external view returns (uint8);
/**
* Returns the description of the underlying price feed aggregator
*/
function description() external view returns (string memory);
}
This provides three functions. getLatestPrice()
will read the latest price data available in the Aggregator contract via the Proxy. We've added the decimals()
function, which returns the number of decimals of the data and the description()
function, which returns a brief description of the price feed available in the Aggregator contract being queried.
Currently, there is an Consumer contract for the the following price pairs:
Base/Quote | Consumer Contract | |
---|---|---|
BTC to USD | 0x2C28701f63a122CCed5E01570dB04101A078041e | |
ETH to USD | 0xfE6676f8A96005445848632a5A2D67721d584dAd | |
DOT to USD | 0xc4F4d983d988Ab4349E04C5bf5Da0d09Ae70D12F | |
KSM to USD | 0x16C64b68F84328627f615BB6E98C87b1d184326C | |
AAVE to USD | 0x8F23f3cc745993c3164Cb7a852497091A452aA6d | |
ALGO to USD | 0x3797E7Db901c8f19ed7bfF3f600D870E15d362b0 | |
BAND to USD | 0x08E04Ee892f77585540f71B13970a9C58D5FEA9a | |
LINK to USD | 0x4b98A257b73DE965115D8FcD8aCe76249354D994 | |
SUSHI to USD | 0x700465bF9014229875d61A74140344AF823923E7 | |
UNI to USD | 0xeBf79a4aefCFf0E55203fAdEE93417A9E040FEC1 |
Let's go ahead and use the interface contract to fetch the price feed of BTC to USD
using Remix.
After creating the file and compiling the contract, head to the "Deploy and Run Transactions" tab, enter the Consumer contract address corresponding to BTC to USD
, and click on "At Address." Make sure you have set the "Environment" to "Injected Web3" so you are connected to Moonbase Alpha.
This will create an instance of the Consumer contract that you can interact with. Use the function getLatestPrice()
to query the data of the corresponding price feed.
Note that to obtain the real price, you must account for the decimals of the price feed, available with the decimals()
method.
If there is any specific pair you want us to include, feel free to reach out to us through our Discord server.
We Want to Hear From You¶
If you have any feedback regarding implementing Chainlink on your project or any other Moonbeam-related topic, feel free to reach out through our official development Discord server.