# Data Feeds API Reference
Source: https://docs.chain.link/data-feeds/api-reference
When you use data feeds, retrieve the feeds through the `AggregatorV3Interface` and the proxy address. Optionally, you can call variables and functions in the `AccessControlledOffchainAggregator` contract to get information about the aggregator behind the proxy.
## AggregatorV3Interface
Import this interface to your contract and use it to run functions in the proxy contract. Create the interface object by pointing to the proxy address. For example, on Sepolia you could create the interface object in the constructor of your contract using the following example:
```solidity
/**
* Network: Sepolia
* Data Feed: ETH/USD
* Address: 0x694AA1769357215DE4FAC081bf1f309aDC325306
*/
constructor() {
priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
}
```
To see examples for how to use this interface, read the [Using Data Feeds](/data-feeds/price-feeds) guide.
You can see the code for the [`AggregatorV3Interface` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol) on GitHub.
### Functions in AggregatorV3Interface
| Name | Description |
| ----------------------------------- | -------------------------------------------------------------------- |
| [decimals](#decimals) | The number of decimals in the response. |
| [description](#description) | The description of the aggregator that the proxy points to. |
| [getRoundData](#getrounddata) | Get data from a specific round. |
| [latestRoundData](#latestrounddata) | Get data from the latest round. |
| [version](#version) | The version representing the type of aggregator the proxy points to. |
#### decimals
Get the number of decimals present in the response value.
```solidity
function decimals() external view returns (uint8);
```
- `RETURN`: The number of decimals.
#### description
Get the description of the underlying aggregator that the proxy points to.
```solidity
function description() external view returns (string memory);
```
- `RETURN`: The description of the underlying aggregator.
#### getRoundData
Get data about a specific round, using the `roundId`.
```solidity
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
```
**Parameters:**
- `_roundId`: The round ID
**Return values:**
- `roundId`: The round ID
- `answer`: The answer for this round
- `startedAt`: Timestamp of when the round started
- `updatedAt`: Timestamp of when the round was updated
- `answeredInRound`: Deprecated - Previously used when answers could take multiple rounds to be computed
#### latestRoundData
Get the data from the latest round.
```solidity
function latestRoundData() external view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
**Return values:**
- `roundId`: The round ID.
- `answer`: The data that this specific feed provides. Depending on the feed you selected, this answer provides asset prices, reserves, and other types of data.
- `startedAt`: Timestamp of when the round started.
- `updatedAt`: Timestamp of when the round was updated.
- `answeredInRound`: Deprecated - Previously used when answers could take multiple rounds to be computed
#### version
The version representing the type of aggregator the proxy points to.
```solidity
function version() external view returns (uint256)
```
- `RETURN`: The version number.
## AccessControlledOffchainAggregator
This is the contract for the aggregator. You can call functions on the aggregator directly, but it is a best practice to use the [AggregatorV3Interface](#aggregatorv3interface) to run functions on the proxy instead so that changes to the aggregator do not affect your application. Read the aggregator contract only if you need functions that are not available in the proxy.
The aggregator contract has several variables and functions that might be useful for your application. Although aggregator contracts are similar for each data feed, some aggregators have different variables. Use the `typeAndVersion()` function on the aggregator to identify what type of aggregator it is and what version it is running.
Always check the contract source code and configuration to understand how specific data feeds operate. For example, the [aggregator contract for BTC/USD on Arbitrum](https://arbiscan.io/address/0x942d00008D658dbB40745BBEc89A93c253f9B882#code) is different from the aggregators on other networks.
For examples of the contracts that are typically used in aggregator deployments, see the [libocr repository](https://github.com/smartcontractkit/libocr/blob/master/contract/) on GitHub.
### Variables and functions in AccessControlledOffchainAggregator
This contract imports `OffchainAggregator` and `SimpleReadAccessController`, which also include their own imports. The variables and functions lists include the publicly accessible items from these imported contracts.
A simple way to read the variables or functions is to get the ABI from a blockchain explorer and point the ABI to the aggregator address. To do this in Remix, follow the [Using the ABI with AtAddress](https://remix-ide.readthedocs.io/en/latest/run.html#using-the-abi-with-ataddress) guide in the Remix documentation. As an example, you can find the ABI for the BTC/USD aggregator by viewing the [contract code in Etherscan](https://etherscan.io/address/0xAe74faA92cB67A95ebCAB07358bC222e33A34dA7#code).
**Variables:**
| Name | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| LINK | The address for the LINK token contract on a specific network. |
| billingAccessController | The address for the billingAccessController, which limits access to the [billing configuration](https://github.com/smartcontractkit/libocr/blob/master/contract/OffchainAggregatorBilling.sol) for the aggregator. |
| checkEnabled | A boolean that indicates if access is limited to addresses on the internal access list. |
| maxAnswer | This value is no longer used on most Data Feeds. Evaluate if your use case for Data Feeds requires a custom circuit breaker and implement it to meet the needs of your application. See the [Risk Mitigation](/data-feeds/selecting-data-feeds#risk-mitigation) page for more information. |
| minAnswer | This value is no longer used on most Data Feeds. Evaluate if your use case for Data Feeds requires a custom circuit breaker and implement it to meet the needs of your application. See the [Risk Mitigation](/data-feeds/selecting-data-feeds#risk-mitigation) page for more information. |
| owner | The address that owns this aggregator contract. This controls which address can execute specific functions. |
**Functions:**
| Name | Description |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [decimals](#decimals-1) | Return the number of digits of precision for the stored answer. Answers are stored in fixed-point format. |
| [description](#description-1) | Return a description for this data feed. This is different depending on which feed you select. |
| [getAnswer](#getanswer) | Deprecated - Do not use this function. |
| [getBilling](#getbilling) | Retrieve the current billing configuration. |
| [getRoundData](#getrounddata-1) | Get the full information for a specific aggregator round including the answer and update timestamps. Use this to get the full historical data for a round. |
| [getTimestamp](#gettimestamp) | Deprecated - Do not use this function. |
| [hasAccess](#hasaccess) | Check if an address has internal access. |
| [latestAnswer](#latestanswer) | Deprecated - Do not use this function. |
| [latestConfigDetails](#latestconfigdetails) | Return information about the current offchain reporting protocol configuration. |
| [latestRound](#latestround) | Deprecated - Do not use this function. |
| [latestRoundData](#latestrounddata-1) | Get the full information for the most recent round including the answer and update timestamps. |
| [latestTimestamp](#latesttimestamp) | Deprecated - Do not use this function. |
| [latestTransmissionDetails](#latesttransmissiondetails) | Get information about the most recent answer. |
| [linkAvailableForPayment](#linkavailableforpayment) | Get the amount of LINK on this contract that is available to make payments to oracles. This value can be negative if there are outstanding payment obligations. |
| [oracleObservationCount](#oracleobservationcount) | Returns the number of observations that oracle is due to be reimbursed for. |
| [owedPayment](#owedpayment) | Returns how much LINK an oracle is owed for its observations. |
| [requesterAccessController](#requesteraccesscontroller) | Returns the address for the access controller contract. |
| [transmitters](#transmitters) | The oracle addresses that can report answers to this aggregator. |
| [typeAndVersion](#typeandversion) | Returns the aggregator type and version. Many aggregators are `AccessControlledOffchainAggregator 3.0.0`, but there are other variants in production. The version is for the type of aggregator, and different from the contract `version`. |
| [validatorConfig](#validatorconfig) | Returns the address and the gas limit for the validator contract. |
| [version](#version-1) | Returns the contract version. This is different from the `typeAndVersion` for the aggregator. |
#### decimals
Return the number of digits of precision for the stored answer. Answers are stored in fixed-point format.
```solidity
function decimals() external view returns (uint8 decimalPlaces);
```
#### description
Return a description for this data feed. Usually this is an asset pair for a price feed.
```solidity
function description() public view override checkAccess returns (string memory) {
return super.description();
}
```
#### getAnswer
#### getBilling
Retrieve the current billing configuration.
```solidity
function getBilling()
external
view
returns (
uint32 maximumGasPrice,
uint32 reasonableGasPrice,
uint32 microLinkPerEth,
uint32 linkGweiPerObservation,
uint32 linkGweiPerTransmission
)
{
Billing memory billing = s_billing;
return (
billing.maximumGasPrice,
billing.reasonableGasPrice,
billing.microLinkPerEth,
billing.linkGweiPerObservation,
billing.linkGweiPerTransmission
);
}
```
#### getRoundData
Get the full information for a specific aggregator round including the answer and update timestamps. Use this to get the full historical data for a round.
```solidity
function getRoundData(
uint80 _roundId
)
public
view
override
checkAccess
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
return super.getRoundData(_roundId);
}
```
#### getTimestamp
#### hasAccess
Check if an address has internal access.
```solidity
function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
return super.hasAccess(_user, _calldata) || _user == tx.origin;
}
```
#### latestAnswer
#### latestConfigDetails
Return information about the current offchain reporting protocol configuration.
```solidity
function latestConfigDetails() external view returns (uint32 configCount, uint32 blockNumber, bytes16 configDigest) {
return (s_configCount, s_latestConfigBlockNumber, s_hotVars.latestConfigDigest);
}
```
#### latestRound
#### latestRoundData
Get the full information for the most recent round including the answer and update timestamps.
```solidity
function latestRoundData()
public
view
override
checkAccess
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
return super.latestRoundData();
}
```
#### latestTimestamp
#### latestTransmissionDetails
Get information about the most recent answer.
```solidity
function latestTransmissionDetails()
external
view
returns (bytes16 configDigest, uint32 epoch, uint8 round, int192 latestAnswer, uint64 latestTimestamp)
{
require(msg.sender == tx.origin, "Only callable by EOA");
return (
s_hotVars.latestConfigDigest,
uint32(s_hotVars.latestEpochAndRound >> 8),
uint8(s_hotVars.latestEpochAndRound),
s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp
);
}
```
#### linkAvailableForPayment
Get the amount of LINK on this contract that is available to make payments to oracles. This value can be negative if there are outstanding payment obligations.
```solidity
function linkAvailableForPayment() external view returns (int256 availableBalance) {
// there are at most one billion LINK, so this cast is safe
int256 balance = int256(LINK.balanceOf(address(this)));
// according to the argument in the definition of totalLINKDue,
// totalLINKDue is never greater than 2**172, so this cast is safe
int256 due = int256(totalLINKDue());
// safe from overflow according to above sizes
return int256(balance) - int256(due);
}
```
#### oracleObservationCount
Returns the number of observations that oracle is due to be reimbursed for.
```solidity
function oracleObservationCount(address _signerOrTransmitter) external view returns (uint16) {
Oracle memory oracle = s_oracles[_signerOrTransmitter];
if (oracle.role == Role.Unset) {
return 0;
}
return s_oracleObservationsCounts[oracle.index] - 1;
}
```
#### owedPayment
Returns how much LINK an oracle is owed for its observations.
```solidity
function owedPayment(address _transmitter) public view returns (uint256) {
Oracle memory oracle = s_oracles[_transmitter];
if (oracle.role == Role.Unset) {
return 0;
}
Billing memory billing = s_billing;
uint256 linkWeiAmount = uint256(s_oracleObservationsCounts[oracle.index] - 1) *
uint256(billing.linkGweiPerObservation) *
(1 gwei);
linkWeiAmount += s_gasReimbursementsLinkWei[oracle.index] - 1;
return linkWeiAmount;
}
```
#### requesterAccessController
Returns the address for the access controller contract.
```solidity
function requesterAccessController() external view returns (AccessControllerInterface) {
return s_requesterAccessController;
}
```
#### transmitters
The oracle addresses that can report answers to this aggregator.
```solidity
function transmitters() external view returns (address[] memory) {
return s_transmitters;
}
```
#### typeAndVersion
Returns the aggregator type and version. Many aggregators are `AccessControlledOffchainAggregator 2.0.0`, but there are other variants in production. The version is for the type of aggregator, and different from the contract `version`.
```solidity
function typeAndVersion() external pure virtual override returns (string memory) {
return "AccessControlledOffchainAggregator 2.0.0";
}
```
#### validatorConfig
Returns the address and the gas limit for the validator contract.
```solidity
function validatorConfig() external view returns (AggregatorValidatorInterface validator, uint32 gasLimit) {
ValidatorConfig memory vc = s_validatorConfig;
return (vc.validator, vc.gasLimit);
}
```
#### version
Returns the contract version. This is different from the `typeAndVersion` for the aggregator.
```solidity
function version() external view returns (uint256);
```
---
# Using Data Feeds on Aptos
Source: https://docs.chain.link/data-feeds/aptos
Aptos is a Layer 1 blockchain that uses the [Move](https://move-language.github.io/move/) programming language for smart contracts.
Chainlink Data Feeds on Aptos provides data through a single price feed contract that handles multiple data feeds. You interact with this contract by passing the specific feed ID(s) for the data you need. This contrasts with Chainlink's integration on other blockchains, where each price feed has a separate contract address.
## Getting Started (CLI)
This guide explains how to use Chainlink Data Feeds with your Move smart contracts on Aptos testnet using the `Benchmark` structure provided by the data feeds contract. You will use the [Aptos CLI](https://aptos.dev/en/build/cli) to compile, publish, and interact with your contract.
### Requirements
Make sure you have the [Aptos CLI](https://aptos.dev/en/build/cli) installed. You can run aptos help in your terminal to verify if the CLI is correctly installed.
### Integrate Chainlink Data Feeds on Aptos
Create a new directory for your project and navigate to it in your terminal:
```bash
mkdir aptos-data-feeds && cd aptos-data-feeds
```
#### Set up your Aptos testnet account
1. Run the following command in your terminal to create a new account on testnet:
```bash
aptos init --network=testnet --assume-yes
```
2. You are prompted to enter a private key:
```bash
Configuring for profile default
Configuring for network Testnet
Enter your private key as a hex literal (0x...) [Current: Redacted | No input: Generate new key (or keep one if present)]
```
Press `Enter` to generate a new key pair with the [`ed25519` key scheme](https://aptos.dev/en/network/blockchain/accounts#authentication-key).
Expect an output similar to the following:
```bash
No key given, generating key...
Account 0x35107a273065fc0f428b2db719145682f6b5bf16a32b071ccd649fcd8b1a44e9 doesn't exist, creating it and funding it with 100000000 Octas
Account 0x35107a273065fc0f428b2db719145682f6b5bf16a32b071ccd649fcd8b1a44e9 funded successfully
---
Aptos CLI is now set up for account 0x35107a273065fc0f428b2db719145682f6b5bf16a32b071ccd649fcd8b1a44e9 as profile default!
See the account here: https://explorer.aptoslabs.com/account/0x35107a273065fc0f428b2db719145682f6b5bf16a32b071ccd649fcd8b1a44e9?network=testnet
Run `aptos --help` for more information about commands
{
"Result": "Success"
}
```
You now have a funded testnet account on Aptos.
#### Set up your project
1. Initialize a Move package in your `aptos-data-feeds` directory:
```bash
aptos move init --name aptos-data-feeds
```
Expect the following output:
```bash
{
"Result": "Success"
}
```
You should now have a Move project with the following structure:
```plaintext
Move.toml
├── /.aptos
├── /scripts
├── /sources
└── /tests
```
2. Update your `Move.toml` file to include the required dependencies and addresses:
```toml
[package]
name = "my-app"
version = "1.0.0"
authors = []
[addresses]
sender = ""
owner = ""
data_feeds = "0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3"
platform = "0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99"
move_stdlib = "0x1"
aptos_std = "0x1"
[dev-addresses]
[dependencies]
AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-framework", rev = "main" }
MoveStdlib = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/move-stdlib", rev = "main" }
ChainlinkDataFeeds = { local = "./ChainlinkDataFeeds" }
```
Where:
- `` is your Aptos testnet account address. You can find your address in the `~/.aptos/config.yaml` file. Alternatively, run the following command in your terminal to retrieve it:
```bash
aptos config show-profiles --profile=default
```
- The `data_feeds` address is the `ChainlinkDataFeeds` package address on Aptos testnet ([`0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3`](https://explorer.aptoslabs.com/object/0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3/modules/packages/ChainlinkDataFeeds?network=testnet)).
- The `platform` address is the `ChainlinkPlatform` package address on Aptos testnet ([`0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99`](https://explorer.aptoslabs.com/object/0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99/modules/packages/ChainlinkPlatform?network=testnet)).
**Note**: You can find the mainnet addresses in the [Feeds Addresses](/data-feeds/price-feeds/addresses?page=1\&testnetPage=1\&network=aptos) page.
3. Run the following command to download the compiled bytecode for the `ChainlinkPlatform` and `ChainlinkDataFeeds` packages:
```bash
aptos move download --account 0x516e771e1b4a903afe74c27d057c65849ecc1383782f6642d7ff21425f4f9c99 --package ChainlinkPlatform && \
aptos move download --account 0xccad6853cabea164842907df3de4f89bb34be5bf249bbf16939f9c90db1bf63b --package ChainlinkDataFeeds
```
Expect an output similar to the following:
```bash
Saved package with 2 module(s) to `~/aptos-data-feeds/ChainlinkPlatform`
{
"Result": "Download succeeded"
}
Saved package with 2 module(s) to `~/aptos-data-feeds/ChainlinkDataFeeds`
{
"Result": "Download succeeded"
}
```
4. Open the `ChainlinkDataFeeds` package configuration file (`ChainlinkDataFeeds/Move.toml`) and update the `ChainlinkPlatform` dependency to point to your local path:
```toml
ChainlinkPlatform = { local = "../ChainlinkPlatform" }
```
#### Write the Move contract to interact with Chainlink Data Feeds
In this step, you will develop a Move module that interacts with Chainlink Data Feeds to fetch the latest price and timestamp for a specified feed ID. This module retrieves benchmark data for the given feed ID, extracts the price and timestamp, and stores this information in the invoking account's global storage. You can then retrieve this data using a view function.
1. Create a new Move module in the `sources` directory of your project:
```bash
touch sources/MyOracleContract.move
```
2. Insert the following code example and save your `MyOracleContract.move` file:
```rust
module sender::MyOracleContractTest {
use std::vector;
use std::signer;
use data_feeds::router::get_benchmarks;
use data_feeds::registry::{Benchmark, get_benchmark_value, get_benchmark_timestamp};
use move_stdlib::option::{Option, some, none};
struct PriceData has copy, key, store {
/// The price value with 18 decimal places of precision
price: u256,
/// Unix timestamp in seconds
timestamp: u256,
}
// Function to fetch and store the price data for a given feed ID
public entry fun fetch_price(account: &signer, feed_id: vector) acquires PriceData {
let feed_ids = vector[feed_id]; // Use the passed feed_id
let billing_data = vector[];
let benchmarks: vector = get_benchmarks(account, feed_ids, billing_data);
let benchmark = vector::pop_back(&mut benchmarks);
let price: u256 = get_benchmark_value(&benchmark);
let timestamp: u256 = get_benchmark_timestamp(&benchmark);
// Check if PriceData exists and update it
if (exists(signer::address_of(account))) {
let data = borrow_global_mut(signer::address_of(account));
data.price = price;
data.timestamp = timestamp;
} else {
// If PriceData does not exist, create a new one
move_to(account, PriceData { price, timestamp });
}
}
// View function to get the stored price data
#[view]
public fun get_price_data(account_address: address): Option acquires PriceData {
if (exists(account_address)) {
let data = borrow_global(account_address);
some(*data)
} else {
none()
}
}
}
```
#### Compile and publish the contract
1. Compile and publish the contract to the Aptos testnet:
```bash
aptos move publish --skip-fetch-latest-git-deps
```
- You are prompted to confirm the transaction details. Type `yes` and press `Enter` to proceed.
```bash
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY ChainlinkDataFeeds
INCLUDING DEPENDENCY ChainlinkKeystone
INCLUDING DEPENDENCY MoveStdlib
BUILDING my-app
package size 2133 bytes
Do you want to submit a transaction for a range of [176700 - 265000] Octas at a gas unit price of 100 Octas? [yes/no] >
```
- Expect an output similar to the following:
```bash
Transaction submitted: https://explorer.aptoslabs.com/txn/0x22a65eedb37ad7e41e195409c06e23b154c71bba11c73e6f67df6ba41e6768a4?network=testnet
{
"Result": {
"transaction_hash": "0x22a65eedb37ad7e41e195409c06e23b154c71bba11c73e6f67df6ba41e6768a4",
"gas_used": 1767,
"gas_unit_price": 100,
"sender": "4006c4623e114a6169b409cc0ad91b0780e62667633ce2e069b31125cc6a5d37",
"sequence_number": 0,
"success": true,
"timestamp_us": 1731707700843776,
"version": 6238520375,
"vm_status": "Executed successfully"
}
}
```
**Notes**:
- Your `default` profile within the `~/.aptos/config.yaml` file is used by default. You can specify a different profile using the `--profile` flag.
- Ensure you have enough testnet APT tokens in your account to cover the deployment fees. You can fund your account with testnet APT tokens using the following command, where the amount used is in Octas (1 APT = 100,000,000 Octas):
```bash
aptos account fund-with-faucet --account --amount 100000000
```
#### Interact with the deployed contract
In this step, you will interact with your deployed contract to fetch the BTC/USD price and timestamp. The BTC/USD feed ID on Aptos testnet is: 0x01a0b4d920000332000000000000000000000000000000000000000000000000. You can find the feed ID for other assets in the [Feed Addresses page](/data-feeds/price-feeds/addresses?network=aptos\&page=1).
1. Fetch benchmark data for the BTC/USD feed, extract the price and timestamp, and store this information in your account's global storage.
- Run the following command, replacing `` with your actual account address:
```bash
aptos move run \
--function-id '::MyOracleContractTest::fetch_price' \
--args hex:0x01a0b4d920000332000000000000000000000000000000000000000000000000
```
**Note**: `` is your Aptos testnet account address. You can find your address in the `~/.aptos/config.yaml` file. Alternatively, run the following command in your terminal to retrieve it:
```bash
aptos config show-profiles --profile=default
```
- You are prompted to confirm the transaction details. Type `yes` and press `Enter` to proceed:
```bash
Do you want to submit a transaction for a range of [47200 - 70800] Octas at a gas unit price of 100 Octas? [yes/no] >
```
- Expect an output similar to the following:
```bash
Transaction submitted: https://explorer.aptoslabs.com/txn/0x7180e890ececbcab4f052f84ecae21d37f33a31d0e475f566f92fc6df157c725?network=testnet
{
"Result": {
"transaction_hash": "0x7180e890ececbcab4f052f84ecae21d37f33a31d0e475f566f92fc6df157c725",
"gas_used": 472,
"gas_unit_price": 100,
"sender": "4006c4623e114a6169b409cc0ad91b0780e62667633ce2e069b31125cc6a5d37",
"sequence_number": 1,
"success": true,
"timestamp_us": 1731707764586551,
"version": 6238524110,
"vm_status": "Executed successfully"
}
}
```
2. Retrieve this data using the view function:
- Run the following command, replacing `` with your actual account address:
```bash
aptos move view \
--function-id '::MyOracleContractTest::get_price_data' \
--args address:
```
- Expect an output similar to the following:
```bash
{
"Result": [
{
"vec": [
{
"price": "70376345325832900000000",
"timestamp": "1730827175"
}
]
}
]
}
```
Where:
- `price` is the latest BTC/USD price with 18 decimal places.
- `timestamp` is the Unix timestamp in seconds when the price data was last updated onchain by the Chainlink oracle network.
---
# Flags Contract Registry
Source: https://docs.chain.link/data-feeds/contract-registry
The Chainlink Flags Contract Registry provides a reliable, onchain source of truth for all active Chainlink price feed proxy contracts across supported networks. It serves as a central reference point for identifying which data feeds are **owned and operated by Chainlink**, verifying the authenticity of the feeds your applications rely on.
A proxy address that returns `true` when checked against this registry indicates two things:
1. The data feed is officially owned and operated by Chainlink
2. The feed is currently active
Inactive feeds are removed from the registry to ensure accuracy. The list is actively maintained as new feeds are deployed, making it a trusted resource for keeping up with the evolving state of Chainlink's oracle infrastructure.
## Querying Flags Onchain
To check if a proxy address is flagged (owned by Chainlink and active) in your Solidity code, you can call the `getFlag` function on the `Flags` contract deployed on the corresponding network:
```solidity
interface IFlags {
function getFlag(address) external view returns (bool);
}
contract Example {
IFlags public flags;
// Initialize with the correct Flags contract address for your network
constructor(address flagsContractAddress) {
flags = IFlags(flagsContractAddress);
}
function isOfficialChainlinkFeed(address proxy) public view returns (bool) {
return flags.getFlag(proxy);
}
}
```
## Flags Contract Addresses
Below is a list of all available Flags contracts on the supported networks:
| | Chain | ChainFlag Contract Address |
| -------------------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| | Arbitrum | |
| | Avalanche | |
| | Base | |
| | Bob | |
| | BNB Chain (BSC) | |
| | Celo | |
| | Ethereum | |
| | Gnosis Chain (xDai) | |
| | Ink | |
| | Linea | |
| | Mantle | |
| | Optimism | |
| | Polygon | |
| | Scroll | |
| | Soneium | |
| | Sonic | |
| | UniChain | |
| | zkSync | |
---
# Deprecation of Chainlink Data Feeds
Source: https://docs.chain.link/data-feeds/deprecating-feeds
---
# Developer Responsibilities: Market Integrity and Application Code Risks
Source: https://docs.chain.link/data-feeds/developer-responsibilities
Chainlink Data Feeds provide access to highly secure, reliable, and decentralized real-world data published onchain. The assets priced by Chainlink Data Feeds are subject to market conditions beyond the ability of Chainlink node operators to control, as such developers are responsible for ensuring that the operation and performance of Chainlink Data Feeds match expectations.
When integrating Chainlink Data Feeds, developers must understand that the performance of feeds is subject to risks associated with both market integrity and application code.
- **Market Integrity Risks** are those associated with external market conditions impacting price behavior and data quality in unanticipated ways. Developers are solely responsible for monitoring and mitigating any potential market integrity risks.
- **Application Code Risks** are those associated with the quality, reliability, and dependencies of the code on which an application operates. Developers are solely responsible for monitoring and mitigating any potential application code risks.
Please [refer to this guide for additional information](https://chain.link/education-hub/market-manipulation-vs-oracle-exploits) about market integrity risks and how developers can protect their applications.
## Developer Responsibilities
Developers are responsible for maintaining the security and user experience of their applications. They must also securely manage all interactions between their applications and third-party services.
In particular, developers implementing Chainlink Data Feeds in their code and applications are responsible for their application's market integrity and code risks that may cause unanticipated pricing data behavior. These are described below in more detail.
### Market Integrity Risks
Market conditions can impact the pricing behavior of assets in ways beyond the ability of Chainlink node operators to predict or control.
Market integrity risk factors can include but are not limited to, [market manipulation](https://chain.link/education-hub/market-manipulation-vs-oracle-exploits) such as Spoofing, Ramping, Bear Raids, Cross-Market Manipulation, Washtrading, and Frontrunning. All assets are susceptible to [market risk](/data-feeds/selecting-data-feeds#data-feed-categories), but in particular, assets with high market risk, such as those with low liquidity, are the most vulnerable to market manipulation. Developers are solely responsible for accounting for such risk factors when integrating Chainlink Data Feeds into their applications. Developers should understand the market risks around the assets they intend their application to support before integrating associated Chainlink Data Feeds and inform their end users about applicable market risks.
Developers should reference the following additional information when implementing Chainlink Data Feeds:
- [Data Feed Categories](/data-feeds/selecting-data-feeds#data-feed-categories) to evaluate market integrity risks associated with specific Chainlink Data Feeds Developers intend to integrate.
- [Evaluating Data Source Risks](/data-feeds/selecting-data-feeds#evaluating-data-sources-and-risks) to evaluate risk mitigation techniques associated with Chainlink Data Feeds broadly.
### Application Code Risks
Developers implementing Chainlink Data Feeds are solely responsible for instituting the requisite [risk mitigation processes](/data-feeds/selecting-data-feeds#risk-mitigation) including, but not limited to, data quality checks, circuit breakers, and appropriate contingency logic for their use case.
- **Code quality and reliability:** Developers must execute code using Chainlink Data Feeds only if the code meets the quality and reliability requirements for their use case and application.
- **Code and application audits:** Developers are responsible for auditing their code and applications before deploying to production. Developers must determine the quality of any audits and ensure that they meet the requirements for their application.
- **Code dependencies and imports:** Developers are responsible for ensuring the quality, reliability, and security of any dependencies or imported packages that they use with Chainlink Data Feeds, and review and audit these dependencies and packages.
---
# Using ENS with Data Feeds
Source: https://docs.chain.link/data-feeds/ens
## Lookup
## Manual Lookup
## Naming structure
Chainlink data feeds fall under the `data.eth` naming suffix. To obtain a specific feed address, prefix this with the assets in the feed, separated by a dash (-).
| Pair | ENS Domain Name |
| :-------- | :----------------- |
| ETH / USD | `eth-usd.data.eth` |
| BTC / USD | `btc-usd.data.eth` |
| ... | `...` |
### Subdomains
By default, the base name structure (`eth-usd.data.eth`) returns the proxy address for that feed. However, subdomains enable callers to retrieve other associated contract addresses, as shown in the following table.
| Contract Addresses | Subdomain Prefix | Example |
| :-------------------- | :--------------- | :---------------------------- |
| Proxy | `proxy` | `proxy.eth-usd.data.eth` |
| Underlying aggregator | `aggregator` | `aggregator.eth-usd.data.eth` |
| Proposed aggregator | `proposed` | `proposed.eth-usd.data.eth` |
## Architecture
### Resolver
For each network, there is a single Chainlink resolver, which does not change. Its address can be obtained using the `data.eth` domain. This resolver manages the subdomains associated with `data.eth`.
| Network | Resolver Address |
| :--------------- | :----------------------------------------------------------------------------------------------------------------------- |
| Ethereum Mainnet | [0x122eb74f9d0F1a5ed587F43D120C1c2BbDb9360B](https://app.ens.domains/address/0x122eb74f9d0F1a5ed587F43D120C1c2BbDb9360B) |
### Listening for address changes
When a new aggregator is deployed for a specific feed, it is first proposed, and when accepted becomes the aggregator for that feed. During this process, the `proposed` and `aggregator` subdomains for that feed will change. With each change, the resolver emits an `AddrChanged` event, using the feed subdomain (for example: `eth-usd.data.eth`) as the indexed parameter.
**Example**: If you want to listen for when the aggregator of the ETH / USD feed changes, set up a listener to track the `AddrChanged` event on the resolver, using a filter like this: `ethers.utils.namehash('aggregator.eth-usd.data.eth')`.
## Obtaining addresses
### Javascript
The example below uses Javascript Web3 library to interact with ENS. See the [ENS documentation](https://docs.ens.domains/dapp-developer-guide/resolving-names) for the full list of languages and libraries that support ENS.
This example logs the address of the data feed on the Ethereum mainnet for ETH / USD prices.
### Solidity
In Solidity, the address of the ENS registry must be known. According to [ENS documentation](https://docs.ens.domains/ens-deployments), this address is the same across Mainnet and testnet:
ENS registry address: [0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e](https://etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e).
Also, instead of using readable string names like `eth-usd.data.eth`, resolvers accept bytes32 hash IDs for names. Hash IDs can be retrieved from [this subgraph](https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH) or via this npm package [eth-ens-namehash](https://www.npmjs.com/package/@ensdomains/eth-ens-namehash).
"ETH / USD" hash: `0xf599f4cd075a34b92169cf57271da65a7a936c35e3f31e854447fbb3e7eb736d`
---
# Consuming Data Feeds
Source: https://docs.chain.link/data-feeds/getting-started
You can use Chainlink Data Feeds to connect your smart contracts to asset pricing data like the [ETH / USD feed](https://data.chain.link/feeds/ethereum/mainnet/eth-usd). These data feeds use data aggregated from many independent Chainlink node operators. Each price feed has an onchain address and functions that enable contracts to read pricing data from that address.
This guide shows you how to read Data Feeds and store the value onchain using Solidity. To learn how to read feeds offchain or use different languages, see the [Using Data Feeds on EVM Chains](/data-feeds/using-data-feeds) guide. Alternatively, you can also learn how to use Data Feeds on [Solana](/data-feeds/solana) or [StarkNet](/data-feeds/starknet).
The code for reading Data Feeds on Ethereum or other EVM-compatible blockchains is the same for each chain and each Data Feed types. You choose different types of feeds for different uses, but the request and response format are the same. The answer decimal length and expected value ranges might change depending on what feed you use.
## Before you begin
If you are new to smart contract development, learn how to [Deploy Your First Smart Contract](/quickstarts/deploy-your-first-contract) before you start this guide.
## Examine the sample contract
This example contract obtains the latest price answer from the [BTC / USD feed](/data-feeds/price-feeds/addresses) on the Sepolia testnet, but you can modify it to read any of the different [Types of Data Feeds](/data-feeds#types-of-data-feeds).
The contract has the following components:
- The `import` line imports an interface named `AggregatorV3Interface`. Interfaces define functions without their implementation, which leaves inheriting contracts to define the actual implementation themselves. In this case, `AggregatorV3Interface` defines that all v3 Aggregators have the function `latestRoundData`. You can [see the complete code](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol) for the `AggregatorV3Interface` on GitHub.
- The `constructor() {}` initializes an interface object named `dataFeed` that uses `AggregatorV3Interface` and connects specifically to a proxy aggregator contract that is already deployed at `0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43`. The interface allows your contract to run functions on that deployed aggregator contract.
- The `getChainlinkDataFeedLatestAnswer()` function calls your `dataFeed` object and runs the `latestRoundData()` function. When you deploy the contract, it initializes the `dataFeed` object to point to the aggregator at `0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43`, which is the proxy address for the Sepolia BTC / USD data feed. Your contract connects to that address and executes the function. The aggregator connects with several oracle nodes and aggregates the pricing data from those nodes. The response from the aggregator includes several variables, but `getChainlinkDataFeedLatestAnswer()` returns only the `answer` variable.
## Compile, deploy, and run the contract
Deploy the `DataConsumerV3` smart contract on the Sepolia testnet.
1. [Open the example contract](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataFeeds/DataConsumerV3.sol) in Remix. Remix opens and shows the contents of the smart contract.
2. Because the code is already written, you can start the compile step. On the left side of Remix, click the **Solidity Compiler** tab to view the compiler settings.
(Image: Screenshot showing the Compiler tab and its settings.)
3. Use the default compiler settings. Click the **Compile DataConsumerV3.sol** button to compile the contract. Remix automatically detects the correct compiler version depending on the `pragma` that you specify in the contract. You can ignore warnings about unused local variables in this example.
(Image: Screenshot of the Compile button.)
4. On the **Deploy** tab, select the **Injected Provider** environment. This contract specifically requires Web3 because it connects with another contract on the blockchain. Running in a JavaScript VM will not work.
(Image: Screenshot showing the Injected Provider environment selected.)
5. Because the example contract has several imports, Remix might select another contract to deploy by default. In the **Contract** section, select the `DataConsumerV3` contract to make sure that Remix deploys the correct contract.
(Image: Screenshot showing DataConsumerV3 as the contract to deploy.)
6. Click **Deploy** to deploy the contract to the Sepolia testnet. MetaMask opens and asks you to confirm payment for deploying the contract. Make sure MetaMask is set to the Sepolia network before you accept the transaction. Because these transactions are on the blockchain, they are not reversible.
(Image: Screenshot of the Deploy button for DataConsumerV3.)
7. In the MetaMask prompt, click **Confirm** to approve the transaction and spend your testnet ETH required to deploy the contract.
(Image: Screenshot showing Metamask asking you to confirm the transaction.)
8. After a few seconds, the transaction completes and your contract appears under the **Deployed Contracts** list in Remix. Click the contract dropdown to view its variables and functions.
(Image: Remix Deployed Contracts Section)
9. Click **getChainlinkDataFeedLatestAnswer** to show the latest answer from the aggregator contract. In this example, the answer is the latest price, which appears just below the button. The returned answer is an integer, so it is missing its decimal point. You can find the correct number of decimal places for this answer on the [Price Feed addresses](/data-feeds/price-feeds/addresses) page by clicking the **Show more details** checkbox. The answer on the BTC / USD feed uses 8 decimal places, so an answer of `3030914000000` indicates a BTC / USD price of `30309.14`. Each feed uses a different number of decimal places for answers.
(Image: A screenshot showing the deployed contract and the latest answer.)
---
# Getting Historical Data
Source: https://docs.chain.link/data-feeds/historical-data
The most common use case for Data Feeds is to [Get the Latest Data](/data-feeds/using-data-feeds) from a feed. However, the [AggregatorV3Interface.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol) also has functions to retrieve data of a previous round IDs.
There are two parameters that can cause Chainlink nodes to update:
| Name | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Deviation Threshold | Chainlink nodes are monitoring data offchain. The deviation of the real-world data beyond a certain interval triggers all the nodes to update. |
| Heartbeat Threshold | If the data values stay within the deviation parameters, it will only trigger an update every *X* minutes / hours. |
You can find these parameters at [data.chain.link](https://data.chain.link/) on an example like [ETH / USD](https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd).
To learn how data feeds update, see the [Decentralized Data Model](/architecture-overview/architecture-decentralized-model?parent=dataFeeds#aggregator) page.
## Historical rounds
As shown in the [decentralized model](/architecture-overview/architecture-decentralized-model), the consumer contracts call the proxy contract, which abstracts the underlying aggregator contract. The main advantage is to enable upgrades of the aggregator without impacting the consumer contracts. That also means that historical data can can be stored in different aggregators.
As shown in the following sequence diagram, to get historical data, call the `getRoundData` [function](/data-feeds/api-reference/#getrounddata) and provide `roundId` as a parameter.
Note that roundIds have different meanings in proxy contracts and in aggregator contracts.
### `roundId` in Aggregator (aggregatorRoundId)
Oracles provide periodic data updates to the aggregators. Data feeds are updated in **rounds**. Rounds are identified by their `roundId`, which increases with each new round. This increase may not be monotonic. Knowing the `roundId` of a previous round allows contracts to consume historical data.
The examples in this document name the aggregator `roundId` as `aggregatorRoundId` to differentiate it from the proxy `roundId`.
### `roundId` in proxy
Because a proxy has references to current and all previous underlying aggregators, it needs a way to fetch data from the correct aggregator. The `roundId` is computed in the [proxy contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.6/AggregatorProxy.sol) as shown in the following example:
```solidity
return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
```
where:
- `_phase` is incremented each time the underlying aggregator implementation is updated. It is used as a key to find the aggregator address.
- `_originalId` is the aggregator `roundId`. The ID starts at 1.
From the above formula, you can think of it as returning a large number containing the `phase` and the aggregator `roundId`.
**Example:**
When you query historical data, it is important to know when you reach the end of the history of the underlying aggregator. As an example, if the [`latestRoundData` function](/data-feeds/api-reference/#latestrounddata) of the [LINK / USD Price Feed on Ethereum Mainnet](/data-feeds/price-feeds/addresses/?network=ethereum) returns `roundId = 92233720368547771158`, you can use this value to compute the `phaseId` and `aggregatorRoundId`:
- `phaseId = 92233720368547771158 >> 64`: Right shifting an integer by 64 bits is equivalent to dividing it by 2^64: `phaseId = 92233720368547771158/ 2^64 = 5`. The current phase id is 5 , which means that this proxy has had 5 underlying aggregators since its initial deployment.
- `aggregatorRoundId = uint64(92233720368547771158)`: This retrieves the first 64 bits from the right. To calculate this offchain, you can use the following JavaScript example:
```javascript
// First parse to BigInt to perform computation with big integers
const num = BigInt("92233720368547771158")
const num2 = BigInt("0xFFFFFFFFFFFFFFFF") // Largest 64bits integer
console.log(Number(num >> 64n)) // returns 5 (phaseId)
console.log(Number(num & num2)) // returns 13078 (aggregatorRoundId) . Use & (AND bitwise operator) which sets each bit to _1_ if both bits are _1_
```
Using *13078* as the current aggregator's round, get its historical data by looping over the [`getRoundData` function](/data-feeds/api-reference/#getrounddata):
- Start from the first round: *92233720368547758081* (result of *92233720368547771158 - 13078 + 1*)
- Continue until the current round: *92233720368547771158*
To get the historical data for previous aggregators, decrement the `phaseId` and start from round *1*. For phase *4*, get the starting `roundId` offchain using the following JavaScript example:
```javascript
const phaseId = BigInt("4")
const aggregatorRoundId = BigInt("1")
roundId = (phaseId << 64n) | aggregatorRoundId // returns 73786976294838206465n
```
Loop over the [`getRoundData` function](/data-feeds/api-reference/#getrounddata). Start at *73786976294838206465* and increment it until you get a revert. This means that you reached the last round for the underlying aggregator. The same process could be repeated for previous `phaseIds` (3,2,1).
### `getRoundData` return values
The [`getRoundData` function](/data-feeds/api-reference/#getrounddata) returns the following values:
- `roundId`: The round in which the answer was updated
- `answer`: The answer reflects the data recorded for the specified round
- `answeredInRound`: Deprecated - Previously used when answers could take multiple rounds to be computed
- `startedAt`: The timestamp when the round started
- `updatedAt`: The timestamp when the answer was computed
### Solidity
### Javascript
### Python
---
# Chainlink Data Feeds
Source: https://docs.chain.link/data-feeds
Chainlink Data Feeds are the quickest way to connect your smart contracts to real-world data such as asset prices, reserve balances, and L2 sequencer health.
If you already started a project and need to integrate Chainlink, you can [add Chainlink to your existing project](/resources/create-a-chainlinked-project?parent=dataFeeds#installing-into-existing-projects) with the [`@chainlink/contracts` NPM package](https://www.npmjs.com/package/@chainlink/contracts).
## Types of data feeds
Data feeds provide many different types of data for your applications.
- [Price Feeds](#price-feeds)
- [SmartData Feeds](#smartdata-feeds)
- [Multiple-Variable Response (MVR) Feeds](#multiple-variable-response-mvr-feeds)
- [Smart Value Recapture (SVR) Feeds](#smart-value-recapture-svr-feeds)
- [Rate and Volatility Feeds](#rate-and-volatility-feeds)
- [L2 sequencer uptime feeds](#l2-sequencer-uptime-feeds)
### Price Feeds
Smart contracts often act in real-time on data such as prices of assets. This is especially true in [DeFi](https://chain.link/use-cases/defi).
For example, lending and borrowing platforms like [Aave](https://aave.com) utilize Data Feeds to assess collateral value accurately, while perpetual platforms such as [GMX](https://gmx.io) rely on these feeds to validate offchain data and ensure that each deposit or withdrawal executes at the correct market value.
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).
- To learn how to use Price Feeds, see the [Price Feeds](/data-feeds/price-feeds) documentation.
- See the [Data Feeds Contract Addresses](/data-feeds/price-feeds/addresses) page for a list of available networks and addresses.
### SmartData Feeds
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.
- To learn more about the broader SmartData suite, see the [SmartData](/data-feeds/smartdata) documentation.
- See the [SmartData Contract Addresses](/data-feeds/smartdata/addresses) page for a list of available networks and addresses.
#### Multiple-Variable Response (MVR) Feeds
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.
- To learn more about MVR Feeds, see the [Multiple-Variable Response (MVR) Feeds](/data-feeds/mvr-feeds) documentation.
- 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.
### Smart Value Recapture (SVR) Feeds
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.
- To learn more, see the [Smart Value Recapture (SVR) Feeds](/data-feeds/svr-feeds) documentation.
- Find SVR-enabled addresses on the [Price Feed Addresses](/data-feeds/price-feeds/addresses) page by filtering for SVR feeds.
### Rate and Volatility Feeds
Several feeds provide interest rate curve data, APY data, and realized asset price volatility.
- To learn more, see the [Rate and Volatility Feeds](/data-feeds/rates-feeds) documentation.
- See the [Rate and Volatility Contract Addresses](/data-feeds/rates-feeds/addresses) page for a list of available networks and addresses.
### L2 sequencer uptime feeds
L2 sequencer feeds track the last known status of the sequencer on an L2 network at a given point in time. This helps you prevent mass liquidations by providing a grace period to allow customers to react to these events.
To learn how to use L2 sequencer uptime feeds, see the [L2 Sequencer Uptime Feeds](/data-feeds/l2-sequencer-feeds) documentation.
## Components of a data feed
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:
### Single-value Feed Components
Data feeds that deliver a single numeric value (such as price feeds or single-value [SmartData feeds](/data-feeds/smartdata)) include the following components:
- **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).
- **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.
- **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).
To learn how to create a consumer contract that uses an existing price feed, read the [Using Data Feeds](/data-feeds/price-feeds) documentation.
### MVR Feed Components
[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:
- **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.
- **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.
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.
## Reading proxy and aggregator configurations
Because the proxy and aggregator contracts are all onchain, you can see the current configuration by reading the variables through an [ABI](https://docs.soliditylang.org/en/latest/abi-spec.html) or using a blockchain explorer for your network. For example, you can see the [BTC/USD proxy configuration](https://etherscan.io/address/0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c#readContract) on the Ethereum network using Etherscan.
If you read the BTC/USD proxy configuration, you can query all of the functions and variables that are publicly accessible for that contract including the `aggregator` address, `latestRoundData()` function, `latestAnswer` variable, `owner` address, `latestTimestamp` variable, and several others. To see descriptions for the proxy contract variables and functions, see the source code for your specific data feed on [Etherscan](https://etherscan.io/address/0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c#code#L568).
The proxy contract points to an aggregator. This allows you to retrieve data through the proxy even if the aggregator is upgraded. If you view the `aggregator` address defined in the proxy configuration, you can see the aggregator and its configuration. For example, see the [BTC/USD aggregator contract](https://etherscan.io/address/0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c#code) in Etherscan. This contract includes several variables and functions, including another `latestRoundData()`. To see descriptions for the aggregator variables and functions, see the source code on [GitHub](https://github.com/smartcontractkit/libocr/blob/master/contract/AccessControlledOffchainAggregator.sol) or [Etherscan](https://etherscan.io/address/0xAe74faA92cB67A95ebCAB07358bC222e33A34dA7#code#F1#L1).
You can call the `latestRoundData()` function directly on the aggregator, but it is a best practice to use the proxy instead so that changes to the aggregator do not affect your application. Similar to the proxy contract, the aggregator contract has a `latestAnswer` variable, `owner` address, `latestTimestamp` variable, and several others.
## Components of an aggregator
The aggregator contract has several variables and functions that might be useful for your application. Although aggregator contracts are similar for each data feed, some aggregators have different variables. Use the `typeAndVersion()` function on the aggregator to identify what type of aggregator it is and what version it is running.
Always check the contract source code and configuration to understand how specific data feeds operate. For example, the [aggregator contract for BTC/USD on Arbitrum](https://arbiscan.io/address/0x942d00008D658dbB40745BBEc89A93c253f9B882#code) is different from the aggregators on other networks.
For examples of the contracts that are typically used in aggregator deployments, see the [libocr repository](https://github.com/smartcontractkit/libocr/blob/master/contract/) on GitHub.
For a complete list of functions and variables available on most aggregator contracts, see the [Data Feeds API Reference](/data-feeds/api-reference/#accesscontrolledoffchainaggregator).
## Updates to proxy and aggregator contracts
To accommodate the dynamic nature of offchain environments, Chainlink Data Feeds are updated from time to time to add new features and capabilities as well as respond to externalities such as token migrations, protocol rebrands, extreme market events, and upstream issues with data or node operations.
These updates include changes to the aggregator configuration or a complete replacement of the aggregator that the proxy uses. If you consume data feeds through the proxy, your applications can continue to operate during these changes.
Proxy and aggregator contracts all have an `owner` address that has permission to change variables and functions. For example, if you read the [BTC/USD proxy contract](https://etherscan.io/address/0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c#readContract) in Etherscan, you can see the `owner` address. This address is a [multi-signature safe](https://docs.safe.global/getting-started/readme) (multisig) that you can also inspect.
If you [view the multisig contract](https://etherscan.io/address/0x21f73D42Eb58Ba49dDB685dc29D3bF5c0f0373CA#readProxyContract) in Etherscan using the *Read as Proxy* feature, you can see the full details of the multisig including the list of addresses that can sign and the number of signers required for the multisig to approve actions on any contracts that it owns.
The multisig-coordinated upgradability of Chainlink Data Feeds involves time-tested processes that balance collusion-resistance with the flexibility required to implement improvements and swiftly react to external conditions. The approach taken to upgradability will continue to evolve over time to meet user requirements.
## Monitoring data feeds
When you build applications and protocols that depend on data feeds, include monitoring and safeguards to protect against the negative impact of extreme market events, possible malicious activity on third-party venues or contracts, potential delays, and outages.
Create your own monitoring alerts based on deviations in the answers that data feeds provide. This will notify you when potential issues occur so you can respond to them.
### Check the latest answer against reasonable limits
The data feed aggregator includes both [`minAnswer` and `maxAnswer` values](https://github.com/smartcontractkit/libocr/blob/9e4afd8896f365b964bdf769ca28f373a3fb0300/contract/AccessControlledOffchainAggregator.sol#L33). On most data feeds, these values are no longer used and they do not stop your application from reading the most recent answer. For monitoring purposes, you must decide what limits are acceptable for your application.
Configure your application to detect when the reported answer is close to reaching reasonable minimum and maximum limits so it can alert you to potential market events. Separately, configure your application to detect and respond to extreme price volatility or prices that are outside of your acceptable limits.
### Check the timestamp of the latest answer
Chainlink Data Feeds do not provide streaming data. Rather, the aggregator updates its `latestAnswer` when the value deviates beyond a specified threshold or when the heartbeat idle time has passed. You can find the heartbeat and deviation values for each data feed at [data.chain.link](https://data.chain.link/) or in the [Contract Addresses](/data-feeds/price-feeds/addresses) lists.
Your application should track the `latestTimestamp` variable or use the `updatedAt` value from the `latestRoundData()` function to make sure that the latest answer is recent enough for your application to use it. If your application detects that the reported answer is not updated within the heartbeat or within time limits that you determine are acceptable for your application, pause operation or switch to an alternate operation mode while identifying the cause of the delay.
When the node detects that the heartbeat is reached, it initiates the latest round. Depending on congestion and network conditions, there may be a slight delay for the latest round to get onchain.
During periods of low volatility, the heartbeat triggers updates to the latest answer. Some heartbeats are configured to last several hours, so your application should check the timestamp and verify that the latest answer is recent enough for your application.
Users should build applications with the understanding that data feeds for wrapped or liquid staking assets might have different heartbeat and deviation thresholds than that of the underlying asset. Heartbeat and deviation thresholds can also differ for the same asset across different blockchains. Combining data from multiple feeds, even those with a common denominator, might result in a margin of error that users must account for in their risk mitigation practices.
To learn more about the heartbeat and deviation threshold, read the [Decentralized Data Model](/architecture-overview/architecture-decentralized-model?parent=dataFeeds#aggregator) page.
---
# L2 Sequencer Uptime Feeds
Source: https://docs.chain.link/data-feeds/l2-sequencer-feeds
Optimistic rollups (e.g., Arbitrum, Optimism) and many ZK-rollups rely on sequencers to efficiently manage transaction ordering, execution, and batching before submitting them to Layer 1 (L1) blockchains like Ethereum. The sequencer plays a crucial role in optimizing transaction throughput, reducing fees, and ensuring fast transaction confirmations on L2 networks, making it a key component of their scalability and performance.
However, if the sequencer becomes unavailable, users will lose access to the standard read/write APIs, preventing them from interacting with applications on the L2 network. Although the L2 chain's security and state commitments remain enforced by Layer 1, no new batched blocks will be produced by the sequencer. Users with sufficient technical expertise can still interact directly with the network through the underlying rollup contracts on L1. However, this process is more complex and costly, creating an unfair advantage for those who can bypass the sequencer. This imbalance in access can lead to disruptions or distortions in applications, such as liquidations or market operations that rely on timely transactions.
To mitigate these risks, your applications can integrate a **Sequencer Uptime Data Feed**, which continuously monitors and records the last known status of the sequencer. By utilizing this feed, you can:
- Detect sequencer downtime in real time.
- Implement a grace period to prevent mass liquidations or unexpected disruptions.
- Ensure fair access to services by temporarily pausing operations during sequencer failures.
This proactive approach enhances the resilience and fairness of applications operating on L2 networks, ensuring a more stable and equitable user experience.
## Supported Networks
You can find proxy addresses for the L2 sequencer feeds at the following addresses:
### Arbitrum
Arbitrum Mainnet: [0xFdB631F5EE196F0ed6FAa767959853A9F217697D](https://arbiscan.io/address/0xFdB631F5EE196F0ed6FAa767959853A9F217697D)
### BASE
BASE Mainnet: [0xBCF85224fc0756B9Fa45aA7892530B47e10b6433](https://basescan.org/address/0xBCF85224fc0756B9Fa45aA7892530B47e10b6433)
### Celo
Celo Mainnet: [0x4CD491Dc27C8B0BbD10D516A502856B786939d18](https://celoscan.io/address/0x4CD491Dc27C8B0BbD10D516A502856B786939d18)
### Mantle
Mantle Mainnet: [0xaDE1b9AbB98c6A542E4B49db2588a3Ec4bF7Cdf0](https://mantlescan.xyz/address/0xaDE1b9AbB98c6A542E4B49db2588a3Ec4bF7Cdf0)
### Metis
Andromeda Mainnet: [0x58218ea7422255EBE94e56b504035a784b7AA204](https://andromeda-explorer.metis.io/address/0x58218ea7422255EBE94e56b504035a784b7AA204)
### OP
OP Mainnet: [0x371EAD81c9102C9BF4874A9075FFFf170F2Ee389](https://optimistic.etherscan.io/address/0x371EAD81c9102C9BF4874A9075FFFf170F2Ee389)
### Scroll
Scroll Mainnet: [0x45c2b8C204568A03Dc7A2E32B71D67Fe97F908A9](https://scrollscan.com/address/0x45c2b8C204568A03Dc7A2E32B71D67Fe97F908A9)
### Soneium
Soneium Mainnet: [0xaDE1b9AbB98c6A542E4B49db2588a3Ec4bF7Cdf0](https://soneium.blockscout.com/address/0xaDE1b9AbB98c6A542E4B49db2588a3Ec4bF7Cdf0)
### ZKsync
zkSync Mainnet: [0x0E6AC8B967393dcD3D36677c126976157F993940](https://explorer.zksync.io/address/0x0E6AC8B967393dcD3D36677c126976157F993940)
## Real-time Monitoring Process
### Arbitrum
The diagram below shows how these feeds update and how a consumer retrieves the status of the Arbitrum sequencer.
1. Chainlink nodes trigger an OCR round every 30s and update the sequencer status by calling the `validate` function in the [`ArbitrumValidator` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol) by calling it through the [`ValidatorProxy` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.8/ValidatorProxy.sol).
2. The `ArbitrumValidator` checks to see if the latest update is different from the previous update. If it detects a difference, it places a message in the [Arbitrum inbox contract](https://developer.offchainlabs.com/docs/inside_arbitrum#the-big-picture).
3. The inbox contract sends the message to the [`ArbitrumSequencerUptimeFeed` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol). The message calls the `updateStatus` function in the `ArbitrumSequencerUptimeFeed` contract and updates the latest sequencer status to 0 if the sequencer is up and 1 if it is down. It also records the block timestamp to indicate when the message was sent from the L1 network.
4. A consumer contract on the L2 network can read these values from the [`ArbitrumUptimeFeedProxy` contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.6/EACAggregatorProxy.sol), which reads values from the `ArbitrumSequencerUptimeFeed` contract.
#### Handling Arbitrum Outages
If the Arbitrum network becomes unavailable, the `ArbitrumValidator` contract continues to send messages to the L2 network through the delayed inbox on L1. This message stays there until the sequencer is back up again. When the sequencer comes back online after downtime, it processes all transactions from the delayed inbox before it accepts new transactions. The message that signals when the sequencer is down will be processed before any new messages with transactions that require the sequencer to be operational.
### Other Supported Networks
On BASE, Celo, Mantle, Metis, OP, Scroll, Soneium and zkSync, the sequencer's status is relayed from L1 to L2 where the consumer can retrieve it.
**On the L1 network:**
1. A network of node operators runs the external adapter to post the latest sequencer status to the `AggregatorProxy` contract and relays the status to the `Aggregator` contract. The `Aggregator` contract calls the `validate` function in the `OptimismValidator` contract.
2. The `OptimismValidator` contract calls the `sendMessage` function in the `L1CrossDomainMessenger` contract. This message contains instructions to call the `updateStatus(bool status, uint64 timestamp)` function in the sequencer uptime feed deployed on the L2 network.
3. The `L1CrossDomainMessenger` contract calls the `enqueue` function to enqueue a new message to the `CanonicalTransactionChain`.
4. The `Sequencer` processes the transaction enqueued in the `CanonicalTransactionChain` contract to send it to the L2 contract.
**On the L2 network:**
1. The `Sequencer` posts the message to the `L2CrossDomainMessenger` contract.
2. The `L2CrossDomainMessenger` contract relays the message to the `OptimismSequencerUptimeFeed` contract.
3. The message relayed by the `L2CrossDomainMessenger` contains instructions to call `updateStatus` in the `OptimismSequencerUptimeFeed` contract.
4. Consumers can then read from the `AggregatorProxy` contract, which fetches the latest round data from the `OptimismSequencerUptimeFeed` contract.
#### Handling Other Supported Network Outages
If the sequencer is down, messages cannot be transmitted from L1 to L2 and **no L2 transactions are executed**. Instead, messages are enqueued in the `CanonicalTransactionChain` on L1 and only processed in the order they arrived later when the sequencer comes back up. As long as the message from the validator on L1 is already enqueued in the `CTC`, the flag on the sequencer uptime feed on L2 will be guaranteed to be flipped prior to any subsequent transactions. The transaction that flips the flag on the uptime feed will be executed before transactions that were enqueued after it. This is further explained in the diagrams below.
When the Sequencer is down, all L2 transactions sent from the L1 network wait in the pending queue.
1. **Transaction 3** contains Chainlink’s transaction to set the status of the sequencer as being down on L2.
2. **Transaction 4** is a transaction made by a consumer that is dependent on the sequencer status.
After the sequencer comes back up, it moves all transactions in the pending queue to the processed queue.
1. Transactions are processed in the order they arrived so **Transaction 3** is processed before **Transaction 4**.
2. Because **Transaction 3** happens before **Transaction 4**, **Transaction 4** will read the status of the Sequencer as being down and responds accordingly.
## Example Consumer Contract
This example code works on any network that supports Solidity. Create the consumer contract for sequencer uptime feeds similarly to the contracts that you use for other [Chainlink Data Feeds](/data-feeds/using-data-feeds). Configure the constructor using the following variables:
- Configure the `sequencerUptimeFeed` object with the [sequencer uptime feed proxy address](#supported-networks) for your L2 network.
- Configure the `dataFeed` object with one of the [Data Feed proxy addresses](/data-feeds/price-feeds/addresses) that are available for your network.
The `sequencerUptimeFeed` object returns the following values:
- `answer`: A variable with a value of either `0` or `1`
- 0: The sequencer is up
- 1: The sequencer is down
- `startedAt`: This timestamp indicates when the sequencer feed changed status. When the sequencer comes back up after an outage, wait for the `GRACE_PERIOD_TIME` to pass before accepting answers from the data feed. Subtract `startedAt` from `block.timestamp` and revert the request if the result is less than the `GRACE_PERIOD_TIME`.
- The `startedAt` variable returns `0` only on Arbitrum when the Sequencer Uptime contract is not yet initialized. For L2 chains other than Arbitrum, `startedAt` is set to `block.timestamp` on construction and `startedAt` is never `0`. After the feed begins rounds, the `startedAt` timestamp will always indicate when the sequencer feed last changed status.
If the sequencer is up and the `GRACE_PERIOD_TIME` has passed, the function retrieves the latest answer from the data feed using the `dataFeed` object.
---
# MVR Feeds API Reference
Source: https://docs.chain.link/data-feeds/mvr-feeds/api-reference
When you use MVR Feeds, you typically retrieve the data through the `IBundleAggregatorProxy` interface and the proxy contract address. This interface combines functions from both `IBundleBaseAggregator` and `ICommonAggregator`. If the aggregator behind the proxy changes, your consumer contract remains compatible as long as you interact through the proxy interface.
To see detailed examples of how to integrate these functions, refer to the [Using MVR Feeds on EVM Chains (Solidity)](/data-feeds/mvr-feeds/guides/evm-solidity) guide.
## IBundleAggregatorProxy
`IBundleAggregatorProxy` extends both `IBundleBaseAggregator` and `ICommonAggregator`. In addition, it provides functions to manage or view the underlying aggregator reference.
A minimal example showing how to set the proxy address and call these functions in your consumer contract:
```solidity
import { IBundleAggregatorProxy } from "./interfaces/IBundleAggregatorProxy.sol";
contract MyMVRConsumer {
IBundleAggregatorProxy public proxy;
constructor(address _proxyAddress) {
proxy = IBundleAggregatorProxy(_proxyAddress);
}
function readLatestData() external view returns (bytes memory, uint256) {
// Retrieve the latest bundle as raw bytes
bytes memory bundle = proxy.latestBundle();
// Get the timestamp of the latest update
uint256 timestamp = proxy.latestBundleTimestamp();
return (bundle, timestamp);
}
}
```
### Functions in IBundleAggregatorProxy
| Name | Description |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| [proposedAggregator](#proposedaggregator) | Returns the address of the aggregator that is proposed to replace the current aggregator, but not yet confirmed. |
| [confirmAggregator](#confirmaggregator) | Confirms a new aggregator address. Typically, only the owner or an authorized entity can call this. |
| [aggregator](#aggregator) | Returns the address of the currently confirmed aggregator behind the proxy. |
| [latestBundle](#latestbundle) | Returns the most recent data bundle as a bytes array. |
| [bundleDecimals](#bundledecimals) | Returns an array of decimals that match each numeric field in the bundle. Non-numeric fields typically have 0. |
| [latestBundleTimestamp](#latestbundletimestamp) | Returns the timestamp of the most recent data bundle. |
| [description](#description) | A short description of the underlying aggregator or feed. |
| [version](#version) | Returns a version number for the aggregator. |
#### proposedAggregator
```solidity
function proposedAggregator() external view returns (address);
```
Returns the address of the aggregator that is proposed to replace the current aggregator. This aggregator is not active until it is confirmed.
#### confirmAggregator
```solidity
function confirmAggregator(address aggregatorAddress) external;
```
Confirms a new aggregator contract address. Only authorized addresses can typically call this function.
- Parameters:
- aggregatorAddress: The address of the new aggregator to confirm.
#### aggregator
```solidity
function aggregator() external view returns (address);
```
Returns the address of the currently confirmed aggregator behind the proxy.
#### latestBundle
```solidity
function latestBundle() external view returns (bytes memory bundle);
```
Returns the most recent data bundle as a bytes array. You can decode this bundle according to the known structure for your specific MVR feed.
#### bundleDecimals
```solidity
function bundleDecimals() external view returns (uint8[] memory);
```
Returns an array of decimals for the numeric fields of the data bundle. Fields of other types, such as bool, typically have a corresponding decimal value of 0.
#### latestBundleTimestamp
```solidity
function latestBundleTimestamp() external view returns (uint256);
```
Returns the timestamp (block time) of the most recent update to the data bundle.
#### description
```solidity
function description() external view returns (string memory);
```
Returns a short description of the underlying aggregator or feed.
#### version
```solidity
function version() external view returns (uint256);
```
Returns the version number of the aggregator behind the proxy.
***
## IBundleBaseAggregator
`IBundleBaseAggregator` defines the core functions for reading multiple-variable data from an onchain aggregator. These functions are inherited by `IBundleAggregatorProxy`.
### Functions in IBundleBaseAggregator
| Name | Description |
| --------------------- | ------------------------------------------------------------------ |
| latestBundle | Returns the most recent data bundle as a bytes array. |
| bundleDecimals | Returns the array of decimals corresponding to each numeric field. |
| latestBundleTimestamp | Returns the timestamp of the most recent data bundle. |
#### latestBundle
```solidity
function latestBundle() external view returns (bytes memory bundle);
```
Returns the latest encoded bundle data as a bytes array.
#### bundleDecimals
```solidity
function bundleDecimals() external view returns (uint8[] memory);
```
Returns an array of decimals where each entry corresponds to a numeric field in the bundle. Non-numeric fields might have a value of 0.
#### latestBundleTimestamp
```solidity
function latestBundleTimestamp() external view returns (uint256);
```
Returns the block timestamp of the most recent data bundle.
***
## ICommonAggregator
`ICommonAggregator` provides common metadata functions for aggregator contracts, such as a human-readable description and a version number.
### Functions in ICommonAggregator
| Name | Description |
| ----------- | -------------------------------------------------------- |
| description | A string describing the underlying aggregator. |
| version | Returns a numeric version identifier for the aggregator. |
#### description
```solidity
function description() external view returns (string memory);
```
Returns a description string for the feed or aggregator.
#### version
```solidity
function version() external view returns (uint256);
```
Returns a numeric value representing the aggregator version.
---
# Using MVR Feeds with ethers.js (JS)
Source: https://docs.chain.link/data-feeds/mvr-feeds/guides/ethersjs
This guide explains how to use [Multiple-Variable Response (MVR) feeds](/data-feeds/mvr-feeds) data in your JavaScript applications using the [ethers.js v5 library](https://docs.ethers.org/v5/).
MVR feeds store multiple data points in a single byte array onchain. To consume this data in your JavaScript application:
1. **Obtain the proxy address and data structure**:
- Find the `BundleAggregatorProxy` address for the specific MVR feed you want to read on the [SmartData Addresses](/data-feeds/smartdata/addresses?page=1#networks) page
- Expand the "MVR Bundle Info" section to see the exact data structure, field types, and decimals
- Note these details as you'll need to match this structure exactly in your code
2. **Set up ethers.js**: Create a provider and contract instance to interact with the feed.
3. **Check data staleness**: Compare the feed's latest timestamp against current time to verify it hasn't exceeded your maximum acceptable staleness threshold.
4. **Fetch and decode the data**: Retrieve the feed's latest bundle and decode the bytes array.
5. **Apply decimals**: Scale numeric values to their true decimal representation for accurate calculations and display.
6. **Use in your application**: Process or display the decoded values.
## Prerequisites
- [Node.js](https://nodejs.org/en/download/) environment (Node.js >=12.x recommended)
- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [yarn](https://yarnpkg.com/getting-started) installed
- [ethers.js](https://docs.ethers.org/v5/) library v5.x installed:
```shell
npm install ethers@^5.0.0
```
or
```shell
yarn add ethers@^5.0.0
```
**Note:** For [ethers.js v6](https://docs.ethers.org/v6/), some API calls differ significantly.
- An RPC URL for the network where the MVR feed is deployed. You can sign up for a personal endpoint from [Alchemy](https://www.alchemy.com/) or [Infura](https://infura.io/).
- Set up environment variables:
```shell
npm install dotenv
```
or
```shell
yarn add dotenv
```
## Step-by-Step Implementation
### 1. Define the BundleAggregatorProxy Interface
First, define the minimal ABI or interface for the `BundleAggregatorProxy` contract:
```javascript
const bundleAggregatorProxyABI = [
"function latestBundle() external view returns (bytes)",
"function bundleDecimals() external view returns (uint8[])",
"function latestBundleTimestamp() external view returns (uint256)",
]
```
### 2. Set Up the Provider and Contract Instance
Connect to a blockchain provider and create the contract instance, using environment variables for sensitive information:
```javascript
// Load environment variables (in Node.js)
require("dotenv").config()
const { ethers } = require("ethers")
// Get RPC URL from environment variables
const rpcUrl = process.env.RPC_URL
if (!rpcUrl) {
throw new Error("RPC_URL not found in environment variables")
}
// Connect to a provider securely
const provider = new ethers.providers.JsonRpcProvider(rpcUrl)
// MVR Feed proxy address - replace with the actual address for your feed
// This can also be stored in environment variables for production
const proxyAddress = process.env.MVR_FEED_ADDRESS || "0x..."
// Create contract instance
const mvrFeed = new ethers.Contract(proxyAddress, bundleAggregatorProxyABI, provider)
```
Create a `.env` file in your project root (and add it to `.gitignore`):
```
# .env
RPC_URL=
MVR_FEED_ADDRESS=
```
### 3. Validate Data Staleness
Before using the data, check its timestamp to ensure it's not stale:
```javascript
/**
* Checks if the feed data is fresh enough to use
* @returns {Promise} True if data is not stale
* @throws {Error} If data is stale
*/
async function checkDataStaleness() {
// Get the latest timestamp
const lastUpdateTime = await mvrFeed.latestBundleTimestamp()
const timestamp = lastUpdateTime.toNumber()
// Current time in seconds
const now = Math.floor(Date.now() / 1000)
// Define staleness threshold
const stalenessThreshold = 86400 // 24 hours in seconds
if (now - timestamp > stalenessThreshold) {
throw new Error(`Data is stale. Last update was ${now - timestamp} seconds ago.`)
}
return true
}
```
**Important**: Don't use arbitrary values for staleness thresholds. The appropriate threshold should be determined by:
1. Find the feed's **heartbeat interval** on the [SmartData Addresses](/data-feeds/smartdata/addresses?page=1#networks) page (click "Show more details")
2. Set a threshold that aligns with this interval, usually the heartbeat plus a small buffer
3. Consider your specific use case requirements (some applications may need very recent data)
### 4. Fetch and Decode the Bundle Data
MVR feeds encode multiple data points in a single bytes array. You need to know the structure to properly decode it:
```javascript
// Define the data structure based on the specific feed's format
// This MUST match the format defined in the feed documentation
const dataStructure = [
"uint256", // netAssetValue
"uint256", // assetsUnderManagement
"uint256", // outstandingShares
"uint256", // netIncomeExpenses
"bool", // openToNewInvestors
]
// Field names for easier access
const fieldNames = [
"netAssetValue",
"assetsUnderManagement",
"outstandingShares",
"netIncomeExpenses",
"openToNewInvestors",
]
/**
* Gets the raw decoded data from the feed
* @returns {Promise