Skip to content

Commit 4aa0e6e

Browse files
authored
init (#2853)
1 parent 5aafcc7 commit 4aa0e6e

File tree

20 files changed

+3050
-575
lines changed

20 files changed

+3050
-575
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.24;
3+
4+
// NOTE: The required interfaces will soon be available in the Chainlink package for you to import.
5+
6+
interface IBundleAggregatorProxy {
7+
/**
8+
* @notice Returns the latest bundle data
9+
* @return bundle The latest bundle as raw bytes
10+
*/
11+
function latestBundle() external view returns (bytes memory);
12+
13+
/**
14+
* @notice Returns the timestamp of the latest bundle
15+
* @return timestamp The timestamp of the latest bundle
16+
*/
17+
function latestBundleTimestamp() external view returns (uint256);
18+
19+
/**
20+
* @notice Returns the decimals for each field in the bundle
21+
* @return decimalsArray Array of decimals for each value in the bundle
22+
*/
23+
function bundleDecimals() external view returns (uint8[] memory);
24+
}
25+
26+
/**
27+
* @notice This struct defines the exact data structure of the MVR feed
28+
* @dev The order and types must match exactly what's defined in the feed
29+
*/
30+
struct Data {
31+
uint256 netAssetValue;
32+
uint256 assetsUnderManagement;
33+
uint256 outstandingShares;
34+
uint256 netIncomeExpenses;
35+
bool openToNewInvestors;
36+
}
37+
38+
contract MVRDataConsumer {
39+
// Reference to the MVR feed proxy
40+
IBundleAggregatorProxy public s_proxy;
41+
42+
// Maximum allowed staleness duration for the data
43+
// IMPORTANT: This should be configured based on the specific feed's heartbeat interval
44+
// Check the feed's documentation for the appropriate value instead of using this example value
45+
uint256 public immutable STALENESS_THRESHOLD;
46+
47+
// Storage for scaled values (after dividing by decimals)
48+
uint256 public netAssetValue;
49+
uint256 public assetsUnderManagement;
50+
uint256 public outstandingShares;
51+
uint256 public netIncomeExpenses;
52+
bool public openToNewInvestors;
53+
54+
// Storage for original onchain values (no decimal adjustments)
55+
uint256 public rawNetAssetValue;
56+
uint256 public rawAssetsUnderManagement;
57+
uint256 public rawOutstandingShares;
58+
uint256 public rawNetIncomeExpenses;
59+
60+
// Keep track of decimals for each field in the struct.
61+
// Non-numeric fields (e.g., bool) typically return 0.
62+
uint8[] public decimals;
63+
64+
// Error for stale data
65+
error StaleData(
66+
uint256 lastUpdateTimestamp,
67+
uint256 blockTimestamp,
68+
uint256 threshold
69+
);
70+
71+
// Error for insufficient decimals array
72+
error InsufficientDecimals(uint256 expected, uint256 actual);
73+
74+
/**
75+
* @notice Constructor that sets the staleness threshold for the feed
76+
* @param _proxy The address of the MVR feed's proxy contract
77+
* @param _stalenessThreshold Maximum time (in seconds) since last update before data is considered stale
78+
* @dev The threshold should be based on the feed's heartbeat interval from documentation
79+
* For example, if a feed updates every 24 hours (86400s), you might set this to 86400s + some buffer
80+
*/
81+
constructor(IBundleAggregatorProxy _proxy, uint256 _stalenessThreshold) {
82+
s_proxy = _proxy;
83+
STALENESS_THRESHOLD = _stalenessThreshold;
84+
}
85+
86+
/**
87+
* @notice Stores the decimals array in your contract for repeated usage.
88+
* @dev Index mapping for this example:
89+
* 0 -> netAssetValue,
90+
* 1 -> assetsUnderManagement,
91+
* 2 -> outstandingShares,
92+
* 3 -> netIncomeExpenses,
93+
* 4 -> openToNewInvestors (likely returns 0).
94+
*/
95+
function storeDecimals() external {
96+
decimals = s_proxy.bundleDecimals();
97+
}
98+
99+
/**
100+
* @notice Returns the timestamp of the most recent MVR feed update.
101+
*/
102+
function getLatestBundleTimestamp() external view returns (uint256) {
103+
return s_proxy.latestBundleTimestamp();
104+
}
105+
106+
/**
107+
* @notice Simple boolean check for data freshness (block explorer friendly)
108+
* @return true if data is fresh, false if stale
109+
*/
110+
function isDataFresh() public view returns (bool) {
111+
uint256 lastUpdateTime = s_proxy.latestBundleTimestamp();
112+
return (block.timestamp - lastUpdateTime) <= STALENESS_THRESHOLD;
113+
}
114+
115+
/**
116+
* @notice Fetches and decodes the latest MVR feed data, then stores both the raw and scaled values.
117+
* @dev This process demonstrates the complete flow of consuming MVR feed data:
118+
* 1. Check data freshness
119+
* 2. Fetch the raw bytes
120+
* 3. Decode into the struct matching the feed's data structure
121+
* 4. Store raw values (preserving original precision)
122+
* 5. Apply decimal conversions to get the true numerical values
123+
*/
124+
function consumeData() external {
125+
// Check data freshness before proceeding
126+
if (!isDataFresh()) {
127+
uint256 lastUpdateTime = s_proxy.latestBundleTimestamp();
128+
revert StaleData(
129+
lastUpdateTime,
130+
block.timestamp,
131+
STALENESS_THRESHOLD
132+
);
133+
}
134+
135+
// Ensure we have the decimals array - if not, fetch it
136+
if (decimals.length == 0) {
137+
decimals = s_proxy.bundleDecimals();
138+
}
139+
140+
// Verify we have enough decimal values for our struct fields
141+
if (decimals.length < 4) {
142+
revert InsufficientDecimals(4, decimals.length);
143+
}
144+
145+
// 1. Retrieve the raw bytes from the MVR feed
146+
// This is the encoded form of all data fields packed together
147+
bytes memory b = s_proxy.latestBundle();
148+
149+
// 2. Decode the raw bytes into our known struct
150+
// The struct Data must match exactly what the feed encodes
151+
Data memory d = abi.decode(b, (Data));
152+
153+
// 3. Store the raw (original onchain) values
154+
// These preserve the full precision as reported by the feed
155+
rawNetAssetValue = d.netAssetValue;
156+
rawAssetsUnderManagement = d.assetsUnderManagement;
157+
rawOutstandingShares = d.outstandingShares;
158+
rawNetIncomeExpenses = d.netIncomeExpenses;
159+
openToNewInvestors = d.openToNewInvestors; // Boolean, no need for decimal adjustment
160+
161+
// 4. Convert values by dividing by 10^decimals[i]
162+
// This removes the decimal scaling factor to get the human-readable representation
163+
// Note: This uses integer division which truncates decimal places
164+
// For example, if decimals[0] = 8 and rawNetAssetValue = 1850000000,
165+
// then netAssetValue = 18 (integer division, decimals are truncated)
166+
netAssetValue = d.netAssetValue / (10 ** decimals[0]);
167+
assetsUnderManagement = d.assetsUnderManagement / (10 ** decimals[1]);
168+
outstandingShares = d.outstandingShares / (10 ** decimals[2]);
169+
netIncomeExpenses = d.netIncomeExpenses / (10 ** decimals[3]);
170+
// Note: We don't need to apply decimals to boolean fields
171+
// The openToNewInvestors field typically has 0 decimals in the array
172+
}
173+
}

public/samples/DataFeeds/ReserveConsumerV3.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
pragma solidity ^0.8.7;
2+
pragma solidity 0.8.24;
33

44
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
55

src/config/sidebar.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,15 @@ export const SIDEBAR: Partial<Record<Sections, SectionEntry[]>> = {
8888
{
8989
title: "SmartData",
9090
url: "data-feeds/smartdata",
91+
children: [
92+
{
93+
title: "Multiple-Variable Response (MVR) Feeds",
94+
url: "data-feeds/mvr-feeds",
95+
},
96+
],
9197
},
9298
{
93-
title: "SVR Feeds",
99+
title: "Smart Value Recapture (SVR) Feeds",
94100
url: "data-feeds/svr-feeds",
95101
},
96102
{
@@ -139,6 +145,24 @@ export const SIDEBAR: Partial<Record<Sections, SectionEntry[]>> = {
139145
title: "Using Data Feeds",
140146
url: "data-feeds/using-data-feeds",
141147
},
148+
{
149+
title: "Using MVR Feeds",
150+
url: "data-feeds/mvr-feeds/guides",
151+
children: [
152+
{
153+
title: "Using MVR Feeds on EVM Chains (Solidity)",
154+
url: "data-feeds/mvr-feeds/guides/evm-solidity",
155+
},
156+
{
157+
title: "Using MVR Feeds with ethers.js (JS)",
158+
url: "data-feeds/mvr-feeds/guides/ethersjs",
159+
},
160+
{
161+
title: "Using MVR Feeds with Viem (TS)",
162+
url: "data-feeds/mvr-feeds/guides/viem",
163+
},
164+
],
165+
},
142166
{
143167
title: "Getting Historical Data",
144168
url: "data-feeds/historical-data",
@@ -218,6 +242,10 @@ export const SIDEBAR: Partial<Record<Sections, SectionEntry[]>> = {
218242
title: "Data Feeds API Reference",
219243
url: "data-feeds/api-reference",
220244
},
245+
{
246+
title: "MVR Feeds API Reference",
247+
url: "data-feeds/mvr-feeds/api-reference",
248+
},
221249
],
222250
},
223251
{

src/content/data-feeds/index.mdx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Data feeds provide many different types of data for your applications.
3535

3636
- [Price Feeds](#price-feeds)
3737
- [SmartData Feeds](#smartdata-feeds)
38+
- [Multiple-Variable Response (MVR) Feeds](#multiple-variable-response-mvr-feeds)
39+
- [Smart Value Recapture (SVR) Feeds](#smart-value-recapture-svr-feeds)
3840
- [Rate and Volatility Feeds](#rate-and-volatility-feeds)
3941
- [L2 sequencer uptime feeds](#l2-sequencer-uptime-feeds)
4042

@@ -46,25 +48,36 @@ For example, lending and borrowing platforms like [Aave](https://aave.com) utili
4648

4749
Data Feeds aggregate many data sources and publish them onchain using a combination of the [Decentralized Data Model](/architecture-overview/architecture-decentralized-model?parent=dataFeeds) and [Offchain Reporting](/architecture-overview/off-chain-reporting?parent=dataFeeds).
4850

49-
To learn how to use Price Feeds, see the [Price Feeds](/data-feeds/price-feeds) documentation.
50-
51-
See the [Data Feeds Contract Addresses](/data-feeds/price-feeds/addresses) page for a list of available networks and addresses.
51+
- To learn how to use Price Feeds, see the [Price Feeds](/data-feeds/price-feeds) documentation.
52+
- See the [Data Feeds Contract Addresses](/data-feeds/price-feeds/addresses) page for a list of available networks and addresses.
5253

5354
### SmartData Feeds
5455

5556
Chainlink SmartData is a suite of onchain data offerings designed to unlock the utility, accessibility, and reliability of tokenized real-world assets (RWAs). By providing secure minting assurances alongside essential real-world data such as reserves, Net Asset Value (NAV), and Assets Under Management (AUM) data, the SmartData suite embeds security and enriches data into tokenized RWA offerings.
5657

57-
To learn more about SmartData Feeds, see the [SmartData](/data-feeds/smartdata) documentation.
58+
- To learn more about the broader SmartData suite, see the [SmartData](/data-feeds/smartdata) documentation.
59+
- See the [SmartData Contract Addresses](/data-feeds/smartdata/addresses) page for a list of available networks and addresses.
60+
61+
#### Multiple-Variable Response (MVR) Feeds
62+
63+
The SmartData suite includes [Multiple-Variable Response (MVR) Feeds](/data-feeds/mvr-feeds), which bundle multiple data points of various types into a single onchain update. Unlike traditional feeds that provide only one numeric value, MVR feeds can return multiple values of different types (both numeric and non-numeric) in a single transaction.
64+
65+
- To learn more about MVR Feeds, see the [Multiple-Variable Response (MVR) Feeds](/data-feeds/mvr-feeds) documentation.
66+
- See the [SmartData Contract Addresses](/data-feeds/smartdata/addresses) page for a list of available networks and addresses. Use the _Show Multiple-Variable Response (MVR) feeds_ checkbox to filter the list.
5867

59-
See the [SmartData Contract Addresses](/data-feeds/smartdata/addresses) page for a list of available networks and addresses.
68+
### Smart Value Recapture (SVR) Feeds
69+
70+
Smart Value Recapture (SVR) Feeds introduce a mechanism to recapture Oracle Extractable Value (OEV), a form of MEV associated with oracle updates, often observed during liquidations.
71+
72+
- To learn more, see the [Smart Value Recapture (SVR) Feeds](/data-feeds/svr-feeds) documentation.
73+
- Find SVR-enabled addresses on the [Price Feed Addresses](/data-feeds/price-feeds/addresses) page by filtering for SVR feeds.
6074

6175
### Rate and Volatility Feeds
6276

6377
Several feeds provide interest rate curve data, APY data, and realized asset price volatility.
6478

65-
To learn more, see the [Rate and Volatility Feeds](/data-feeds/rates-feeds) documentation.
66-
67-
See the [Rate and Volatility Contract Addresses](/data-feeds/rates-feeds/addresses) page for a list of available networks and addresses.
79+
- To learn more, see the [Rate and Volatility Feeds](/data-feeds/rates-feeds) documentation.
80+
- See the [Rate and Volatility Contract Addresses](/data-feeds/rates-feeds/addresses) page for a list of available networks and addresses.
6881

6982
### L2 sequencer uptime feeds
7083

@@ -74,13 +87,26 @@ To learn how to use L2 sequencer uptime feeds, see the [L2 Sequencer Uptime Feed
7487

7588
## Components of a data feed
7689

77-
Data Feeds are an example of a decentralized oracle network and include the following components:
90+
Data Feeds are examples of decentralized oracle networks with different interfaces depending on the type of data they provide. Each feed type includes specific components:
91+
92+
### Single-value Feed Components
93+
94+
Data feeds that deliver a single numeric value (such as price feeds or single-value [SmartData feeds](/data-feeds/smartdata)) include the following components:
7895

7996
- **Consumer**: A consumer is an onchain or offchain application that uses Data Feeds. Consumer contracts use the [`AggregatorV3Interface`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol) to call functions on the proxy contract and retrieve information from the aggregator contract. For a complete list of functions available in the `AggregatorV3Interface`, see the [Data Feeds API Reference](/data-feeds/api-reference/#aggregatorv3interface).
8097
- **Proxy contract**: Proxy contracts are onchain proxies that point to the aggregator for a particular data feed. Using proxies enables the underlying aggregator to be upgraded without any service interruption to consuming contracts. Proxy contracts can vary from one data feed to another, but the [`EACAggregatorProxy.sol` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.6/EACAggregatorProxy.sol) on Github is a common example.
8198
- **Aggregator contract**: An aggregator is a contract that receives periodic data updates from the oracle network. Aggregators store aggregated data onchain so that consumers can retrieve it and act upon it within the same transaction. For a complete list of functions and variables available on most aggregator contracts, see the [Data Feeds API Reference](/data-feeds/api-reference/#accesscontrolledoffchainaggregator).
8299

83-
To learn how to create a consumer contract that uses an existing data feed, read the [Using Data Feeds](/data-feeds/price-feeds) documentation.
100+
To learn how to create a consumer contract that uses an existing price feed, read the [Using Data Feeds](/data-feeds/price-feeds) documentation.
101+
102+
### MVR Feed Components
103+
104+
[Multiple-Variable Response (MVR) Feeds](/data-feeds/mvr-feeds), which deliver multiple data points of various types in a single bundle, include the following components:
105+
106+
- **Consumer**: A consumer is an onchain or offchain application that uses MVR Feeds. Consumer contracts use the [`IBundleAggregatorProxy`](/data-feeds/mvr-feeds/api-reference/#ibundleaggregatorproxy) interface to call functions on the proxy contract and retrieve bundle data.
107+
- **Bundle Aggregator Proxy**: This proxy contract points to the underlying bundle aggregator. Using proxies enables the underlying aggregator to be upgraded without service interruption to consuming contracts. The proxy exposes functions like [`latestBundle()`](/data-feeds/mvr-feeds/api-reference/#latestbundle), [`bundleDecimals()`](/data-feeds/mvr-feeds/api-reference/#bundledecimals), and [`latestBundleTimestamp()`](/data-feeds/mvr-feeds/api-reference/#latestbundletimestamp) for consumers to access the bundled data.
108+
109+
To learn how to create a consumer contract that uses an existing MVR feed, read the [Using MVR Feeds on EVM Chains (Solidity)](/data-feeds/mvr-feeds/guides/evm-solidity) guide.
84110

85111
## Reading proxy and aggregator configurations
86112

0 commit comments

Comments
 (0)