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