> For the complete documentation index, see [llms.txt](https://docs.kpk.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kpk.io/funds/infrastructure/onchain-accounting/price-feeds/eth-plus.md).

# ETH+

Custom price feed for **Reserve Protocol's ETH+ RToken**. ETH+ is derived from the value of the collateral basket that backs each token, using Reserve Protocol's on-chain basket accounting.

**Source:** [`ETHPlus_CustomPriceFeed.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/prices/protocols/ETHPlus_CustomPriceFeed.sol)

{% hint style="warning" %}
**This feed is built and tested but deployed on no chain, and ETH+ is not priced by it.** ETH+ is registered against a **direct Chainlink "Calculated ETH+/USD" feed** with an `86400` heartbeat, so it follows the ordinary [direct-feed](/funds/infrastructure/onchain-accounting/price-feeds.md#supported-feed-types) path — not the basket logic described below.

The two cannot co-exist: this Custom feed reports 8 decimals while the Chainlink feed reports 18, and an asset's feeds must agree on decimals (`PriceFeedDecimalsMismatch`). The contract is kept deliberately, as the record of why a custom feed was not used for ETH+. Everything below documents the contract's behaviour, not how ETH+ is valued today.
{% endhint %}

***

## Approach

`(basketsNeeded / totalSupply) × basket low price` — the conservative (low-bound) USD value of the basket backing one ETH+.

<figure><img src="/files/i6z44GZuPA7XHIoegCXF" alt="Left-to-right flowchart of the ETH+ price feed: rate and Chainlink inputs combine into a USD price, with a staleness gate."><figcaption><p>ETH+ price feed — how its oracle inputs compose into a USD price, with the staleness gate.</p></figcaption></figure>

***

## Price calculation

```
basketUnitsPerToken = basketsNeeded × 1e18 / totalSupply
usdPerToken18       = basketUnitsPerToken × basketLow / 1e18
price               = usdPerToken18 / 1e10
```

| Input           | Source                               | Description                                        |
| --------------- | ------------------------------------ | -------------------------------------------------- |
| `basketsNeeded` | `IRToken(ETHPLUS).basketsNeeded()`   | Basket units the RToken must be backed by (18 dec) |
| `totalSupply`   | `IRToken(ETHPLUS).totalSupply()`     | ETH+ total supply                                  |
| `basketLow`     | `IBasketHandler.price()` (low bound) | Conservative USD value of one basket unit (18 dec) |

Computation is carried at 18-decimal precision and divided by `1e10` to yield the **8-decimal** USD price. The low price bound from `basketHandler.price()` is used deliberately for conservative NAV valuation. If `basketsNeeded`, `totalSupply`, the per-token basket units, or the basket price is zero, the feed returns stale.

***

## Constructor

```solidity
constructor(address ethPlus_, uint256 reserveProtocolHeartbeat_)
```

| Parameter                   | Description                                                                      |
| --------------------------- | -------------------------------------------------------------------------------- |
| `ethPlus_`                  | The ETH+ RToken address                                                          |
| `reserveProtocolHeartbeat_` | Max age (seconds) of the worst-case collateral refresh before the price is stale |

***

## Staleness

`getLatestPrice()` enforces two internal checks; no external Chainlink heartbeat is read directly:

1. **Basket status** — `IBasketHandler.status()` must be `SOUND`. An `IFFY` or `DISABLED` basket returns stale, even though the basket may still report a non-zero price.
2. **Refresh age** — `updatedAt = min(IAsset.lastSave())` across all components of the **active** reference basket, read via `IBasketHandler.quote(1e18, RoundingMode.CEIL)` — the same basket `price()`/`basketsNeeded()` are computed over, rather than the governance-configured prime basket. `lastSave()` is set when a keeper calls `refresh()` on a collateral plugin (which reads the underlying Chainlink oracle). If `block.timestamp − updatedAt ≥ reserveProtocolHeartbeat`, the price is stale. Taking the minimum gives the worst-case freshness of the whole basket. Using the active set matters after a default swaps in backup collateral: the active backups differ from the prime config, and checking the prime set would skip the backups' oracle freshness (stale accepted as fresh). The deployed `BasketHandler` has no `basketTokens()` getter, so `quote` is used to read the live basket.

`latestRoundData()` exposes `updatedAt = min(lastSave())` for observability only; staleness is handled entirely in `getLatestPrice()`.

***

## Chains

Deployed on **Ethereum mainnet**.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kpk.io/funds/infrastructure/onchain-accounting/price-feeds/eth-plus.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
