Skip to content

Chainlink Oracle

Chainlink Moonbeam Banner

Introduction

Developers can now use Chainlink's decentralized Oracle network to fetch data from a Moonbeam-based network. Price Feeds contain real-time price data that is continuously updated by Oracle operators in a smart contract so that other smart contracts can fetch and consume it. This guide will cover the available price feeds and how to fetch the latest price data.

Basic Request Model

Before going into fetching the data itself, it is important to understand the basics of the "basic request model." The generic flow for requesting and receiving data from a Chainlink oracle is as follows:

  1. A client contract creates and makes a request for data to a Chainlink oracle
  2. The request is sent through the transferAndCall function of the LINK token contract, which is an ERC-677 compliant token, that allows tokens to be transferred and relays the request to the oracle contract
  3. Once the token is transferred the LINK contract calls the oracle contract's onTokenTransfer function
  4. The oracle contract is owned by the oracle node operators and is responsible for handling on-chain requests made through the LINK token. Once the request is received by the oracle contract an event is emitted to the node which acts upon it
  5. After the request has been fulfilled by the oracle node, the node uses the fulfillOracleRequest function of the oracle contract to return the result to the client via the callback function defined in the original request

Basic Request Diagram

When a request for data is created through the client contract, the following parameters need to be passed in to ensure the transaction will go through and the correct information will be returned:

  • Oracle address - address of the contract deployed by the oracle node
  • Job ID - the task to be executed. An oracle node has a set of job IDs, where each job corresponds to a task that can be requested by a user, for example, fetching a price feed
  • Payment - payment in LINK tokens that the oracle will receive for fulfiling the request

Fetching Data

There are a few ways you can get started fetching data from an oracle on Moonbeam:

  • You can use the pre-deployed client contract, LINK token contract, and oracle contract that rely on the oracle node being run by Moonbeam
  • You can create your own custom client contract instead of the pre-deployed client contract to be used with the Moonbeam oracle node
  • You can create your own custom contracts and run your own oracle node

The pre-deployed contracts and oracle node run by Moonbeam support a limited set of job IDs that can be used to fetch price data for various asset pairs. If you need additional data, please refer to the Create Custom Contracts using your own Oracle Node section below to learn how to get started.

It's also important to note that the client contract must have a LINK tokens balance to be able to pay for requests. For the pre-deployed setup, the LINK value has been set to zero. If you deploy your own setup, you can also set the LINK value to zero in your ChainlinkClient.sol contract, and you can choose to deploy your own LINK token contract or use the pre-deployed one.

Use Pre-deployed Contracts

If you want to skip the hurdles of deploying all contracts, setting up your oracle node, creating job IDs, and so on, you can use a custom client contract that has already been deployed to Moonbase Alpha. The contract makes all requests to an oracle contract that has also already been deployed, with a zero LINK token payment. These requests are fulfilled by an oracle node that is run by the Moonbeam team.

The client contract deployed on Moonbase Alpha is as follows:

pragma solidity ^0.6.6;

import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.6/ChainlinkClient.sol";

/**
 * @title Client based in ChainlinkClient
 * @notice End users can deploy this contract to request the Prices from an Oracle
 */
contract Client is ChainlinkClient {
  // Stores the answer from the Chainlink oracle
  uint256 public currentPrice;
  address public owner;

  // Deploy with the address of the LINK token
  constructor(address _link) public {
    // Set the address for the LINK token for the network
    setChainlinkToken(_link);
    owner = msg.sender;
  }

  // Creates Chainlink Request
  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);
  }

  // Callback function called by the Oracle when it has resolved the request
  function fulfill(bytes32 _requestId, uint256 _price)
    public
    recordChainlinkFulfillment(_requestId)
  {
    currentPrice = _price;
  }

  // Allows the owner to cancel an unfulfilled request
  function cancelRequest(
    bytes32 _requestId,
    uint256 _payment,
    bytes4 _callbackFunctionId,
    uint256 _expiration
  )
    public
  {
    cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration);
  }

  // Allows the owner to withdraw the LINK tokens in the contract to the address calling this function
  function withdrawLink()
    public
    onlyOwner
  {
    LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
    require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer");
  }

  // Decodes an input string in a bytes32 word
  function stringToBytes32(string memory _source)
    private
    pure
    returns (bytes32 result) 
  {
    bytes memory emptyStringTest = bytes(_source);
    if (emptyStringTest.length == 0) {
      return 0x0;
    }

    assembly { // solhint-disable-line no-inline-assembly
      result := mload(add(_source, 32))
    }

    return result;
  }

  // Reverts if the sender is not the owner of the contract
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
}

The core functions of the contract are as follows:

  • 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

Note that the client contract must have a LINK tokens balance to be able to pay for this request. However, in this instance, the LINK value has been set to zero.

The client contract is deployed at 0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11. You can try interacting with the client contract by using following interface contract:

pragma solidity ^0.6.6;

/**
 * @title Simple Interface to interact with Universal Client Contract
 * @notice Client Address 0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11
 */
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 data for the following pairs:

Base/Quote Job ID Reference
AAVE to USD 541b8f7db7374d78b38285ef1b8bfacc
ALGO to USD cdb48696e2314133a1dc8ea27922dd24
BAND to USD 6b0983e0cb6d4aca908b615302a9d672
BTC to USD 82ceee2897824a0e8b014ed4ed2ab31e
DOT to USD 6f6371a780324b90aaf195a0d39c723c
ETH to USD 60160cdd0e10489681967e9d7ef4c927
KSM to USD 30a1686f657249f4b6ab01e384b2beaa
LINK to USD aad8dbdb0c1840ab905728d85117b681
SUSHI to USD b4b07d0fc218455caaff2223a05ec208
UNI to USD 22b567acabdb419abe8136a2bab6ade8

For this example, you can go ahead and use the interface contract with the BTC to USD job ID in Remix. After creating the file and compiling the contract, you can take the following steps:

  1. Head to the Deploy and Run Transactions tab
  2. Make sure you have set the Environment to Injected Web3, and you have your MetaMask connected to Moonbase Alpha
  3. Enter the client contract address, 0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11, and click on At Address. This will create an instance of the client contract that you can interact with
  4. Under the Deployed Contracts section, use the requestPrice() function to query the data of the corresponding job ID
  5. Confirm the transaction. You will have to wait until the whole request process that was previously explained occurs
  6. You can then check the price using the view function, currentPrice()

Chainlink Basic Request on Moonbase Alpha

If there is any specific pair you want to be included, feel free to reach out to the Moonbeam team through Discord.

Create a Custom Client Contract

If you want to run your own custom client contract but use the oracle node being run by Moonbeam, you can do so with the following information:

Contract Type Address
Oracle Contract 0x1d693d883BeAeE16edD0D7588D6a9f7E1967E798
LINK Token 0xa36085F69e2889c224210F603D836748e7dC0088

If you decide to go this route, please keep in mind that the oracle node only supports the job IDs listed in the previous section. You'll only be able to access the pricing data for the supported pairs. If you need more functionality or want to use another API, please check out the Create Custom Contracts using your own Oracle Node section.

To build your own client contract using the ChainlinkClient, you'll need to start by importing the contract:

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.8/ChainlinkClient.sol";

You can checkout out the Chainlink documentation on ChainlinkClient API Reference for more information.

Keep in mind that the LINK token payment is set to zero.

Create Custom Contracts using your own Oracle Node

To get started with your own setup, including your own client contract, oracle contract, and oracle node, you'll need to start off running an oracle node. You can follow the Run a Chainlink Oracle Node on Moonbeam guide to spin up your own oracle node. You'll also learn how to setup your oracle contract and create jobs.

If you created a job to be used with any API, you can then create a client contract that sets the API endpoint URL to perform the GET request on.

Note that the client contract must have a LINK tokens balance to be able to pay for requests. Therefore, you will need to set the LINK value to zero in your ChainlinkClient.sol contract. You'll also need to make sure that your oracle node has a MINIMUM_CONTRACT_PAYMENT of 0. You can verify that it has been set to zero by checking out the Configuration section of your node.

The following client contract is an example of how to use any API from within your client contract:

pragma solidity ^0.8.7;

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

contract Client is ChainlinkClient {
    using Chainlink for Chainlink.Request;

    address private oracle;
    bytes32 private jobId;
    uint256 private fee;
    uint256 public volume;

    /**
    This example uses the LINK token address on Moonbase Alpha.
    Make sure to update the oracle and jobId.
    */
    constructor() {
        setChainlinkToken(address(0xa36085F69e2889c224210F603D836748e7dC0088));
        oracle = {INSERT-YOUR-ORACLE-NODE-ADDRESS};
        jobId = "{INSERT-YOUR-JOB-ID}";
        fee = 0;
    }

    /**
     * Create a Chainlink request to retrieve API response, find the target
     * data, then multiply by 1000000000000000000 (to remove decimal places from data).
     */
    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");

        // Set the path to find the desired data in the API response, where the response format is:
        // {"RAW":
        //   {"ETH":
        //    {"USD":
        //     {
        //      "VOLUME24HOUR": xxx.xxx,
        //     }
        //    }
        //   }
        //  }
        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);
    }

    /**
     * Receive the response in the form of uint256
     */ 
    function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)
    {
        volume = _volume;
    }
}

Note

The above example uses the pre-deployed LINK token contract address. You also have the option of deploying your own LINK token contract and using that instead.

Once you've deployed the contract on Remix, you can begin to request the volume data. After you make a request, you can check the status of the job by going to the Jobs section of your node.

Price Feeds

Before going 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. The aggregator contract receives periodic data updates from the network of oracles and aggregates and stores the data on-chain so that consumers can easily fetch it. 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 an aggregator interface, or via a Consumer interface through the Proxy.

Price Feed Diagram

Fetch Price Data

There are data feed contracts available for Moonbeam-based networks to help simplify the process of requesting price feeds. In the current configuration for Moonbase Alpha, the Moonbeam team is 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 Moonbeam and Moonriver data feed contracts are updated by multiple Chainlink nodes on a regular basis.

The data lives in a series of smart contracts (one per price feed) and can be fetched with the aggregator interface:

pragma solidity ^0.8.0;

interface AggregatorV3Interface {
    /**
     * 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);

    /**
     * Returns the version number representing the type of aggregator the proxy points to
     */
    function version() external view returns (uint256);

    /**
     * Returns price data about a specific round
     */
    function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    /**
     * Returns price data from the latest round
     */
    function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

As seen above in the interface, there are five functions for fetching data: decimal, description, version, getRoundData, and latestRoundData.

Currently, there are data feed contracts for Moonbeam, Moonriver, and Moonbase Alpha for the the following price pairs:

Base/Quote Data Feed Contract
ATOM to USD 0x4F152D143c97B5e8d2293bc5B2380600f274a5dd
BNB to USD 0x0147f2Ad7F1e2Bc51F998CC128a8355d5AE8C32D
BTC to USD 0x8c4425e141979c66423A83bE2ee59135864487Eb
ETH to USD 0x9ce2388a1696e22F870341C3FC1E89710C7569B5
GLMR to USD 0x4497B606be93e773bbA5eaCFCb2ac5E2214220Eb
LINK to USD 0xd61D7398B7734aBe7C4B143fE57dC666D2fe83aD
USDC to USD 0xA122591F60115D63421f66F752EF9f6e0bc73abC
Base/Quote Data Feed Contract
1INCH to USD 0x1466b4bD0C4B6B8e1164991909961e0EE6a66d8c
AAVE to USD 0x37f35ef6735c594e6E803bC81577bAC759d8179C
ANKR to USD 0x94Ee35E8b9B1b4Cd3BDB720242d6d1796b43C2Ff
AVAX to USD 0x992F9B8Aa09B8e084acf4e3213d8b2da5D366D6a
AXS to USD 0x9322CeAd48BA0C76Fecc78e82499ce8a829Eab89
BNB to USD 0xD6B013A65C22C372F995864CcdAE202D0194f9bf
BTC to USD 0x1B5C6cF9Df1CBF30387C24CC7DB1787CCf65C797
CAKE to USD 0xc44ecD8C11fd1F281A3d6044CA65e649484B228c
COMP to USD 0x29710821d57a1Fc46E2D9FdDE65Df2cF205bad2A
CRV to USD 0x03d44d68EdF41c540A90C6eB2BE27C4a75ee689f
DAI to USD 0x7ba0e3EbCe25DD3b5A0f36dd7aB34019B863b08D
DOT to USD 0x54B584eb643375C41c55ddD8Da4b90124b18d05c
ETH to USD 0xc3cF399566220dc5Ed6C8CFbf8247214Af103C72
EUR to USD 0xe6Ccbe1Cb33dF799a59E37a1382c7009dbaBE9ff
FRAX to USD 0xD080d4760318710e795B0a59f181f6C1512ffB15
FTM to USD 0x5e70fC5f38cB930F9BE8BEAEaF80CF927Af3B17E
FXS to USD 0xE5B624e1098C25C94279bA20A0CC68Fa9215e63b
KSM to USD 0x6e0513145FCE707Cd743528DB7C1cAB537DE9d1B
LINK to USD 0xdD27789b504fEd690F406A82F16B45a0901172C0
LUNA to USD 0x5F8E0c452EcA522a2208Fff7443515AaFF3cAaE6
MANA to USD 0x424807fA7B16f747CbD30963fAe25fB8Db0b97bF
MIM to USD 0xdD6296BD7515271F7E4b10C3A87A2f9863fECa97
MKR to USD 0xD8542f327FaD60b80D8C19025147E6b9d857bb99
MOVR to USD 0x3f8BFbDc1e79777511c00Ad8591cef888C2113C1
SAND to USD 0x5403385DF6eb607fc1fA6983eF5801A11eC7fD9a
SNX to USD 0x26E3F9273abC8a01228bE97a106E60FA38b98df2
SUSHI to USD 0x28A9E2747a10eE94D2d7359DEB60023D19FfdD96
THETA to USD 0xA0784167e040906b5580e3c4a53932B288f615ce
UNI to USD 0x05Ec3Fb5B7CB3bE9D7150FBA1Fb0749407e5Aa8a
USDC to USD 0x12870664a77Dd55bBdcDe32f91EB3244F511eF2e
USDT to USD 0xF80DAd54AF79257D41c30014160349896ca5370a
XRP to USD 0x3FD363679fb59596d45881bbfBe4bb864f3545A2
YFI to USD 0xE3324ea60FA272BBB4511dDBD4776feFE4674fa0
Base/Quote Data Feed Contract
AAVE to USD 0x64B22D2B8c3CA311a0C2de34bf799f8101c89362
ALGO to USD 0x9fc3b0BF1156868085AFC1cFf4Bf6D85ea301371
BAND to USD 0xC5aeD933FEb49794A8Bf2FB0e73D9c958c8a07ba
BTC to USD 0xCf88A8d7fc1A687895fC8ffAad567f303926B094
DOT to USD 0xA873F6b30aD79fCAF9b03A0A883d6D1f18D661d7
ETH to USD 0x3669da30c33D27A6A579548fCfc345fE5dEdda6e
KSM to USD 0x0C515E77897b2A7181C875c88FaF9BC8E5661E3b
LINK to USD 0x446b93236B4d34642732B8dcbeB3cb4f4FA03C70
SUSHI to USD 0x4a6Cf10C0f5c4D4e7cf7385bFfecDAec0778357C
UNI to USD 0x326997c21451DaB916F9f01684991B6169dAf3E5

For example, you can use the aggregator interface to fetch the price feed of BTC to USD using Remix. If you need help loading a contract into Remix, check out the Using Remix page of the documentation site.

You will need to connect your MetaMask account to Remix, so make sure you have MetaMask installed and are connected to the correct network. To get help setting up MetaMask, check out the Interacting with Moonbeam Using MetaMask guide.

After creating the file and compiling the contract, you will need to follow these steps:

  1. Head to the Deploy and Run Transactions tab
  2. Set the Environment to Injected Web3
  3. If your MetaMask is already connected it will appear in the Account selector. Otherwise, you will be prompted by MetaMask to select and connect your account(s)
  4. Select the AggregatorV3Interface contract from the Contract dropdown
  5. Enter the Data Feed contract address corresponding to BTC to USD in the At Address field and click the At Address button:

    0x8c4425e141979c66423A83bE2ee59135864487Eb
    
    0x1B5C6cF9Df1CBF30387C24CC7DB1787CCf65C797
    
    0xCf88A8d7fc1A687895fC8ffAad567f303926B094
    

Load the Chainlink Price Feed Aggregator Interface on Moonriver

This will create an instance of the aggregator interface that you can interact with and it will appear under the Deployed Contracts section in Remix. To get the latest price data you can follow these steps:

  1. Expand the AggregatorV3Interface contract to reveal the available functions
  2. Click latestRoundData() to query the data of the corresponding price feed, in this case BTC to USD

Interact with the Chainlink Price Feed Aggregator Interface on Moonriver

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 to be included, feel free to reach out through Discord.

The information presented herein has been provided by third parties and is made available solely for general information purposes. Moonbeam Foundation does not warrant the accuracy, completeness or usefulness of this information. Any reliance you place on such information is strictly at your own risk. Moonbeam Foundation disclaims all liability and responsibility arising from any reliance placed on this information by you or by anyone who may be informed of any of its contents. All statements and/or opinions expressed in these materials are solely the responsibility of the person or entity providing those materials and do not necessarily represent the opinion of Moonbeam Foundation. The information should not be construed as professional or financial advice of any kind. Advice from a suitably qualified professional should always be sought in relation to any particular matter or circumstance. The information herein may link to or integrate with other websites operated or content provided by third parties, and such other websites may link to this website. Moonbeam Foundation has no control over any such other websites or their content and will have no liability arising out of or related to such websites or their content. The existence of any such link does not constitute an endorsement of such websites, the content of the websites, or the operators of the websites. These links are being provided to you only as a convenience and you release and hold Moonbeam Foundation harmless from any and all liability arising from your use of this information or the information provided by any third-party website or service.