# Oracles

ThunderCore oracle provides a series of asset prices on the chain to make your smart contract connect with real world.

For projects already integrated with [Chainlink](https://chain.link) data feed , we are providing the same function interfaces to make your project cross-chain onto ThunderCore instantly.

{% hint style="danger" %}
**Pending for Review**

This is pre-release version section. Contents can be changed anytime, please use in your caution.
{% endhint %}

## Get Started

You can get the latest contract interfaces and ABIs through [npm](https://docs.npmjs.com):

```bash
$ npm install @thundercore/oracle
```

or [Yarn](https://yarnpkg.com/getting-started/install):

```bash
$ yarn add @thundercore/oracle
```

And just a few lines of code, so you can get the latest price:

{% tabs %}
{% tab title="New project" %}

```solidity
import { FeedRegistryConsumerInterface } from '@thundercore/oracle/TTOracle.sol';

contract MyProject {
    address public constant TT_ORACLE = 0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE;

    function getThunderTokenPrice() public view returns (int256) {
        (
            /* uint80 roundId */,
            int256 answer,
            /* uint256 startedAt */,
            /* uint256 updatedAt */,
            /* uint80 answeredInRound */
        ) = FeedRegistryConsumerInterface(TT_ORACLE)
                .latestRoundData('TT/USDT');

        return answer;
    }
}
```

{% endtab %}

{% tab title="Project integrated with Chainlink" %}

```solidity
import { FeedRegistryInterface } from "@chainlink/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol";
import { Denominations } from '@thundercore/oracle/TTOracle.sol';

contract MyProject {
    address public constant TT_ORACLE = 0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE;

    function getThunderTokenPrice() public view returns (int256) {
        (
            /* uint80 roundId */,
            int256 answer,
            /* uint256 startedAt */,
            /* uint256 updatedAt */,
            /* uint80 answeredInRound */
        ) = FeedRegistryInterface(TT_ORACLE)
                .latestRoundData(
                    Denominations.TT,
                    Denominations.USDT
                );

        return answer;
    }
}
```

{% endtab %}

{% tab title="Truffle (web3.js)" %}

```bash
# npm exec -- truffle console --network thundercore
# or
# yarn truffle console --network thundrcore
truffle(thundercore)> const abi = require('@thundercore/oracle/abi/FeedRegistryConsumerInterface.json')

truffle(thundercore)> const oracle = new web3.eth.Contract(abi, TT_ORACLE)

truffle(thundercore)> oracle.methods.latestRoundData('TT/USDT').call().then(res => res.answer).then(web3.utils.fromWei)
'0.01663655'
```

{% endtab %}

{% tab title="Hardhat (ethers.js)" %}

```bash
# npm exec -- hardhat console --network thundercore
# or
# yarn truffle console --network thundrcore
> const abi = require('@thundercore/oracle/abi/FeedRegistryConsumerInterface.json')

> const oracle = new hre.ethers.Contract(TT_ORACLE, abi, hre.ethers.provider)

> await oracle.latestRoundData('TT/USDT').then(res => res.answer).then(hre.ethers.utils.formatEther)
'0.01663655'
```

{% endtab %}
{% endtabs %}

You can try it on ThunderCore testnet:

{% embed url="<https://codepen.io/o-p/pen/jOGLgbv>" %}
ThunderCore Testnet Oracle Example
{% endembed %}

## Using Data Feeds

Data Feeds are the quickest way to connect with the real-world asset prices.

{% hint style="info" %}
**Feed Registry**

You can use the [Feed Registry](#feed-registry) to reference data feed assets by pair name or currency identifier.
{% endhint %}

### Solidity

To consume price data, your smart contract should reference `DataFeedConsumerInterface`, which defines the external functions implemented by Data Feeds.

{% hint style="info" %}
If you've applied Chainlink aggregator in your service, i.e. [`AggregatorInterface`](https://github.com/smartcontractkit/chainlink/blob/d8982d45739a2157c3c5d73d2462b8b449165308/contracts/src/v0.8/interfaces/AggregatorInterface.sol), [`AggregatorV3Interface`](https://github.com/smartcontractkit/chainlink/blob/d8982d45739a2157c3c5d73d2462b8b449165308/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol), or [`AggregatorV2V3Interface`](https://github.com/smartcontractkit/chainlink/blob/d8982d45739a2157c3c5d73d2462b8b449165308/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol), you don't have to change the interface. The only difference is the feed address, just change it and test the result on the fly.
{% endhint %}

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

import { DataFeedConsumerInterface } from "@thundercore/oracle/TTOracle.sol";

contract PriceConsumer {

    DataFeedConsumerInterface internal priceFeed;

    /**
     * Network: ThunderCore testnet [18]
     * Pair: TT/USDT
     * Address: 0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c
     */
    constructor() {
        priceFeed = DataFeedConsumerInterface(0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c);
    }

    /**
     * Returns the latest price
     */
    function getLatestPrice() public view returns (int256) {
        (
            /* uint80 roundID */,
            int256 price,
            /* uint256 startedAt */,
            /* uint256 updatedAt */,
            /* uint80 answeredInRound */
        ) = priceFeed.latestRoundData();

        return price;
    }
}
```

The `latestRoundData` function returns five values representing information about the latest price data. See the [Data Feeds API Reference](#api-reference) for more details.

### ECMAScript (JavaScript)

{% tabs %}
{% tab title="web3.js (Node.js)" %}

```javascript
const Web3 = require('web3')
const web3 = new Web3('https://testnet-rpc.thundercore.com')
const abi = require('@thundercore/oracle/abi/DataFeedConsumerInterface.json')
const address = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
const priceFeed = new web3.eth.Contract(abi, address)

priceFeed.methods.latestRoundData().call()
    .then(res => res.answer)
    .then(web3.utils.fromWei)
    .then((price) => { console.log(price) })
```

{% endtab %}

{% tab title="ethers.js (Node.js)" %}

```javascript
const { ethers } = require('ethers')
const provider = new ethers.providers.JsonRpcProvider('https://testnet-rpc.thundercore.com')
const abi = require('@thundercore/oracle/abi/DataFeedConsumerInterface.json')
const address = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
const priceFeed = new ethers.Contract(address, abi, provider)

priceFeed.latestRoundData()
    .then(({ answer }) => { console.log(ethers.utils.formatEther(answer)) })
```

{% endtab %}
{% endtabs %}

### Python

This example uses [Web3.py](https://web3py.readthedocs.io/en/stable/) to retrieve feed data from the TT/USDT feed on the ThunderCore testnet network.

```python
from web3 import Web3
web3 = Web3(Web3.HTTPProvider('https://testnet-rpc.thundercore.com'))
abi = '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"int256","name":"current","type":"int256"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"updatedAt","type":"uint256"}],"name":"AnswerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"NewRound","type":"event"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"hasRoundData","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getPreviousRoundId","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getNextRoundId","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"}]'
addr = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
contract = web3.eth.contract(address=addr, abi=abi)
latestData = contract.functions.latestRoundData().call()
print(latestData)
```

### Getting a different price denomination

If you require a denomination other than what we have provided, you can use two data feeds to derive the pair that you need. For example, if you need a `TT/ETH` price, you could take the `TT/USDT` feed and the `ETH/USDT` feed and derive `TT/ETH` using division.

$$
\frac{TT / USDT}{ETH / USDT} = \frac{TT / \sout{USDT}}{ETH / \sout{USDT}} = TT / ETH
$$

## Historical Price Data

The most common use case for data feeds is to get the latest price. However, we also exposes functions which can be used to retrieve price data of a previous round ID.

{% hint style="warning" %}
**Never over rely on previous round data of oracle**

If your project requires price before one week or longer ago, always store those prices inside your smart contract.

In ThunderCore oracle, we guarantee to keeps one recent week prices. And after one week, those data could be purged anytime.
{% endhint %}

### Solidity

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

import { DataFeedConsumerInterface } from "@thundercore/oracle/TTOracle.sol";

contract HistoricalPriceConsumer {

    DataFeedConsumerInterface internal priceFeed;

    /**
     * Network: ThunderCore testnet [18]
     * Pair: TT/USDT
     * Address: 0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c
     */
    constructor() {
        priceFeed = DataFeedConsumerInterface(0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c);
    }

    /**
     * Returns historical price for a round id
     */
    function getHistoricalPrice(uint80 roundId) public view returns (int256) {
        require(priceFeed.hasRoundData(roundId), 'No this round data');

        (
            /* uint80 roundID */,
            int256 price,
            /* uint256 startedAt */,
            /* uint256 updatedAt */,
            /* uint80 answeredInRound */
        ) = priceFeed.getRoundData(roundId);

        return price;
    }
}
```

### ECMAScript (JavaScript)

{% tabs %}
{% tab title="web3.js (Node.js)" %}

```javascript
const Web3 = require('web3')
const web3 = new Web3('https://testnet-rpc.thundercore.com')
const abi = require('@thundercore/oracle/abi/DataFeedConsumerInterface.json')
const address = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
const priceFeed = new web3.eth.Contract(abi, address)

priceFeed.methods.getRoundData(1).call()
    .then(res => res.answer)
    .then(web3.utils.fromWei)
    .then((price) => { console.log(price) })
```

{% endtab %}

{% tab title="ethers.js (Node.js)" %}

```javascript
const { ethers } = require('ethers')
const provider = new ethers.providers.JsonRpcProvider('https://testnet-rpc.thundercore.com')
const abi = require('@thundercore/oracle/abi/DataFeedConsumerInterface.json')
const address = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
const priceFeed = new ethers.Contract(address, abi, provider)

priceFeed.getRoundData(1)
    .then(({ answer }) => { console.log(ethers.utils.formatEther(answer)) })
```

{% endtab %}
{% endtabs %}

### Python

```python
from web3 import Web3
web3 = Web3(Web3.HTTPProvider('https://testnet-rpc.thundercore.com'))
abi = '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"int256","name":"current","type":"int256"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"updatedAt","type":"uint256"}],"name":"AnswerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"NewRound","type":"event"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"hasRoundData","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getPreviousRoundId","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getNextRoundId","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"}]'
addr = '0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c'
contract = web3.eth.contract(address=addr, abi=abi)
roundData = contract.functions.getRoundData(1).call()
print(roundData)
```

## Feed Registry

The Feed Registry is an on-chain mapping of assets to feeds. It enables you to query data feeds from asset addresses directly, without needing to know the feed contract addresses.

### Base and Quote

You are free to choose querying feeds from their pair names, e.g. `TT/USDT`; or you can keep Chainlink style by token addresses.

{% tabs %}
{% tab title="Query by pair name" %}

```solidity
registry.latestRoundData('TT/USDT');
```

{% endtab %}

{% tab title="Query by address" %}

```solidity
registry.latestRoundData(
    Denominations.TT,
    Denominations.USDT
);
```

{% endtab %}
{% endtabs %}

### Solidity example code

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

import { FeedRegistryInterface } from '@thundercore/oracle/TTOracle.sol';

contract PriceConsumer {
    FeedRegistryInterface internal registry;

    /**
     * Network: ThunderCore testnet
     * Feed Registry: 0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE
     */
    constructor(address _registry) {
        registry = FeedRegistryInterface(_registry);
    }

    /**
     * Returns the TT / USDT price
     */
    function getTTPrice() public view returns (int) {
        (
            uint80 roundID,
            int256 price,
            uint256 startedAt,
            uint256 timeStamp,
            uint80 answeredInRound
        ) = registry.latestRoundData('TT/USDT');
        return price;
    }
}
```

### ECMAScript (JavaScript) example

```javascript
const Web3 = require('web3')
const web3 = new Web3('https://testnet-rpc.thundercore.com')
const abi = require('@thundercore/oracle/abi/FeedRegistryConsumerInterface.json')
const address = '0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE'
const registry = new web3.eth.Contract(abi, address)

registry.methods.latestRoundData('TT/USDT').call()
    .then(res => res.answer)
    .then(web3.utils.fromWei)
    .then((price) => { console.log(price) })
```

## API Reference

API reference for `DataFeedConsumerInterface`.

| Name                                        | Description                                  |
| ------------------------------------------- | -------------------------------------------- |
| [`decimals`](#decimals)                     | Decimals of "answer" in responsed round data |
| [`description`](#description)               | Feed pair name, format "${BASE}/${QUOTE}"    |
| [`version`](#version)                       | The code version of the feed proxy points to |
| [`hasRoundData`](#hasrounddata)             | Check if the round data existed              |
| [`getRoundData`](#getrounddata)             | Get data from a specific round               |
| [`latestRoundData`](#latestrounddata)       | Get data from the latest round               |
| [`getPreviousRoundId`](#getpreviousroundid) | Get previous data existed round ID           |
| [`getNextRoundId`](#getnextroundid)         | Get next data existed round ID               |

#### decimals

Get the decimals of "answer" in responsed round data

```solidity
function decimals() external view returns (uint8)
```

* `RETURN`: The number of decimals.

#### description

Get the feed pair name, format "${BASE}/${QUOTE}"

```solidity
function description() external view returns (string memory);
```

* `RETURN`: The pair name of data feed.

#### version

Get the code version of the feed proxy points to

```solidity
function version() external view returns (uint256);
```

* `RETURN`: The code version.

#### hasRoundData

Check if the round data existed

```solidity
function hasRoundData(uint80 roundId) external view returns (bool);
```

* **Petermeters**
  * `roundId`: The round ID to check if round data existed in data feed.
* **Return Values**
  * `RETURN`: Round data exists or not.

#### getRoundData

Get data from a specific round

```solidity
function getRoundData(uint80 _roundId)
    external
    view
    returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
```

* **Petermeters**
  * `roundId`: The round ID
* **Return Values**
  * `roundId`: The round ID.
  * `answer`: The price.
  * `startedAt`: Timestamp of when the round started.
  * `updatedAt`: Timestamp of when the round was last updated.
  * `answeredInRound`: The round ID of the round in which the answer was computed.

#### latestRoundData

Get data from the latest round

```solidity
function latestRoundData()
    external
    view
    returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
```

* **Return Values**
  * `roundId`: The round ID.
  * `answer`: The price.
  * `startedAt`: Timestamp of when the round started.
  * `updatedAt`: Timestamp of when the round was last updated.
  * `answeredInRound`: The round ID of the round in which the answer was computed.

#### getPreviousRoundId

Get previous data existed round ID

```solidity
function getPreviousRoundId(uint80 roundId) external view returns (uint80);
```

* **Petermeters**
  * `roundId`: The round ID to look up closest previous valid round ID.
* **Return Values**
  * `RETURN`: The previous valid round ID to parameter specified round ID; return 0 if no valid round ID before.

#### getNextRoundId

Get next data existed round ID

```solidity
function getNextRoundId(uint80 roundId) external view returns (uint80);
```

* **Petermeters**
  * `roundId`: The round ID to look up closest next valid round ID.
* **Return Values**
  * `RETURN`: The next valid round ID to parameter specified round ID; return 0 if no valid round ID after.

## Contract Addresses

{% tabs %}
{% tab title="ThunderCore mainnet (108)" %}
**FeedRegistry:** `0x70B3B30066b4F7B927f26C408461b3F176AA07Dc`

|     Pair     | Dec | Proxy                                        |
| :----------: | --: | -------------------------------------------- |
|   `TT/USDT`  |  18 | `0x0808Ac0b6f4B502C59e40D147dDA7Bfa1AE7BD6C` |
|  `ETH/USDT`  |  18 | `0x1Cc8dE741C6d32c321B46a23D8289F337aBEf4D9` |
|  `WBTC/USDT` |  18 | `0x632Ce66C7C973eE2662F207cBbA04A6E2d9B91bb` |
|  `BNB/USDT`  |  18 | `0xFB19d0dC0194A99913Daaa3d11f8d00ff4affa81` |
|   `HT/USDT`  |  18 | `0x53BD5eB3fF53F19F1eD7871D48B200f3312E8017` |
| {% endtab %} |     |                                              |

{% tab title="ThunderCore testnet (18)" %}
**FeedRegistry:** `0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE`

|      Pair     | Dec | Proxy                                        |
| :-----------: | --: | -------------------------------------------- |
|   `TT/USDT`   |  18 | `0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c` |
|   `ETH/USDT`  |  18 | `0x555Ad57b0EA33e2ac49D267C96eF98aa254a54c4` |
|  `WBTC/USDT`  |  18 | `0x83Ac10Bc6dC02E07c71dfBF45ca7355675D340B5` |
|   `BNB/USDT`  |  18 | `0xcE6bd682263F58e1e2828ccA5AEc6A671FFfd739` |
|   `HT/USDT`   |  18 | `0xB6FEB586A86b4C542295E1232B45DE5F80961a16` |
|  {% endtab %} |     |                                              |
| {% endtabs %} |     |                                              |
