Skip to content

Chainlink Oracle

Introduction

Developers can now use Chainlink's decentralized Oracle network to fetch data from a Moonbeam-based network. There are two main architectures: Price Feeds and Basic Request Model. 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. The Basic Request Model describes an on-chain architecture for requesting data from a single oracle source. This guide will show you how to fetch the latest price data using both architectures.

The information presented herein is for informational purposes only and has been provided by third parties. Moonbeam does not endorse any project listed and described on the Moonbeam docs website (https://docs.moonbeam.network/).

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 and updated in the smart contracts every 12 hours. As such, the price feeds on Moonbase Alpha are not authoritative and are for testing purposes only. 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:

// SPDX-License-Identifier: MIT
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: decimals, description, version, getRoundData, and latestRoundData.

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

Base/Quote Data Feed Contract (Proxy Address)
ATOM to USD 0x4F152D143c97B5e8d2293bc5B2380600f274a5dd
BNB to USD 0x0147f2Ad7F1e2Bc51F998CC128a8355d5AE8C32D
BTC to USD 0x8c4425e141979c66423A83bE2ee59135864487Eb
DOT to USD 0x1466b4bD0C4B6B8e1164991909961e0EE6a66d8c
ETH to USD 0x9ce2388a1696e22F870341C3FC1E89710C7569B5
FRAX to USD 0x05Ec3Fb5B7CB3bE9D7150FBA1Fb0749407e5Aa8a
GLMR to USD 0x4497B606be93e773bbA5eaCFCb2ac5E2214220Eb
LINK to USD 0xd61D7398B7734aBe7C4B143fE57dC666D2fe83aD
USDC to USD 0xA122591F60115D63421f66F752EF9f6e0bc73abC
Base/Quote Data Feed Contract (Proxy Address)
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
BUSD to USD 0x596129F6ABCaB2E6E81D19284B78eA73C176D170
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 (Proxy Address)
AAVE to USD 0x9DA76AE39AECA424801DF1b63e9E2750643F9e76
ALGO to USD 0x49E1021974902B4a279fC7D359b4890FC38F8261
AVAX to USD 0xA356990bCDed8Cc6865Be606D64E7381bfe00B72
BAND to USD 0x8997c0A8Dc9d13061FCf303711D1db4278e53a1B
BNB to USD 0x4ADfBA6138A5bdA31f980EF5D59Fc7f8440A5D92
BTC to USD 0xa39d8684B8cd74821db73deEB4836Ea46E145300
COMP to USD 0xaa18FFaF5E2f410250D318b57EAd2C68603A382c
CRV to USD 0x1a435D01CB50E6B1F8e429a32EE03BF164B43ece
CVX to USD 0x3A1Ac4DA79a3c813b2B335d1a631adEec360A59c
DAI to USD 0x5A5737F6C0683be63863131bfCAd793a9F828DDa
DOT to USD 0xA8B2138c8E765288D4f6fb7D3ebCe2507A006a9C
ETH to USD 0x0BAA6E884cfD628b33867F9E081B44a76276fA2D
FRAX to USD 0xB616464cB6135AF542de908FAa3FbE3a0b3048e4
FTM to USD 0xD23f056ceD090Bc9Cf6c75d2FB43c0ccC35AD542
GLMR to USD 0x537879A0beA294c1ce04161Ae827919e92C23e92
KSM to USD 0x4E9A1ebc0bEebe3516a8A9a911D781c37414bb39
LINK to USD 0x5310f2d4B531BCEA8126e2aEE40BAd71B707f530
MKR to USD 0x2257c46D4ddF160bACBD00cd67cD6Fd87C48c581
OP to USD 0xC5837B50a86fD87e1D2FbE4FEbFe7C042a9CFBE8
stETH to USD 0x49CEee3f8e9e05f26A81f1122A78D058Ef3c05a9
SUSHI to USD 0xd265FAfc809108D9cf47b6970d750419ba9355B8
UNI to USD 0x326fAFb9E9B5188d7c2e95C0D782ECB19E5EfDF9
USDC to USD 0xCb1c08B86C6EaC617b1bEd5D1E9cD7Fd308FdA4d
USDT to USD 0x141Ec95dB9298cB4Bb20CbAC422A42A047a764b1
YFI to USD 0x8E812AA9f56BBAa5B936c8F08B0a260f3eF7cF46

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
    
    0xa39d8684B8cd74821db73deEB4836Ea46E145300
    

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.

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:

// SPDX-License-Identifier: MIT
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:

// SPDX-License-Identifier: MIT
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 7ddc5b9e9eef410184666620dbc740a4
ALGO to USD 9ac119b772ed4d2580965fa84ef26f56
AVAX to USD 007ba4a3bf424ea79d2fad13fd763808
BAND to USD 6405edbca42e4b92bd4de2601310de79
BNB to USD f4d1d07ce01d4039a410f0592ab9df4c
BTC to USD 02d8ae1716924ea7af12dba7d08aa9a2
COMP to USD 35c893eca2eb4a35b6ee8b786920df2d
CRV to USD f8b5326162214b22874d3df2394f4da2
CVX to USD a0f8e66321a64383b4ab92b98c44a500
DAI to USD e191bd95b8134508bb258fc8c4375956
DOT to USD 58a5aba286a143b68f9fcf9c1f022fd5
ETH to USD e539daf468bb465c9c795deb5ff7fe22
FRAX to USD 2a6d5b0b0e4b4d38b63dc4ea7a4aa2b9
FTM to USD 6bf63cdfd9d54512b53671375d489079
KSM to USD db34748e6cad4eab895eae8cbfacc80b
LINK to USD caea548c900d49959c91d595ef3854b6
MKR to USD 2b32e18e39404182a8357c1caa9c6f74
OP to USD 99c4ff9ec4194208a06c082353cef852
stETH to USD 7d8b047a8e5e4a88adf4d24896a833e5
SUSHI to USD 36e18c5b30b24b988c007dc9343cad5e
UNI to USD 49f014fd0475434cbe32e8f14d0d5d6d
USDC to USD 76dcaca128a24ec4aa46a3f17d084d02
USDT to USD 7407e378b901429d884eebee1c1b7ee6
YFI to USD f0f56b5c976649cb9d681f18efce34d1

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/blob/develop/contracts/src/v0.6/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.

The information presented herein has been provided by third parties and is made available solely for general information purposes. Moonbeam does not endorse any project listed and described on the Moonbeam Doc Website (https://docs.moonbeam.network/). 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.
Last update: February 16, 2024
| Created: November 26, 2020