Historical Data Feed Store
Historical Data Feed Store contract is where all data is stored and where all contracts read from. It is written in Solidity assembly and contains only one function: fallback
.
This contract serves as the core logic that dictates how the
UpgradeableProxy
contract manages its own storage, handling both data writing and reading
operations. It's crucial to understand that all data is exclusively stored
within the UpgradeableProxy contract. To access this data, users must
interact with the fallback
function of the UpgradeableProxy contract
which underneath calls the fallback
function of the
HistoricDataFeedStore contract.
Code Examples
Solidity
Use ProxyCall library for easy and gas optimised interaction with the contract.
Here is a complete working example. After that, there are more details on how to perform a raw call.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ProxyCall} from 'lib/ProxyCall.sol';
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract UpgradeableProxyConsumer {
address public immutable dataFeedStore;
constructor(address feedAddress) {
dataFeedStore = feedAddress;
}
function getDataById(
uint32 key
) external view returns (uint256 value, uint64 timestamp) {
bytes32 data = ProxyCall._callDataFeed(
dataFeedStore,
abi.encodePacked(0x80000000 | key)
);
return (uint256(uint192(bytes24(data))), uint64(uint256(data)));
}
function getFeedAtCounter(
uint32 key,
uint32 counter
) external view returns (uint256 value, uint64 timestamp) {
bytes32 data = ProxyCall._callDataFeed(
dataFeedStore,
abi.encodeWithSelector(bytes4(0x20000000 | key), counter)
);
return (uint256(uint192(bytes24(data))), uint64(uint256(data)));
}
function getLatestCounter(uint32 key) external view returns (uint32 counter) {
return uint32(ProxyCall._latestRound(key, dataFeedStore));
}
function getLatestRoundData(
uint32 key
) external view returns (int256 value, uint256 timestamp, uint80 counter) {
(counter, value, timestamp, , ) = ProxyCall._latestRoundData(
key,
dataFeedStore
);
}
}
In order to retrieve data from the contract, the client must call the fallback
function as follows:
- To read the latest price and timestamp:
function getDataById(
uint32 key
) external view returns (uint256 value, uint64 timestamp) {
bytes32 data = ProxyCall._callDataFeed(
dataFeedStore,
abi.encodePacked(0x80000000 | key)
);
return (uint256(uint192(bytes24(data))), uint64(uint256(data)));
}
- To read historical data for a specific round:
function getFeedAtCounter(
uint32 key,
uint32 counter
) external view returns (uint256 value, uint64 timestamp) {
bytes32 data = ProxyCall._callDataFeed(
dataFeedStore,
abi.encodeWithSelector(bytes4(0x20000000 | key), counter)
);
return (uint256(uint192(bytes24(data))), uint64(uint256(data)));
}
- To get the latest round:
function getLatestCounter(uint32 key) external view returns (uint32 counter) {
return uint32(ProxyCall._latestRound(key, dataFeedStore));
}
- To get the latest round data:
function getDataById(
uint32 key
) external view returns (int256 value, uint256 timestamp, uint80 counter) {
(counter, value, timestamp, , ) = ProxyCall
._latestRoundData(key, dataFeedStore);
}
Solidity Hardhat Example
You can find a working Hardhat project here. Clone the repo and follow the setup instructions to run the example locally.
Ethers.js v6.x
To read the latest value and timestamp:
const historicDataFeedStore = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = '0x' + ((key | 0x80000000) >>> 0).toString(16).padStart(8, '0');
const res = await network.provider.send('eth_call', [
{
to: historicDataFeedStore.target,
data,
},
'latest',
]);
const value = Number(res.slice(0, 50));
const timestamp = Number('0x' + res.slice(50, 66));
To read historical data for a specific round:
const historicDataFeedStore = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const round = 1;
const data = '0x' + ((key | 0x20000000) >>> 0).toString(16).padStart(8, '0');
const res = await network.provider.send('eth_call', [
{
to: historicDataFeedStore.target,
data: ethers.solidityPacked(['bytes4', 'uint256'], [data, round]),
},
'latest',
]);
const value = Number(res.slice(0, 50));
const timestamp = Number('0x' + res.slice(50, 66));
To get the latest round:
const historicDataFeedStore = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = '0x' + ((key | 0x40000000) >>> 0).toString(16).padStart(8, '0');
const res = await network.provider.send('eth_call', [
{
to: historicDataFeedStore.target,
data,
},
'latest',
]);
const round = Number('0x' + res.slice(66));
To get the latest round data:
const historicDataFeedStore = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = '0x' + ((key | 0xc0000000) >>> 0).toString(16).padStart(8, '0');
const res = await network.provider.send('eth_call', [
{
to: historicDataFeedStore.target,
data,
},
'latest',
]);
const value = Number(res.slice(0, 50));
const timestamp = Number('0x' + res.slice(50, 66));
const round = Number('0x' + res.slice(66));