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 data feed , we are providing the same function interfaces to make your project cross-chain onto ThunderCore instantly.
Pending for Review
This is pre-release version section. Contents can be changed anytime, please use in your caution.
Get Started
You can get the latest contract interfaces and ABIs through npm :
Copy $ npm install @thundercore/oracle
or Yarn :
Copy $ yarn add @thundercore/oracle
And just a few lines of code, so you can get the latest price:
New project Project integrated with Chainlink Truffle (web3.js) Hardhat (ethers.js)
Copy 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;
}
}
Copy 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;
}
}
Copy # 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'
Copy # 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'
You can try it on ThunderCore testnet:
Using Data Feeds
Data Feeds are the quickest way to connect with the real-world asset prices.
Feed Registry
You can use the Feed Registry to reference data feed assets by pair name or currency identifier.
Solidity
To consume price data, your smart contract should reference DataFeedConsumerInterface
, which defines the external functions implemented by Data Feeds.
Copy // 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 for more details.
ECMAScript (JavaScript)
web3.js (Node.js) ethers.js (Node.js)
Copy 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) })
Copy 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)) })
Python
This example uses Web3.py to retrieve feed data from the TT/USDT feed on the ThunderCore testnet network.
Copy 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.
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.
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.
Solidity
Copy // 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)
web3.js (Node.js) ethers.js (Node.js)
Copy 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) })
Copy 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)) })
Python
Copy 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.
Query by pair name Query by address
Copy registry. latestRoundData ( 'TT/USDT' );
Copy registry. latestRoundData (
Denominations.TT ,
Denominations.USDT
);
Solidity example code
Copy // 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
Copy 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
.
Decimals of "answer" in responsed round data
Feed pair name, format "${BASE}/${QUOTE}"
The code version of the feed proxy points to
Check if the round data existed
Get data from a specific round
Get data from the latest round
Get previous data existed round ID
Get next data existed round ID
decimals
Get the decimals of "answer" in responsed round data
Copy function decimals () external view returns ( uint8 )
RETURN
: The number of decimals.
description
Get the feed pair name, format "${BASE}/${QUOTE}"
Copy 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
Copy function version () external view returns ( uint256 );
RETURN
: The code version.
hasRoundData
Check if the round data existed
Copy 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
Copy function getRoundData ( uint80 _roundId )
external
view
returns (
uint80 roundId ,
int256 answer ,
uint256 startedAt ,
uint256 updatedAt ,
uint80 answeredInRound
);
Return Values
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
Copy function latestRoundData ()
external
view
returns (
uint80 roundId ,
int256 answer ,
uint256 startedAt ,
uint256 updatedAt ,
uint80 answeredInRound
);
Return Values
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
Copy 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
Copy 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
ThunderCore mainnet (108) ThunderCore testnet (18)
FeedRegistry: 0x70B3B30066b4F7B927f26C408461b3F176AA07Dc
0x0808Ac0b6f4B502C59e40D147dDA7Bfa1AE7BD6C
0x1Cc8dE741C6d32c321B46a23D8289F337aBEf4D9
0x632Ce66C7C973eE2662F207cBbA04A6E2d9B91bb
0xFB19d0dC0194A99913Daaa3d11f8d00ff4affa81
0x53BD5eB3fF53F19F1eD7871D48B200f3312E8017
FeedRegistry: 0xD477d4d8132C6fce38f0bf38f46FcB62D6c5ddaE
0x1Ce18a95C56Db619C070AeF58918EaB09FE2634c
0x555Ad57b0EA33e2ac49D267C96eF98aa254a54c4
0x83Ac10Bc6dC02E07c71dfBF45ca7355675D340B5
0xcE6bd682263F58e1e2828ccA5AEc6A671FFfd739
0xB6FEB586A86b4C542295E1232B45DE5F80961a16