Aggregated Data Feed Store (ADFS)
Aggregated 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.
The examples below show how to call the contract to fetch data. All read calls must start with the first bit set to 1, where the whole selector is 1 byte (0x80 in binary is 0b10000000, i.e. first bit set to 1). To simplify the calls, we have the following types of calls:
- selector 0x86: getFeedAtRound(uint8 stride, uint120 feedId, uint16 round, uint32 startSlot?, uint32 slots?) returns (bytes)
- selector 0x81: getLatestRound(uint8 stride, uint120 feedId) returns (uint16)
- selector 0x82: getLatestSingleData(uint8 stride, uint120 feedId) returns (bytes)
- selector 0x84: getLatestData(uint8 stride, uint120 feedId, uint32 startSlot?, uint32 slots?) returns (bytes)
Code Examples
Solidity
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 {Blocksense} from 'lib/Blocksense.sol';
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract BlocksenseADFSConsumer {
address public immutable adfs;
constructor(address _adfs) {
adfs = _adfs;
}
function getLatestSingleFeedData(uint256 id) external view returns (bytes32) {
return Blocksense._getLatestSingleFeedData(adfs, id);
}
function getLatestFeedData(
uint256 stride,
uint256 id
) external view returns (bytes32[] memory) {
return Blocksense._getLatestFeedData(adfs, stride, id);
}
function getLatestSlicedFeedData(
uint256 stride,
uint256 id,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory) {
return
Blocksense._getLatestSlicedFeedData(
adfs,
stride,
id,
startSlot,
slotsCount
);
}
function getLatestRound(
uint256 stride,
uint256 id
) external view returns (uint256 round) {
return Blocksense._getLatestRound(adfs, stride, id);
}
function getSingleFeedDataAtRound(
uint256 id,
uint256 round
) external view returns (bytes32) {
return Blocksense._getSingleFeedDataAtRound(adfs, id, round);
}
function getFeedDataAtRound(
uint256 stride,
uint256 id,
uint256 round
) external view returns (bytes32[] memory) {
return Blocksense._getFeedDataAtRound(adfs, stride, id, round);
}
function getSlicedFeedDataAtRound(
uint256 stride,
uint256 id,
uint256 round,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory) {
return
Blocksense._getSlicedFeedDataAtRound(
adfs,
stride,
id,
round,
startSlot,
slotsCount
);
}
function getLatestSingleFeedDataAndRound(
uint256 id
) external view returns (bytes32 data, uint256 round) {
return Blocksense._getLatestSingleFeedDataAndRound(adfs, id);
}
function getLatestFeedDataAndRound(
uint256 stride,
uint256 id
) external view returns (bytes32[] memory data, uint256 round) {
return Blocksense._getLatestFeedDataAndRound(adfs, stride, id);
}
function getLatestSlicedFeedDataAndRound(
uint256 stride,
uint256 id,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory data, uint256 round) {
return
Blocksense._getLatestSlicedFeedDataAndRound(
adfs,
stride,
id,
startSlot,
slotsCount
);
}
}
In order to retrieve data from the contract, the client must call the fallback
function as follows:
- To read the latest data stored in a single feed (stride = 0):
function getLatestSingleFeedData(uint256 id) external view returns (bytes32) {
return
Blocksense._callSingleDataFeed(adfs, (uint256(0x82) << 248) | (id << 120));
}
- To read the latest data stored in a feed of a greater stride (stride > 0):
function getLatestFeedData(
uint256 stride,
uint256 id
) external view returns (bytes32[] memory) {
return
Blocksense._callDataFeed(
adfs,
(uint256(0x84) << 248) | (stride << 240) | (id << 120),
17,
1 << stride
);
}
- To read a slice of the latest data stored in a feed of a greater stride (stride > 0):
function getLatestSlicedFeedData(
uint256 stride,
uint256 id,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory) {
return
Blocksense._callDataFeed(
adfs,
(uint256(0x84) << 248) |
(stride << 240) |
(id << 120) |
(startSlot << 88) |
(slotsCount << 56),
slotsCount == 0 ? 21 : 25,
slotsCount > 0 ? slotsCount : (1 << (stride - startSlot))
);
}
- To get the latest round:
function getLatestRound(
uint256 stride,
uint256 id
) external view returns (uint256 round) {
return
uint256(
Blocksense._callSingleDataFeed(
adfs,
(uint256(0x81) << 248) | (stride << 240) | (id << 120)
)
);
}
- To read historical data stored in a single feed (stride = 0):
function getSingleFeedDataAtRound(
uint256 id,
uint256 round
) external view returns (bytes32) {
return
Blocksense._callSingleDataFeed(
adfs,
(uint256(0x86) << 248) | (id << 120) | (_round << 104)
);
}
- To read historical data stored in a feed of a greater stride (stride > 0):
function getFeedDataAtRound(
uint256 stride,
uint256 id,
uint256 round
) external view returns (bytes32[] memory) {
return
Blocksense._callDataFeed(
adfs,
(uint256(0x86) << 248) |
(stride << 240) |
(id << 120) |
(_round << 104),
19,
1 << stride
);
}
- To read a slice of the historical data stored in a feed of a greater stride (stride > 0):
function getSlicedFeedDataAtRound(
uint256 stride,
uint256 id,
uint256 round,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory) {
return
Blocksense._callDataFeed(
adfs,
(uint256(0x86) << 248) |
(stride << 240) |
(id << 120) |
(_round << 104) |
(startSlot << 72) |
(slotsCount << 40),
slotsCount == 0 ? 23 : 27,
slotsCount > 0 ? slotsCount : (1 << (stride - startSlot))
);
}
- To read the latest data stored in a single feed (stride = 0) with its round:
function getLatestSingleFeedDataAndRound(
uint256 id
) external view returns (bytes32 data, uint256 round) {
return Blocksense._getLatestSingleFeedDataAndRound(adfs, id);
}
- To read the latest data stored in a feed of a greater stride (stride > 0) with its round:
function getLatestFeedDataAndRound(
uint256 stride,
uint256 id
) external view returns (bytes32[] memory data, uint256 round) {
return
Blocksense._callDataFeedAndRound(
adfs,
(uint256(0x85) << 248) | (stride << 240) | (id << 120),
17,
1 << stride
);
}
- To read a slice of the latest data stored in a feed of a greater stride (stride > 0) with its round:
function getLatestSlicedFeedDataAndRound(
uint256 stride,
uint256 id,
uint256 startSlot,
uint256 slotsCount
) external view returns (bytes32[] memory data, uint256 round) {
return
Blocksense._callDataFeedAndRound(
adfs,
(uint256(0x85) << 248) |
(stride << 240) |
(id << 120) |
(startSlot << 88) |
(slotsCount << 56),
slotsCount == 0 ? 23 : 27,
slotsCount > 0 ? slotsCount : (1 << (stride - startSlot))
);
}
Solidity Hardhat Example
Ethers.js v6.x
To read the latest data stored in a single feed (stride = 0):
// inputs: key
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120'],
['0x82', 0, key],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = Number(res.slice(0, 50));
const timestamp = Number('0x' + res.slice(50, 66));
To read the latest data stored in a feed of a greater stride (stride > 0):
// inputs: key, stride
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120'],
['0x82', stride, key],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);
To read a slice of the latest data stored in a feed of a greater stride (stride > 0):
// inputs: key, stride, startSlot, slots
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120', 'uint32', 'uint32'],
['0x84', stride, key, startSlot, slots],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);
To get the latest round:
// inputs: key, stride?
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120'],
['0x81', stride ?? 0n, key],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const round = Number(res)
To read historical data stored in a single feed (stride = 0):
// inputs: key, roundId
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120', 'uint16'],
['0x86', 0, key, roundId],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = Number(res.slice(0, 50));
const timestamp = Number('0x' + res.slice(50, 66));
To read historical data stored in a feed of a greater stride (stride > 0):
// inputs: key, stride, roundId
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120', 'uint16'],
['0x86', stride, key, roundId],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);
To read a slice of the historical data stored in a feed of a greater stride (stride > 0):
// inputs: key, stride, roundId, startSlot, slots
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120', 'uint16', 'uint32', 'uint32'],
['0x86', stride, key, roundId, startSlot, slots],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);
To read the latest data stored in a single feed (stride = 0) with its round:
// inputs: key
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120'],
['0x83', 0, key],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const feedData = '0x' + res.slice(66);
const round = Number(res.slice(0, 66));
const value = Number(feedData.slice(0, 50));
const timestamp = Number('0x' + feedData.slice(50, 66));
To read the latest data stored in a feed of a greater stride (stride > 0) with its round:
// inputs: key, stride
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120'],
['0x85', stride, key],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);
To read a slice of the latest data stored in a feed of a greater stride (stride > 0) with its round:
// inputs: key, stride, startSlot, slots
const adfs = new ethers.Contract(
contractAddress,
abiJson,
provider,
);
const data = ethers.solidityPacked(
['bytes1', 'uint8', 'uint120', 'uint32', 'uint32'],
['0x85', stride, key, startSlot, slots],
);
const res = await network.provider.send('eth_call', [
{
to: adfs.target,
data,
},
'latest',
]);
const value = splitInto32bChunks(res);