The main purpose of oracles is to allow smart contracts to communicate to the outside world, hence why you see most examples focusing on this. From where exactly are you trying to access this data from? eg from a webUI or something?
To answer your question, you can't access individual oracle node responses with Chainlink. Getting data from an individual node or data source isn't recommended due to it being prone to manipulation or a price oracle attack. This is why Chainlink aggregates responses, taking things into account such as volume, liquidity etc to come to a final aggregated response that contains sufficient market coverage, and this is the value that you should use.
If you just want to access a particular price from a particular website, regardless of the warnings described above, then you can use Chainlink Any-API functionality
Each proxy contract can have multiple underlying aggregator contracts, with one of them being set as the 'active' one at any given instance. Ie whenever the Chainlink Labs team deploys new versions of the aggregator, they update the proxy contract to set the current aggregator version to be the new one, and all new price data starts getting written to the new aggregator.
Based on this, if you want ALL historical price data for a proxy contract/price pair, you'd need to go through all aggregators and get all the price data in each one, then combine it all at the end. Eg let's say the ETH/USD proxy used aggregator with phase ID of 3 from July to now, then they used aggregator with phase ID 2 from jan to july, then they used aggregator with phase ID 1 from nov 2020 - jan 2020. You would grab all the pricing data from each aggregator, then combine the results.
To find the current aggregator contract address for a proxy contract, you can call the aggregator
getter function in the proxy contract
The phase ID can be thought of as an incrementing ID number that the proxy contract uses to identify each aggregator as it gets added to the proxy, so the first one is 1, second is 2 etc. To find out what the current phase ID is of the current aggregator, you can use the phaseId
getter function in the proxy contract.
If the current one is phase ID 4, that means that there are 3 other aggregators that were used in the price feed at some stage. You can get the address of these other aggregators by passing in a phase ID into the proxy contract phaseAggregators
getter function. Ie if the current phaseID is 4, you can pass in the parameters 3, 2, and then 1 to get the address of the previous aggregator contracts.
Once you have all the aggregator contract addresses, it's simply a matter of going through each one and getting the pricing data using the getRoundData
function. However take note there's a difference between proxy contract round ID and aggregator contract round ID.
Aggregator round IDs start from 1 and increase by 1 each time. Eg you can use the aggregator latestRound
getter function to find the last round that it stored, and then start from that number as an input parameter to getRoundData
, and go back down to 1
Proxy round IDs are those big really long numbers like 36893488147419113293
, and are actually just a derived value based on the aggregator phase ID and the aggregator round ID. This is done to ensure the proxy round IDs are always increasing in value, and that there will never be any overlap between the same rounds across different aggregators, eg round 5000 from aggregator with phase ID 1 should have a lower proxy round ID that round 5000 from aggregator with phase ID 2
In JS you can easily replicate the proxy round ID derived value with the following formula, passing in the phase ID of the aggregator and the aggregator round ID
(BigInt(_aggregatorPhaseId) << BigInt(64) | BigInt(_aggregatorRoundId)).toString()
You can even then take that derived value and pass it into the proxy contract getRoundData
function, and get the same data result as calling getRoundData
in the aggregator contract for the given aggregator round ID.
I find A good way to play around with these is to interact with the functions directly on etherescan, and see the data returned. Ie here's the Kovan ETH/USD proxy and its latest aggregator
One thing to note is that there is a chance that multiple aggregators could be getting updated at the same time as well (for fail-safe scenarios, when new aggregators get rolled out etc), so in your code that grabs all the data, you may need to handle the scenario where multiple aggregators have stored price data for the same time frame, and remove duplicates.
Regarding the answer your second question, the best way is to start from 'now', ie get the latest aggregator, grab the latest round in the aggregator and then go back to round ID 1 getting all pricing data, then get the previous aggregator and repeat, and keep going back in aggregators (phase ID = phase ID - 1) until you get all the data from the aggregator with phase ID of 1. Then you have all the data
Finally, not all aggregators are the same, some may have slightly different function implementations depending on what you're doing, so be aware of that. Each aggregator has a version
getter function that will tell you what version of the aggregator contract it is. Here's an implementation of some javascript that gets historical price data at a particular point in time (as opposed to all data), you may find some code snippets useful. This one looks at the ethereum logs to get round IDs though, because it's looking for data at a particular point in time as opposed to all historical data
Best Answer
Pricing/Economics of Data Feeds
The economics of Chainlink decentralized oracles are similar to Ethereum and other L1s. Chainlink Node operators are economically incentivized to run nodes as long as people use their services.
With an L1, the node operators get paid to mine blocks, and as soon as people stop paying for the blocks, the operators would stop supporting the chain. It works the same way with Chainlink, except instead of mining blocks, the nodes are delivering data to a blockchain. Once it stops being economical for nodes to deliver data or external execution, the nodes wouldn’t do it.
That being said, same as ETH, we see node operators consistently upping participation since there is so much demand for data on-chain, just like there is a ton of demand for blocks in an L1.
Chainlink price feeds are (presently) paid for by the protocols that are sponsoring them, which isn’t really fair since (as you have correctly observed) anyone can consume the price feed data for free.
On Going Support of Feeds
There are a few scenarios where a feed might change or no longer be supported:
A best practice if you’re nervous about the state of a feed, would be to add a check to see when the last update on a feed was. If the last update was not recent enough for your requirements (you can check timestamp data using the API), you can freeze functionality that depends on the feed.